280 lines
9.2 KiB
NASM
280 lines
9.2 KiB
NASM
; 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
|