summaryrefslogblamecommitdiffstats
path: root/private/mvdm/dpmi/ntintr32.asm
blob: cea70632c4eca8b7a14cba213dd086dfe4bb1ee3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                               
include intmac.inc
        public rgw032Stack

            dw      64 dup (?)          ; DOSX Ring -> Ring 0 transition stack
;
; Interrupts in the range 0-1fh cause a ring transition and leave
; an outer ring IRET frame right here.
;
Ring0_EH_DS             dw      ?       ; place to put user DS
Ring0_EH_AX             dw      ?       ; place to put user AX
Ring0_EH_BX             dw      ?       ; place to put user BX
Ring0_EH_CX             dw      ?       ; place to put user CX
Ring0_EH_BP             dw      ?       ; place to put user BP
Ring0_EH_PEC            dw      ?       ; lsw of error code for 386 page fault
                                ; also near return to PMFaultEntryVector
Ring0_EH_EC             dw      ?       ; error code passed to EH
Ring0_EH_IP             dw      ?       ; interrupted code IP
                        dw      ?
Ring0_EH_CS             dw      ?       ; interrupted code CS
                        dw      ?
Ring0_EH_Flags          dw      ?       ; interrupted code flags
                        dw      ?
Ring0_EH_SP             dw      ?       ; interrupted code SP
                        dw      ?
Ring0_EH_SS             dw      ?       ; interrupted code SS
                        dw      ?
rgw032Stack   label   word

; ------------------------------------------------------------------
;   PMFaultAnalyzer -- This routine is the entry point for
;       the protected mode fault/trap/exception handler.  It tries
;       to distinguish between bona fide processor faults and
;       hardware/software interrupts which use the range of
;       interrupts that is reserved by Intel.  If a fault is
;       detected, then format the stack for a DPMI fault handler,
;       then vector to the handler whose address is stored in
;       PMFaultVector.  If it looks more like an interrupt, then
;       set up the stack for an interrupt handler, jump to the
;       handler whose address is stored in PMIntelVector.
;
;   Input:  none
;   Output: none

        assume  ds:NOTHING,es:NOTHING,ss:DGROUP
        public  PMFaultAnalyzer

PMFaultAnalyzer  proc near

;
; Make sure we are on the right stack.  Else, something fishy is going on.
; Note that stack faults won't do too well here.
;
        push    ax
        mov     ax,ss
        cmp     ax,SEL_DXDATA or STD_RING
        pop     ax
        je      pmfa_stack_passed
        jmp     pmfa_bad_stack
pmfa_stack_passed:
;
; Is the stack pointer pointing at a word error code (minus 2)?
;
        cmp     sp,offset DGROUP:Ring0_EH_PEC
        je      pmfa_fault              ; Yes, processor fault.

;
; Is it pointing to where it is supposed to be for a hardware or
; software interrupt?
;
        cmp     sp,offset DGROUP:Ring0_EH_EC
        je      pmfa_20
        jmp     pmfa_bad_stack
pmfa_20:jmp     pmfa_inspect

pmfa_fault:
;
; Getting here, we have a known exception with a word error code of some
; sort on the stack.  Perform an outward ring transition, switch to the
; client stack, then vector through the exception handler vector to the
; appropriate handler.
;
        push    bp
        push    cx
        push    bx
        push    ax
        push    ds
        lea     bp,Ring0_EH_SS
        mov     ax,word ptr [bp]
        mov     cx,selEHStack
        cmp     ax,cx
        jne     pmfa_stack_OK
        mov     bx,[bp-2]
        jmp     pmfa_copy_stack
pmfa_stack_OK:
        mov     bx,npEHStackLimit
pmfa_copy_stack:
        mov     ds,cx                   ; DS:BX = user SS:SP
        mov     cx,13
        add     bp,2                    ; put both halves of ss
pmfa_copy_stack_loop:
        dec     bx
        dec     bx
        mov     ax,word ptr [bp]
        mov     word ptr [bx],ax
        dec     bp
        dec     bp
        loop    pmfa_copy_stack_loop

; DS:BX points to stack on entry to PMFaultReflector

