;**************************************************************************** ;* ;* 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