/bootloader/stage2.asm
http://github.com/Pokelover980/Poke-and-Necro-Hobby-OS · Assembly · 306 lines · 257 code · 33 blank · 16 comment · 0 complexity · 31317b6405af56d1720196b9bc3ec892 MD5 · raw file
- ; -------------------------------------------------------------
- ; Pokelover and Necro's Hobby OS Bootloader - Stage 2
- ;
- ; Reuses some code from stage1 to help with disk loading. This
- ; stage simply loads the kernel, sets up 32 bit protected mode,
- ; and then jumps to the kernel for execution. Fun!
- ; -------------------------------------------------------------
- [BITS 16]
- Start: ; A lot of code here is reused from stage1 so we can
- ; load the kernel before entering protected mode
- mov ax, 0x0200 ; This is our entry point
- add ax, 544 ; 544 paragraph disk buffer
- cli ; No interrupts while changing stack!
- mov ss, ax
- mov sp, 4096
- sti ; Now we can have interrupts back :D
- mov ax, 0x0200 ; Set data segment to where we're loaded
- mov ds, ax
- mov byte [BootDev], dl ; Save boot device number
- mov eax, 0 ; May be needed by older BIOSes
- mov si, S2Message
- call PrintString
-
- FloppyOk: ; Ready to read first block of data
- mov ax, 19 ; Root dir starts at logical sector 19
- call l2hts
- mov si, Buffer ; Set ES:BX to our buffer
- mov bx, ds
- mov es, bx
- mov bx, si
- mov ah, 2 ; int 0x13 params: Read floppy sectors
- mov al, 14 ; 14 floppy sectors to read
- pusha ; Push registers to stack to prepare for loop
-
- ReadRootDir:
- popa ; Just in case int 0x13 modified anything
- pusha
- stc ; Not all BIOSes set properly on error
- int 0x13 ; Read sectors
- jnc SearchDir ; If read was OK, skip ahead
- call ResetFloppy ; If not, reset floppy and try again
- jnc ReadRootDir ; Did the floppy reset fine?
- jmp Reboot ; If it didn't, double error, restart
-
- SearchDir:
- popa
- mov ax, ds ; Root dir is now in [Buffer]
- mov es, ax ; Give this info to DI
- mov di, Buffer
- mov cx, word [224] ; Search all entries
- mov ax, 0 ; Start at 0
-
- NextRootEntry:
- xchg cx, dx ; We use cx in the inner loop
- mov si, BootFileName ; Start searching for stage2 filename
- mov cx, 11
- rep cmpsb
- je FoundFileToLoad ; Pointer DI will be at offset 11
- add ax, 32 ; Bump by one entry (32 bytes per entry)
- mov di, Buffer ; Point to next entry
- add di, ax
- xchg dx, cx ; Get cx back
- loop NextRootEntry
- mov si, NoFile
- call PrintString
- jmp Reboot
-
- FoundFileToLoad: ; Let's load the FAT into RAM
- mov ax, word[es:di+0x0F] ; Offset 11+15=26 contains our first cluster
- mov word[Cluster], ax
- mov ax, 1 ; Sector 1 = first sector of FAT
- call l2hts
- mov di, Buffer
- mov bx, di ; ES:BX points to our buffer
- mov ah, 2 ; int 0x13 params: read FAT sectors
- mov al, 9 ; All 9 sectors of first FAT
- pusha ; Push registers to stack to prepare for loop
-
- ReadFat:
- popa ; In case int 0x13 modified anything
- pusha
- stc
- int 0x13 ; Read sectors using BIOS
- jnc ReadFatOkay ; If read was okay, skip ahead
- call ResetFloppy ; Otherwise, reset floppy and try again
- jnc ReadFat ; Floppy reset OK?
- mov si, DiskError
- call PrintString ; If not, print an error and reboot
- jmp Reboot
- ReadFatOkay:
- popa
- mov ax, 0x7E00 ; Segment where we're loading kernel
- mov es, ax
- mov bx, 0
- mov ah, 2 ; int 0x13 read params
- mov al, 1
- push ax ; Save in case it gets lost
-
- ; We're gonna load the FAT from the disk now
- LoadFileSector:
- mov ax, word [Cluster] ; Convert sector to logical
- add ax, 31
- call l2hts
- mov ax, 0x7E00 ; Set buffer past what we've read
- mov es, ax
- mov bx, word [Pointer]
- pop ax ; Save in case it gets lost
- push ax
- stc
- int 0x13
- jnc CalculateNextCluster ; If there's no error
- call ResetFloppy ; If there is, reset floppy
- jmp LoadFileSector
-
- CalculateNextCluster:
- mov ax, [Cluster]
- mov dx, 0
- mov bx, 3
- mul bx
- mov bx, 2
- div bx ; DX = [Cluster] mod 2
- mov si, Buffer
- add si, ax ; AX = word in FAT for 12 bit entry
- mov ax, word [ds:si]
- or dx, dx ; If DX = 0 [Cluster] is even; if 1, [Cluster] is odd
- jz even ; If [Cluster] is even, drop last 4 bits of word with
- ; next clusters; if odd, drop first 4 bits
-
- odd:
- shr ax, 4
- jmp short NextClusterCont
-
- even:
- and ax, 0x0FFF ; Mask out final four bits
-
- NextClusterCont:
- mov word [Cluster], ax ; Store cluster
- cmp ax, 0x0FF8 ; 0xFF8 = end of file in FAT12
- jae end
- add word [Pointer], 512 ; Increase buffer point 1 sector
- jmp LoadFileSector
-
- end: ; Hooray, we've reached the end and got the file to load!
- pop ax ; Clean up stack, as we pushed AX earlier
- mov dl, byte[BootDev] ; Provide Kernel with boot device info
-
- mov si, BeforePMode
- call PrintString
-
- ; BEFORE WE JUMP TO KERNEL BELOW WE WANT TO SET UP PROTECTED MODE!
-
- cli ; We can't use these anymore!
- ; We have to enable the A20 line first, so that's what we'll do. It's boring and stupid.
- call wkc ; Wait for keyboard buffer to clear
- mov al, 0xD1 ; We want to write to output port
- out 0x64, al
- call wkc ; Wait for keyboard buffer again
- mov al, 0xDF ; Set A20 gate
- out 0x60, al ; Send value to data reg
- call wkc ; Again...
- mov cx, 0x10
- kbdwait:
- xor ax, ax ; Waste time
- out 0xE0, ax ; Waste more time
- loop kbdwait ; Loop and waste even more time
-
- ; Was A20 enabled?
- mov al, 0xD0
- out 0x64, al ; Tell the controller we want to read output port
- call wkf ; Wait for the data to get in it
- in al, 0x60 ; Get it
- test al, 2 ; Is A20 on?
- jnz A20On ; Not zero - we're good
- ; Otherwise, we failed
- mov si, A20Failure
- call PrintString
- hlt
- jmp $
-
- A20On:
- xor ax, ax
- mov ds, ax
- lgdt [gdt_desc]
- mov eax, cr0 ; Clear this bit, signaling the processor to enter protected mode
- or eax, 1
- mov cr0, eax
- jmp 0x08:ClearPipe
- [BITS 32]
- ClearPipe: ; We need 32 bit now
- mov ax, 0x10 ; Save data segment identifier
- mov ds, ax ; Move valid data segment into data segment register
- mov ss, ax ; Move valid data segment into stack segment register
- mov esp, 0x090000 ; Move stack pointer to 0x090000
- mov byte [ds:0x0B8000], 'P'
- mov byte [ds:0x0B8000], 0x1B ; Here we're just testing to make sure we entered
- ; protected mode without issues.
- jmp 0x08:0x07E00 ; Jump to entry point of stage2!
- jmp $ ; Continually jump here just in case kernel decides to return
-
- ; Subroutines below
- [BITS 16]
- Reboot:
- mov ax, 0
- int 0x16 ; Wait for keystroke
- mov ax, 0
- int 0x19 ; Reboot
-
- PrintString:
- pusha
- mov ax, 0x0E
-
- .repeat:
- lodsb
- cmp al, 0
- je .done ; If char is 0, we've reached the end of the string
- int 0x10 ; Otherwise, we print it
- jmp short .repeat
-
- .done:
- popa
- ret
-
- ResetFloppy: ; Carry set on error
- push ax
- push dx
- mov ax, 0
- mov dl, byte [BootDev]
- stc
- int 0x13
- pop dx
- pop ax
- ret
-
- l2hts: ; Calculate head, track and sector for int 0x13
- push bx
- push ax
- mov bx, ax
- mov dx, 0
- div word [18]
- add dl, 0x01
- mov cl, dl
- mov ax, bx
- mov dx, 0
- div word [18]
- mov dx, 0
- div word [2]
- mov dh, dl
- mov ch, al
- pop ax
- pop bx
- mov dl, byte [BootDev]
- ret
-
- wkc:
- xor al, al
- in al, 0x64
- test al, 2
- jnz wkc
- ret
-
- wkf:
- xor cx, cx
- in al, 0x64
- test al, 1
- jz wkf
- ret
-
- BootFileName db "KERNEL BIN"
- DiskError db "FATAL: Disk error! Press any key to reboot...", 0
- NoFile db "FATAL: KERNEL.BIN not found!", 0
- A20Failure db "FATAL: A20 failed to enable!", 0
- BeforePMode db "About to enter protected mode...", 0
- S2Message db "Stage2 entered", 0
- BootDev db 0
- Cluster dw 0
- Pointer dw 0
- gdt:
- gdt_null:
- dd 0
- dd 0
- gdt_code:
- dw 0x0FFFF
- dw 0
- db 0
- db 10011010b
- db 11001111b
- db 0
- gdt_data:
- dw 0x0FFFF
- dw 0
- db 0
- db 10010010b
- db 11001111b
- db 0
- gdt_end:
- gdt_desc:
- dw gdt_end - gdt - 1
- dd gdt
-
- Buffer: