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