1 ;****************************************************************************
3 ;* SciTech OS Portability Manager Library
5 ;* ========================================================================
7 ;* The contents of this file are subject to the SciTech MGL Public
8 ;* License Version 1.0 (the "License"); you may not use this file
9 ;* except in compliance with the License. You may obtain a copy of
10 ;* the License at http://www.scitechsoft.com/mgl-license.txt
12 ;* Software distributed under the License is distributed on an
13 ;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 ;* implied. See the License for the specific language governing
15 ;* rights and limitations under the License.
17 ;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
19 ;* The Initial Developer of the Original Code is SciTech Software, Inc.
20 ;* All Rights Reserved.
22 ;* ========================================================================
24 ;* Language: 80386 Assembler, TASM 4.0 or NASM
25 ;* Environment: 32-bit SMX embedded systems development
27 ;* Description: Low level assembly support for the PM library specific to
28 ;* SMX interrupt handling.
30 ;****************************************************************************
34 include "scitech.mac" ; Memory model macros
36 header _pmsmx ; Set up memory model
38 ; Define the size of our local stacks. For real mode code they cant be
39 ; that big, but for 32 bit protected mode code we can make them nice and
40 ; large so that complex C functions can be used.
49 ; Macro to load DS and ES registers with correct value.
52 mov ds,[cs:_PM_savedDS]
53 mov es,[cs:_PM_savedDS]
56 ; Note that interrupts we disable interrupts during the following stack
57 ; %imacro for correct operation, but we do not enable them again. Normally
58 ; these %imacros are used within interrupt handlers so interrupts should
59 ; already be off. We turn them back on explicitly later if the user code
60 ; needs them to be back on.
62 ; Macro to switch to a new local stack.
73 ; %imacro to switch back to the old stack.
81 ; %imacro to swap the current stack with the one saved away.
93 ; Macro to load DS and ES registers with correct value.
96 mov ds,[cs:_PM_savedDS]
97 mov es,[cs:_PM_savedDS]
100 ; Note that interrupts we disable interrupts during the following stack
101 ; macro for correct operation, but we do not enable them again. Normally
102 ; these macros are used within interrupt handlers so interrupts should
103 ; already be off. We turn them back on explicitly later if the user code
104 ; needs them to be back on.
106 ; Macro to switch to a new local stack.
110 mov [seg_&stkname&],ss
111 mov [ptr_&stkname&],_sp
114 mov _sp,offset stkname
117 ; Macro to switch back to the old stack.
119 MACRO RESTSTK stkname
121 mov ss,[seg_&stkname&]
122 mov _sp,[ptr_&stkname&]
125 ; Macro to swap the current stack with the one saved away.
127 MACRO SWAPSTK stkname
130 xchg ax,[seg_&stkname&]
132 xchg _sp,[ptr_&stkname&]
139 cextern _PM_savedDS,USHORT
140 cextern _PM_critHandler,CPTR
141 cextern _PM_breakHandler,CPTR
142 cextern _PM_timerHandler,CPTR
143 cextern _PM_rtcHandler,CPTR
144 cextern _PM_keyHandler,CPTR
145 cextern _PM_key15Handler,CPTR
146 cextern _PM_mouseHandler,CPTR
147 cextern _PM_int10Handler,CPTR
149 cextern _PM_ctrlCPtr,DPTR
150 cextern _PM_ctrlBPtr,DPTR
151 cextern _PM_critPtr,DPTR
153 cextern _PM_prevTimer,FCPTR
154 cextern _PM_prevRTC,FCPTR
155 cextern _PM_prevKey,FCPTR
156 cextern _PM_prevKey15,FCPTR
157 cextern _PM_prevBreak,FCPTR
158 cextern _PM_prevCtrlC,FCPTR
159 cextern _PM_prevCritical,FCPTR
160 cextern _PM_prevRealTimer,ULONG
161 cextern _PM_prevRealRTC,ULONG
162 cextern _PM_prevRealKey,ULONG
163 cextern _PM_prevRealKey15,ULONG
164 cextern _PM_prevRealInt10,ULONG
166 cpublic _PM_pmsmxDataStart
168 ; Allocate space for all of the local stacks that we need. These stacks
169 ; are not very large, but should be large enough for most purposes
170 ; (generally you want to handle these interrupts quickly, simply storing
171 ; the information for later and then returning). If you need bigger
172 ; stacks then change the appropriate value in here.
175 dclb MOUSE_STACK ; Space for local stack (small)
176 MsStack: ; Stack starts at end!
177 ptr_MsStack DUINT 0 ; Place to store old stack offset
178 seg_MsStack dw 0 ; Place to store old stack segment
181 dclb INT10_STACK ; Space for local stack (small)
182 Int10Stack: ; Stack starts at end!
183 ptr_Int10Stack DUINT 0 ; Place to store old stack offset
184 seg_Int10Stack dw 0 ; Place to store old stack segment
187 dclb TIMER_STACK ; Space for local stack (small)
188 TmStack: ; Stack starts at end!
189 ptr_TmStack DUINT 0 ; Place to store old stack offset
190 seg_TmStack dw 0 ; Place to store old stack segment
193 dclb TIMER_STACK ; Space for local stack (small)
194 RtcStack: ; Stack starts at end!
195 ptr_RtcStack DUINT 0 ; Place to store old stack offset
196 seg_RtcStack dw 0 ; Place to store old stack segment
197 RtcInside dw 0 ; Are we still handling current interrupt
200 dclb KEY_STACK ; Space for local stack (small)
201 KyStack: ; Stack starts at end!
202 ptr_KyStack DUINT 0 ; Place to store old stack offset
203 seg_KyStack dw 0 ; Place to store old stack segment
204 KyInside dw 0 ; Are we still handling current interrupt
207 dclb KEY_STACK ; Space for local stack (small)
208 Ky15Stack: ; Stack starts at end!
209 ptr_Ky15Stack DUINT 0 ; Place to store old stack offset
210 seg_Ky15Stack dw 0 ; Place to store old stack segment
212 TempSeg dw 0 ; Place to store stack segment
214 cpublic _PM_pmsmxDataEnd
218 begcodeseg _pmsmx ; Start of code segment
220 cpublic _PM_pmsmxCodeStart
222 ;----------------------------------------------------------------------------
223 ; PM_mouseISR - Mouse interrupt subroutine dispatcher
224 ;----------------------------------------------------------------------------
225 ; Interrupt subroutine called by the mouse driver upon interrupts, to
226 ; dispatch control to high level C based subroutines. Interrupts are on
227 ; when we call the user code.
229 ; It is _extremely_ important to save the state of the extended registers
230 ; as these may well be trashed by the routines called from here and not
231 ; restored correctly by the mouse interface module.
233 ; NOTE: This routine switches to a local stack before calling any C code,
234 ; and hence is _not_ re-entrant. For mouse handlers this is not a
235 ; problem, as the mouse driver arbitrates calls to the user mouse
238 ; Entry: AX - Condition mask giving reason for call
239 ; BX - Mouse button state
240 ; CX - Horizontal cursor coordinate
241 ; DX - Vertical cursor coordinate
242 ; SI - Horizontal mickey value
243 ; DI - Vertical mickey value
245 ;----------------------------------------------------------------------------
246 cprocfar _PM_mouseISR
248 push ds ; Save value of DS
250 pushad ; Save _all_ extended registers
251 cld ; Clear direction flag
253 LOAD_DS ; Load DS register
254 NEWSTK MsStack ; Switch to local stack
256 ; Call the installed high level C code routine
258 clrhi dx ; Clear out high order values
271 sti ; Enable interrupts
272 call [CPTR _PM_mouseHandler]
275 RESTSTK MsStack ; Restore previous stack
277 popad ; Restore all extended registers
284 ;----------------------------------------------------------------------------
285 ; PM_timerISR - Timer interrupt subroutine dispatcher
286 ;----------------------------------------------------------------------------
287 ; Hardware interrupt handler for the timer interrupt, to dispatch control
288 ; to high level C based subroutines. We save the state of all registers
289 ; in this routine, and switch to a local stack. Interrupts are *off*
290 ; when we call the user code.
292 ; NOTE: This routine switches to a local stack before calling any C code,
293 ; and hence is _not_ re-entrant. Make sure your C code executes as
294 ; quickly as possible, since a timer overrun will simply hang the
296 ;----------------------------------------------------------------------------
297 cprocfar _PM_timerISR
299 push ds ; Save value of DS
301 pushad ; Save _all_ extended registers
302 cld ; Clear direction flag
304 LOAD_DS ; Load DS register
306 NEWSTK TmStack ; Switch to local stack
307 call [CPTR _PM_timerHandler]
308 RESTSTK TmStack ; Restore previous stack
310 popad ; Restore all extended registers
313 iret ; Return from interrupt
317 ;----------------------------------------------------------------------------
318 ; PM_chainPrevTimer - Chain to previous timer interrupt and return
319 ;----------------------------------------------------------------------------
320 ; Chains to the previous timer interrupt routine and returns control
321 ; back to the high level interrupt handler.
322 ;----------------------------------------------------------------------------
323 cprocstart PM_chainPrevTimer
329 pushfd ; Push flags on stack to simulate interrupt
330 mov ax,250Eh ; Call real mode procedure function
331 mov ebx,[_PM_prevRealTimer]
332 mov ecx,1 ; Copy real mode flags to real mode stack
333 int 21h ; Call the real mode code
340 SWAPSTK TmStack ; Swap back to previous stack
341 pushf ; Save state of interrupt flag
342 pushf ; Push flags on stack to simulate interrupt
344 call far dword [_PM_prevTimer]
348 popf ; Restore state of interrupt flag
349 SWAPSTK TmStack ; Swap back to C stack again
355 ; Macro to delay briefly to ensure that enough time has elapsed between
356 ; successive I/O accesses so that the device being accessed can respond
357 ; to both accesses even on a very fast PC.
383 ;----------------------------------------------------------------------------
384 ; PM_rtcISR - Real time clock interrupt subroutine dispatcher
385 ;----------------------------------------------------------------------------
386 ; Hardware interrupt handler for the timer interrupt, to dispatch control
387 ; to high level C based subroutines. We save the state of all registers
388 ; in this routine, and switch to a local stack. Interrupts are *off*
389 ; when we call the user code.
391 ; NOTE: This routine switches to a local stack before calling any C code,
392 ; and hence is _not_ re-entrant. Make sure your C code executes as
393 ; quickly as possible, since a timer overrun will simply hang the
395 ;----------------------------------------------------------------------------
398 push ds ; Save value of DS
400 pushad ; Save _all_ extended registers
401 cld ; Clear direction flag
403 ; Clear priority interrupt controller and re-enable interrupts so we
404 ; dont lock things up for long.
410 ; Clear real-time clock timeout
412 in al,70h ; Read CMOS index register
413 push _ax ; and save for later
420 ; Call the C interrupt handler function
422 LOAD_DS ; Load DS register
423 cmp [BYTE RtcInside],1 ; Check for mutual exclusion
425 mov [BYTE RtcInside],1
426 sti ; Re-enable interrupts
427 NEWSTK RtcStack ; Switch to local stack
428 call [CPTR _PM_rtcHandler]
429 RESTSTK RtcStack ; Restore previous stack
430 mov [BYTE RtcInside],0
433 out 70h,al ; Restore CMOS index register
434 popad ; Restore all extended registers
437 iret ; Return from interrupt
441 ;----------------------------------------------------------------------------
442 ; PM_keyISR - keyboard interrupt subroutine dispatcher
443 ;----------------------------------------------------------------------------
444 ; Hardware interrupt handler for the keyboard interrupt, to dispatch control
445 ; to high level C based subroutines. We save the state of all registers
446 ; in this routine, and switch to a local stack. Interrupts are *off*
447 ; when we call the user code.
449 ; NOTE: This routine switches to a local stack before calling any C code,
450 ; and hence is _not_ re-entrant. However we ensure within this routine
451 ; mutual exclusion to the keyboard handling routine.
452 ;----------------------------------------------------------------------------
455 push ds ; Save value of DS
457 pushad ; Save _all_ extended registers
458 cld ; Clear direction flag
460 LOAD_DS ; Load DS register
462 cmp [BYTE KyInside],1 ; Check for mutual exclusion
465 mov [BYTE KyInside],1
466 NEWSTK KyStack ; Switch to local stack
467 call [CPTR _PM_keyHandler] ; Call C code
468 RESTSTK KyStack ; Restore previous stack
469 mov [BYTE KyInside],0
471 @@Exit: popad ; Restore all extended registers
474 iret ; Return from interrupt
476 ; When the BIOS keyboard handler needs to change the SHIFT status lights
477 ; on the keyboard, in the process of doing this the keyboard controller
478 ; re-issues another interrupt, while the current handler is still executing.
479 ; If we recieve another interrupt while still handling the current one,
480 ; then simply chain directly to the previous handler.
482 ; Note that for most DOS extenders, the real mode interrupt handler that we
483 ; install takes care of this for us.
490 pushfd ; Push flags on stack to simulate interrupt
491 mov ax,250Eh ; Call real mode procedure function
492 mov ebx,[_PM_prevRealKey]
493 mov ecx,1 ; Copy real mode flags to real mode stack
494 int 21h ; Call the real mode code
502 call far dword [_PM_prevKey]
511 ;----------------------------------------------------------------------------
512 ; PM_chainPrevkey - Chain to previous key interrupt and return
513 ;----------------------------------------------------------------------------
514 ; Chains to the previous key interrupt routine and returns control
515 ; back to the high level interrupt handler.
516 ;----------------------------------------------------------------------------
517 cprocstart PM_chainPrevKey
523 pushfd ; Push flags on stack to simulate interrupt
524 mov ax,250Eh ; Call real mode procedure function
525 mov ebx,[_PM_prevRealKey]
526 mov ecx,1 ; Copy real mode flags to real mode stack
527 int 21h ; Call the real mode code
535 ; YIKES! For some strange reason, when execution returns from the
536 ; previous keyboard handler, interrupts are re-enabled!! Since we expect
537 ; interrupts to remain off during the duration of our handler, this can
538 ; cause havoc. However our stack macros always turn off interrupts, so they
539 ; will be off when we exit this routine. Obviously there is a tiny weeny
540 ; window when interrupts will be enabled, but there is nothing we can
543 SWAPSTK KyStack ; Swap back to previous stack
544 pushf ; Push flags on stack to simulate interrupt
546 call far dword [_PM_prevKey]
550 SWAPSTK KyStack ; Swap back to C stack again
556 ;----------------------------------------------------------------------------
557 ; PM_key15ISR - Int 15h keyboard interrupt subroutine dispatcher
558 ;----------------------------------------------------------------------------
559 ; This routine gets called if we have been called to handle the Int 15h
560 ; keyboard interrupt callout from real mode.
562 ; Entry: AX - Hardware scan code to process
563 ; Exit: AX - Hardware scan code to process (0 to ignore)
564 ;----------------------------------------------------------------------------
565 cprocfar _PM_key15ISR
571 jnz @@NotOurs ; Quit if not keyboard callout
574 cld ; Clear direction flag
575 xor ah,ah ; AX := scan code
576 NEWSTK Ky15Stack ; Switch to local stack
578 call [CPTR _PM_key15Handler] ; Call C code
580 RESTSTK Ky15Stack ; Restore previous stack
583 stc ; Set carry to process as normal
585 @@1: clc ; Clear carry to ignore scan code
587 jmp @@Exit ; We are done
594 pushfd ; Push flags on stack to simulate interrupt
595 mov ax,250Eh ; Call real mode procedure function
596 mov ebx,[_PM_prevRealKey15]
597 mov ecx,1 ; Copy real mode flags to real mode stack
598 int 21h ; Call the real mode code
606 call far dword [_PM_prevKey15]
617 ;----------------------------------------------------------------------------
618 ; PM_breakISR - Control Break interrupt subroutine dispatcher
619 ;----------------------------------------------------------------------------
620 ; Hardware interrupt handler for the Ctrl-Break interrupt. We simply set
621 ; the Ctrl-Break flag to a 1 and leave (note that this is accessed through
622 ; a far pointer, as it may well be located in conventional memory).
623 ;----------------------------------------------------------------------------
624 cprocfar _PM_breakISR
627 push ds ; Save value of DS
631 LOAD_DS ; Load DS register
632 mov ebx,[_PM_ctrlBPtr]
635 ; Run alternate break handler code if installed
637 cmp [CPTR _PM_breakHandler],0
643 call [CPTR _PM_breakHandler] ; Call C code
650 iret ; Return from interrupt
654 ;----------------------------------------------------------------------------
655 ; int PM_ctrlBreakHit(int clearFlag)
656 ;----------------------------------------------------------------------------
657 ; Returns the current state of the Ctrl-Break flag and possibly clears it.
658 ;----------------------------------------------------------------------------
659 cprocstart PM_ctrlBreakHit
664 pushf ; Save interrupt status
666 mov ebx,[_PM_ctrlBPtr]
667 cli ; No interrupts thanks!
669 test [BYTE clearFlag],1
674 popf ; Restore interrupt status
680 ;----------------------------------------------------------------------------
681 ; PM_ctrlCISR - Control Break interrupt subroutine dispatcher
682 ;----------------------------------------------------------------------------
683 ; Hardware interrupt handler for the Ctrl-C interrupt. We simply set
684 ; the Ctrl-C flag to a 1 and leave (note that this is accessed through
685 ; a far pointer, as it may well be located in conventional memory).
686 ;----------------------------------------------------------------------------
687 cprocfar _PM_ctrlCISR
690 push ds ; Save value of DS
694 LOAD_DS ; Load DS register
695 mov ebx,[_PM_ctrlCPtr]
698 ; Run alternate break handler code if installed
700 cmp [CPTR _PM_breakHandler],0
706 call [CPTR _PM_breakHandler] ; Call C code
713 iret ; Return from interrupt
718 ;----------------------------------------------------------------------------
719 ; int PM_ctrlCHit(int clearFlag)
720 ;----------------------------------------------------------------------------
721 ; Returns the current state of the Ctrl-C flag and possibly clears it.
722 ;----------------------------------------------------------------------------
723 cprocstart PM_ctrlCHit
728 pushf ; Save interrupt status
730 mov ebx,[_PM_ctrlCPtr]
731 cli ; No interrupts thanks!
733 test [BYTE clearFlag],1
739 popf ; Restore interrupt status
745 ;----------------------------------------------------------------------------
746 ; PM_criticalISR - Control Error handler interrupt subroutine dispatcher
747 ;----------------------------------------------------------------------------
748 ; Interrupt handler for the MSDOS Critical Error interrupt, to dispatch
749 ; control to high level C based subroutines. We save the state of all
750 ; registers in this routine, and switch to a local stack. We also pass
751 ; the values of the AX and DI registers to the as pointers, so that the
752 ; values can be modified before returning to MSDOS.
753 ;----------------------------------------------------------------------------
754 cprocfar _PM_criticalISR
757 push ds ; Save value of DS
759 push _bx ; Save register values changed
760 cld ; Clear direction flag
762 LOAD_DS ; Load DS register
763 mov ebx,[_PM_critPtr]
767 ; Run alternate critical handler code if installed
769 cmp [CPTR _PM_critHandler],0
775 call [CPTR _PM_critHandler] ; Call C code
782 iret ; Return from interrupt
785 mov ax,3 ; Tell MSDOS to fail the operation
789 iret ; Return from interrupt
793 ;----------------------------------------------------------------------------
794 ; int PM_criticalError(int *axVal,int *diVal,int clearFlag)
795 ;----------------------------------------------------------------------------
796 ; Returns the current state of the critical error flags, and the values that
797 ; MSDOS passed in the AX and DI registers to our handler.
798 ;----------------------------------------------------------------------------
799 cprocstart PM_criticalError
801 ARG axVal:DPTR, diVal:DPTR, clearFlag:UINT
804 pushf ; Save interrupt status
806 mov ebx,[_PM_critPtr]
807 cli ; No interrupts thanks!
812 test [BYTE clearFlag],1
814 mov [ULONG _ES _bx],0
821 popf ; Restore interrupt status
827 ;----------------------------------------------------------------------------
828 ; void PM_setMouseHandler(int mask, PM_mouseHandler mh)
829 ;----------------------------------------------------------------------------
830 cprocstart _PM_setMouseHandler
837 mov ax,0Ch ; AX := Function 12 - install interrupt sub
838 mov _cx,[mouseMask] ; CX := mouse mask
839 mov _dx,offset _PM_mouseISR
841 pop es ; ES:_DX -> mouse handler
842 int 33h ; Call mouse driver
850 ;----------------------------------------------------------------------------
851 ; void PM_mousePMCB(void)
852 ;----------------------------------------------------------------------------
853 ; Mouse realmode callback routine. Upon entry to this routine, we recieve
854 ; the following from the DPMI server:
856 ; Entry: DS:_SI -> Real mode stack at time of call
857 ; ES:_DI -> Real mode register data structure
858 ; SS:_SP -> Locked protected mode stack to use
859 ;----------------------------------------------------------------------------
860 cprocfar _PM_mousePMCB
863 mov eax,[es:_di+1Ch] ; Load register values from real mode
869 call _PM_mouseISR ; Call the mouse handler
873 mov [es:_di+2Ah],ax ; Plug in return IP address
875 mov [es:_di+2Ch],ax ; Plug in return CS value
876 add [WORD es:_di+2Eh],4 ; Remove return address from stack
877 iret ; Go back to real mode!
881 ;----------------------------------------------------------------------------
882 ; void PM_int10PMCB(void)
883 ;----------------------------------------------------------------------------
884 ; int10 realmode callback routine. Upon entry to this routine, we recieve
885 ; the following from the DPMI server:
887 ; Entry: DS:ESI -> Real mode stack at time of call
888 ; ES:EDI -> Real mode register data structure
889 ; SS:ESP -> Locked protected mode stack to use
890 ;----------------------------------------------------------------------------
891 cprocfar _PM_int10PMCB
900 mov [es:edi+20h],ax ; Save return flag status
902 mov [es:edi+2Ah],ax ; Plug in return IP address
904 mov [es:edi+2Ch],ax ; Plug in return CS value
905 add [WORD es:edi+2Eh],4 ; Remove return address from stack
907 ; Call the install int10 handler in protected mode. This function gets called
908 ; with DS set to the current data selector, and ES:EDI pointing the the
909 ; real mode DPMI register structure at the time of the interrupt. The
910 ; handle must be written in assembler to be able to extract the real mode
911 ; register values from the structure
914 pop fs ; FS:EDI -> real mode registers
916 NEWSTK Int10Stack ; Switch to local stack
918 call [_PM_int10Handler]
920 RESTSTK Int10Stack ; Restore previous stack
925 iret ; Go back to real mode!
929 cpublic _PM_pmsmxCodeEnd