; | |
; FILE: myos.asm | |
; AUTHOR: nkeck72 (board.flatassembler.net) | |
; EDITOR: Trinitek (board.flatassembler.net) | |
; DATE: 15 June 2015 | |
; | |
; COMMENTS FROM EDITOR: | |
; | |
; I made a lot of changes to this file in my effort to get this thing to work in DOSbox. The | |
; original code and final build process did not like DOSbox at all. I found out that the boot | |
; media has to have a filesize of any common floppy disk image. Instead of concatenating the | |
; int21.bin file to the end of myos.bin and booting that, I needed to fill up the trailing | |
; space with 0's to expand the image size to 1474560 bytes, the same size as that of a 1.44 MB | |
; floppy. I modified the build process as follows: | |
; | |
; > fasm myos.asm | |
; > fasm int21.asm | |
; > fasm floppy.asm floppy.ima | |
; > dosbox -c "boot floppy.ima" | |
; | |
; The file contents of floppy.asm is as follows: | |
; | |
; file 'myos.bin' | |
; file 'int21.bin' | |
; times 1474560-($-$$) db 0 | |
; | |
use16 | |
org 7C00h | |
; Setup stack, segment regs | |
mov ax, 9ch | |
mov ss, ax | |
mov sp, 4096d | |
; There's some data at the bottom of the file that is modified/read from. | |
; Since we're loaded at 0000:7C00, we want our data segment to be the same as our code segment. | |
mov ax, cs ;mov ax, 7c0h | |
mov ds, ax | |
; Save the boot drive number | |
mov byte [bootDrive], dl ; The boot drive is not guaranteed to be 0 !! | |
;mov ah, 02h | |
;mov dx, 0000h | |
;int 10h | |
; | |
; Eh, why not just re-set the video mode? | |
mov ax, 0x0003 | |
int 0x10 | |
;---------------------------------------- | |
loadup: | |
; Reset the floppy drive | |
mov ah, 00h | |
mov dl, byte [bootDrive] ;mov dl, 00h | |
int 13h | |
;mov ah, 01h | |
;mov dl, 00h ; Remember! Boot drive isn't guaranteed to be 0 ! | |
;int 13h | |
;cmp al, 00h ; What!? You didn't change it to AH like I said! | |
;jne stop | |
; | |
; Check for a successful reset | |
; | |
; No need to use INT 0x13 function AH=0x01 because AH already | |
; contains the status of the operation (if you want to use that), | |
; and the carry flag is always set if there is an error (AH != 0), | |
; which is what I changed it to check here. | |
mov al, 'A' | |
jc stop | |
;; Load the int 21h code (For later implementation) | |
; Load at 0000:1400 | |
mov ah, 02h ; Read sectors into memory | |
mov al, 01h ; Sectors to read = 0 | |
mov ch, 00h ; low 8 bits of cylinder number = 0 | |
mov dh, 00h ; head number = 0 | |
mov cl, 01h ; high 2 bits of cylinder number = 0 | |
; starting sector number (bits 0-5) = 1 | |
mov dl, byte [bootDrive] ; drive number = byte [bootDrive] | |
mov bx, 1400h ; destination segment = 0x1400 | |
mov es, bx | |
mov bx, 0000h ; destination offset = 0x0000 | |
int 13h | |
;mov ah, 01h | |
;mov dl, 00h | |
;int 13h | |
;cmp al, 00h ; Again, errorcode is in AH, not AL! Read your documentation! | |
;jne stop | |
; | |
; Check for a successful read | |
mov al, 'B' | |
jc stop | |
;; Set up the IVT to recognize my int 21h | |
cli | |
mov ax, 0000h | |
mov es, ax | |
; You could just hardcode the offset to the IVT, like... | |
; mov bx, 0x21*4 | |
; ...instead of making the processor calculate it at runtime. | |
mov al, 21h | |
mov bl, 04h | |
mul bl | |
add ax, 02h | |
mov bx, ax | |
; Program the 0x21 offset | |
mov dx, 1400h | |
mov [es:bx], dx | |
; Program the 0x21 segment | |
;mov al, 21h | |
;mov bl, 04h | |
;mul bl | |
;mov bx, ax | |
sub bx, 2 ; Why recalc the IVT offset? Just offset back 2 bytes. | |
mov dx, 0000h | |
mov [es:bx], dx | |
sti | |
;; Clear the screen | |
;mov ah, 02h | |
;mov dh, 00h | |
;mov dl, 00h | |
;int 10h | |
;mov ah, 07h | |
;mov al, 00h | |
;mov ch, 00h | |
;mov cl, 00h | |
;mov dh, 80d | |
;mov dl, 25d | |
;int 10h | |
;mov cx, 01h | |
; Just re-set the video mode if you want to clear the screen. | |
mov ax, 0x0003 | |
int 0x10 | |
;; Display 'NOS 1.0' message | |
;mov ah, 09h | |
;mov al, 4Eh | |
;int 10h | |
;mov ah, 02h | |
;inc dl | |
;int 10h | |
;mov ah, 09h | |
;mov al, 4Fh | |
;int 10h | |
;mov ah, 02h | |
;inc dl | |
;int 10h | |
;mov ah, 09h | |
;mov ah, 53h | |
;int 10h | |
;mov ah, 02h | |
;inc dl | |
;mov ah, 09h | |
;mov al, 20h | |
;int 10h | |
;mov ah, 02h | |
;inc dl | |
;int 10h | |
;mov ah, 09h | |
;mov al, 31h | |
;int 10h | |
;mov ah, 02h | |
;inc dl | |
;int 10h | |
;mov ah, 09h | |
;mov al, 2Eh | |
;int 10h | |
;mov ah, 02h | |
;inc dl | |
;mov ah, 09h | |
;mov al, 30h | |
;int 10h | |
;mov ah, 02h | |
;mov dl, 00h | |
;add dh, 01h | |
;int 10h | |
; | |
; Uh. Ever heard of AH=0x0E ? Teletype output. It increments the cursor | |
; on each character write, and even accounts for page scrolling and newlines. | |
; | |
; Also, I noticed in that block of code above that you took the time to translate | |
; the ASCII characters for "NOS 1.0" into hexadecimal. Why? Did you not know that you | |
; can deliberately specify ASCII characters like | |
; mov al, 'N' | |
; ?? If that is the case, then why are you trying to write an operating system when | |
; you're not even fully acquainted with the assembler you're using? That's nuts! | |
mov si, hello | |
call printString | |
;; Begin setting up the environment in which the user will type | |
; I'm not going to look at this very closely, but you can probably reimplement this | |
; using AH=0x0E. It'll save you a lot of bytes on the output binary, which is a blessing | |
; since you're trying to fit this thing inside a single 512-byte sector. | |
typer: | |
mov ah, 00h | |
int 16h | |
cmp al, 0Dh | |
je print_enter | |
cmp al, 08h | |
je print_back | |
mov ah, 09h | |
int 10h | |
push ax | |
cmp dl, 80d | |
je mcdn | |
pop ax | |
mov ah, 09h | |
int 10h | |
mov ah, 02h | |
inc dl | |
int 10h | |
jmp typer | |
print_enter: | |
cmp dh, 25d | |
je scr_dn | |
mov ah, 02h | |
add dh, 01h | |
mov dl, 00h | |
int 10h | |
jmp typer | |
print_back: | |
mov ah, 02h | |
dec dl | |
int 10h | |
mov ah, 09h | |
mov al, 20h | |
int 10h | |
jmp typer | |
scr_dn: | |
mov ah, 07h | |
mov al, 01h | |
int 10h | |
ret | |
mcdn: | |
cmp dh, 25d | |
je scr_dn | |
mov ah, 02h | |
add dh, 01h | |
mov dl, 00h | |
int 10h | |
ret | |
; SI = pointer to string, null terminated | |
printString: | |
push ax ; preserve registers | |
push si | |
pushf ; preserve direction flag | |
cld ; LODSB increments SI | |
mov ah, 0x0E ; INT 0x10 function 0x0E (teletype) | |
.nextChar: | |
lodsb ; load byte from SI, increment SI | |
cmp al, 0 ; if byte is 0, return | |
jz .end | |
int 0x10 ; put character | |
jmp .nextChar ; get the next character | |
.end: | |
popf ; restore direction flag | |
pop si ; restore registers | |
pop ax | |
ret | |
stop: | |
; Show error symbol on screen. | |
; AL is set to the character that is to be displayed before the | |
; jump here. This allows you to determine where the problem is | |
; occuring in the code, but it does not communicate the actual | |
; errorcode. | |
mov ah, 0x0E | |
int 0x10 | |
; Halting without disabling interrupts will continue execution | |
; whenever an interrupt is received from hardware. | |
; If you want to stop the processor indefinitely, clear interrupts, | |
; halt, and then, in the event that a non-maskable interrupt (which | |
; can't be disabled in software) is triggered, jump back to the stop | |
; label. The only downside to doing this is that you lose the ability | |
; to use CTRL-ALT-DEL to reboot. | |
.loopForever: | |
cli | |
hlt | |
jmp .loopForever | |
;---------------------------------------- | |
hello db "NOS 1.0", 0 | |
bootDrive db ? | |
times 510-($-$$) db 0 | |
dw 0xAA55 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment