|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Enable_A20:
|
|
; Enables A20 gate using:the BIOS, keyboard controller, or Fast A20
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
pushad ;Preserve registers
|
|
pushfd ;Preserve EFLAGS because we might disable interrupts
|
|
mov ax,0x2402 ;INT 15h AX=2402h: Query A20 status
|
|
int 0x15 ;Check to see if A20 gate is enabled
|
|
jc A20_No_BIOS ;Error? Don't use BIOS to enable gate
|
|
test al,1 ;Test Bit 0 of AL
|
|
je A20_Enabled ;A20 gate already enabled
|
|
mov ax,0x2401 ;INT 15h AX=2401h: Enable A20 gate
|
|
int 0x15 ;Use BIOS to enable A20 gate
|
|
jc A20_No_BIOS ;Error? Don't use BIOS to enable gate
|
|
or ah,ah ;Test AH
|
|
jnz A20_No_BIOS ;Non-zero? A20 gate may not be enabled
|
|
A20_Enabled:
|
|
popfd ;Restore EFLAGS
|
|
popad ;Restore registers
|
|
ret ;Return
|
|
A20_No_BIOS:
|
|
mov ax,0x2403 ;INT 15h AX=2403h: Query A20 support
|
|
int 0x15 ;Call BIOS to find out how the A20 gate can be enabled
|
|
jc A20_KBD ;Error? Assume that keyboard controller is the only option
|
|
test bx,1 ;Bit 0:Keyboard controller supported
|
|
je A20_KBD ;BIOS indicates that keyboard controller is supported
|
|
test bx,2 ;Bit 1:Fast A20 supported
|
|
je FAST_A20 ;BIOS indicated that Fast A20 is supported
|
|
call Check_A20 ;Test A20 gate manually
|
|
jc A20_Enabled ;A20 gate already enabled
|
|
A20_Fail:
|
|
lea si,[A20_ERROR] ;"Unable to enable A20 gate!"
|
|
call PrintString
|
|
call A20_OUT_Wait ;Print string
|
|
cli ;Disable interrupts
|
|
hlt ;Halt machine
|
|
FAST_A20:
|
|
in al,0x92 ;Read System Control Port A
|
|
test al,2 ;Test Fast A20 bit
|
|
jnz A20_Fail ;Bit already set, failed to enable A20 gate
|
|
or al,2 ;Set Bit 1:Enable Fast A20
|
|
and al,0xFE ;Always clear Bit 0 so that machine does not reboot
|
|
out 0x92,al ;Write to port 92h
|
|
call Check_A20 ;Check A20 gate
|
|
jc A20_Enabled ;Success?
|
|
cli ;No? something must have gone wrong
|
|
hlt ;Clear interrupts and halt machine
|
|
A20_KBD:
|
|
cli ;Disable interrupts
|
|
mov cx,50 ;Number of attempts to enable gate
|
|
Use_KBD:
|
|
call A20_OUT_Wait ;Wait for keyboard
|
|
mov al,0xAD ;Disable Keyboard
|
|
out 0x64,al ;Wait for Keyboard
|
|
mov al,0xD0 ;Read Controller Output Port
|
|
out 0x64,al
|
|
call A20_IN_Wait ;Wait for Keyboard
|
|
in al,0x60 ;Read Data Port
|
|
push ax ;Save
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
mov al,0xD1 ;Write Controller Output Port
|
|
out 0x64,al
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
pop ax ;Get port data back
|
|
or al,10b ;Bit 1: Enable A20 gate
|
|
out 0x60,al ;Write to data port
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
mov al,0xAE ;Enable Keyboard
|
|
out 0x64,al
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
call Check_A20 ;Verify that gate is enabled
|
|
jc A20_Enabled ;Yes? Return
|
|
loop Use_KBD ;No? Keep trying
|
|
jmp A20_Fail ;Could not enable gate after 50 tries
|
|
A20_OUT_Wait:
|
|
in al,0x64 ;Read status register
|
|
test al,10b ;Is port ready?
|
|
jnz A20_OUT_Wait ;Nope, wait
|
|
ret ;Yep, continue
|
|
A20_IN_Wait:
|
|
in al,0x64 ;Read status register
|
|
test al,1b ;Is port ready?
|
|
jz A20_IN_Wait ;Nope, wait
|
|
ret ;Yep, continue
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Check_A20:
|
|
; Test if A20 gate is enabled by comaparing the value in FFFF:7E0E
|
|
; with the VBR boot signature at 0000:7DFE
|
|
; Out: Carry flag set if A20 gate is enabled
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
pushad ;Preserve registers
|
|
push es
|
|
mov ax, 0xFFFF
|
|
mov es, ax ;Extra segment:FFFF
|
|
cmp WORD [es:0x7DFE + 16], 0xAAFF ;See if boot signature is wrapped at FFFF:7E0E (Meaning FFFF:7E0E = 0:7DFE)
|
|
stc ; Assume we didn't wrap, yes A20
|
|
jne .End
|
|
clc ; We did wrap, no A20
|
|
.End:
|
|
pop es
|
|
popad
|
|
ret
|
|
|
|
A20_ERROR db "Unable to enable A20 gate!",0
|