;
; Build a far return frame on user stack, switch to user stack, and return
;
        lea     bp,Ring0_EH_PEC
        mov     word ptr [bp+6],ds
        mov     word ptr [bp+4],bx

        sub     bx,2
        mov     word ptr ds:[bx],SEL_DXPMCODE OR STD_RING; push cs
        sub     bx,2
        mov     ds:[bx],offset DXPMCODE:PMFaultReflector; push ip
        lea     bp,Ring0_EH_PEC
        mov     ax,Ring0_EH_CX                  ; get BP value
        sub     bx,2
        mov     ds:[bx],ax                      ; push bp
        mov     Ring0_EH_PEC,bx                 ; sp for lss
        mov     bx,ds
        mov     Ring0_EH_EC,bx                  ; ss for lss
        pop     ds
        pop     ax
        pop     bx
        pop     cx
.386p
        lss     sp,[bp]                         ; switch stack
.286p
        pop     bp
        retf

pmfa_inspect:
;
; Stack is set up as for an interrupt or exception without error code.
; Adjust the stack pointer and put an error code of zero.  Then try to
; determine whether we have an exception or an interrupt.  First test
; is the interrupt number.
;
        push    Ring0_EH_EC
        mov     Ring0_EH_EC,0
        cmp     Ring0_EH_PEC,offset PMFaultEntryVector + ((7 + 1) * 3)
        ja      pfma_50
        push    Ring0_EH_PEC
        mov     Ring0_EH_PEC,0
        jmp     pmfa_fault      ; Yes, definitely a fault.
pfma_50:
;
; At this point, all valid exceptions have been eliminated except for
; exception 9, coprocessor segment overrun, and exception 16, coprocessor
; error.
;

;         **********************************************
;         * Your code to detect these exceptions here. *
;         **********************************************

        push    bp
        push    cx
        push    bx
        push    ax
        push    ds                              ; SP -> Ring0_EH_DS
;
; Point to the user's stack.
;
        lea     bp,Ring0_EH_SS
        mov     cx,[bp]
        mov     ds,cx
        mov     bx,[bp-2]                       ; DS:[BX] -> user stack
;
; Copy the IRET frame to the user stack.
;
        lea     bp,Ring0_EH_Flags
        mov     cx,6
pmfa_copy_IRET:
        mov     ax,[bp]
        dec     bx
        dec     bx
        mov     [bx],ax
        dec     bp
        dec     bp
        loop    pmfa_copy_IRET
;
; Point BP at vector entry for this (reserved) interrupt.
;
        mov     ax,Ring0_EH_PEC                 ; fetch near return address
        sub     ax,offset DXPMCODE:PMFaultEntryVector+3
        mov     cl,3
        div     cl                              ; AX = interrupt number
        shl     ax,2                            ; AX = vector entry offset
        lea     bp,PMIntelVector
        add     bp,ax                   ; BP -> interrupt handler address
        mov     ax,[bp]                         ; AX = IP of handler
        mov     cx,[bp+2]                       ; CX = CS of handler
;
; Build a far return frame on user stack, switch to user stack, and return
;

        sub     bx,2
        mov     ds:[bx],cx                      ; push cs
        sub     bx,2
        mov     ds:[bx],ax                      ; push ip
        lea     bp,Ring0_EH_PEC
        mov     ax,Ring0_EH_BP
        sub     bx,2
        mov     ds:[bx],ax                      ; push bp
        mov     Ring0_EH_PEC,bx                 ; sp for lss
        mov     bx,ds
        mov     Ring0_EH_EC,bx                  ; ss for lss
        pop     ds
        pop     ax
        pop     bx
        pop     cx
.386p
        lss     sp,[bp]                         ; switch stack
.286p
        pop     bp
        retf                                    ; Out of here.

pmfa_bad_stack:

if      DEBUG
        mov     ax,ss
        mov     bx,sp
        Trace_Out       "Fault Handler Aborting with SS:SP = #AX:#BX"
        pop     ax
        sub     ax, (offset DXPMCODE:PMFaultEntryVector) + 3
        mov     bx,3
        div     bl
        Trace_Out       "Fault Number #AX"
        pop     ax
        pop     bx
        pop     cx
        pop     dx
        Trace_Out       "First four stack words: #AX #BX #CX #DX."
endif
        push    selDgroupPM
        push    offset DGROUP:rgwStack
        rpushf
        push    SEL_DXPMCODE or STD_RING
        push    offset DXPMCODE:pmfr_death
        riret
pmfr_death:
        mov     ax,cs
        mov     ds,ax
        mov     dx,offset szRing0FaultMessage
        pmdossvc        09h
        jmp     PMAbort

PMFaultAnalyzer  endp

FR_Stack        Struc
        FR_BP           dd      ?
        FR_AX           dd      ?
        FR_BX           dd      ?
        FR_DS           dw      ?
        FR_ENTRY        dd      ?       ; SS:[SP] points here on entry
                                        ; to PMReservedReflector
        FR_Toss         dd      ?       ; DPMI return IP
        FR_Ret_IP       dd      ?       ; actual fault handler gets put
        FR_Ret_CS       dd      ?       ; here to return to
        FR_IP           dd      ?
        FR_CS           dd      ?
        FR_FL           dd      ?
        FR_SP           dd      ?
        FR_SS           dd      ?
FR_Stack        Ends
;
; Alternate names so the structure above makes more sense to
; PMFaultReflector.
;
FR_Handler_IP           equ     FR_DS
FR_Handler_CS           equ     FR_ENTRY
FR_Handler_Ret_IP       equ     FR_Toss
FR_Handler_Ret_CS       equ     FR_Ret_IP
FR_Handler_Entry        equ     FR_Handler_Ret_CS
FR_EC                   equ     FR_Ret_CS

; ------------------------------------------------------------------
; PMFaultReflector -- Dispatch a fault to a fault handler installed
;       in PMFaultVector.  When the fault handler returns, return
;       to the faulting code, using the addresses placed on the
;       DPMI fault handler stack by the last called fault handler.
;
;   Input:
;       Entry is by a NEAR call, with an IP within the range
;       of PMFaultEntryVector on the stack.  The stack has been
;       set up for use by a DPMI fault handler.
;
;   Output:
;       Controlled by fault handler.
;
;   Uses:
;       Controlled by fault handler.
;
;   Notes:
;       Fault handlers are called on a static stack.  This routine
;       is NOT REENTRANT.
;
        public  PMFaultReflector
        public  PMFaultReflectorIRET
PMFaultReflector        proc    near
        assume  ss:nothing,ds:nothing,es:nothing

        sub     sp,6
        push    bx
        push    eax
        push    bp
        mov     bp,sp
        push    ds
        mov     ax,SEL_DXDATA or STD_RING
        mov     ds,ax
        assume  ds:dgroup
        mov     ax,[bp.FR_Handler_Entry]
        sub     ax,offset DXPMCODE:PMFaultEntryVector+3
        mov     bl,3
        div     bl                      ; AX = interrupt number
        shl     ax,3                    ; AX = offset of fault handler

        lea     bx,PMFaultVector
        add     bx,ax                   ; SS:[BX] -> fault vector entry
        mov     eax,word ptr ds:[bx]
        mov     [bp.FR_Handler_IP],eax
        mov     ax,word ptr ds:[bx+2]
        mov     [bp.FR_Handler_CS],ax

        lea     ax,pmfr_cleanup
        movzx   eax,ax
        mov     [bp.FR_Handler_Ret_IP],eax
        push    cs
        pop     [bp.FR_Handler_Ret_CS]

        pop     ds
        assume  ds:nothing
        pop     bp
        pop     ax
        pop     bx
        db 066h,0CBh                    ; This calls the fault handler.

PMFaultReflectorIRETCall:
        dd      (SEL_RZIRET or STD_RING) shl 10h

pmfr_cleanup:
;
; Unwind the fault handler stack.  Return to the faulting code.
; This works by calling a Ring 0 procedure to do the actual IRET.
; If we do it that way, we can return to the faulting code without
; actually touching the faulting code's stack.
;
PMFaultReflectorIRET:

        ; BUGBUG Daveh using user stack this way is less robust!!!

