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
|