📄 _pmdos.asm
字号:
pushf ; Save state of interrupt flag
pushf ; Push flags on stack to simulate interrupt
ifdef USE_NASM
call far dword [_PM_prevTimer]
else
call [_PM_prevTimer]
endif
popf ; Restore state of interrupt flag
SWAPSTK TmStack ; Swap back to C stack again
ret
endif
cprocend
; Macro to delay briefly to ensure that enough time has elapsed between
; successive I/O accesses so that the device being accessed can respond
; to both accesses even on a very fast PC.
ifdef USE_NASM
%macro DELAY 0
jmp short $+2
jmp short $+2
jmp short $+2
%endmacro
%macro IODELAYN 1
%rep %1
DELAY
%endrep
%endmacro
else
macro DELAY
jmp short $+2
jmp short $+2
jmp short $+2
endm
macro IODELAYN N
rept N
DELAY
endm
endm
endif
;----------------------------------------------------------------------------
; PM_rtcISR - Real time clock interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; Hardware interrupt handler for the timer interrupt, to dispatch control
; to high level C based subroutines. We save the state of all registers
; in this routine, and switch to a local stack. Interrupts are *off*
; when we call the user code.
;
; NOTE: This routine switches to a local stack before calling any C code,
; and hence is _not_ re-entrant. Make sure your C code executes as
; quickly as possible, since a timer overrun will simply hang the
; system.
;----------------------------------------------------------------------------
cprocfar _PM_rtcISR
push ds ; Save value of DS
push es
pushad ; Save _all_ extended registers
cld ; Clear direction flag
; Clear priority interrupt controller and re-enable interrupts so we
; dont lock things up for long.
mov al,20h
out 0A0h,al
out 020h,al
; Clear real-time clock timeout
in al,70h ; Read CMOS index register
push _ax ; and save for later
IODELAYN 3
mov al,0Ch
out 70h,al
IODELAYN 5
in al,71h
; Call the C interrupt handler function
LOAD_DS ; Load DS register
cmp [BYTE RtcInside],1 ; Check for mutual exclusion
je @@Exit
mov [BYTE RtcInside],1
NEWSTK RtcStack ; Switch to local stack
sti ; Re-enable interrupts
call [CPTR _PM_rtcHandler]
RESTSTK RtcStack ; Restore previous stack
mov [BYTE RtcInside],0
@@Exit: pop _ax
out 70h,al ; Restore CMOS index register
popad ; Restore all extended registers
pop es
pop ds
iret ; Return from interrupt
cprocend
ifdef flatmodel
;----------------------------------------------------------------------------
; PM_irqISRTemplate - Hardware interrupt handler IRQ template
;----------------------------------------------------------------------------
; Hardware interrupt handler for any interrupt, to dispatch control
; to high level C based subroutines. We save the state of all registers
; in this routine, and switch to a local stack. Interrupts are *off*
; when we call the user code.
;
; NOTE: This routine switches to a local stack before calling any C code,
; and hence is _not_ re-entrant. Make sure your C code executes as
; quickly as possible.
;----------------------------------------------------------------------------
cprocfar _PM_irqISRTemplate
push ebx
mov ebx,0 ; Relocation adjustment factor
jmp __IRQEntry
; Global variables stored in the IRQ thunk code segment
_CHandler dd 0 ; Pointer to C interrupt handler
_PrevIRQ dd 0 ; Previous IRQ handler
dd 0
_IRQ dd 0 ; IRQ we are hooked for
ptr_IRQStack DUINT 0 ; Place to store old stack offset
seg_IRQStack dw 0 ; Place to store old stack segment
_Inside db 0 ; Mutual exclusion flag
ALIGN 4
dclb IRQ_STACK ; Space for local stack
_IRQStack: ; Stack starts at end!
; Check for and reject spurious IRQ 7 signals
__IRQEntry:
cmp [BYTE cs:ebx+_IRQ],7 ; Spurious IRQs occur only on IRQ 7
jmp @@ValidIRQ
push eax
mov al,1011b ; OCW3: read ISR
out 20h,al ; (Intel Peripheral Components, 1991,
in al,20h ; p. 3-188)
shl al,1 ; Set C = bit 7 (IRQ 7) of ISR register
pop eax
jc @@ValidIRQ
iret ; Return from interrupt
; Save all registers for duration of IRQ handler
@@ValidIRQ:
push ds ; Save value of DS
push es
pushad ; Save _all_ extended registers
cld ; Clear direction flag
LOAD_DS ; Load DS register
; Send an EOI to the PIC
mov al,20h ; Send EOI to PIC
cmp [BYTE ebx+_IRQ],8 ; Clear PIC1 first if IRQ >= 8
jb @@1
out 0A0h,al
@@1: out 20h,al
; Check for mutual exclusion
cmp [BYTE ebx+_Inside],1
je @@ChainOldHandler
mov [BYTE ebx+_Inside],1
; Call the C interrupt handler function
mov [ebx+seg_IRQStack],ss ; Switch to local stack
mov [ebx+ptr_IRQStack],esp
mov [TempSeg],ds
mov ss,[TempSeg]
lea esp,[ebx+_IRQStack]
sti ; Re-enable interrupts
push ebx
call [DWORD ebx+_CHandler]
pop ebx
cli
mov ss,[ebx+seg_IRQStack] ; Restore previous stack
mov esp,[ebx+ptr_IRQStack]
or eax,eax
jz @@ChainOldHandler ; Chain if not handled for shared IRQ
@@Exit: mov [BYTE ebx+_Inside],0
popad ; Restore all extended registers
pop es
pop ds
pop ebx
iret ; Return from interrupt
@@ChainOldHandler:
cmp [DWORD ebx+_PrevIRQ],0
jz @@Exit
mov [BYTE ebx+_Inside],0
mov eax,[DWORD ebx+_PrevIRQ]
mov ebx,[DWORD ebx+_PrevIRQ+4]
mov [DWORD _PrevIRQ],eax
mov [DWORD _PrevIRQ+4],ebx
popad ; Restore all extended registers
pop es
pop ds
pop ebx
jmp [cs:_PrevIRQ] ; Chain to previous IRQ handler
cprocend
cpublic _PM_irqISRTemplateEnd
endif
;----------------------------------------------------------------------------
; PM_keyISR - keyboard interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; Hardware interrupt handler for the keyboard interrupt, to dispatch control
; to high level C based subroutines. We save the state of all registers
; in this routine, and switch to a local stack. Interrupts are *off*
; when we call the user code.
;
; NOTE: This routine switches to a local stack before calling any C code,
; and hence is _not_ re-entrant. However we ensure within this routine
; mutual exclusion to the keyboard handling routine.
;----------------------------------------------------------------------------
cprocfar _PM_keyISR
push ds ; Save value of DS
push es
pushad ; Save _all_ extended registers
cld ; Clear direction flag
LOAD_DS ; Load DS register
cmp [BYTE KyInside],1 ; Check for mutual exclusion
je @@Reissued
mov [BYTE KyInside],1
NEWSTK KyStack ; Switch to local stack
call [CPTR _PM_keyHandler] ; Call C code
RESTSTK KyStack ; Restore previous stack
mov [BYTE KyInside],0
@@Exit: popad ; Restore all extended registers
pop es
pop ds
iret ; Return from interrupt
; When the BIOS keyboard handler needs to change the SHIFT status lights
; on the keyboard, in the process of doing this the keyboard controller
; re-issues another interrupt, while the current handler is still executing.
; If we recieve another interrupt while still handling the current one,
; then simply chain directly to the previous handler.
;
; Note that for most DOS extenders, the real mode interrupt handler that we
; install takes care of this for us.
@@Reissued:
ifdef TNT
push eax
push ebx
push ecx
pushfd ; Push flags on stack to simulate interrupt
mov ax,250Eh ; Call real mode procedure function
mov ebx,[_PM_prevRealKey]
mov ecx,1 ; Copy real mode flags to real mode stack
int 21h ; Call the real mode code
popfd
pop ecx
pop ebx
pop eax
else
pushf
ifdef USE_NASM
call far dword [_PM_prevKey]
else
call [_PM_prevKey]
endif
endif
jmp @@Exit
cprocend
;----------------------------------------------------------------------------
; PM_chainPrevkey - Chain to previous key interrupt and return
;----------------------------------------------------------------------------
; Chains to the previous key interrupt routine and returns control
; back to the high level interrupt handler.
;----------------------------------------------------------------------------
cprocstart PM_chainPrevKey
ifdef TNT
push eax
push ebx
push ecx
pushfd ; Push flags on stack to simulate interrupt
mov ax,250Eh ; Call real mode procedure function
mov ebx,[_PM_prevRealKey]
mov ecx,1 ; Copy real mode flags to real mode stack
int 21h ; Call the real mode code
popfd
pop ecx
pop ebx
pop eax
ret
else
; YIKES! For some strange reason, when execution returns from the
; previous keyboard handler, interrupts are re-enabled!! Since we expect
; interrupts to remain off during the duration of our handler, this can
; cause havoc. However our stack macros always turn off interrupts, so they
; will be off when we exit this routine. Obviously there is a tiny weeny
; window when interrupts will be enabled, but there is nothing we can
; do about this.
SWAPSTK KyStack ; Swap back to previous stack
pushf ; Push flags on stack to simulate interrupt
ifdef USE_NASM
call far dword [_PM_prevKey]
else
call [_PM_prevKey]
endif
SWAPSTK KyStack ; Swap back to C stack again
ret
endif
cprocend
;----------------------------------------------------------------------------
; PM_key15ISR - Int 15h keyboard interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; This routine gets called if we have been called to handle the Int 15h
; keyboard interrupt callout from real mode.
;
; Entry: AX - Hardware scan code to process
; Exit: AX - Hardware scan code to process (0 to ignore)
;----------------------------------------------------------------------------
cprocfar _PM_key15ISR
push ds
push es
LOAD_DS
cmp ah,4Fh
jnz @@NotOurs ; Quit if not keyboard callout
pushad
cld ; Clear direction flag
xor ah,ah ; AX := scan code
NEWSTK Ky15Stack ; Switch to local stack
push _ax
call [CPTR _PM_key15Handler] ; Call C code
_add sp,2,4
RESTSTK Ky15Stack ; Restore previous stack
test ax,ax
jz @@1
stc ; Set carry to process as normal
jmp @@2
@@1: clc ; Clear carry to ignore scan code
@@2: popad
jmp @@Exit ; We are done
@@NotOurs:
ifdef TNT
push eax
push ebx
push ecx
pushfd ; Push flags on stack to simulate interrupt
mov ax,250Eh ; Call real mode procedure function
mov ebx,[_PM_prevRealKey15]
mov ecx,1 ; Copy real mode flags to real mode stack
int 21h ; Call the real mode code
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -