blob: 8352ce30c15e5b83142114a2b508ec98802779e1 [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 SMX embedded systems development
;*
;* Description: Low level assembly support for the PM library specific to
;* SMX interrupt handling.
;*
;****************************************************************************
IDEAL
include "scitech.mac" ; Memory model macros
header _pmsmx ; Set up memory model
; Define the size of our local stacks. For real mode code they cant be
; that big, but for 32 bit protected mode code we can make them nice and
; large so that complex C functions can be used.
MOUSE_STACK EQU 4096
TIMER_STACK EQU 4096
KEY_STACK EQU 1024
INT10_STACK EQU 1024
ifdef USE_NASM
; Macro to load DS and ES registers with correct value.
%imacro LOAD_DS 0
mov ds,[cs:_PM_savedDS]
mov es,[cs:_PM_savedDS]
%endmacro
; Note that interrupts we disable interrupts during the following stack
; %imacro for correct operation, but we do not enable them again. Normally
; these %imacros are used within interrupt handlers so interrupts should
; already be off. We turn them back on explicitly later if the user code
; needs them to be back on.
; Macro to switch to a new local stack.
%imacro NEWSTK 1
cli
mov [seg_%1],ss
mov [ptr_%1],_sp
mov [TempSeg],ds
mov ss,[TempSeg]
mov _sp,offset %1
%endmacro
; %imacro to switch back to the old stack.
%imacro RESTSTK 1
cli
mov ss,[seg_%1]
mov _sp,[ptr_%1]
%endmacro
; %imacro to swap the current stack with the one saved away.
%imacro SWAPSTK 1
cli
mov ax,ss
xchg ax,[seg_%1]
mov ss,ax
xchg _sp,[ptr_%1]
%endmacro
else
; Macro to load DS and ES registers with correct value.
MACRO LOAD_DS
mov ds,[cs:_PM_savedDS]
mov es,[cs:_PM_savedDS]
ENDM
; Note that interrupts we disable interrupts during the following stack
; macro for correct operation, but we do not enable them again. Normally
; these macros are used within interrupt handlers so interrupts should
; already be off. We turn them back on explicitly later if the user code
; needs them to be back on.
; Macro to switch to a new local stack.
MACRO NEWSTK stkname
cli
mov [seg_&stkname&],ss
mov [ptr_&stkname&],_sp
mov [TempSeg],ds
mov ss,[TempSeg]
mov _sp,offset stkname
ENDM
; Macro to switch back to the old stack.
MACRO RESTSTK stkname
cli
mov ss,[seg_&stkname&]
mov _sp,[ptr_&stkname&]
ENDM
; Macro to swap the current stack with the one saved away.
MACRO SWAPSTK stkname
cli
mov ax,ss
xchg ax,[seg_&stkname&]
mov ss,ax
xchg _sp,[ptr_&stkname&]
ENDM
endif
begdataseg _pmsmx
cextern _PM_savedDS,USHORT
cextern _PM_critHandler,CPTR
cextern _PM_breakHandler,CPTR
cextern _PM_timerHandler,CPTR
cextern _PM_rtcHandler,CPTR
cextern _PM_keyHandler,CPTR
cextern _PM_key15Handler,CPTR
cextern _PM_mouseHandler,CPTR
cextern _PM_int10Handler,CPTR
cextern _PM_ctrlCPtr,DPTR
cextern _PM_ctrlBPtr,DPTR
cextern _PM_critPtr,DPTR
cextern _PM_prevTimer,FCPTR
cextern _PM_prevRTC,FCPTR
cextern _PM_prevKey,FCPTR
cextern _PM_prevKey15,FCPTR
cextern _PM_prevBreak,FCPTR
cextern _PM_prevCtrlC,FCPTR
cextern _PM_prevCritical,FCPTR
cextern _PM_prevRealTimer,ULONG
cextern _PM_prevRealRTC,ULONG
cextern _PM_prevRealKey,ULONG
cextern _PM_prevRealKey15,ULONG
cextern _PM_prevRealInt10,ULONG
cpublic _PM_pmsmxDataStart
; Allocate space for all of the local stacks that we need. These stacks
; are not very large, but should be large enough for most purposes
; (generally you want to handle these interrupts quickly, simply storing
; the information for later and then returning). If you need bigger
; stacks then change the appropriate value in here.
ALIGN 4
dclb MOUSE_STACK ; Space for local stack (small)
MsStack: ; Stack starts at end!
ptr_MsStack DUINT 0 ; Place to store old stack offset
seg_MsStack dw 0 ; Place to store old stack segment
ALIGN 4
dclb INT10_STACK ; Space for local stack (small)
Int10Stack: ; Stack starts at end!
ptr_Int10Stack DUINT 0 ; Place to store old stack offset
seg_Int10Stack dw 0 ; Place to store old stack segment
ALIGN 4
dclb TIMER_STACK ; Space for local stack (small)
TmStack: ; Stack starts at end!
ptr_TmStack DUINT 0 ; Place to store old stack offset
seg_TmStack dw 0 ; Place to store old stack segment
ALIGN 4
dclb TIMER_STACK ; Space for local stack (small)
RtcStack: ; Stack starts at end!
ptr_RtcStack DUINT 0 ; Place to store old stack offset
seg_RtcStack dw 0 ; Place to store old stack segment
RtcInside dw 0 ; Are we still handling current interrupt
ALIGN 4
dclb KEY_STACK ; Space for local stack (small)
KyStack: ; Stack starts at end!
ptr_KyStack DUINT 0 ; Place to store old stack offset
seg_KyStack dw 0 ; Place to store old stack segment
KyInside dw 0 ; Are we still handling current interrupt
ALIGN 4
dclb KEY_STACK ; Space for local stack (small)
Ky15Stack: ; Stack starts at end!
ptr_Ky15Stack DUINT 0 ; Place to store old stack offset
seg_Ky15Stack dw 0 ; Place to store old stack segment
TempSeg dw 0 ; Place to store stack segment
cpublic _PM_pmsmxDataEnd
enddataseg _pmsmx
begcodeseg _pmsmx ; Start of code segment
cpublic _PM_pmsmxCodeStart
;----------------------------------------------------------------------------
; PM_mouseISR - Mouse interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; Interrupt subroutine called by the mouse driver upon interrupts, to
; dispatch control to high level C based subroutines. Interrupts are on
; when we call the user code.
;
; It is _extremely_ important to save the state of the extended registers
; as these may well be trashed by the routines called from here and not
; restored correctly by the mouse interface module.
;
; NOTE: This routine switches to a local stack before calling any C code,
; and hence is _not_ re-entrant. For mouse handlers this is not a
; problem, as the mouse driver arbitrates calls to the user mouse
; handler for us.
;
; Entry: AX - Condition mask giving reason for call
; BX - Mouse button state
; CX - Horizontal cursor coordinate
; DX - Vertical cursor coordinate
; SI - Horizontal mickey value
; DI - Vertical mickey value
;
;----------------------------------------------------------------------------
cprocfar _PM_mouseISR
push ds ; Save value of DS
push es
pushad ; Save _all_ extended registers
cld ; Clear direction flag
LOAD_DS ; Load DS register
NEWSTK MsStack ; Switch to local stack
; Call the installed high level C code routine
clrhi dx ; Clear out high order values
clrhi cx
clrhi bx
clrhi ax
sgnhi si
sgnhi di
push _di
push _si
push _dx
push _cx
push _bx
push _ax
sti ; Enable interrupts
call [CPTR _PM_mouseHandler]
_add sp,12,24
RESTSTK MsStack ; Restore previous stack
popad ; Restore all extended registers
pop es
pop ds
ret ; We are done!!
cprocend
;----------------------------------------------------------------------------
; PM_timerISR - Timer 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_timerISR
push ds ; Save value of DS
push es
pushad ; Save _all_ extended registers
cld ; Clear direction flag
LOAD_DS ; Load DS register
NEWSTK TmStack ; Switch to local stack
call [CPTR _PM_timerHandler]
RESTSTK TmStack ; Restore previous stack
popad ; Restore all extended registers
pop es
pop ds
iret ; Return from interrupt
cprocend
;----------------------------------------------------------------------------
; PM_chainPrevTimer - Chain to previous timer interrupt and return
;----------------------------------------------------------------------------
; Chains to the previous timer interrupt routine and returns control
; back to the high level interrupt handler.
;----------------------------------------------------------------------------
cprocstart PM_chainPrevTimer
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_prevRealTimer]
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
SWAPSTK TmStack ; Swap back to previous stack
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
sti ; Re-enable interrupts
NEWSTK RtcStack ; Switch to local stack
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
;----------------------------------------------------------------------------
; 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
popfd
pop ecx
pop ebx
pop eax
else
pushf
ifdef USE_NASM
call far dword [_PM_prevKey15]
else
call [_PM_prevKey15]
endif
endif
@@Exit: pop es
pop ds
retf 4
cprocend
;----------------------------------------------------------------------------
; PM_breakISR - Control Break interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; Hardware interrupt handler for the Ctrl-Break interrupt. We simply set
; the Ctrl-Break flag to a 1 and leave (note that this is accessed through
; a far pointer, as it may well be located in conventional memory).
;----------------------------------------------------------------------------
cprocfar _PM_breakISR
sti
push ds ; Save value of DS
push es
push _bx
LOAD_DS ; Load DS register
mov ebx,[_PM_ctrlBPtr]
mov [UINT _ES _bx],1
; Run alternate break handler code if installed
cmp [CPTR _PM_breakHandler],0
je @@Exit
pushad
mov _ax,1
push _ax
call [CPTR _PM_breakHandler] ; Call C code
pop _ax
popad
@@Exit: pop _bx
pop es
pop ds
iret ; Return from interrupt
cprocend
;----------------------------------------------------------------------------
; int PM_ctrlBreakHit(int clearFlag)
;----------------------------------------------------------------------------
; Returns the current state of the Ctrl-Break flag and possibly clears it.
;----------------------------------------------------------------------------
cprocstart PM_ctrlBreakHit
ARG clearFlag:UINT
enter_c
pushf ; Save interrupt status
push es
mov ebx,[_PM_ctrlBPtr]
cli ; No interrupts thanks!
mov _ax,[_ES _bx]
test [BYTE clearFlag],1
jz @@Done
mov [UINT _ES _bx],0
@@Done: pop es
popf ; Restore interrupt status
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; PM_ctrlCISR - Control Break interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; Hardware interrupt handler for the Ctrl-C interrupt. We simply set
; the Ctrl-C flag to a 1 and leave (note that this is accessed through
; a far pointer, as it may well be located in conventional memory).
;----------------------------------------------------------------------------
cprocfar _PM_ctrlCISR
sti
push ds ; Save value of DS
push es
push _bx
LOAD_DS ; Load DS register
mov ebx,[_PM_ctrlCPtr]
mov [UINT _ES _bx],1
; Run alternate break handler code if installed
cmp [CPTR _PM_breakHandler],0
je @@Exit
pushad
mov _ax,0
push _ax
call [CPTR _PM_breakHandler] ; Call C code
pop _ax
popad
@@Exit: pop _bx
pop es
pop ds
iret ; Return from interrupt
iretd
cprocend
;----------------------------------------------------------------------------
; int PM_ctrlCHit(int clearFlag)
;----------------------------------------------------------------------------
; Returns the current state of the Ctrl-C flag and possibly clears it.
;----------------------------------------------------------------------------
cprocstart PM_ctrlCHit
ARG clearFlag:UINT
enter_c
pushf ; Save interrupt status
push es
mov ebx,[_PM_ctrlCPtr]
cli ; No interrupts thanks!
mov _ax,[_ES _bx]
test [BYTE clearFlag],1
jz @@Done
mov [UINT _ES _bx],0
@@Done:
pop es
popf ; Restore interrupt status
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; PM_criticalISR - Control Error handler interrupt subroutine dispatcher
;----------------------------------------------------------------------------
; Interrupt handler for the MSDOS Critical Error 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. We also pass
; the values of the AX and DI registers to the as pointers, so that the
; values can be modified before returning to MSDOS.
;----------------------------------------------------------------------------
cprocfar _PM_criticalISR
sti
push ds ; Save value of DS
push es
push _bx ; Save register values changed
cld ; Clear direction flag
LOAD_DS ; Load DS register
mov ebx,[_PM_critPtr]
mov [_ES _bx],ax
mov [_ES _bx+2],di
; Run alternate critical handler code if installed
cmp [CPTR _PM_critHandler],0
je @@NoAltHandler
pushad
push _di
push _ax
call [CPTR _PM_critHandler] ; Call C code
_add sp,4,8
popad
pop _bx
pop es
pop ds
iret ; Return from interrupt
@@NoAltHandler:
mov ax,3 ; Tell MSDOS to fail the operation
pop _bx
pop es
pop ds
iret ; Return from interrupt
cprocend
;----------------------------------------------------------------------------
; int PM_criticalError(int *axVal,int *diVal,int clearFlag)
;----------------------------------------------------------------------------
; Returns the current state of the critical error flags, and the values that
; MSDOS passed in the AX and DI registers to our handler.
;----------------------------------------------------------------------------
cprocstart PM_criticalError
ARG axVal:DPTR, diVal:DPTR, clearFlag:UINT
enter_c
pushf ; Save interrupt status
push es
mov ebx,[_PM_critPtr]
cli ; No interrupts thanks!
xor _ax,_ax
xor _di,_di
mov ax,[_ES _bx]
mov di,[_ES _bx+2]
test [BYTE clearFlag],1
jz @@NoClear
mov [ULONG _ES _bx],0
@@NoClear:
_les _bx,[axVal]
mov [_ES _bx],_ax
_les _bx,[diVal]
mov [_ES _bx],_di
pop es
popf ; Restore interrupt status
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; void PM_setMouseHandler(int mask, PM_mouseHandler mh)
;----------------------------------------------------------------------------
cprocstart _PM_setMouseHandler
ARG mouseMask:UINT
enter_c
push es
mov ax,0Ch ; AX := Function 12 - install interrupt sub
mov _cx,[mouseMask] ; CX := mouse mask
mov _dx,offset _PM_mouseISR
push cs
pop es ; ES:_DX -> mouse handler
int 33h ; Call mouse driver
pop es
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; void PM_mousePMCB(void)
;----------------------------------------------------------------------------
; Mouse realmode callback routine. Upon entry to this routine, we recieve
; the following from the DPMI server:
;
; Entry: DS:_SI -> Real mode stack at time of call
; ES:_DI -> Real mode register data structure
; SS:_SP -> Locked protected mode stack to use
;----------------------------------------------------------------------------
cprocfar _PM_mousePMCB
pushad
mov eax,[es:_di+1Ch] ; Load register values from real mode
mov ebx,[es:_di+10h]
mov ecx,[es:_di+18h]
mov edx,[es:_di+14h]
mov esi,[es:_di+04h]
mov edi,[es:_di]
call _PM_mouseISR ; Call the mouse handler
popad
mov ax,[ds:_si]
mov [es:_di+2Ah],ax ; Plug in return IP address
mov ax,[ds:_si+2]
mov [es:_di+2Ch],ax ; Plug in return CS value
add [WORD es:_di+2Eh],4 ; Remove return address from stack
iret ; Go back to real mode!
cprocend
;----------------------------------------------------------------------------
; void PM_int10PMCB(void)
;----------------------------------------------------------------------------
; int10 realmode callback routine. Upon entry to this routine, we recieve
; the following from the DPMI server:
;
; Entry: DS:ESI -> Real mode stack at time of call
; ES:EDI -> Real mode register data structure
; SS:ESP -> Locked protected mode stack to use
;----------------------------------------------------------------------------
cprocfar _PM_int10PMCB
pushad
push ds
push es
push fs
pushfd
pop eax
mov [es:edi+20h],ax ; Save return flag status
mov ax,[ds:esi]
mov [es:edi+2Ah],ax ; Plug in return IP address
mov ax,[ds:esi+2]
mov [es:edi+2Ch],ax ; Plug in return CS value
add [WORD es:edi+2Eh],4 ; Remove return address from stack
; Call the install int10 handler in protected mode. This function gets called
; with DS set to the current data selector, and ES:EDI pointing the the
; real mode DPMI register structure at the time of the interrupt. The
; handle must be written in assembler to be able to extract the real mode
; register values from the structure
push es
pop fs ; FS:EDI -> real mode registers
LOAD_DS
NEWSTK Int10Stack ; Switch to local stack
call [_PM_int10Handler]
RESTSTK Int10Stack ; Restore previous stack
pop fs
pop es
pop ds
popad
iret ; Go back to real mode!
cprocend
cpublic _PM_pmsmxCodeEnd
endcodeseg _pmsmx
END ; End of module