; NOTICE ; This was taken from the os2 bios sources and was slightly modified to ; enable the a20 line. There's still some work to do and much clean-up to ; bring the file upto coding standards. I'll do this when time permits. ; TomP ;* _EnableA20 ;* Description: * ;* This routine enables and disables the A20 address line, depending on * ;* the value in ax * ;* * ;* In general when in real mode we want the A20 line disabled, * ;* when in protected mode enabled. However if there is no high * ;* memory installed we can optimise out unnecessary switching * ;* of the A20 line. Unfortunately the PC/AT ROM does not allow * ;* us to completely decouple mode switching the 286 from gating * ;* the A20 line. * ;* * ;* In real mode we would want A20 enabled if we need to access * ;* high memory, for example in a device driver. We want it * ;* disabled while running arbitrary applications because they * ;* may rely on the 1 meg address wrap feature which having the * ;* A20 line off provides. * ;* * ;* This code is largely duplicated from the PC/AT ROM BIOS. * ;* See Module "BIOS1" on page 5-155 of the PC/AT tech ref. * ;* * ;* WARNING: * ;* * ;* The performance characteristics of these routines * ;* are not well understood. There may be worst case * ;* scenarios where the routine could take a relatively * ;* long time to complete. * ;* * ;* Linkage: * ;* far call * ;* * ;* Input: * ;* * ;* Exit: * ;* A20 line enabled/disabled * ;* * ;* Uses: * ;* ax * ;* * ;* Internal References: * ;* empty_8042 -- waits for 8042 input buffer to drain * .386p include su.inc IODelay macro jmp $+2 endm extrn _puts:near extrn _Empty_8042Failed:near ; Equates for cmos CMOS_DATA equ 71h ; I/O word for cmos chip SHUT_ADDR equ 8fh ; shutdown byte address in cmos SHUT_CODE equ 9 ; block copy return code we use ; equates for 8042 STATUS_PORT equ 64h ; 8042 com port PORT_A equ 60h ; 8042 data port BUF_FULL equ 2 ; 8042 busy bit SHUT_CMD equ 0feh ; RESET 286 command MSW_VIRTUAL equ 1 ; protected mode bit of MSW MASTER_IMR equ 21h ; mask port for master 8259 ;CONST SEGMENT WORD USE16 PUBLIC 'CONST' ;CONST ENDS ;_BSS SEGMENT WORD USE16 PUBLIC 'BSS' ;_BSS ENDS ;DGROUP GROUP CONST, _BSS, _DATA ; ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP _TEXT segment para use16 public 'CODE' ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP ;++ ; ;VOID ;EnableA20( ; VOID ; ) ; ;Routine Description: ; ; Enables the A20 line for any machine. ; ;Arguments: ; ; None ; ;Return Value: ; ; None. ; ; The A20 line is enabled. ; ;-- public _EnableA20 _EnableA20 proc near ; Check if empty_8042 has failed before ; If so, skip this function. This would occur ; on legacy free systems. mov di,offset DGROUP:_Empty_8042Failed cmp byte ptr [di],1 jz EA2 ; cmp byte ptr [di],0 call empty_8042 ; ensure 8042 input buffer empty jnz EA2 ; 8042 error return ; Enable or disable the A20 line mov al,0d1h ; 8042 cmd to write output port out STATUS_PORT,al ; send cmd to 8042 call empty_8042 ; wait for 8042 to accept cmd jnz EA2 ; 8042 error return mov al,0dfh ; 8042 port data out PORT_A,al ; output port data to 8042 call empty_8042 ; We must wait for the a20 line to settle down, which (on an AT) ; may not happen until up to 20 usec after the 8042 has accepted ; the command. We make use of the fact that the 8042 will not ; accept another command until it is finished with the last one. ; The 0FFh command does a NULL 'Pulse Output Port'. Total execution ; time is on the order of 30 usec, easily satisfying the IBM 8042 ; settling requirement. (Thanks, CW!) mov al,0FFh ;* Pulse Output Port (pulse no lines) out STATUS_PORT,al ;* send cmd to 8042 call empty_8042 ;* wait for 8042 to accept cmd EA2: ret _EnableA20 endp ;++ ; ;VOID ;DisableA20( ; VOID ; ) ; ;Routine Description: ; ; Disables the A20 line for any machine. ; ;Arguments: ; ; None ; ;Return Value: ; ; None. ; ; The A20 line is disabled. ; ;-- public _DisableA20 _DisableA20 proc near ; Check if empty_8042 has failed before ; If so, skip this function. This would occur ; on legacy free systems. mov di,offset DGROUP:_Empty_8042Failed cmp byte ptr [di],1 jz EA2 cmp byte ptr [di],0 DA1: call empty_8042 ; ensure 8042 input buffer empty jnz DA2 ; 8042 error return ; Disable the A20 line mov al,0d1h ; 8042 cmd to write output port out STATUS_PORT,al ; send cmd to 8042 call empty_8042 ; wait for 8042 to accept cmd jnz DA2 ; 8042 error return mov al,0ddh ; 8042 port data out PORT_A,al ; output port data to 8042 call empty_8042 ; We must wait for the a20 line to settle down, which (on an AT) ; may not happen until up to 20 usec after the 8042 has accepted ; the command. We make use of the fact that the 8042 will not ; accept another command until it is finished with the last one. ; The 0FFh command does a NULL 'Pulse Output Port'. Total execution ; time is on the order of 30 usec, easily satisfying the IBM 8042 ; settling requirement. (Thanks, CW!) mov al,0FFh ;* Pulse Output Port (pulse no lines) out STATUS_PORT,al ;* send cmd to 8042 call empty_8042 ;* wait for 8042 to accept cmd DA2: ret _DisableA20 endp ;** ; empty_8042 -- wait for 8042 input buffer to drain ; ; Input: ; interrupts disabled ; ; Exit: ; al=0, z=0 => 8042 input buffer empty ; ; Uses: ; ax, flags public Empty8042 Empty8042 proc near empty_8042: sub cx,cx ; cx = 0, timeout loop counter emp1: in al,STATUS_PORT ; read 8042 status port IODelay IODelay IODelay IODelay and al,BUF_FULL ; test buffer full bit loopnz emp1 cmp cx,0 ; see if buffer is full jnz emp2 ; if we reached this point this indicates an error mov di,offset DGROUP:_Empty_8042Failed mov byte ptr [di],1 ; mov [_Empty_8042Failed],1 ; set Empty_8042Failed global to "TRUE" ; mov _Empty_8042Failed,1 ; set Empty_8042Failed global to "TRUE" ; mov cx, offset _Empty_8042Failed ; mov [cx],ah emp2: and al,BUF_FULL ; reset the Z flag ret Empty8042 endp _TEXT ends end