.386p
        add     sp,4                    ; pop error code
        push    bp
        mov     bp,sp
        push    ebx
        push    ds
        push    eax
        mov     ax,[bp + 18]
        mov     ds,ax
        mov     ebx,[bp + 14]           ; ds:bx -> user stack
        sub     ebx,4
        mov     eax,[bp + 10]
        mov     ds:[ebx],eax            ; push flags
        sub     ebx,4
        mov     eax,[bp + 4]
        mov     ds:[ebx],eax            ; push cs
        sub     ebx,4
        mov     eax,[bp + 2]
        mov     ds:[ebx],eax            ; push ip
        sub     ebx,2
        mov     ax,[bp]
        mov     ds:[ebx],ax             ; push bp
        mov     [bp + 8],bx
        pop     eax
        pop     ds
        pop     ebx
        pop     bp

        add     sp,6                    ; point to ss:sp
        mov     bp,sp

        lss     esp,[bp]
.286p
        pop     bp                      ; restore bp
        riretd
endif
PMFaultReflector        endp
;
; -------------------------------------------------------
;   PMReservedReflector -- This routine is for reflecting
;       exceptions to a protected mode interrupt handler.
;       The default for exceptions 1, 2, and 3 is to have
;       a near call to this routine placed in the PMFaultVector.
;
;       This routine strips off the fault handler stack set
;       up by PMFaultAnalyzer, switches to the stack pointed
;       to by the pushed SS and SP values, sets up an IRET
;       frame for use by PMIntrReflector, and jumps to
;       PMIntrReflector.  Eventual return is via an IRET
;       from PMIntrReflector.
;
;   Input:
;       Entry is by a NEAR call, with an IP within the range
;       of PMReservedEntryVector on the stack.  The stack has been
;       set up for use by a DPMI fault handler.
;
;   Output:
;       Switch to stack registers set up by any previous fault
;       handler, jump to PMIntrReflector with an IRET frame set up
;       for direct return to the interrupted code.
;
;   Errors: none
;
;   Uses:   Modifies SS, SP.  Does not return to caller.
;

        assume  ds:NOTHING,es:NOTHING,ss:NOTHING
        public  PMReservedReflector
PMReservedReflector:

        push    ds
        push    ebx
        push    eax
        push    ebp
        mov     bp,sp
;
; BP now points to a stack frame described by the structure
; above.  This will be copied to a stack frame on the stack pointed to
; by FR_SS:FR_SS.  In most cases, the destination stack is actually
; the same as the present stack, offset by four bytes.  The following
; block of code is therefore very likely an overlapping copy.  Think
; carefully before modifying how it works.
;

        mov     bx,[bp.FR_SS]
        mov     ds,bx
        mov     ebx,[bp.FR_SP]  ; DS:[BX] -> interrupted code's stack
        sub     bx, (size FR_Stack) - 8         ; (not copying SP or SS)
                                ; DS:[BX] -> place to copy our stack frame

        mov     eax,[bp.FR_FL]  ; Push user IRET frame onto the destination
        mov     [ebx.FR_FL],eax ; stack.
        mov     eax,[bp.FR_CS]
        mov     [ebx.FR_CS],eax
        mov     eax,[bp.FR_IP]
        mov     [ebx.FR_IP],eax

        mov     eax,[bp.FR_ENTRY]       ; Copy our caller's near return.
        mov     [ebx.FR_ENTRY],eax

        mov     ax,[bp.FR_DS]   ; Copy saved registers.
        mov     [ebx.FR_DS],ax
        mov     eax,[bp.FR_BX]
        mov     [ebx.FR_BX],eax
        mov     eax,[bp.FR_AX]
        mov     [ebx.FR_AX],eax
        mov     eax,[bp.FR_BP]
        mov     [ebx.FR_BP],eax

        mov     ax,ds           ; Switch to user stack.
        mov     ss,ax
        mov     esp,ebx
        mov     ebp,esp

        mov     ax,[ebp.FR_ENTRY]        ; AX = offset of caller
        sub     ax,offset DXPMCODE:PMReservedEntryVector + 3
        mov     bl,3
        div     bl                      ; AX = interrupt number
        shl     ax,2                    ; AX = offset into PMIntelVector
        mov     ds,SelDgroupPM
        assume  ds:DGROUP
        lea     bx,PMIntelVector
        add     bx,ax                   ; DS:[BX] -> interrupt handler

        mov     eax,[bx]                 ; Place vector entry just below
        mov     [ebp.FR_Ret_IP],eax       ; IRET frame.
        mov     eax,[bx+4]
        mov     [ebp.FR_Ret_CS],eax

        lea     esp,[ebp.FR_BP]           ; Point to saved registers.
        pop     ebp                      ; Pop 'em.
        pop     eax
        pop     ebx
        pop     ds
        add     esp,4                    ; Fix up stack.

        db 066h,0CBh              ; jump to interrupt handler via far return


