blob: 11824a0afc5ac3c86f977c402f00f508b95f7edc [file] [log] [blame]
;****************************************************************************
;*
;* SciTech OS Portability Manager Library
;*
;* ========================================================================
;*
;* The contents of this file are subject to the SciTech MGL Public
;* License Version 1.0 (the "License"); you may not use this file
;* except in compliance with the License. You may obtain a copy of
;* the License at http://www.scitechsoft.com/mgl-license.txt
;*
;* Software distributed under the License is distributed on an
;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
;* implied. See the License for the specific language governing
;* rights and limitations under the License.
;*
;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
;*
;* The Initial Developer of the Original Code is SciTech Software, Inc.
;* All Rights Reserved.
;*
;* ========================================================================
;*
;* Language: 80386 Assembler, TASM 4.0 or NASM
;* Environment: 32-bit Windows NT device driver
;*
;* Description: Low level assembly support for the PM library specific to
;* Windows NT device drivers.
;*
;****************************************************************************
IDEAL
include "scitech.mac" ; Memory model macros
header _irq ; Set up memory model
begdataseg _irq
cextern _PM_rtcHandler,CPTR
cextern _PM_prevRTC,FCPTR
RtcInside dw 0 ; Are we still handling current interrupt
sidtBuf df 0 ; Buffer for sidt instruction
enddataseg _irq
begcodeseg _irq ; Start of code segment
cpublic _PM_irqCodeStart
; 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
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; If we enable interrupts and call into any C based interrupt handling code,
; we need to setup a bunch of important information for the NT kernel. The
; code below takes care of this housekeeping for us (see Undocumented NT for
; details). If we don't do this housekeeping and interrupts are enabled,
; the kernel will become very unstable and crash within 10 seconds or so.
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
pushad
pushfd
push fs
mov ebx,00000030h
mov fs,bx
sub esp,50h
mov ebp,esp
; Setup the exception frame to NULL
mov ebx,[DWORD cs:0FFDFF000h]
mov [DWORD ds:0FFDFF000h], 0FFFFFFFFh
mov [DWORD ebp],ebx
; Save away the existing KSS ebp
mov esi,[DWORD cs:0FFDFF124h]
mov ebx,[DWORD esi+00000128h]
mov [DWORD ebp+4h],ebx
mov [DWORD esi+00000128h],ebp
; Save away the kernel time and the thread mode (kernel/user)
mov edi,[DWORD esi+00000137h]
mov [DWORD ebp+8h],edi
; Set the thread mode (kernel/user) based on the code selector
mov ebx,[DWORD ebp+7Ch]
and ebx,01
mov [BYTE esi+00000137h],bl
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; End of special interrupt Prolog code
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 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 eax ; and save for later
IODELAYN 3
mov al,0Ch
out 70h,al
IODELAYN 5
in al,71h
; Call the C interrupt handler function
cmp [BYTE RtcInside],1 ; Check for mutual exclusion
je @@Exit
mov [BYTE RtcInside],1
sti ; Enable interrupts
cld ; Clear direction flag for C code
call [CPTR _PM_rtcHandler]
cli ; Disable interrupts on exit!
mov [BYTE RtcInside],0
@@Exit: pop eax
out 70h,al ; Restore CMOS index register
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Start of special epilog code to restore stuff on exit from handler
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Restore the KSS ebp
mov esi,[DWORD cs:0FFDFF124h]
mov ebx,[DWORD ebp+4]
mov [DWORD esi+00000128h],ebx
; Restore the exception frame
mov ebx,[DWORD ebp]
mov [DWORD fs:00000000],ebx
; Restore the thread mode
mov ebx,[DWORD ebp+8h]
mov esi,[DWORD fs:00000124h]
mov [BYTE esi+00000137h],bl
add esp, 50h
pop fs
popfd
popad
; Return from interrupt
iret
cprocend
cpublic _PM_irqCodeEnd
;----------------------------------------------------------------------------
; void _PM_getISR(int irq,PMFARPTR *handler);
;----------------------------------------------------------------------------
; Function to return the specific IRQ handler direct from the IDT.
;----------------------------------------------------------------------------
cprocstart _PM_getISR
ARG idtEntry:UINT, handler:DPTR
enter_c 0
mov ecx,[handler] ; Get address of handler to fill in
sidt [sidtBuf] ; Get IDTR register into sidtBuf
mov eax,[DWORD sidtBuf+2] ; Get address of IDT into EAX
mov ebx,[idtEntry]
lea eax,[eax+ebx*8] ; Get entry in the IDT
movzx edx,[WORD eax+6] ; Get high order 16-bits
shl edx,16 ; Move into top 16-bits of address
mov dx,[WORD eax] ; Get low order 16-bits
mov [DWORD ecx],edx ; Store linear address of handler
mov dx,[WORD eax+2] ; Get selector value
mov [WORD ecx+4],dx ; Store selector value
leave_c
ret
cprocend _PM_getISR
;----------------------------------------------------------------------------
; void _PM_setISR(int irq,void *handler);
;----------------------------------------------------------------------------
; Function to set the specific IRQ handler direct in the IDT.
;----------------------------------------------------------------------------
cprocstart _PM_setISR
ARG irq:UINT, handler:CPTR
enter_c 0
mov ecx,[handler] ; Get address of new handler
mov dx,cs ; Get selector for new handler
sidt [sidtBuf] ; Get IDTR register into sidtBuf
mov eax,[DWORD sidtBuf+2] ; Get address of IDT into EAX
mov ebx,[idtEntry]
lea eax,[eax+ebx*8] ; Get entry in the IDT
cli
mov [WORD eax+2],dx ; Store code segment selector
mov [WORD eax],cx ; Store low order bits of handler
shr ecx,16
mov [WORD eax+6],cx ; Store high order bits of handler
sti
leave_c
ret
cprocend _PM_setISR
;----------------------------------------------------------------------------
; void _PM_restoreISR(int irq,PMFARPTR *handler);
;----------------------------------------------------------------------------
; Function to set the specific IRQ handler direct in the IDT.
;----------------------------------------------------------------------------
cprocstart _PM_restoreISR
ARG irq:UINT, handler:CPTR
enter_c 0
mov ecx,[handler]
mov dx,[WORD ecx+4] ; Get selector for old handler
mov ecx,[DWORD ecx] ; Get address of old handler
sidt [sidtBuf] ; Get IDTR register into sidtBuf
mov eax,[DWORD sidtBuf+2] ; Get address of IDT into EAX
mov ebx,[idtEntry]
lea eax,[eax+ebx*8] ; Get entry in the IDT
cli
mov [WORD eax+2],dx ; Store code segment selector
mov [WORD eax],cx ; Store low order bits of handler
shr ecx,16
mov [WORD eax+6],cx ; Store high order bits of handler
sti
leave_c
ret
cprocend _PM_restoreISR
endcodeseg _irq
END ; End of module