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): ; - BYTE: Drive number ; - WORD: Number of blocks to transfer ; - DWORD: Transfer buffer ; - DWORD: Partial LBA of first block to transfer ; - 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: LBA ; SP + 12: Transfer buffer ; SP + 16: Block ; SP + 18: Drive number read_sectors_extended: PUSH DX ; save DX PUSH SI ; save SI PUSH BP ; save BP MOV BP, SP ; estabilish stack frame MOV AX, [BP + 10] ; load first part of LBA address MOV [packet_lba], AX ; write fist part of LBA to packet MOV AX, [BP + 8] ; load second part of LBA to packet MOV [packet_lba + 2], AX ; write second part of LBA to packet MOV AX, [BP + 14] ; load first part of transfer buffer MOV [packet_buffer], AX ; write first part of transfer buffer MOV AX, [BP + 12] ; load second part of transfer buffer MOV [packet_buffer + 2], AX ; write second part of transfer buffer MOV AX, [BP + 16] ; load number of blocks MOV [packet_blocks], AX ; write number of blocks MOV DX, 0 ; zero-out DX MOV DL, [BP + 18] ; load drive number MOV SI, read_sectors_packet ; load address of packet MOV AX, 4200h ; set AH to 42h, 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 ; 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, 0x7C00 ; set up stack pointer (0x6c00-0x7BFF) ; 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 ; ^^^ MOV FS, AX ; ^^^ MOV GS, AX ; ^^^ PUSH DX ; save drive number (DL) ; Load drive parameters MOV AX, 4800h ; load AH=48h, AL=0h MOV SI, disk_info_packet ; load address of Disk_Info_Packet INT 13H ; INT 14h AH=48h: get drive parameters ; Print message MOV AX, msg ; load address of msg PUSH AX ; push parameter 'address of string' MOV AX, 12 ; load size of msg PUSH AX ; push parameter 'size of string' CALL print ; call function 'print' SUB SP, 4 ; clear previous call parameters 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, 6 ; load size of 'a20_msg' PUSH AX ; push parameter 'size of string' CALL print ; print 'a20_msg' JMP endless_loop ; loop endlessly after_a20: endless_loop: JMP endless_loop ; loop endlessly section .data disk_info_packet: info_size dw 0x1A info_flags dw 0x0 info_cyls dd 0x0 info_heads dd 0x0 info_sectors dd 0x0 info_total_sec dq 0x0 info_bps dw 0x0 read_sectors_packet: packet_size db 10h packet_reserved db 0h packet_blocks dw 0h packet_buffer dd 0h packet_lba dq 0h build_settings: build_settings_magic_0 dd 0x49494949 build_settings_bytes dw 0x0 build_settings_magic_1 dd 0x49494949 msg db "OS3_BOOT_OK!" a20_msg db "NO_A20"