DXPMCODE    ends

; -------------------------------------------------------
        subttl  Real Mode Interrupt Reflector
        page
; -------------------------------------------------------
;           REAL MODE INTERRUPT REFLECTOR
; -------------------------------------------------------

DXCODE  segment
        assume  cs:DXCODE
; -------------------------------------------------------
;   RMIntrEntryVector   -- This table contains a vector of
;       near jump instructions to the real mode interrupt
;       reflector.  Real mode interrupts that have been hooked
;       by the protected mode application have their vector
;       set to entry the real mode reflector through this table.

        public      RMIntrEntryVector

RMIntrEntryVector:

        rept    256
        call    RMIntrReflector
        endm

; -------------------------------------------------------
;   RMIntrReflector -- This routine is the entry point for
;       the real mode interrupt reflector.  This routine
;       is entered when an interrupt occurs (either software
;       or hardware) that has been hooked by the protected mode
;       application.  It switches the processor to protected mode
;       and transfers control to the appropriate interrupt
;       service routine for the interrupt.  After the interrupt
;       service routine completes, it switches back to real
;       mode and returns control to the originally interrupted
;       real mode code.
;       Entry to this routine comes from the RMIntrEntryVector,
;       which contains a vector of near call instructions, which
;       all call here.  The interrupt number is determined from
;       the return address of the near call from the interrupt
;       entry vector.
;       The address of the protected mode interrupt service routine
;       to execute is determined from the protected mode interrupt
;       descriptor tabel and the interrupt number.
;
;   Input:  none
;   Output: none
;   Errors: none
;   Uses:   The segment registers are explicitly preserved by
;           this routine.  Other registers are as preserved or
;           modified by the interrutp service routine.

        assume  ds:NOTHING,es:NOTHING,ss:NOTHING
        public  RMIntrReflector

RMIntrReflector:
;
; On entry, the stack layout is:
;   [6] FLAGS   -        "
;   [4] CS      -        "
;   [2] IP      - from original interrupt
;   [0] IP      - from interrupt entry vector call
;
	FCLI
        cld
        push    ds
IFDEF   ROM
        SetRMDataSeg
ELSE
        mov     ds,selDgroup
ENDIF
        assume  ds:DGROUP
if DEBUG
;
; Are we on a DOSX interrupt reflector stack?
;
        push    ax
        push    cx
        mov     ax,ss
        mov     cx,ds
        cmp     ax,cx
        pop     cx
        jne     @F

        cmp     sp,offset bReflStack
        jb      @F
        cmp     sp,offset pbReflStack
        jnb     @F
;
; If so, have we overflowed a stacklet?
;
        mov     ax,pbReflStack
        cmp     sp,ax
        ja      @F
        add     ax,CB_STKFRAME
        cmp     sp,ax
        jb      @F
        pop     ax
        Real_Debug_Out "DOSX:RMIntrReflector--Reflector stack overflow."
        push    ax
@@:
        pop     ax
endif ;DEBUG
        mov     regUserAX,ax    ;save user AX for later
        push    bp              ;stack ->   BP  DS  IP  IP  CS  FL
        mov     bp,sp           ;           [0] [2] [4] [6] [8] [A]
        mov     ax,[bp+0Ah]     ;get the interrupted routine's flags
        and     ax,NOT 4100h    ;clear the trace flag in case we got
                                ; an interrupt on an instruction about
                                ; to be single stepped
        mov     regUserFL,ax    ;and save for later
        mov     ax,es
        xchg    ax,[bp+4]       ;save ES and get entry vector address
        pop     bp

