287 lines
No EOL
8 KiB
NASM
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 |