pcjs/pubs/docs/x86/ops/LOADALL/loadfns.286.asm

382 lines
15 KiB
NASM

;-----------------------------------------------------------------------------
;
; LOADFNS.286
;
; Copyright (c) 1991, 1995-Present Robert Collins
;
; You have my permission to copy and distribute this software for
; non-commercial purposes. Any commercial use of this software or
; source code is allowed, so long as the appropriate copyright
; attributions (to me) are intact, *AND* my email address is properly
; displayed.
;
; Basically, give me credit, where credit is due, and show my email
; address.
;
;-----------------------------------------------------------------------------
;
; Robert R. Collins email: rcollins@x86.org
;
;-----------------------------------------------------------------------------
;---------------------------------------------------------------
; Equates & local variables
;---------------------------------------------------------------
; I/O Ports
;---------------------------------------------------------------
Mstrmsk equ 021h ; 8259 master mask addr
KBC_CTL equ 060h ; 8042 control port
KBC_STAT equ 064h ; 8042 status port
Cmos_index equ 070h ; CMOS address port
Cmos_data equ 071h ; CMOS data port
Slv_msk equ 0a1h ; 8259 slave mask addr
;---------------------------------------------------------------
; CMOS RAM
;---------------------------------------------------------------
Shut_down equ 00fh ; CMOS index for shutdwn
Type5 equ 5 ; Shutdown type-5
;---------------------------------------------------------------
; Keyboard Controller
;---------------------------------------------------------------
inpt_buf_full equ 2 ; Input buffer full
Shutdown_CMD equ 0feh ; Shutdown CMD for KBC
enable_bit20 equ 0dfh ; enable A20 command
disable_bit20 equ 0ddh ; disable A20 command
;---------------------------------------------------------------;
RESET_CPU:; Resets the CPU by sending a shutdown command to
; the keyboard controller.
;---------------------------------------------------------------
; Input: None
; Output: None
; Register(s) modified: Doesn't matter, the CPU is reset
;---------------------------------------------------------------
mov al,Shutdown_CMD ; get shutdown command
out KBC_STAT,al ; send command to shutdown CPU
cli ; disable interrupts so that
; an INT can't come through
; before the CPU resets
hlt ;
;---------------------------------------------------------------;
; SETPM_RET_ADDR: Save the real-mode return address @ 40:67
; from protected mode.
;---------------------------------------------------------------
; Input: CS:AX = Return address from PM.
; DS = Better darn well have a PM segment selector!
; (Or else Kablooie!)
; Output: None
; Register(s) modified: None
;---------------------------------------------------------------
Setpm_ret_addr proc near
;---------------------------------------------------------------
push dx ; save it
push ds
mov dx,ABS0 ;
mov ds,dx
ASSUME DS:ABS0
mov DS:PM_Ret_off,ax
mov DS:PM_Ret_seg,cs
ASSUME DS:_DATA
pop ds
pop dx
ret
Setpm_ret_addr endp
;---------------------------------------------------------------
; Get_INT_status: Saves the master and slave mask register
; contents from the 8259 interrupt controller.
;---------------------------------------------------------------
; Input: DS = _DATA SEGMENT
; Output: i8259_1 = Status of master device
; i8259_2 = Status of slave device
; Register(s) modified: None
;---------------------------------------------------------------
Get_int_status proc near
;---------------------------------------------------------------
push ax
in al,mstrmsk ; get master PIC mask
mov i8259_1,al
IO_Delay ; I/O delay
in al,slv_msk ; get slave PIC mask
mov i8259_2,al
pop ax
ret ; exit
Get_int_status endp
;---------------------------------------------------------------;
; Set_INT_status: Restores the interrupt status of the 8259A
; programmable interrupt controller (PIC).
;---------------------------------------------------------------
; Input: i8259_1 = Status of master device
; i8259_2 = Status of slave device
; DS = _DATA SEGMENT
; Output: None
; Register(s) modified: None
;---------------------------------------------------------------
Set_int_status proc near
;---------------------------------------------------------------
pushf ; save interrupt flag
cli ; we REALLY don't want an int
; to come through while we are
push ax ; reprogramming the PIC masks
mov al,i8259_1
out mstrmsk,al ; restore master PIC mask
IO_Delay ; I/O delay
mov al,i8259_2
out slv_msk,al ; restore slave PIC mask
pop ax
popf ; restore interrupt flag
ret ; exit
Set_int_status endp
;---------------------------------------------------------------
; SET_SHUTDOWN_TYPE: Set the processor shutdown type-5 in CMOS.
;---------------------------------------------------------------
; Input: None
; Output: None
; Register(s) modified: None
;---------------------------------------------------------------
Set_shutdown_type proc near
;---------------------------------------------------------------
pushf ; save interrupt status
cli ; disable ints so somebody else
; doesn't do this right now
push ax
mov al,shut_down ; Set shutdown byte
out cmos_index,al ; to shut down x05.
IO_Delay ; I/O delay
mov al,Type5 ;
out cmos_data,al ; CMOS data port
pop ax
popf
ret
set_shutdown_type endp
;---------------------------------------------------------------
; Enable_gate20: Turn on A20, and check for errors.
;---------------------------------------------------------------
; Input: None
; Output: CY=ERROR
; Register(s) modified: None
;---------------------------------------------------------------
Enable_gate20 proc near
;---------------------------------------------------------------
push ax
mov ah,enable_bit20 ; gate address bit 20 on
Call Gate_A20
or al,al ; command accepted?
jz A20_OK ; go if yes
stc ; set error flag
A20_OK: pop ax
ret ; exit
Enable_gate20 endp
;---------------------------------------------------------------
; SHUT_A20: Disable A20 from CPU address BUS.
;---------------------------------------------------------------
; Input: None
; Output: CY=ERROR
; Register(s) modified: None
;---------------------------------------------------------------
Shut_a20 proc near
;---------------------------------------------------------------
push ax
mov ah,disable_bit20 ; gate address bit 20 on
Call Gate_A20
or al,al ; was command accepted?
jz A20_Shut ; go if yes
stc ; set error flag
A20_Shut:
pop ax
ret ; exit
Shut_a20 endp
;---------------------------------------------------------------
; GATE_A20: This routine controls a signal which gates address
; line 20 (A20). The gate A20 signal is an output of
; of the 8042 slave processor (keyboard controller).
; A20 should be gated on before entering protected
; mode, to allow addressing of the entire 16M address
; space of the 80286, or 4G address space of the
; 80386 & 80486. It should be gated off after
; entering real mode -- from protected mode.
;---------------------------------------------------------------
; Input: AH = DD ==> A20 gated off (A20 always 0)
; AH = DF ==> A20 gated on (CPU controls A20)
; Output: AL = 0 ==> Operation successful
; AL = 2 ==> Operation failed, 8042 can't accept cmd
; Register(s) modified: AX
;---------------------------------------------------------------
Gate_a20 proc near
;---------------------------------------------------------------
pushf ; save interrupt status
cli ; disable ints while using 8042
Call Empty_8042 ; insure 8042 input buffer empty
jnz A20_Fail ; ret: 8042 unable to accept cmd
IO_Delay ; I/O Delay
mov al,0D1h ; 8042 cmd to write output port
out KBC_STAT,al ; output cmd to 8042
Call Empty_8042 ; wait for 8042 to accept cmd
jnz A20_Fail ; ret: 8042 unable to accept cmd
mov al,ah ; 8042 port data
out KBC_CTL,al ; output port data to 8042
Call Empty_8042 ; wait for 8042 to port data
push cx ; save it
mov cx,14h ;
@DLY: IO_Delay ; Wait for KBC to execute the
loop @DLY ; command. (about 25uS)
pop cx ; restore it
A20_Fail:
popf ; restore flags
ret
Gate_a20 endp
;---------------------------------------------------------------
; EMPTY_8042: This routine waits for the 8042 buffer to empty.
;---------------------------------------------------------------
; Input: None
; Output: AL = 0, 8042 input buffer empty: ZF
; AL = 2, Time out; 8042 buffer full: NZ
; Register(s) modified: AX
;---------------------------------------------------------------
Empty_8042 proc near
;---------------------------------------------------------------
push cx ; save CX
xor cx,cx ; CX=0: timeout value
Try_KBC:
IO_Delay ;
in al,KBC_STAT ; read 8042 status port
and al,inpt_buf_full; input buffer full flag (D1)
loopnz Try_KBC ; loop until input buffer empty
; or timeout
pop cx ; restore CX
ret
Empty_8042 endp
;---------------------------------------------------------------
; CALC_PM_ADDRESS: Calculate 32-bit protected mode address.
; Used for building descriptor tables.
;---------------------------------------------------------------
; Input: ES:SI = Real mode address
; Output: DX:AX = 32-bit linear address
; Register(s) modified: AX, DX
;---------------------------------------------------------------
Calc_pm_address proc near
;---------------------------------------------------------------
mov ax,es ; point to control block
xor dh,dh ; clear upper register
mov dl,ah ; build high byte of 32-bit addr
shr dl,4 ; use only high nibble from (AX)
shl ax,4 ; strip high nibble from segment
add ax,si ; add GDT offset for low word
adc dx,0 ; adj high byte if CY from low
ret ; back to calling program
calc_pm_address endp
;---------------------------------------------------------------
Save_state proc near ; Save the machine state before
; ; LOADALL
;---------------------------------------------------------------
push ax
push ds
mov si,0
mov di,offset Machine_State.ES_Desc
mov ax,3000h ; ES descriptor
mov ds,ax
mov bx,0303h
movsw
mov word ptr [si-2],bx
add ax,1000h ; SS descriptor
add bx,0101h
mov si,0
add di,0ah
mov ds,ax
movsw
mov word ptr [si-2],bx
sub ax,2000h ; DS descriptor
sub bx,0202h
mov si,0
add di,4
mov ds,ax
movsw
mov word ptr [si-2],bx
pop ds
smsw ax
mov Machine_State._Msw,ax
pushf
pop ax
mov Machine_State._Flags,ax
pop ax
mov Machine_State._DI,di
mov Machine_State._SI,si
mov Machine_State._BP,bp
mov Machine_State._BX,bx
mov Machine_State._DX,dx
mov Machine_State._CX,cx
mov Machine_State._AX,ax
mov ax,ds
mov Machine_State._DS,ax
mov ax,es
mov Machine_State._ES,ax
ret
Save_state endp
;---------------------------------------------------------------
Restore_state proc near ; Restore the machine state
; ; after LOADALL
;---------------------------------------------------------------
mov ax,_data
mov ds,ax
mov ax,3000h ; ES
mov es,ax
mov si,offset Machine_State.DS_Desc
mov di,0
movsw
add ax,1000h ; SS
add si,0ah
mov di,0
mov es,ax
movsw
sub ax,2000h ; DS
add si,4
mov di,0
mov es,ax
movsw
mov ax,Machine_State._ES
mov es,ax
mov ax,Machine_State._DS
mov ds,ax
mov ax,Machine_State._Flags
push ax
popf
mov ax,Machine_State._Msw
lmsw ax
mov ax,Machine_State._AX
mov cx,Machine_State._CX
mov dx,Machine_State._DX
mov bx,Machine_State._BX
mov bp,Machine_State._BP
mov si,Machine_State._SI
mov di,Machine_State._DI
ret
Restore_State endp