; Some software (like older versions of Smartdrv.sys) may enable A20 on
; their own, and get very 'suprised' to find it turned off by our PM->RM
; mode switch.  If they used Himem.sys, this wouldn't be necessary, but...

if VCPI
        cmp     fVCPI,0
        jnz     @f
endif
        push    ax              ;get/save current A20 state on stack
        push    bx
        xmssvc  7
        mov     regUserSP,ax    ;use regUserSP as a temp var
        pop     bx
        pop     ax
@@:
        push    regUserSP

; The state that we want to save on the user's stack has been set up.
; Convert the entry vector return address into an interrupt number.

        sub     ax,offset RMIntrEntryVector+3
        push    cx
        mov     cl,3
        div     cl
        pop     cx

if DEBUG
        mov     PMIntNo,ax
endif

; Allocate a new stack frame, and then switch to the reflector stack
; frame.

        mov     regUserSP,sp    ;save entry stack pointer so we can
        mov     regUSerSS,ss    ; switch to our own stack
IFDEF   ROM
        push    ds
        pop     ss
ELSE
        mov     ss,selDgroup    ;switch to the reflector stack frame
ENDIF
        mov     sp,pbReflStack
        push    pbReflStack     ;save stack frame ptr on stack
        sub     pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame

; We are now running on our own stack, so we can switch into protected mode.

        push    ax              ;save interrupt vector table offset
        SwitchToProtectedMode
        pop     ax

if      DEBUG   ;--------------------------------------------------------

        push    0DEADh          ;debugging id & interrupt number
        push    PMIntNo

        cmp     fTraceReflect,0
        jz      @f
        push    ax
        mov     ax,PMIntNo
        Trace_Out "(rp#AL)",x
        pop     ax
@@:

; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
; reflector stack.

        cmp     StackGuard,1022h
        jz      @f
        Debug_Out "DOSX:RMIntrReflector--Global reflector stack overflow."
@@:
endif   ;DEBUG  ---------------------------------------------------------

; Build an IRET frame on the stack so that the protected mode interrupt service
; routine will return to us when it is finished.

        push    regUserSS       ;save user stack address on our own stack
        push    regUserSP       ; frame so we can restore it later
        push    ds
        push    regUserFL
        push    cs
        push    offset rmrf50

; Build an IRET frame on the stack to use to transfer control to the
; protected mode ISR

        and     byte ptr regUserFL+1,not 02h    ;use entry flags less the
        push    0                               ; high half esp
        push    regUserFL                       ;  interrupt flag (IF)

        xchg    bx,ax           ;interrupt vector offset to BX, preserve BX
        cmp     bx,CRESERVED    ;Interrupt in reserved range?
        jc      rmrf_reserved
        shl     bx,3
        mov     es,selIDT
        jmp     rmrf_setISR
rmrf_reserved:
        shl     bx,2
        mov     es,SelDgroupPM
        add     bx,offset DGROUP:PMIntelVector
rmrf_setISR:
        push    dword ptr es:[bx+4] ;push segment of isr
        push    dword ptr es:[bx]   ;push offset of isr
        xchg    bx,ax
        mov     ax,regUserAX    ;restore entry value of AX
        push    ds
        pop     es

; At this point the interrupt reflector stack looks like this:
;
;   [18]    previous stack frame pointer
;   [16]    stack segment of original stack
;   [14]    stack pointer of original stack
;   [12]    protected mode dos extender data segment
;   [10]    dos extender flags
;   [8]     segment of return address back to interupt reflector
;   [6]     offset of return address back to interrupt reflector
;   [4]     user flags as on entry from original interrupt
;   [2]     segment of protected mode ISR
;   [0]     offset of protected mode ISR
;
; Execute the protected mode interrupt service routine

        iretd

; The protected mode ISR will return here after it is finsished.

rmrf50: pop     ds
        pushf                   ;save flags as returned by PM Int routine

	FCLI			 ;We have to clear interrupts here, because
        cld                     ; the interrupt routine may have returned
                                ; with interrupts on and our code that uses
                                ; static variables must be protected.  We
                                ; turn them off after to pushf instruction so
                                ; that we can preserve the state of the
                                ; interrupt flag as returned by the ISR.
        mov     regUserAX,ax
        pop     ax
        pop     regUserSP
        pop     regUserSS

