os3/mbr/mbr.asm
John 17a18e4f91
[Stage1] Start IDT implementation
[Stage1] Screen.h improvements
2023-01-18 10:59:09 +01:00

287 lines
No EOL
8 KiB
NASM

USE16
NULL equ 0
section .text
global _main
_main:
JMP entry
; FAT32 Header.
global fat_header
fat_header:
dw 0x4949
times 85 db 0
; function READ_SECTORS_EXTENDED: Read sectors from disk
; Uses INT 13h AH=42h from: http://www.ctyme.com/intr/rb-0708.htm
; Stack Parameters (in order of PUSH):
; - WORD: Drive number (interpret as BYTE)
; - WORD: Address of packet
; - WORD: RETURN ADDRESS
; Returns:
; AX: 0 = Ok, Other = Error
;
;
; SP + 0: Old BP
; SP + 2: Old SI
; SP + 4: Old DX
; SP + 6: return address
; SP + 8: Address of packet
; SP + 10: Drive number
read_sectors_extended:
PUSH DX ; save DX
PUSH SI ; save SI
PUSH BP ; save BP
MOV BP, SP ; estabilish stack frame
MOV DX, 0 ; zero-out DX
MOV DX, [BP + 10] ; load drive number
MOV DH, 0 ; Zero-out high byte of drive number
MOV SI, [BP + 8] ; load address of packet
MOV AH, 42h ; set AH to 42h
MOV AL, 0h ; set AL to 0h
INT 13h ; call interrupt 13H, AH=42h
JNC read_sectors_extended_exit ; exit if no error
read_sectors_extended_error: ; save error if it happened
MOV AL, AH ; move error to Al
MOV AH, 0 ; zero-out AH
read_sectors_extended_exit: ; leave function
POP BP ; restore BP
POP SI ; restore SI
POP DX ; restore DX
RET ; return
; function PRINT: teletype to display
; uses INT 10h AH=0Eh from: http://www.ctyme.com/intr/rb-0106.htm
; parameters (in order of PUSH):
; - WORD: address of string
; - WORD: size of string
; returns:
; AX: 0
print:
PUSH BX ; save BX
PUSH CX ; save CX
PUSH BP ; save BP (can change due to scrolling)
PUSH SI ; save SI
MOV BP, SP ; set up stack frame
MOV SI, [BP + 12] ; load parameter 'address'
MOV CX, [BP + 10] ; load parameter 'size'
print_loop:
MOV AL, [SI] ; load character of string
MOV AH, 0Eh ; load INT 10h parameter
MOV BX, 0 ; zero-out BX (extra parameters)
INT 10h ; call INT 10h AH=0Eh
INC SI ; move to next character
LOOP print_loop ; loop until all characters are printed
POP SI ; restore SI
POP BP ; restore BP
POP CX ; restore CX
POP BX ; restore BX
RET ; return
; function check_a20: get status of address line 20 (21st bit of any address)
; Returns:
; AX=0 if A20 off, AX=1 if A20 on
check_a20:
PUSHF ; save FLAGS
PUSH DS ; save DS
PUSH ES ; save ES
PUSH DI ; save DI
PUSH SI ; save SI
CLI ; disable interrupts
XOR AX, AX ; AX = 0
MOV ES, AX ; ES = 0
NOT AX ; AX = 0xFFFF
MOV DS, AX ; DS = 0xFFFF
MOV DI, 0x0500 ; DI = 0x0500
MOV SI, 0x0510 ; SI = 0x0510
MOV AL, [ES:DI] ; AL = Memory[0x0500]
PUSH AX ; save original value at Memory[0x500]
MOV AL, [DS:SI] ; AL = Memory[0xFFFF:0x0510]
PUSH AX ; save original value at Memory[0xFFFF:0x0510]
MOV BYTE [ES:DI], 0x00 ; Memory[0x0500] = 0x00
MOV BYTE [DS:SI], 0xFF ; Memory[0xFFFF:0x0510] = 0xFF
CMP BYTE [ES:DI], 0xFF ; Compare Memory[0x0500] to 0xFF
; if the bytes match, the memory is wrapping around
; therefore, the A20 line is disabled
POP AX ; Restore memory at [0xFFFF:0x0510]
MOV [DS:SI], AL ; ^^^^
POP AX ; Restore memory at [0x0500]
MOV [ES:DI], AL ; ^^^^
MOV AX, 0 ; AX (return value) = 0 (A20 disabled)
JE check_a20_exit ; If the last CMP was equal (A20 disabled), exit
MOV AX, 1 ; AX (return value) = 1 (A20 enabled)
check_a20_exit:
POP SI ; restore SI
POP DI ; restore DI
POP ES ; restore ES
POP DS ; restore DS
POPF ; restore FLAGS
RET ; return
enable_a20:
CALL a20_wait ; wait for keyboard controller
MOV AL, 0xAD ; 0xAD = disable keyboard
OUT 0x64, AL ; send command 0xAD to keyboard controller
CALL a20_wait ; wait for keyboard controller
MOV AL, 0xD0 ; 0xD0 = read from input
OUT 0x64, AL ; send command 0xD0
CALL a20_wait2 ; wait for keyboard input
IN AL, 0x60 ; read input from keyboard controller
PUSH AX ; save input
CALL a20_wait ; wait for keybaord controller
MOV AL, 0xD1 ; 0xD1 = write to output
OUT 0x64, AL ; send command 0xD1
CALL a20_wait ; wait for keyboard controller
POP AX ; restore input value
OR AL, 2 ; enable bit 1
OUT 0x60, AL ; send output to keyboard
CALL a20_wait ; wait for keyboard controller
MOV AL, 0xAE ; 0xAE = enable keyboard
OUT 0x64, AL ; send command 0xAE
RET
a20_wait:
IN AL, 0x64 ; read status of keyboard controller
TEST AL, 2 ; read bit 1 of status
JNZ a20_wait ; if not zero, keyboard controller isn't ready. Retry
RET ; return
a20_wait2:
IN AL, 0x64 ; read status of keyboard controller
TEST AL, 1 ; read bit 0 of status
JZ a20_wait2 ; if zero, input isn't ready
RET ; return
entry:
; Set up stack and segments
MOV AX, 0 ; set up stack segment
MOV SS, AX ; ^^^
MOV SP, 0x7bFF ; set up stack pointer (0x6c00-0x7BFE)
; Sp HAS TO be set in the instruction after settings SS
; as setting SS disables interrupts for the next instruction
; Not doing this may cause an interrupt to use an old SP and new SS
MOV DS, AX ; set up segment registers
MOV ES, AX ; ^^^
PUSH DX ; save drive number (DL)
; Load kernel image
; Comment out check, presume that the packet info is populated
;MOV BX, packet_blocks ; Load address of packet_blocks
;MOV AX, [BX] ; Load value of packet_blocks
;CMP AX, 0 ; See if amount of packets to read is 0
;JE endless_loop ; if no packets are to be loaded, the kernel is not present. Loop endlessly
; Parameter drive number is already in the stack
MOV AX, read_sectors_packet ; load 'packet address'
PUSH AX ; push parameter 'packet address'
CALL read_sectors_extended ; try to read these damned sectors
ADD SP, 4 ; clear previous call parameters
; Enable A20
CALL check_a20 ; call check_a20. Result in Ax
CMP AX, 0 ; compare AX to 0 (0 = A20 disabled)
JNE after_a20 ; if AX != 0, a20 is enabled, skip enabling it
CALL enable_a20 ; enable A20 line via keyboard controller
CALL check_a20 ; check status of A20 line (again)
CMP AX, 0 ; compare AX to 0 (0 = A20 disabled)
JNE after_a20 ; if AX != 0, a20 is enabled
MOV AX, a20_msg ; load address of 'a20_msg'
PUSH AX ; push parameter 'address of string'
MOV AX, 8 ; load size of 'a20_msg'
PUSH AX ; push parameter 'size of string'
CALL print ; print 'a20_msg'
JMP endless_loop ; loop endlessly
after_a20:
; set video mode
STI ; re-enable interrupts
MOV AH, 00h ; int 10h AH=00h = set video mode command
MOV AL, 03h ; desired video mode (80x25 characters, 8 colors)
INT 10h ; call interrupt
after_video:
; load GDT
CLI ; disable interrupts
LGDT [gdt_ptr] ; load GDT
; Enable protected mode
MOV EAX, CR0 ; load Control Register 0
OR AL, 1 ; set Protection Enable bit
MOV CR0, EAX ; save Control Register 0
JMP 08h:init_32 ; long jump to 32 bit mode
init_32:
USE32
MOV AX, 10h ; load 32 bit segment registers
MOV AX, 10h
MOV DS, AX ; ^^^^
MOV SS, AX ; ^^^^
MOV FS, AX ; ^^^^
MOV GS, AX ; ^^^^
MOV SS, AX ; ^^^^
MOV EAX, 0x7bFF ; reset stack
MOV ESP, EAX ; ^^^^
JMP 0x10000 ; jump to stage1 bootloader
USE16
endless_loop:
JMP endless_loop ; loop endlessly
section .data
packet_sig_0 dw 0x4954
read_sectors_packet:
packet_size db 10h
packet_reserved db 0h
packet_blocks dw 0h
packet_buffer_off dw 0h
packet_buffer_seg dw 0h
packet_lba dq 0h
packet_sig_1 dw 0x4955
a20_msg db "NO_A20", 10, 13
gdt:
null_sec dq 0x0
kernel_code dq 0xcf9a000000ffff
kernel_data dq 0xcf92000000ffff
user_code dq 0xcffa000000ffff
user_data dq 0xcff2000000ffff
gdt_ptr:
gdt_ptr_limit dw 40
gdt_ptr_base dd gdt