if DEBUG
        add     sp,4            ;'pop' off debugging info
endif

        pop     pbReflStack     ;deallocate stack frame(s)

; Switch back to real mode.

        push    ax              ;preserve AX
        SwitchToRealMode
        pop     ax

; Switch back to the original stack.

        mov     ss,regUserSS
        mov     sp,regUserSP

; Make sure the A20 line matches whatever state it was when the int occured.
; This is for the benefit of any software that diddles A20 without using
; an XMS driver

        pop     regUserSP       ;A20 state at time of interrupt to temp var
if VCPI
        cmp     fVCPI,0
        jnz     rmrf75
endif
        push    ax              ;save current ax
        mov     ax,regUserSP    ;ax = A20 state at time of interrupt
        or      ax,ax           ;if it was off, don't sweat it
        jz      rmrf70
        push    bx              ;save bx (XMS calls destroy bl)
        push    ax
        xmssvc  7               ;ax = current A20 state
        pop     bx              ;bx = old A20 state
        cmp     ax,bx           ;if A20 is still on, don't need to diddle
        jz      @f
        xmssvc  5               ;force A20 back on
        inc     A20EnableCount  ;  and remember that we did this
if DEBUG
        or      fA20,04h
endif
@@:
        pop     bx
rmrf70:
        pop     ax
rmrf75:

; Put the flags returned by the real mode interrupt routine back into
; the caller's stack so that they will be returned properly.

        push    bp              ;stack ->   BP  DS  ES  IP  CS  FL
        mov     bp,sp           ;           [0] [2] [4] [6] [8] [10]
        and     [bp+10],0300h   ;clear all but the interrupt and trace flags
                                ; in the caller's original flags
        or      [bp+10],ax      ;combine in the flags returned by the
                                ; interrupt service routine.  This will cause
                                ; us to return to the original routine with
                                ; interrupts on if they were on when the
                                ; interrupt occured, or if the ISR returned
                                ; with them on.
        pop     bp

; And return to the original interrupted program.

        mov     ax,regUserAX
        pop     ds
        pop     es
        iret

DXCODE  ends

WowIntr3216 proc

	FCLI
        push    ebp
        mov     ebp,esp
        mov     regUserAX,eax
        mov     regUserBX,ebx
        mov     regUserDS,ds
        mov     regUserES,es
        mov     regUserFlags,[ebp + 16]
        mov     ax,SEL_DXDATA OR STD_RING
        mov     ds,ax
        assume ds:dgroup
        mov     ebx,esp
        mov     regUserSp,eax
        mov     ax,ss
        mov     regUserSs,ax
        mov     bx,pbReflStack                  ; get pointer to new frame
        sub     bpReflStack,CB_STKFRAME

;
; put user stack pointer on new stack
;
        sub     bx,4
        mov     [bx],regUserSs
        sub     bx,4
        mov     [bx],dword ptr regUserSp

        mov     ax,[ebp + 4]

;
; switch to new stack
;

        push    ds
        pop     ss
        movzx   esp,bx

;
; Save ss:esp for lss esp
;
        push    regUserSs
        push    regUserSp
;
; Create an int frame
;
        pushf
        push    cs
        push    offset wi30

;
; Put handler address on stack
;

        shl     ax,3
        mov     es,selDgroupPM
        add     bx,offset DGROUP:Intr16Vector
        push    [bx + 2]
        push    [bx]

;
; Restore Registers
;
        mov     eax,regUserAx
        mov     ebx,regUserBX
        mov     es,regUserES
        mov     ds,regUserDS
        assume ds:nothing
;
; call handler
;
        retf

wi30:
;
; handler will return here
;

;
; Switch stacks
;
        mov     ebp,esp
        lss     esp,[ebp]
        mov     ebp,esp

        push    eax
        push    ds
        mov     ds,SEL_DXDATA OR STD_RING
        assume ds:DGROUP

;
; Deallocate stack frame
;
        add     pbReflStack,CB_STKFRAME
        pop     eax
        pop     ds

;
; Return flags from int handler
;
        pushf
        pop     [ebp + 16]

;
; Return to interrupted code
;
        pop     ebp
        riretd

WowIntr3216 endp