
PAGE    ,132
NAME    TRAP
TITLE   Exception and Interrupt Handlers for Protected Mode

SCCSID  equ     "@(#)trap.asm   6.47 92/03/28"

;**     TRAP.ASM
;
;      Version 1.5.4.3, for trap.asm, task, c.krnl
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989, 1996
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       DESCRIPTION
;
;       These routines are entered when an internal or external
;       interrupt occurs.  They do some minimal processing and
;       dispatch to other routines.
;
;       NOTES
;
;       "tsf_esp" must not be used directly because the high word
;       may not be valid if from a 16 bit program.  If the high
;       word is zero, no problem; otherwise, check the B bit in
;       the SS selector and zero the high word if set.
;
; 09/18/91  Khoa Dang Huynh
;       CP20 DCR 1486  Provide selector packing capability.
; 10/29/91  Steve Best
;       CP20 B729110   Remove vdm perfview counter
; 12/13/91  Bill Madden         6.31
;       CP20 B729845   Fixed problem in RebuildStackFrame
; 01/21/92  Bill Madden         6.33
;       CP20 B731032   Reflect int 01 and int 03 only if VDM has hooked them
; 02/29/92  Bill Madden         6.41
;       CP20 B733602   Reflect trap 0b's to VDM that occur when returning to a
;                      DPMI app.
; 07/20/92  Bill Madden         1.4
;       CP20   26154   Reflect fatal page faults to DPMI apps.
; 11/12/92  Bill Madden         1.5.1.4
;       CP20   54268   Insure DPMI app CS:EIP is valid before emulating inst.
; 08/27/93  Charlie Schmitt
;       CP20   72252   Back out previous change in KernelFaultEntry
; 01/06/94  Change Team
;              74409   Skip F_TRACE flag check in TRAP0D if VME enabled.
; 03/09/94  Franz Tigges        F78693      shipped under  defect      ;78486
;       CP20   F78693  No interrupts while display fo IPE popups       ;78486
; 03/09/94  Franz Tigges        F78693      shipped under  defect      ;78486
;       CP20   F78693  Prevent recursive IPE popups in systemFatalFault;78486
; 03/09/94  Arthur Roberts      78486                                   78486
;       CP20   78486   Registered Kernel Exit Support                   78486
; 06/02/94  Arthur Roberts      83839 merged 78486 into above releases  83839
;       CP20   83839   Registered Kernel Exit receiving break point and 83839
;       CP20   83839   single step notifications when using debug krnl  83839
; 06/17/94  TPL        fix to 83839 to prevent IPE                      86946
; 08/01/94  Arthur Roberts      91568 Merge SMP DEFECT 83230 and add checking
; 10/01/94  Arthur Roberts      94815 Remove check for DUMP_ON to       94815
;                      support CTRL-ALT-NUMLOCK-NUMLOCK, to prevent     94815
;                      SCSI HANG. This code is also in SUBR.ASM         94815
; 11/17/94   Change Team Roll defect 91744 into r207 sparse tree.      105282
;              91744   SystemFatalFault not always being passed trapnum in EAX
;
; 06/14/95  Change Team Added cld in FormatRegDump to ensure that the   124620
;                      direction flag is cleared before processing.     124620
; 06/23/95 Change Team - Retain addressability to KSF in                116367
;                        UserSegmentNPFault                             116367
; 08/21/95 Change Team - Moved code from NMIFault to NWDHandler in      122474
;                        nwd.asm to avoid tss editing on user svcd NMI  122474
; 02/29/96 Gordon Kuo  - Add support for VDPMI 1.0 Exception Handler    149007
;                        include CR2, V86 exception
; 06/06/96 GCM         - Fixed problem related to I/O breakpoints       157516


INCL_ERROR_H    EQU     1               ; for error.inc  EQUates
INCL_ERROR2_H   EQU     1               ; for error2.inc EQUates
INCL_VDH        EQU     1               ; for flVDMStatus flags

.xlist
        include dosseg.inc
        include basemaca.inc
        include fnotask.inc
        include osmaca.inc
        include dossymc.inc
        include osconst.inc
        include infoseg.inc
        include sigdef.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include tkexport.inc
        include curdir.inc
        include dirent.inc
        include screen.inc
        include sem.inc
        include find.inc
        include except.inc
        include tkptda.inc
        include protmode.inc
        include seldesc.inc
        include kmode.inc
        include basemid.inc
        include exec.inc
        include timer.inc
        include proc.inc
        include bseerr.inc
        include mi.inc
        include dbgex.inc
        include dynxport.inc
        include except32.inc
        include doshlp.inc
        include mmpha.inc
        include bsetib.inc
        include tklocal.inc
;       include perfvw.inc                                             ;b728xxx
        include exmg.inc
        include mvdm.inc
        include iodelay.inc                                            ; 83230
IF RAS                                                                 ; 78486
        include krnlexit.inc                                           ; 78486
ENDIF                                                                  ; 78486

.list

        ModName  ,FNO_TRAP_ASM
        CpuMode 386

        ReferFlat       _tkPanicActive,BYTE                            ;F78693
        ReferFlat       apfnOpcode,DWORD
        ReferFlat       flPrefix,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pTSDCur,DWORD
        ReferFlat       _SELGDTLaddr,DWORD
        ReferFlat       _fB1NPX,DWORD
        ReferFlat       _flVdmStatus,DWORD
        ReferFlat       privateStack,DWORD                              ;b726833
        ReferFlat       os2Stack,FWORD                                  ;b726833
        ReferFlat       _pfntkDosExit,DWORD
        ReferFlat       _vpvdmIRet,DWORD

        ReferGDT        GDT_DFTSS,QWORD
        ReferGDT        GDT_DFSTACK,QWORD
        ReferGDT        GDT_NWDTSS,QWORD
        ReferGDT        GDT_TSS,WORD                                   ; 78486

        ReferGlobal     TSS,BYTE
        ReferGlobal     InDos,BYTE
        ReferGlobal     InterruptLevel,DWORD
        ReferGlobal     TaskNumber,DWORD
        ReferGlobal     ABIOS_present,BYTE
        ReferGlobal     _CpuType,DWORD
        ReferGlobal     pemulahndlr,DWORD
        ReferGlobal     DHF,DWORD
        ReferGlobal     NWDTss, byte
        ReferGlobal     fNWD, byte
        ReferGlobal     fNWDEnabled, byte
        ReferGlobal     DUMP_ON,BYTE                                   ;83230

        EXTRN16         OutMessage
        EXTRN16         FindDevName
        EXTRN16         IntNMI
        EXTRN16         DelayHardErr
        EXTRN16         EnterCritSecHoldSig
        EXTRN16         ExitCritSecUnHoldSig
        EXTRN16         NWDDisableWatchdogNMI
        EXTRN16         WrtToScrn
        EXTRN16         ClearAllInts                                   ;F78693
        EXTRN16         ByteToScrnDM
        EXTRN16         DisableNMI
        EXTRN16         EnableNMI
        EXTRN16         DelayHardErrTrap2

        EXTRN32         SwitchNPX
        EXTRN32         HexToAscii32
        EXTRN32         _IntToDecAscii
        EXTRN32         EM86ReflectV86Exception
        EXTRN32         EM86ReflectVPMException
        EXTRN32         VDHCHECKVPMEXCEPT                       ;6.33
        EXTRN32         VDHCHECKVPMV86EXCEPT                    ;GK 6.33 149007
        EXTRN32         EM86opARPL
        EXTRN32         KMREBUILDSTACKFRAME
        EXTRN32         KMQUERYCLIENTREGISTER
        EXTRN32         KMQUERYSSESPLADDR
        EXTRN32         KMQUERYCSEIPLADDR
        EXTRN32         KMSETCLIENTREGISTER
        EXTRN32         KMPUSHCLIENTSTACK
        EXTRN32         KMPOPCLIENTSTACK
        EXTRN32         _PGPageFault

ifdef INCDEBUG
        EXTRN32         _DbgTrap                         ; d83278
        EXTRN32         DbgWPScan
endif

        EXTRN32         _XCPTBuildR3DispatcherStack
        EXTRN32         NWDHandler
        EXTRN32         _DHGetMessage32
        EXTRN32         _TKFuBuff
        EXTRN32         _SELVirtToLin:NEAR
        EXTRN32         _selGetLDTAddr:NEAR
        EXTRN32         B1NPX_Errata_02
        EXTRN32         SELLoadSegment
;;;;;;; EXTRN           _VMObjectInfo:near
        EXTRN           _VMGetOd:near
        EXTRN           NWDAccessTSS:near                  ; 78486
        EXTRN32         _PGTrap2FindPage:NEAR
        EXTRN32         _TKExit
        EXTRN           KMExitKmodeEvents:near

IF      RAS
        ReferFlat       _pKExitListHead,DWORD              ;  78486
        EXTRN16         Call16bitKrnlExitDD                ;  78486
        ReferFlat       _DYNNullResetPoint,DWORD
        ReferFlat       _pbDYNResetPoint,DWORD
        ReferFlat       _DYNSavedUserFlags,DWORD
        ReferFlat       _fDYNActive,DWORD
        EXTRN32 _DYNRecordPoint
        EXTRN32 _DYNTrap3
        EXTRN16         K_S_Systrace
ENDIF ; RAS

IFDEF   PMDEBUG
        EXTRN16         _Debug_Trap32
        EXTRN16         _Debug_Fatal
        EXTRN32         TMR32AddTime
        ReferFlat       GoTimeL,DWORD
        ReferFlat       GoTimeH,DWORD
IFDEF   PGSTRICT
        ReferFlat       _apfi,DWORD
ENDIF
ENDIF

DATA segment

;*   Fault in device driver message

GPFDevMsg       db      'Exception in device driver: '
GPFDevName      db      '        ',0ah,0dh      ; buffer for bad device name
GPFDevMsgLen    equ     $ - GPFDevMsg

;*   Register dump message

RegMsg          label   byte
                db      'TRAP '
TRAPval         db      'XXXX       '

                db      'ERRCD='
ERRCval         db      'XXXX  '

                db      'ERACC='
ERRCacc         db      'XXXX  '

                db      'ERLIM='
ERRClim         db      'XXXXXXXX'

CrLfMsg         label   byte
                db      c_CR, c_LF
CrLfMsgLen      equ     $ - CrLfMsg

FaultMsg        label   byte
                db      'EAX='
EAXval          db      'XXXXXXXX  '

                db      'EBX='
EBXval          db      'XXXXXXXX  '

                db      'ECX='
ECXval          db      'XXXXXXXX  '

                db      'EDX='
EDXval          db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'ESI='
ESIval          db      'XXXXXXXX  '

                db      'EDI='
EDIval          db      'XXXXXXXX  '

                db      'EBP='
EBPval          db      'XXXXXXXX  '

                db      'FLG='
EFLAGval        db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'CS:EIP='
CSval           db      'XXXX:'
EIPval          db      'XXXXXXXX  '

                db      'CSACC='
CSacc           db      'XXXX  '

                db      'CSLIM='
CSlimit         db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'SS:ESP='
SSval           db      'XXXX:'
ESPval          db      'XXXXXXXX  '

                db      'SSACC='
SSacc           db      'XXXX  '

                db      'SSLIM='
SSlimit         db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'DS='
DSval           db      'XXXX  '

                db      'DSACC='
DSacc           db      'XXXX  '

                db      'DSLIM='
DSlimit         db      'XXXXXXXX  '

                db      'CR0='
CR0val          db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'ES='
ESval           db      'XXXX  '

                db      'ESACC='
ESacc           db      'XXXX  '

                db      'ESLIM='
ESlimit         db      'XXXXXXXX  '

                db      'CR2='
CR2val          db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'FS='
FSval           db      'XXXX  '

                db      'FSACC='
FSacc           db      'XXXX  '

                db      'FSLIM='
FSlimit         db      'XXXXXXXX'
                db      c_CR, c_LF

                db      'GS='
GSval           db      'XXXX  '

                db      'GSACC='
GSacc           db      'XXXX  '

                db      'GSLIM='
GSlimit         db      'XXXXXXXX'
RegMsgLen       equ     $ - RegMsg
                db      0               ; null terminate "RegMsg" string also
IF2
;;IF (RegMsgLen+1 GT HEMaxSubsLen)              ; Add 1 for the above null
IF (RegMsgLen+1 GT 512)         ; Add 1 for the above null
        %out    Trap register dump string too long
ENDIF
ENDIF

;*   Double fault handling support

ALIGN 4
                public  DFSTKLEN, DFStackBase
DFSTKLEN        equ     200H

DFStackBase     label byte
                db      DFSTKLEN dup (?)        ; Double Fault Stack
DFStack         label byte

                public  DFTss                   ; Double Fault TSS

DFTss           label   byte
                tss_s   <0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
                offset DOSGROUP:TSS, DFSTKLEN, 0, 0, 0, seg FLAT:DGROUP, 0, \
                seg FLAT:DOSHIGH32CODE, 0, GSEL GDT_DFSTACK, 0, DOSGROUP, 0, \
                0, 0, 0, 0, 0, 0, 0, -1>

NMIMsg          db      'XXXXXXXX'
                db      0

pksf            dd      ?       ;Preserve pointer to KSF                116367

DATA ends

_DATA   segment

;*   Address of kernel exit routines for stack frame rebuilding

                        public  _fpfnTRAPKernelExit, _fpfnVDMKernelExit
_fpfnTRAPKernelExit     dd      offset FLAT:OS2PMFaultExit
                        dw      seg FLAT:OS2PMFaultExit
_fpfnVDMKernelExit      dd      offset FLAT:VDMFaultExit
                        dw      seg FLAT:VDMFaultExit

;*   User install handler PTDA table index

TrapNumToIndex  label   dword
                dd      EXCDIVZI * (type Exc_vec)       ; 0
                dd      0                               ; 1
                dd      0                               ; 2
                dd      0                               ; 3
                dd      EXCOVI * (type Exc_vec)         ; 4
                dd      EXCBOUNDI * (type Exc_vec)      ; 5
                dd      EXCOPI * (type Exc_vec)         ; 6
                dd      EXCNOFPI * (type Exc_vec)       ; 7
                dd      0                               ; 8
                dd      0                               ; 9
                dd      0                               ; A
                dd      0                               ; B
                dd      0                               ; C
                dd      0                               ; D
                dd      0                               ; E
                dd      0                               ; F
                dd      EXCFPERRI * (type Exc_vec)      ; 10


;*  Trap Number to (32-bit) Exception Number Translation Table

TrapNumToExceptNum label   dword
                dd      XCPT_INTEGER_DIVIDE_BY_ZERO     ; 0
                dd      XCPT_SINGLE_STEP                ; 1
                dd      0                               ; 2
                dd      XCPT_BREAKPOINT                 ; 3
                dd      XCPT_INTEGER_OVERFLOW           ; 4
                dd      XCPT_ARRAY_BOUNDS_EXCEEDED      ; 5
                dd      XCPT_ILLEGAL_INSTRUCTION        ; 6
                dd      0                               ; 7
                dd      0                               ; 8
                dd      XCPT_ACCESS_VIOLATION           ; 9
                dd      0                               ; A (access violation?)
                dd      XCPT_ACCESS_VIOLATION           ; B (???)
                dd      XCPT_ACCESS_VIOLATION           ; C (???)
                dd      -1                              ; D (special)
                dd      XCPT_IN_PAGE_ERROR              ; E
                dd      0                               ; F
                dd      -1                              ; 10 (special)
                dd      XCPT_DATATYPE_MISALIGNMENT      ; 11


;*   User program fault message numbers

TrapMsgNum      label   dword
                dd      MSG_TRAP0
                dd      MSG_TRAP1
                dd      MSG_TRAP2
                dd      MSG_TRAP3
                dd      MSG_TRAP4
                dd      MSG_TRAP5
                dd      MSG_TRAP6
                dd      MSG_TRAP7
                dd      MSG_TRAP8
                dd      MSG_TRAP9
                dd      MSG_TRAPA
                dd      MSG_TRAPB
                dd      MSG_TRAPC
                dd      MSG_TRAPD
;; BUGBUG - remove the IFDEF once basemid.h and oso001.txt are fixed
                dd      MSG_TRAPE
TrapMsgNumCnt   equ     ($ - TrapMsgNum)/2

;**     UFaultFmt, UFaultLimAccFmt
;
;       User code (ProcessFatalFault) fault register dump format tables

UFaultFmt       label   dword
                dd      KM_REG_EAX, offset FLAT:EAXval, 4
                dd      KM_REG_EBX, offset FLAT:EBXval, 4
                dd      KM_REG_ECX, offset FLAT:ECXval, 4
                dd      KM_REG_EDX, offset FLAT:EDXval, 4
                dd      KM_REG_EBP, offset FLAT:EBPval, 4
                dd      KM_REG_ESI, offset FLAT:ESIval, 4
                dd      KM_REG_EDI, offset FLAT:EDIval, 4
                dd      KM_REG_DS, offset FLAT:DSval, 2
                dd      KM_REG_ES, offset FLAT:ESval, 2
                dd      KM_REG_FS, offset FLAT:FSval, 2
                dd      KM_REG_GS, offset FLAT:GSval, 2
                dd      KM_REG_EFLAG, offset FLAT:EFLAGval, 4
                dd      KM_REG_CS, offset FLAT:CSval, 2
                dd      KM_REG_EIP, offset FLAT:EIPval, 4
                dd      KM_REG_SS, offset FLAT:SSval, 2
                dd      KM_REG_ESP, offset FLAT:ESPval, 4
UFaultFmtLen    equ     ($-UFaultFmt)/12

UFaultLimAccFmt label   dword
                dd      KM_REG_CS, offset FLAT:CSacc, offset FLAT:CSlimit
                dd      KM_REG_DS, offset FLAT:DSacc, offset FLAT:DSlimit
                dd      KM_REG_ES, offset FLAT:ESacc, offset FLAT:ESlimit
                dd      KM_REG_FS, offset FLAT:FSacc, offset FLAT:FSlimit
                dd      KM_REG_GS, offset FLAT:GSacc, offset FLAT:GSlimit
                dd      KM_REG_SS, offset FLAT:SSacc, offset FLAT:SSlimit
UFaultLimAccFmtLen      equ     ($-UFaultLimAccFmt)/12

ErrCodeLimAccFmt        label   dword
                dd      0, offset FLAT:ERRCacc, offset FLAT:ERRClim
ErrCodeLimAccFmtLen     equ     ($-ErrCodeLimAccFmt)/12

;**     KFaultFmt, KFaultLimAccFmt, KFaultFmtSSESP, KFaultLimAccFmtSSESP
;
;       Kernel code (SystemFatalFault) fault register dump format tables

KFaultFmt       label   dword
                dd      ksf_trapnum, offset FLAT:TRAPval, 2
                dd      ksf_errcode, offset FLAT:ERRCval, 2
                dd      ksf_eax, offset FLAT:EAXval, 4
                dd      ksf_ebx, offset FLAT:EBXval, 4
                dd      ksf_ecx, offset FLAT:ECXval, 4
                dd      ksf_edx, offset FLAT:EDXval, 4
                dd      ksf_ebp, offset FLAT:EBPval, 4
                dd      ksf_esi, offset FLAT:ESIval, 4
                dd      ksf_edi, offset FLAT:EDIval, 4
                dd      ksf_ds, offset FLAT:DSval, 2
                dd      ksf_es, offset FLAT:ESval, 2
                dd      ksf_fs, offset FLAT:FSval, 2
                dd      ksf_gs, offset FLAT:GSval, 2
                dd      ksf_eflag, offset FLAT:EFLAGval, 4
                dd      ksf_cs, offset FLAT:CSval, 2
                dd      ksf_eip, offset FLAT:EIPval, 4
KFaultFmtLen    equ     ($-KFaultFmt)/12

KFaultLimAccFmt label   dword
                dd      ksf_errcode, offset FLAT:ERRCacc, offset FLAT:ERRClim
                dd      ksf_cs, offset FLAT:CSacc, offset FLAT:CSlimit
                dd      ksf_ds, offset FLAT:DSacc, offset FLAT:DSlimit
                dd      ksf_es, offset FLAT:ESacc, offset FLAT:ESlimit
                dd      ksf_fs, offset FLAT:FSacc, offset FLAT:FSlimit
                dd      ksf_gs, offset FLAT:GSacc, offset FLAT:GSlimit
KFaultLimAccFmtLen      equ     ($-KFaultLimAccFmt)/12

KFaultFmtSSESP  label   dword
                dd      Segmt32, offset FLAT:SSval, 2
                dd      Offst32, offset FLAT:ESPval, 4
KFaultFmtSSESPLen       equ     ($-KFaultFmtSSESP)/12

KFaultLimAccFmtSSESP    label   dword
                dd      Segmt32, offset FLAT:SSacc, offset FLAT:SSlimit
KFaultLimAccFmtSSESPLen equ     ($-KFaultLimAccFmtSSESP)/12

;**     fpRing0Stack - temporary ring 0 stack holder
;
;       Temporary holder for ring 0 SS:ESP during register
;       display formatting.

fpRing0Stack    df      0

;**     apfnOS2PMFault - OS/2 protected mode (application) fault handlers
;
;       The routine in this table is called after entering kernel
;       mode for OS/2 applications (non-ring 0) faults.
;
;       On entry to these routines:
;
;               (EAX) = trap number
;               (EBX) = -1
;               (EDX) = error code

apfnOS2PMFault  label   dword
        dd      offset FLAT:ProcessFatalFault           ; trap 0 - divide by 0
        dd      offset FLAT:UserTraceFault              ; trap 1 - single step
        dd      offset FLAT:NMIFault                    ; trap 2 - NMI
        dd      offset FLAT:UserBPFault                 ; trap 3 - breakpoint
        dd      offset FLAT:ProcessFatalFault           ; trap 4 - overflow
        dd      offset FLAT:ProcessFatalFault           ; trap 5 - bound inst
        dd      offset FLAT:ProcessFatalFault           ; trap 6 - invalid op
        dd      offset FLAT:UserNPXNAFault              ; trap 7 - NPX not avail
        dd      offset FLAT:SystemFatalFault            ; trap 8 - double fault
        dd      offset FLAT:ProcessFatalFault           ; trap 9 - NPX overrun
        dd      offset FLAT:UserInvalidTSSFault         ; trap A - invalid TSS
        dd      offset FLAT:UserSegmentNPFault          ; trap B - seg not pres
        dd      offset FLAT:UserStackFault              ; trap C - stack fault
        dd      offset FLAT:ProcessFatalFault           ; trap D - GP fault
        dd      offset FLAT:UserPageFault               ; trap E - page fault
        dd      offset FLAT:ProcessFatalFault           ; trap F - reserved
        dd      offset FLAT:ProcessFatalFault           ; trap 10 - NPX error
        dd      offset FLAT:ProcessFatalFault           ; trap 11 - alignment
        dd      offset FLAT:ProcessFatalFault           ; trap 12-1F generic
        .errnz  EXCGENERIC - 12h

;**     apfnV86Fault - V86 mode (VDM) fault handlers
;
;       The routine in this table is called after entering kernel
;       mode for V86 mode VDM applications (non-ring 0) faults.
;
;       On entry to these routines:
;
;               (EAX) = trap number
;               (EBX) = pvsf
;               (EDX) = error code

apfnV86Fault    label   dword
        dd      offset FLAT:EM86ReflectV86Exception     ; trap 0 - divide by 0
        dd      offset FLAT:UserTraceFault              ; trap 1 - single step
        dd      offset FLAT:EM86ReflectV86Exception     ; trap 2 - nmi
        dd      offset FLAT:UserBPFault                 ; trap 3 - breakpoint
        dd      offset FLAT:EM86ReflectV86Exception     ; trap 4 - overflow
        dd      offset FLAT:EM86ReflectV86Exception     ; trap 5 - bound inst
        dd      offset FLAT:EM86ReflectV86Exception     ; trap 6 - invalid op
        dd      offset FLAT:UserNPXNAFault              ; trap 7 - NPX not avail
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap 8 - double fault
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap 9 - NPX overrun
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap A - invalid TSS
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap B - seg not pres
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap C - stack fault
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap D - GP fault
        dd      offset FLAT:UserPageFault               ; trap E - page fault
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap F - reserved
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap 10 - NPX error
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap 11 - alignment
        dd      offset FLAT:ProcessFatalFaultV86    ;149007trap 12-1F generic
        .errnz  EXCGENERIC - 12h

;**     apfnVPMFault - VDM protected mode fault handlers
;
;       The routine in this table is called after entering kernel mode
;       for VDM protected mode applications (non-ring 0) faults.
;
;       On entry to these routines:
;
;               (EAX) = trap number
;               (EBX) = pvsf
;               (EDX) = error code

apfnVPMFault    label   dword
        dd      offset FLAT:EM86ReflectVPMException     ; trap 0 - divide by 0
        dd      offset FLAT:UserTraceFault              ; trap 1 - single step
        dd      offset FLAT:EM86ReflectVPMException     ; trap 2 - nmi
        dd      offset FLAT:UserBPFault                 ; trap 3 - breakpoint
        dd      offset FLAT:EM86ReflectVPMException     ; trap 4 - overflow
        dd      offset FLAT:EM86ReflectVPMException     ; trap 5 - bound inst
        dd      offset FLAT:EM86ReflectVPMException     ; trap 6 - invalid op
        dd      offset FLAT:UserNPXNAFault              ; trap 7 - NPX not avail
        dd      offset FLAT:EM86ReflectVPMException     ; trap 8 - double fault
        dd      offset FLAT:EM86ReflectVPMException     ; trap 9 - NPX overrun
        dd      offset FLAT:EM86ReflectVPMException     ; trap A - invalid TSS
        dd      offset FLAT:EM86ReflectVPMException     ; trap B - seg not pres
        dd      offset FLAT:EM86ReflectVPMException     ; trap C - stack fault
        dd      offset FLAT:EM86ReflectVPMException     ; trap D - GP fault
        dd      offset FLAT:UserPageFault               ; trap E - page fault
        dd      offset FLAT:EM86ReflectVPMException     ; trap F - reserved
        dd      offset FLAT:EM86ReflectVPMException     ; trap 10 - NPX error
        dd      offset FLAT:EM86ReflectVPMException     ; trap 11 - alignment
        dd      offset FLAT:EM86ReflectVPMException     ; trap 12-1F generic
        .errnz  EXCGENERIC - 12h

;**     apfnKernelFault - Kernel fault handlers
;
;       The routine in this table is called for kernel faults
;       (ring 0) after saving the registers in the ksf frame
;       format.
;
;       On entry to these routines:
;
;               (EAX) = trap number
;               (EBX) = pksf
;               (EDX) = error code

apfnKernelFault label   dword
        dd      offset FLAT:SystemFatalFault            ; trap 0 - divide by 0
        dd      offset FLAT:KernelTraceFault            ; trap 1 - single step
        dd      offset FLAT:NMIFault                    ; trap 2 - NMI
        dd      offset FLAT:TRAPDebugFault              ; trap 3 - breakpoint
        dd      offset FLAT:SystemFatalFault            ; trap 4 - overflow
        dd      offset FLAT:SystemFatalFault            ; trap 5 - bound inst
        dd      offset FLAT:SystemFatalFault            ; trap 6 - invalid op
        dd      offset FLAT:UserNPXNAFault              ; trap 7 - NPX not avail
        dd      offset FLAT:SystemFatalFault            ; trap 8 - double fault
        dd      offset FLAT:SystemFatalFault            ; trap 9 - NPX overrun
        dd      offset FLAT:KernelInvalidTSSFault       ; trap A - invalid TSS
        dd      offset FLAT:KernelSegmentNPFault        ; trap B - seg not pres
        dd      offset FLAT:SystemFatalFault            ; trap C - stack fault
        dd      offset FLAT:KernelGPFault               ; trap D - GP fault
        dd      offset FLAT:KernelPageFault             ; trap E - page fault
        dd      offset FLAT:SystemFatalFault            ; trap F - reserved
        dd      offset FLAT:SystemFatalFault            ; trap 10 - copro error
        dd      offset FLAT:SystemFatalFault            ; trap 11 - alignment
        dd      offset FLAT:SystemFatalFault            ; trap 12-1F generic
        .errnz  EXCGENERIC - 12h

IFDEF   PMDEBUG

;***    fTrapToScreen - send kernel fatal faults to screen, not debugger if set
;
                public  fTrapToScreen
fTrapToScreen   dd      0

ENDIF

public _NMIPhysAddr
public _NMIPTDA
_NMIPhysAddr dd      0
_NMIPTDA     dd      0


_DATA   ends

         FARCODE INIT
INITCODE segment

;***EP  TRAPInitRM - Initialize the trap manager
;
;       The IDT has to be initialized by moving the statically
;       initialized flat model offset from the first dword
;       (g_handler) to the g_lowoffset and seperate g_extoffset
;       fields.  The g_sel field is initialized with the flat
;       mode selector (R0CS).
;
;       ENTRY   DS = DOSGROUP
;
;       EXIT    NONE
;
;       USES    EAX, ECX, ESI, Flags
;
;       NOTE    The double fault TSS's EIP field and the NMI
;               TSS's EIP field are being runtime initailized
;               because of a masm bug preventing static
;               "offset FLAT:" in strucs.
;
        ASSUME  DS:DOSGROUP, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure TRAPInitRM,near
        mov     DFTss.tss_eip,offset FLAT:trap08; initialize double fault eip
        mov     NWDTss.tss_eip,offset FLAT:NWDHandler; initialize NMI eip

        mov     esi,offset DOSGROUP:IDT ; (DS:ESI) = start of IDT
        mov     ecx,IDTLEN/(size gate)          ; (ECX) = number of IDT entries

;       (ESI) = starting IDT entry address
;       (ECX) = number of IDT entries

tri10:  mov     al,[esi].g_access
        or      al,al                           ; is it invalid?
        jz      short tri20                     ;  yes, don't do anything

        and     al,D_TYPEMASK
        cmp     al,D_TASKGATE                   ; is it a task gate?
        je      short tri20                     ;  yes, don't do anything

        mov     eax,[esi].g_handler             ; (EAX) = flat relative offset
        shr     eax,16                          ; (AX) = high word of EAX
        mov     [esi].g_extoffset,ax            ; set high word of offset
        mov     [esi].g_sel,seg FLAT:DOSHIGH32CODE ; set selector to flat CS

tri20:  add     esi,(size gate)                 ; (ESI) = next IDT entry
        loop    short tri10
        ret
EndProc   TRAPInitRM


INITCODE ends

           FARCODE HIGH32
HIGH32CODE segment


        EXTRN32         EM86REFLECTEXCEPTION

;***    trapXX - protected mode trap entry points
;
;       These entry points are for internally generated exceptions,
;       such as a general protection fault.  They do not handle
;       external hardware interrupts, or user software interrupts.
;
;       ENTRY   On entry the stack looks like:
;                       [ss]
;                       [esp]
;                       eflags
;                       cs
;                       eip
;               ss:sp-> [error]
;
;               The cpu saves the previous SS:ESP, eflags, and CS:EIP on
;               the new stack if there was a privilige transition. If no
;               priviledge level transition occurred, then there is no
;               saved SS:ESP.
;
;               Some exceptions save an error code, others do not.
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       push error code if needed
;       push trap number
;       jump to CommonFaultEntry
;

;***LP  trap00 - Divide by zero trap handler
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap00,near
        push    NO_ERROR
        push    EXCDIVZ
        jmp     CommonFaultEntry
EndProc   trap00

;***LP  ReadDRn - read debug register address

ReadDRjmptbl label      dword
        dd      offset FLAT:ReadDR0             ; use DR0
        dd      offset FLAT:ReadDR1             ; use DR1
        dd      offset FLAT:ReadDR2             ; use DR2
        dd      offset FLAT:ReadDR3             ; use DR3

Procedure ReadDR0,near,local
        mov     eax,dr0
        RETC
EndProc ReadDR0

Procedure ReadDR1,near,local
        mov     eax,dr1
        RETC
EndProc ReadDR1

Procedure ReadDR2,near,local
        mov     eax,dr2
        RETC
EndProc ReadDR2

Procedure ReadDR3,near,local
        mov     eax,dr3
        RETC
EndProc ReadDR3

;***LP  UserCSEIPToLaddr - Convert ring 2 or ring 3 CS:EIP to linear address
;       CSEIPToLaddr - Convert CS:EIP to linear address
;
;       Entry:  (EBP) = trap frame pointer
;               Trap frame CS:EIP is from a protect mode process
;
;       Exit:   V86 mode:
;                       EDX = linearized cs:eip
;
;               Ring 0 CS:
;                       UserCSEIPToLaddr: EDX unchanged
;                       CSEIPToLaddr: EDX = linearized CS:EIP
;
;               Ring 2 or 3 CS:
;                       EDX = linearized CS:EIP
;
;       Uses:   EAX, ECX
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure UserCSEIPToLaddr,near,local
        testb   [ebp].tf_cs,RPL_MASK    ; fault at ring 0?
        jz      short ctlx              ;  yes, jump if ring 0 cs

Entry   CSEIPToLaddr,near,local
        testb   [ebp].tf_eflags,F_VM    ; V86 mode trap?
        jnz     short ctl30
        movzx   eax,[ebp].tf_cs

        mov     ecx,[_SELGDTLaddr]      ; (ECX) = _SELGDTLaddr
        shr     eax,3                   ; shift out RPL and TABLE bits
        jnc     short ctl10             ; GDT selector - use descriptor base.

        mov     edx,eax
        shl     edx,16                  ; build base linear address via CRMA
        jmp     short ctl20             ; got LDT address - continue.

;       (EAX) = (CS selector >> 3)
;       (ECX) = Descriptor table to use

ctl10:  mov     edx,[ecx+eax*8+1]       ; (high 3 bytes of EDX) = low 3 bytes
        mov     dl,[ecx+eax*8+7]        ; (low byte of EDX) = high base byte
        ror     edx,8                   ; (EDX) = Base Address of traced CS
ctl20:  add     edx,[ebp].tf_eip
        jmp     ctlx

;       vdm linear address is found in the same way a physical
;       address is formed from segment:offset in real mode.
;       laddr = (seg << 4) + offset

ctl30:  movzx   edx,[ebp].tf_cs         ; get code segment
        shl     edx,4
        add     edx,[ebp].tf_eip        ; form linear address
ctlx:   RETC
EndProc UserCSEIPToLaddr

; DR7 bits denoting DRn is armed
;
; if (DR7 & DR7Armed(i) != 0) then DRi is armed

DR7Armed        label   dword
        dd      DR7_L0+DR7_G0
        dd      DR7_L1+DR7_G1
        dd      DR7_L2+DR7_G2
        dd      DR7_L3+DR7_G3

; DR7 RWn bits
;
; if (DR7 & DR7RW(i) == 0) then DRi is a code breakpoint

DR7RW           label   dword
        dd      DR7_RW0
        dd      DR7_RW1
        dd      DR7_RW2
        dd      DR7_RW3

; Masks to clear Bn in DR6 image

        public  DR6NotBn
DR6NotBn        label   dword
        dd      NOT DR6_B0
        dd      NOT DR6_B1
        dd      NOT DR6_B2
        dd      NOT DR6_B3

; Masks to set Bn in DR6 image

DR6Bn           label   dword
        dd      DR6_B0
        dd      DR6_B1
        dd      DR6_B2
        dd      DR6_B3

;***LP  trap01 - Single step trap handler
;
;       trap01 is the first stage of a number of procedures that handle
;       the debug exception.  The clients of the debug exception, and
;       the high-level path followed, is as follows:
;
;       Paths
;       -----
;       (1) trap01, in-line
;       (2) CommonFaultEntry
;       (3) UserTraceFault
;       (4) KernelTraceFault
;       (5) ProcessFatalFault
;       (6) SystemFatalFault
;       (7) DbgWPScan
;
;       Clients
;       -------
;       * Dynamic Trace
;       (1)     * Single step a DynmicTrace tracepoint
;       (1,3)   * Needs to handle debugging single steps across the
;                 tracepoint without clearing the TF bit.
;
;       * V86 mode
;       (2)     * Reflect INT 1 to the V86 mode process.
;        ?      * Handle global debug register hits in the context of a
;                 V86 mode process.
;
;       * DosDebug
;       (3)     * Single step a debuggee thread
;       (3,7) * Ring 2 or 3 debug register hits (exit kmode handles F_TRC)
;       (3,4,7) * Ring 0 debug register hits (no block)
;       (3,6,7) * Interrupt-time debug register hits (no block, delayed event)
;       (3,4)   * Simulate a single step across a callgate to ring 0 without
;                 single stepping the kernel itself.
;       (1,3) * Software INT 1
;
;       * Kernel Debugger
;       (6)     * Single stepping any ring
;       (6)     * Debug register hits.
;       (1,6)   * Software INT 1
;
;       * Process Fatal Faults
;       (5)     * F_TRACE set in non-debugged process on non-debug kernel
;       (1,5)   * Software INT 1
;
;       * System Fatal Faults (non-debug kernel)
;       (6)     * F_TRACE set in non-debugged process on non-debug kernel
;       (1,6)   * Software INT 1
;
;       To handle this level of complexity, we observe that the DR6
;       register tells us which events are to be handled, and that the
;       event (almost always) has only one client.  To prevent events
;       from being perceived twice, we enter with interrupts off and
;       clear DR6 immediately.  An image of DR6 is placed on the stack
;       over the error code field.  Once some client services an event,
;       it clears the bit in the DR6 image for that event, and then
;       continues to look for more events.  When all the normal clients
;       have been serviced, if there are any set bits remaining in the DR6
;       image, we either panic or enter the kernel debugger.
;
;       Note that many of these events may occur at the same time:
;
;       For instance, the user may elect to single-step a DynmaicTrace
;       tracepoint in the context of a debuggee process that was set on
;       an indirect far call from ring 3 to ring 0, while he has a
;       DosDebug execute-type debug register set on the instruction,
;       (which he rearms after it gets hit the first time) and while the
;       debugger process, the kernel debugger, and another debuggee
;       process all have data watchpoints on the not-present page that
;       holds the call address...
;
;       In other words, don't work on this too late at night.
;
;       We do not support the following:
;
;       * ICE/386 (debug registers not accessable)
;
;       * DynamicTrace tracepoints in V86 mode.  See trap03.
;
;       EFFECTS:  DR6 and DR7 may change
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure trap01,near
        push    NO_ERROR
        push    EXCSSI
        movzxESP                                ; clear high word of ESP
        cld
        push    ebp
        lea     ebp,[esp+4]                     ; (EBP) = tf frame pointer
        push    ds
        push    eax
        push    ebx

;       Save and clear contents of DR6 while interrupts are off to
;       prevent the next trap 01 from handling an event twice.

        mov     ebx,dr6                         ; (EBX) = DR6 image
        mov     eax,ebx                         ; (EAX) = DR6 image also
        andb    eax,%(NOT (DR6_B0+DR6_B1+DR6_B2+DR6_B3+DR6_BS))
        mov     dr6,eax                         ; clear DR6 events
        FlatContext <ds>,eax

        and     ebx,DR6_VALID
        test    bl,(DR6_B0+DR6_B1+DR6_B2+DR6_B3); watchpoints hit?
        jnz     stp50                           ;  yes, take care of them.

;       486 errata - DR6 Bn bits will be clear for instruction exection
;       breakpoints if the instruction branches (call, jxx)

        cmp     [_CpuType],CPU_486              ; 486?
        jae     stp50                           ;  yes, look for watchpoint hit

;       Do not put any code between the above two jumps and the following
;       label - the watchpoint code jumps to the following label
;       when it is done.

stp10:  mov     [ebp].tf_errcode,ebx            ; save DR6 in error code field


IF      RAS

        testb   [ebp].tf_eflags,F_VM            ; V86 mode trap
        jnz     short stp30                     ;  yes, goto common fault hdlr

        testb   [ebp].tf_cs,RPL_MASK            ; ring 0 fault?
        jz      short stp15                     ;  yes, skip proctype check

        cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM ; VDM protected mode trap?
        je      short stp30                     ;  yes, goto common fault hdlr

;       Protect mode single step trap

stp15:  DYNTrapReset eax,[ebp].tf_eflags        ; reset DynTrc tracepoint.

;       (EAX) = previous DynTrc reset point address.

        cmp     eax,OFFSET FLAT:_DYNNullResetPoint ; DynTrc single step?
        je      short stp30                     ; no - handle normal trap01.

;       _DYNRecordPoint is normally swappable, but will not get called
;       unless a tracepoint actually occurs.

        SaveReg <edx, esi>
        CCALL32 _DYNRecordPoint                 ; record the tracepoint
        RestoreReg <esi, edx>

;       Here, we watch out for single-stepping across a DynTrc tracepoint.
;       _DYNSavedUserFlags is the EFlags value when _DYNTrap3 was called.
;       If the F_Trace bit is ON in this image, then we were trying to
;       single-step the instruction that had a DynTrc tracepoint on it.
;       In this case, we simply leave the BS bit set, and jmp to
;       CommonFaultEntry.
;
;       The TF bit is restored to its original state at the same time the
;       INT3 is restored, in all cases.  (as part of DYNTrapReset)  This
;       prevents spurious single-steps when taking a restartable fault
;       other than a single-step, when attempting the DynTrc single-step.

        testb   _DYNSavedUserFlags,F_TRACE      ; Single-stepped tracepoint?
        jnz     short stp30                     ; yes - CommonFaultEntry(BS)

;       If there are any leftover bits in the DR6 image, handle them via
;       CommonFaultEntry.  The DR6 image is in [esp].tf_errcode.
;       The user may not place a DynTrc tracepoint on an INT 1 in the code.

        .errnz  DR6_VALID AND DBGDR6IMAGE_ENTERKMODE
        testb   [ebp].tf_errcode,%(DR6_VALID+DBGDR6IMAGE_ENTERKMODE)
        jnz     short stp20

;       The DynTrc tracepoint is complete, and it was the only event, so
;       we clear the F_TRACE flag and leave directly.

        clrb    [ebp].tf_eflags,F_TRACE         ; don't single step twice.
        pop     ebx
        pop     eax
        pop     ds
        pop     ebp
        add     esp,8                           ; remove err code and trap num
        iretd                                   ; tracepoint complete.

stp20:  clrb    [ebp].tf_errcode,DR6_BS         ; clear BS in DR6 image

ENDIF ; RAS

stp30:  pop     ebx
        pop     eax
        pop     ds
        pop     ebp
        jmp     CommonFaultEntry

;       Handle watchpoint hits, (EBX) = DR6 image; original EBX on stack
;
;       There are three errata that need software workarounds here.
;
;       80386 and 80486:
;
;       RF bit not set in flags image when interrupt 1 entered due to
;       instruction execution breakpoint.
;
;       DR6 Bn bit set if DRn matches even if DRn is not armed.
;
;       80486:
;
;       DR6 Bn bit clear for valid debug register hit if instruction is
;       a branch (call, jxx)

        ASSUME DS:FLAT

;       Check that at least one debug register is armed

ifdef INCDEBUG
stp50:  mov     eax,dr7                         ; (EAX) = DR7 image
        test    al,(DR7_L0+DR7_G0+DR7_L1+DR7_G1+DR7_L2+DR7_G2+DR7_L3+DR7_G3)
                                                ; at least one DR armed?
        jnz     short stp55                     ;  yes, handle hit
else
stp50:
endif ;DEBUG

;       No debug registers armed - clear DR6 image Bn bits and continue

        and     ebx,NOT (DR6_B0+DR6_B1+DR6_B2+DR6_B3)
        jmp     stp10

;       At least one DR armed

stp55:  SaveReg <ecx,edx,edi,esi>
        xor     edx,edx
        mov     esi,eax                         ; (ESI) = DR7 image

;       For each DR that is not hit but is armed and matches, turn on
;       bit in EBX.

        mov     edi,DR_COUNT-1

stp60:  bt      ebx,edi                         ; Bn set?
        jnc     stp140                          ;  No, check 486 errata

        test    esi,DR7Armed[edi*4]             ; DRn armed?
        jz      short stp130                    ;  No, clear Bn (errata)

        test    esi,DR7RW[edi*4]                ; Code breakpoint?
        jnz     short stp80                     ;  No, continue

stp70:  orb     [ebp].tf_eflags,F_RF            ; Set RF in flags image (errata)

stp80:  dec     edi                             ; More debug registers?
        jns     short stp60                     ;  Yes, repeat

        test    bl,DR6_B0+DR6_B1+DR6_B2+DR6_B3 ; Watchpoints hit?
        jz      short stp120                    ;  No, check for other events

;       Some watchpoints hit

        or      edx,edx                         ; Fault address known?
        jnz     short stp110                    ;  Yes, check ring level

        CCALL32 UserCSEIPToLaddr                ; (EDX) = Linear fault address
stp90:  mov     eax,_pPTDACur                   ; (EAX) = PTDA pointer
        cmp     [eax].ptda_ProcType,PT_VDM      ; VDM process?
        jne     short stp91                     ;  no, call DosDebug

        cmp     [eax].ptda_pdcb,0               ; Any VDM watchpoints?
        jz      short stp91                     ;  no, check for DosDebug

        ; If we get here we know that there is at least one VDM watchpoint set,
        ; but only issue the int 01 in the VDM if it came from ring 3.
        testb   [ebp].tf_cs,RPL_MASK            ; ring 3 fault?
        jnz     short stp100                    ; yes, issue int 01 in VDM
        stc                                     ; no, restore regs and exit
        jmp     short stp100

ifdef INCDEBUG

stp91:  SaveReg <es>
        FlatContext <es>,ax
        CCALL32 DbgWPScan
        RestoreReg <es>
else
stp91:
endif ;PMDEBUG

        ASSUME ES:NOTHING

stp100: RestoreReg <esi,edi,edx,ecx>
        jnc     stp10
        pop     ebx
        pop     eax
        pop     ds
        ASSUME DS:NOTHING
        pop     ebp
        add     esp,8
        iretd

;       (EDX) = linear fault address

        ASSUME DS:FLAT
stp110: testb   [ebp].tf_cs,RPL_MASK            ; Fault at ring 0?
        jnz     short stp90                     ;  No, scan watchpoints

        xor     edx,edx                         ; Zero out fault address
        jmp     short stp90                     ; Scan watchpoints

;       No watchpoints hit

        ASSUME DS:FLAT
stp120: test    ebx,DR6_VALID-DR6_BD            ; More work to be done?
        jnz     short stp100                    ;  Yes, carry clear
        stc
        jmp     short stp100                    ;  No, carry set

;       Bn set but DRn not armed

        ASSUME DS:FLAT
stp130: and     ebx,DR6NotBn[edi*4]             ; Clear DR6 Bn
        jmp     short stp80                     ; Continue

;       DR6 Bn not set

        ASSUME DS:FLAT
stp140: cmp     [_CpuType],CPU_486              ; 486?
        jb      short stp80                     ;  No, continue

        test    esi,DR7Armed[edi*4]             ; DRn armed?
        jz      short stp80                     ;  No, continue

        test    esi,DR7RW[edi*4]                ; Code breakpoint?
        jnz     stp80                           ;  No, continue

        or      edx,edx                         ; Fault address known?
        jnz     short stp150                    ;  Yes, skip calculation

        CCALL32 CSEIPToLaddr                    ; (EDX) = Linear fault address
stp150: CCALL32 ReadDRjmptbl[edi*4]             ; (EAX) = DR(EDI)
        cmp     eax,edx                         ; Address match?
        jne     stp80                           ; No, continue

        or      ebx,DR6Bn[edi*4]                ; Set DR6 Bn
        jmp     stp70                           ; Set RF bit and continue
        ASSUME DS:NOTHING
EndProc   trap01

;***LP  trap02 - NMI
;
;       This trap entry is now handled by the NMI Watchdog handler in nwd.asm

;***LP  trap03 - Breakpoint trap handler
;
;       USES: Clears the high word of ring 0 ESP.  Outer rings not affected.
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure trap03,near
        push    NO_ERROR
        push    EXCBP

;       If the kernel debugger is enabled, we want to snap the perfview
;       timer here, ASAP, to minimize overhead.

IFDEF   PMDEBUG
        cmp     cs:[GoTimeL],0                  ; snapshot time for "GS" cmd?
        je      short bpt10                     ;  no, jump around
        movzxESP
        cld
        SaveReg <eax, ecx, edx, ds, es>
        FlatContext <ds,es>,eax
        lea     ecx,[GoTimeL]
        CCALL32 TMR32AddTime
        RestoreReg <es, ds, edx, ecx, eax>
    ASSUME   DS:NOTHING, ES:NOTHING
bpt10:
ENDIF
IF      RAS
        movzxESP                                ; must be before TEST
        cld
        testb   [esp].tf_eflags,F_VM            ; V86 mode trap
        jnz     V86FaultEntry                   ;  yes, goto V86 fault handler

        testb   [esp].tf_cs,RPL_MASK            ; ring 0 fault?
        jz      short bpt20                     ;  yes, skip proctype check

        cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM ; VDM protected mode trap?
        je      VPMFaultEntry                   ;  yes, handle VDM trap

;       Protected mode INT 3.
;       We need to see if DynTrc is enabled before calling _DYNTrap3
;       because _DYNTrap3 is normally swappable.  (Note the CS override)

bpt20:  cmp     cs:[_fDYNActive],0              ; DynTrc active?
        jz      CommonFaultEntry                ; not a DynTrc tracepoint.

        CCALL32 _DYNTrap3                       ; handle DynTrc tracepoint
        jc      CommonFaultEntry                ; not a DynTrc tracepoint.

;       The Trap 3 portion of the DynTrc tracepoint is Complete.

        add     esp,8                           ; pop twice to clear frame
        iretd
ELSE
        jmp     CommonFaultEntry                ; handle the trap directly
ENDIF
EndProc   trap03

;***LP  trap04 - INTO trap handler
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap04,near
        push    NO_ERROR
        push    EXCOV
        jmp     CommonFaultEntry
EndProc   trap04

;***LP  trap05 - BOUND trap handler
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap05,near
        push    NO_ERROR
        push    EXCBOUND
        jmp     CommonFaultEntry
EndProc   trap05

;***LP  trap06 - Invalid opcode trap handler
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap06,near
        push    NO_ERROR
        push    EXCOP
        movzxESP                                ; clear high word of ESP
        testb   [esp].tf_eflags,F_VM            ; trap happened in V86 mode?
        jz      CommonFaultEntry                ;  no, jump common entry
        KMEnterKmodeVDM v86

;       (EAX) = client's EAX register
;       (EDX) = client's EDX register
;       (EBX) = VSF stack frame pointer
;       (DS, ES) = flat context
;       Direction flag clear
;       Interrupts enabled

        call    EM86opARPL
        KMExitKmodeVDM v86orvpm                 ; exit kernel mode and return
EndProc   trap06

;***LP  trap07 - Processor Extension Not Available Exception
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap07,near
        push    NO_ERROR
        push    EXCNOFP
        jmp     CommonFaultEntry
EndProc   trap07

;***LP  trap08 - Double Exception Detected
;
;       ENTRY   DS:EBX = DOSGROUP:TSS
;               SS:ESP = GDT_DFSTACK:DFSTKLEN
;               ES = FLAT DGROUP selector
;               CLI
;               CLD
;
;       EXIT    NONE
;
;       USES    ALL
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:DOSGROUP, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap08,near
        push    dword ptr [ebx].tss_ss          ; push old (ss:esp)
        push    [ebx].tss_esp
        push    [ebx].tss_eflags                ; push old flags
        push    dword ptr [ebx].tss_cs          ; push old (cs:eip)
        push    [ebx].tss_eip

;       Change old TSS to our context

        mov     es:[_TKSSBase],offset FLAT:DFStackBase
        mov     [ebx].tss_ss,ss
        mov     [ebx].tss_esp,esp
        and     [ebx].tss_eflags,not (F_INTERRUPT + F_DIRECTION + F_NT)
        mov     [ebx].tss_cs,cs                 ; set continuation cs:eip
        mov     [ebx].tss_eip,offset FLAT:dft10

;       Now, we IRET back to original TSS, but CS:EIP and SS:ESP are
;       set up to point to the continuation point below.

        iretd
    ASSUME   DS:NOTHING

;       We come here if ANOTHER double fault occurs.  We reset SP and EIP,
;       everything else (BX,DS,CS,SS,flags) is preserved by the task
;       switch caused by the IRET above.

        mov     esp,DFSTKLEN
        jmp     short trap08

dft10:  push    NO_ERROR
        push    EXCDED
        jmp     OS2PMFaultEntry
EndProc   trap08

;***LP  trap09 - Processor Extension Segment Overrun Interrupt
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap09,near
        fninit                  ; re-initialize the NPX
        fninit                  ; re-initialize the NPX for 80387 errata
        push    NO_ERROR
        push    EXCFPOVR
        jmp     CommonFaultEntry
EndProc   trap09

;***LP  trap0a - Invalid TSS
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap0a,near
        push    EXCITSS
        jmp     CommonFaultEntry
EndProc   trap0a

;***LP  trap0b - Segment Not Present
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap0b,near
        push    EXCSNP
        jmp     CommonFaultEntry
EndProc   trap0b

;***LP  Trap0c - Stack Segment Overrun or Not Present
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap0c,near
        push    EXCSSONP
        jmp     CommonFaultEntry
EndProc   trap0c

;***LP  trap0d - General Protection Fault
;***LP  VDMDispatchOpcode - Decode and emulate opcode
;
;       ENTRY   Trap error code
;               Trap stack frame
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       if trap happened in V86 mode
;           enter kernel mode
;           get client's CS:IP
;           get opcode at client's CS:IP
;           increment client's CS:IP past opcode
;           initialize prefix flag
;           call entry in opcode table (apfnOpcode)
;           exit kernel mode and return from trap
;       else if trap happened in user code and faulting instruction is pop
;           zero top of user stack
;           restart faulting instruction
;       else if trap happened in user code at DosR3ExitAddr on thread 2-n
;           call _TKExit
;       else
;           CommonFaultEntry
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure trap0d,near
        push    EXCGP

Entry   VDMDispatchOpcode,near,local
        movzxESP                                ; clear high word of ESP
        testb   [esp].tf_eflags,F_VM            ; trap happened in V86 mode?
        jz      gpt10                           ;  no, jump VDM check

        KMEnterKmodeVDM v86,flatcontext,trap
;       PvwCnt  vdm,V86Trap,0,strict            ; perfview hook        ;b729110

;       (EAX) = client's EAX register
;       (EDX) = client's EDX register
;       (EBX) = VSF stack frame pointer
;       (DS, ES, FS) = flat context
;       Direction flag clear
;       Interrupts enabled

        movzx   esi,[ebx].vsf_cs                ; (ESI) = client CS
        shl     esi,4                           ; (ESI) = linear CS
        mov     edi,[ebx].vsf_eip               ; (EDI) = client EIP
        mov     [flPrefix],0                    ; initialize prefix flag

;       (FS:ESI+EDI) = pointer to instruction

        mov     cx,word ptr [esi+edi]           ; (CL) = low opcode, (CH) = high
        movzx   ebp,cl

        db      MI_SEGDS                        ; required because EBP uses SS
        call    apfnOpcode[ebp*type apfnOpcode] ; call emulation routine

        ; em86opINTnn (in mvdm\em86int.asm) skips the trace flag check and
        ; the checks inside KMExitKmodeVDM, so be sure to add any additional
        ; code between here and KMExitKmodeVDM in between the equates.

        GPTTFBEGIN = $
        test    [_flVdmStatus], VDM_STATUS_VME  ; VME Supported?        ;74409;
        jnz     short gpt05                     ; yes - skip trace check;74409;
        test    [ebx].vsf_flag,F_TRACE          ; is the trace flag enabled?
        jnz     short gpt08                     ; yes, reflect trap 01

        GPTTFCHECKSIZE = $ - GPTTFBEGIN
        PUBLIC GPTTFCHECKSIZE

gpt05:
        KMExitKmodeVDM v86orvpm                 ; exit kernel mode and return

gpt08:
        mov     eax,EXCSSI                      ; trap 01
        mov     edx,DR6_BS                      ; imitate DR6 reg, set DS bit
IFDEF PMDEBUG
        mov     [ebx].vsf_trapnum,EXCSSI        ; make debugger think it's a trap 01
        mov     [ebx].vsf_errcode,0             ; clear error code field
ENDIF
        call    UserTraceFault                  ; simulate the trace
        jmp     short gpt05

gpt10:  testb   [esp].tf_cs,RPL_MASK            ; ring 0 fault?
        jz      KernelFaultEntry                ;  yes, goto kernel fault entry

        cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM ; VDM protected mode trap?
        jne     gpt20                           ;  no, opcode check

        KMEnterKmodeVDM vpm,nothing
;       PvwCnt  vdm,V86Trap,0,strict            ; perfview hook        ;b729110

;       (EAX) = client's EAX register
;       (EDX) = client's EDX register
;       (EBX) = VSF stack frame pointer
;       (DS, ES) = flat context
;       (GS) = null context
;       Direction flag clear
;       Interrupts enabled

        mov     edi,[ebx].vsf_eip               ; (EDI) = client's EIP

        movzx   esi,[ebx].vsf_cs                ; get client's CS               ;54268
        lsl     esi,esi                         ; get segment limit             ;54268
        jnz     short gpt15                     ; error, reflect trap D         ;54268
        cmp     esi,edi                         ; compare seg lim with EIP      ;54268
        jbe     short gpt15                     ; EIP is beyond limit, get out  ;54268

        mov     fs,[ebx].vsf_cs                 ; (FS) = client's CS
        sub     esi,esi
        mov     [flPrefix],esi                  ; initialize prefix flag

;       (FS:ESI+EDI) = pointer to instruction

        mov     cx,word ptr fs:[edi]            ; (CL) = low opcode, (CH) = high
        movzx   ebp,cl

        db      MI_SEGDS                        ; required because EBP uses SS
        call    apfnOpcode[ebp*type apfnOpcode] ; call emulation routine
gpt13:
        KMExitKmodeVDM v86orvpm                 ; exit kernel mode and return
gpt15:
        mov     eax,[ebx].vsf_trapnum           ; (EAX) = trap number (0x0D)    ;54268
        mov     edx,[ebx].vsf_errcode           ; (EDX) = error code            ;54268
        call    EM86ReflectVPMException                                         ;54268
        jmp     short gpt13                                                     ;54268


; We get here for a ring 3 user fault.  We want to check whether or not the
; fault occurred on a pop into ES/DS/FS/GS  of an invalid selector.  If so,
; we  want  to replace  the  invalid  selector with  zero and  restart  the
; instruction.  This is done to maintain compatability for 16-bit programs.
; Flat model programs should not encounter this problem because there is no
; need to push and pop registers across calls.
;
; Before placing the code selector  into DS in order to decipher the opcode
; of the faulting instruction,  we must enable read access in case the code
; selector is execute only.  Otherwise we fault.  This is  true whether the
; selector is GDT or LDT.
;
; We need to save off and restore EAX,  EDI, DS, and EBX so that these regs
; aren't corrupted.  Because we save them on the stack and because the trap
; frame is based off of ESP,  we need to be wary of how much information is
; pushed on the stack above the trap frame.  We must therefore use ESP+n in
; order to reference the trap frame while we are between the pushes and the
; pops of EAX, EDI, DS, and EBX.

POP_DS_OPCODE   EQU     1Fh
POP_ES_OPCODE   EQU     07h
POP_FS_OPCODE   EQU     0A10Fh
POP_GS_OPCODE   EQU     0A90Fh

gpt20:  SaveReg <eax,edi>                       ; trap frame (tf) at ESP+8
        SaveReg <ds,ebx>                        ; tf @ ESP+16
        FlatContext <ds>,ax                     ; set up to refer to flat data

        mov     ebx,[_SELGDTLaddr]              ; (EBX) = GDT address
        movzx   edi,[esp+16].tf_cs              ; (DI) = faulting CS
        test    edi,TABLE_MASK                  ; is selector GDT?
        jz      short gpt30                     ; yes, EBX has correct address

        SaveReg <ecx,edx>
        CCALL32 _selGetLDTAddr, <0>             ; (EAX) = LDT address
        RestoreReg <edx,ecx>
        mov     ebx,eax                         ; (EBX) = LDT address

gpt30:  and     edi,SELECTOR_MASK               ; mask off RPL and table bits
        add     ebx,edi                         ; (EBX) = selector address
        mov     al,[ebx].d_access               ; (AX) = descriptor access byte
        push    eax                             ; save access byte (tf @ ESP+20)
        or      [ebx].d_access,D_RX             ; force enable read access

        lds     edi,fword ptr [esp+20].tf_eip   ; (DS:EDI)= faulting CS:EIP

        mov     ax,ds                           ; (AX)= faulting CS
        movzx   eax,ax                          ; clear high word of EAX
        lsl     eax,eax                         ; (EAX)= segment limit of CS
        jnz     short gpt40                     ; can't handle, get out
        cmp     eax,edi                         ; compare seg lim with EIP
        jbe     short gpt40                     ; EIP is beyond limit, get out

        mov     ax,[edi]                        ; (AX)= faulting opcode
        cmp     al,POP_ES_OPCODE                ; was it pop ES?
        je      short gpt50                     ; yes, zero selector
        cmp     al,POP_DS_OPCODE                ; was it pop DS?
        je      short gpt50                     ; yes, zero selector
        cmp     ax,POP_FS_OPCODE                ; was it pop FS?
        je      short gpt50                     ; yes, zero selector
        cmp     ax,POP_GS_OPCODE                ; was it pop GS?
        je      short gpt50                     ; yes, zero selector

gpt40:  xor     edi,edi                         ; (EDI) = fixed (FALSE)
        jmp     short gpt60

gpt50:  lds     edi,fword ptr [esp+20].tf_esp   ; (DS:EDI)= SS:ESP at fault
        mov     ax,ds                           ; (AX)= faulting SS
        test    ax,TABLE_MASK                   ; is selector GDT?
        jz      short gpt55                     ; yes, EDI can be used
        and     edi,0FFFFh                      ; no, only DI can be used
gpt55:  mov     word ptr [edi],0                ; zero top of user stack
        mov     edi,1                           ; (EDI) = fixed (TRUE)

gpt60:  FlatContext <ds>,ax
        pop     eax                             ; restore access byte (tf @ ESP+16)
        mov     [ebx].d_access,al               ; (AX) = descriptor access byte

        RestoreReg <ebx,ds>                     ; tf @ ESP+8

        cmp     edi,1                           ; did we fix trap?
        RestoreReg <edi,eax>                    ; tf @ ESP
        jne     short gpt70                     ; no, do normal processing

        add     esp,(tf_eip-tf_trapnum)         ; yes, remove errcode and trapnum

        iretd                                   ; restart faulting instruction

; Before we move on to OS2PMFaultEntry and blow this thread away, check to
; see if the trap occurred in DOSCALL1.DLL at the entry to DosR3ExitAddr.
; This trap occurs when a thread returns from its main routine (instead of
; calling DosExit) AND generates a page fault to swap in DosR3ExitAddr AND
; has its stack freed by another thread during the ensuing task switches
; (the initial push in DosR3ExitAddr produces the trap).  If the thread is
; not thread 1, go straight to _TKExit and avoid the pop-up.  Thread 1
; should never encounter this problem because its stack is allocated by
; the system.

gpt70:  SaveReg <eax,ebx,ds>                    ; tf @ ESP+12
        FlatContext <ds>,ax                     ; set up to refer to flat data
        movzx   ebx,[esp+12].tf_cs              ; (EBX) = faulting CS
        test    ebx,TABLE_MASK                  ; is faulting CS GDT?
        jz      short gpt80                     ; Yes, 32-bit faulting CS
                                                ; No, 16-bit faulting CS
; Faulting CS is 16-bit

        shl     ebx,16                          ; Put CS into high word
        add     ebx,[esp+12].tf_eip             ; Add IP onto low word
        jmp     short gpt90                     ; (EBX) = CS:IP

; Faulting CS is 32-bit

gpt80:  mov     ebx,[esp+12].tf_eip             ; (EBX) = linear EIP
        push    bx                              ; Save IP (tf @ ESP+14)
        mov     bx,word ptr [esp+14].tf_cs      ; (BX) = faulting CS
        or      bl,4
        shl     bx,13
        shl     ebx,3                           ; Put CS into high word
        pop     bx                              ; (EBX) = CS:IP (tf @ ESP+12)

gpt90:  cmp     ebx,[_pfntkDosExit]             ; Fault in DosR3ExitAddr?
        RestoreReg <ds,ebx,eax>                 ; tf @ ESP
        jne     OS2PMFaultEntry                 ; No, do normal processing

; The trap indeed occurred at the entry to the DosR3ExitAddr routine, so
; pass client's EAX (main routine's return code) to _TKExit for threads
; 2-n only.

        SaveReg <eax>
        FlatContext <ds,es>,ax
        mov     eax,[_pTCBCur]                  ; (EAX) = thread's TCB
        movzx   eax,word ptr [eax]              ; (EAX) = thread's ordinal
        cmp     eax,1                           ; Is this thread 1?
        RestoreReg <eax>
        je      OS2PMFaultEntry                 ; Yes, do normal processing

        CCALL32 _TKExit, <0, eax>               ; (EAX) = client's EAX

EndProc   trap0d

;***LP  trap0e - Page Not Present Fault
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure trap0e,near
        push    EXCPG
        jmp     CommonFaultEntry
EndProc   trap0e

;***LP  trap0f - Intel Reserved
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap0f,near
        push    0fh
        jmp     CommonFaultEntry
EndProc   trap0f

;***LP  trap10 - 287/387/486 NPX exception
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap10,near
        push    EXCFPERR
        jmp     CommonFaultEntry
EndProc   trap10

;***LP  trap11 - Alignment Check (486 specific)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap11,near
        push    EXCALIGN
        jmp     CommonFaultEntry
EndProc   trap11

;***LP  trap1f - Generic Intel Reserved Trap
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure trap1f,near
        push    EXCGENERIC
        jmp     CommonFaultEntry
EndProc   trap1f

;***EP  TRAPCommonFaultEntry - Public common trap entry
;***LP  CommonFaultEntry - Common trap entry
;***LP  OS2PMFaultEntry - OS/2 protected mode trap entry
;***LP  OS2PMFaultExit - OS/2 protected mode trap exit
;
;       This is the common entry code for traps generated by
;       both V86 and protected mode code.
;
;       ENTRY   Trap number
;               Trap error code
;               Trap stack frame
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       if trap happened in V86 mode
;           goto V86FaultEntry
;       else if trap happened at ring 0
;           goto KernelFaultEntry
;       else if trap happened in a VDM
;           goto VPMFaultEntry
;       else
;           perform RAS tracing, if any.
;           enter kernel mode with protected mode trap stack frame
;           call user fault trap routine
;           exit kernel mode and return from trap
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure TRAPCommonFaultEntry,near             ; public entry
Entry   CommonFaultEntry,near,local
        movzxESP                                ; clear high word of ESP
        testb   [esp].tf_eflags,F_VM            ; trap happened in V86 mode?
        jnz     V86FaultEntry                   ;  no, jump to prot mode entry
        testb   [esp].tf_cs,RPL_MASK            ; fault at ring 0?
        jz      KernelFaultEntry                ;  yes, jump if ring 0 cs
        cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM ; VDM protected mode?
        je      VPMFaultEntry                   ; yes, entry VDM PM kernel mode

;       OS/2 protected mode ring 3 or ring 2 code trap entry

Entry   OS2PMFaultEntry,near,local
        MMPHR3PreExceptionTest                  ; performance Hook
IF RAS
        TestTraceFlatCS RAS_MC_MACHINEEXCEPT    ; check major code (via CS)
        jnz     cte20                           ; jump out-of-line if enabled
trapR3PreTraceDone:                             ; RAS tracing complete, if any
ENDIF

cte10:  KMEnterKmodeTrap

;       (EBX) = TSF stack frame pointer
;       (DS, ES) = flat context
;       Direction flag clear
;       Interrupts enabled

        DYNTrapReset eax,[ebx].tsf_eflag        ; reset DynTrc tracepoint.
        mov     eax,[ebx].tsf_trapnum           ; (EAX) = trap number
        mov     edx,[ebx].tsf_errcode           ; (EDX) = error code
        or      ebx,-1                          ; no stack frame pointer
        call    apfnOS2PMFault[eax*type apfnOS2PMFault]
Entry   OS2PMFaultExit,near,local
        KMExitKmodeTrap normal,debug            ; exit kernel mode and return

if RAS
cte20:  call    trapPreTrace
        jmp     cte10
endif

EndProc   TRAPCommonFaultEntry

;***LP  V86FaultEntry - V86 mode VDM fault entry
;
;       Enter the kernel in a VDM from V86 mode.
;
;       ENTRY   Trap number
;               Trap error code
;               Trap stack frame
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       enter kernel mode with VDM stack frame
;       call V86 trap routine
;       exit kernel mode and return from trap
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure V86FaultEntry,near,local
        KMEnterKmodeVDM v86

;       (EBX) = VSF stack frame pointer
;       (DS, ES) = flat context
;       Direction flag clear
;       Interrupts enabled

        mov     eax,[ebx].vsf_trapnum           ; (EAX) = trap number
        mov     edx,[ebx].vsf_errcode           ; (EDX) = error code
        call    apfnV86Fault[eax*type apfnV86Fault]
        jmp     short VDMFaultExit
EndProc   V86FaultEntry

;***LP  VPMFaultEntry - VDM protected mode VDM Fault entry
;***LP  VDMFaultExit - Exit the kernel to VDM code
;
;       Enter the kernel from VDM protected mode.
;
;       ENTRY   Trap number
;               Trap error code
;               Trap stack frame
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       enter kernel mode with VDM stack frame
;       call VDM protected mode trap routine
;       exit kernel mode and return from trap
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure VPMFaultEntry,near,local
        KMEnterKmodeVDM vpm

;       (EBX) = VSF stack frame pointer
;       (DS, ES) = flat context
;       Direction flag clear
;       Interrupts enabled

        mov     eax,[ebx].vsf_trapnum           ; (EAX) = trap number
        mov     edx,[ebx].vsf_errcode           ; (EDX) = error code
        call    apfnVPMFault[eax*type apfnVPMFault]
Entry   VDMFaultExit,near,local
        KMExitKmodeVDM v86orvpm,normal,debug    ; exit kernel mode and return
EndProc   VPMFaultEntry

;***LP  KernelFaultEntry - in kernel mode trap re-entry
;
;       Re-entry the kernel on a trap while already in kernel mode.
;
;       ENTRY   Trap number
;               Trap error code
;               Trap stack frame
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       save the registers (KSF)
;       if interrupt time
;           SystemFatalFault
;       call kernel fault trap routine
;       restore the registers and return from trap
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure KernelFaultEntry,near,local
        MMPHR0PreExceptionTest                  ; performance Hook
IF RAS
        TestTraceFlatCS RAS_MC_MACHINEEXCEPT    ; check major code (via CS)
        jnz     kte20                           ; jump out-of-line if enabled
trapR0PreTraceDone:                             ; RAS tracing complete, if any
ENDIF
kte10:  KMEnterKernelTrap_PS

;       (EBX) = KSF stack frame pointer
;       (DS, ES) = flat context
;       Direction flag clear
;       Interrupt flag state preserved

        cmp     [InterruptLevel],-1             ; is it interrupt time?
        jne     SystemFatalFault                ;  yes, always fatal trap
        DYNTrapReset eax,[ebx].ksf_eflag        ; reset DynTrc tracepoint.
;        mov     eax,[_pTCBCur]                  ;                              122474
;;;     push    [eax].TCBForcedActions          ; 72252 - save forceflags
        mov     eax,[ebx].ksf_trapnum           ; (EAX) = trap number
        mov     edx,[ebx].ksf_errcode           ; (EDX) = error code
        push    eax                             ; save trapnum
        call    apfnKernelFault[eax*type apfnKernelFault]
        pop     eax                             ; get trapnum
        cmp     eax,EXCNMI                      ; is the trap an NMI?
        jnz     short kte15                     ; 72252 -  jmp if no
;;;     jnz     short kte14                     ; 72252 -  jmp if no
; 78486  mov     ax,[ebx].tsf_padcs              ; reset _TKSSBase to original
; 78486  shl     eax,16                          ;  value (see nwd.asm)
; 78486  mov     ax,[ebx].tsf_padss              ;
         pushad                                  ; 78486
         mov     ax,GSEL GDT_TSS                 ; TSS GDT              78486
         call    NWDAccessTSS                    ; 78486
         mov     eax,[esi].tss_esp1              ; 78486
         mov     [esi].tss_esp1,0                ; 78486
         mov     [_TKSSBase],eax                 ; 78486
         popad                                   ; 78486

;;; 72252 - begin block
;;;kte14:  pop     eax                          ; get forceflags
;;;     test    eax,TK_FF_EXIT                  ; was exit set on entry ?
;;;     jnz     short kte15                     ;  jmp if yes
;;;     mov     eax,[_pTCBCur]                    ; (EAX) = forceflags
;;;     test    [eax].TCBForcedActions,TK_FF_EXIT ; exit ?
;;;     jz      short kte15                       ;  jmp if no
;;;     call    KMExitKmodeEvents                 ; process forceflags
;;; 72252 - end of block
kte15:
        KMExitKernelTrap normal,debug           ; restore the regs and return

if RAS
kte20:  call    trapPreTrace
        jmp     kte10
endif

EndProc   KernelFaultEntry

;***LP  UserTraceFault - User code single step fault handler
;
;       This handler gets control for single step faults when
;       faulting CS is not ring 0.
;
;       ENTRY   EAX = trap number
;               EDX = error code (DR6 image)
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure UserTraceFault,near,local
        mov     esi,edx                         ; (ESI) = DR6 image
        mov     edi,[_pPTDACur]
        test    esi,DR6_BS
        jz      short utf10
        cmp     [edi].ptda_ProcType,PT_VDM      ; VDM issued INT 01h or DPMI?
        je      short utf20                     ;  yes, skip ahead

utf05:  cmp     [edi].ptda_flDbgEnabled,0       ; debug enabled?
        je      short utf20                     ;  no, kill process

ifdef INCDEBUG                                  ; D83278
        CCALL32 _DbgTrap, <eax>
        cmp     eax,XCPT_CONTINUE_EXECUTION     ; single step handled?
        je      short utf10                     ;  yes. Continue
        cmp     eax,XCPT_CONTINUE_STOP          ;  yes. Continue
        jne     short utf20                     ;  no. Kill process
else
        jmp     short utf20
endif; INCDEBUG                                   D83278

utf10:  and     esi,NOT (DR6_BS OR DBGDR6IMAGE_ENTERKMODE) ; unhandled events?
        jnz     short utf20                     ;  yes, kill process
        RETC

utf20:  cli                                     ; don't allow DR6 to change
        mov     eax,dr6
        or      eax,esi
        mov     dr6,eax                         ; restore DR6
        sti
        mov     eax,EXCSSI                      ; (EAX) = exception number
        mov     esi,[_pTCBCur]                  ; (ESI) = current TCB pointer
        mov     ebx,[esi].TCB_pFrameBase        ; (EBX) = pvsf (if VDM)

IFDEF   PMDEBUG
        cmp     [edi].ptda_flDbgEnabled,0       ; any watchpoints?
        jnz     utf25                           ; yes
        cmp     [edi].ptda_ProcType,PT_VDM      ; VDM?
        jne     utf22                           ; not VDM, call kernel debugger
        testb   [_flVdmStatus],VDM_STATUS_VPM_EXEC      ; VPM app?
        jz      short utf30                     ; no, reflect exception to VDM
        SaveReg <eax,ebx>                       ;149007
        PCall   VDHCHECKVPMEXCEPT, <EXCSSI>     ; see if they hooked exc/int 01
        or      eax,eax
        RestoreReg <ebx,eax>                    ;149007
        jnz     short utf30                     ; yes, reflect to VDM
utf22:
        or      ebx,-1
        jmp     TRAPDebugFault                  ; jump to kernel debugger
ENDIF

utf25:
        cmp     [edi].ptda_ProcType,PT_VDM      ; VDM?
        je      short utf30                     ;  yes, do normal VDM processing

        jmp     ProcessFatalFault

;       VDM trace trap
;
;       (EDX) = DR6 (we skipped call to DbgTrap)
;       (EDI) = pPTDACur

utf30:
        mov     esi,[edi].ptda_pdcb             ; (ESI) = watchpoint block
        or      esi,esi                         ; is there a watchpoint block?
        jz      short utf35                     ;  no, reflect exception
        mov     [esi].dbgv_dr6,edx              ;  yes, save DR6 image

utf35:
IFDEF  PMDEBUG
        jmp     EM86REFLECTEXCEPTION            ; reflect exception to VDM
ELSE
        testb   [_flVdmStatus],VDM_STATUS_VPM_EXEC      ; VPM app?
        jz      short utf40                     ; no, check real mode IVT
        PCall   VDHCHECKVPMEXCEPT, <EXCSSI>     ; see if they hooked exc/int 01
        or      eax,eax                         ;
        mov     eax,EXCSSI                      ; (EAX) = trap 01
        jnz     EM86REFLECTEXCEPTION            ; hooked, so reflect it
utf40:
        mov     esi,[eax*4]                     ; get V86 int 01
        cmp     esi,_vpvdmIRet                  ; have they hooked it?
        jnz     EM86REFLECTEXCEPTION            ; it's hooked, reflect it

        testb   [_flVdmStatus],VDM_STATUS_VPM_APP   ; VPM app? ;149007
        jz      utf50                                          ;149007
                                                               ;149007
        push    eax                                            ;149007
        push    ebx                                            ;149007
        push    edx                                            ;149007
        PCall   VDHCHECKVPMV86EXCEPT, <eax>     ; Are V86 exceptions hooked?
                                                ;149007 GK 1
        or      ax,ax                                          ;149007
        pop     edx                                            ;149007
        pop     ebx                                            ;149007
        pop     eax                                            ;149007
                                                               ;149007
        jnz     EM86REFLECTEXCEPTION            ; it's hooked, reflect it
                                                               ;149007
utf50:                                                         ;149007
        and     [ebx].vsf_flag,NOT F_TRACE      ; else, turn off trace flag
        ret                                     ; and return
ENDIF
EndProc   UserTraceFault


;***LP  UserBPFault - User code int 3 fault handler
;
;       This handler gets control for int 3 breakpoints
;       when faulting CS is not ring 0.
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;

Procedure UserBPFault,near,local
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

        mov     edi,[_pPTDACur]                 ; (EDI) = PTDA pointer
        mov     esi,[_pTCBCur]                  ; (ESI) = current TCB pointer
        mov     ebx,[esi].TCB_pFrameBase        ; (EBX) = pvsf (if VDM)
        cmp     [edi].ptda_ProcType,PT_VDM      ; VDM process?
        jne     short ubf03                     ;  no, skip ahead
IFDEF   PMDEBUG
        testb   [_flVdmStatus],VDM_STATUS_VPM_EXEC
        jz      EM86REFLECTEXCEPTION            ; jump if not VDM protected mode
        SaveReg <eax>                           ; save trap number
        PCall   VDHCHECKVPMEXCEPT, <EXCBP>      ; see if they hooked exc/int 03
        or      eax,eax
        RestoreReg <eax>                        ; restore trap number
        jnz     EM86REFLECTEXCEPTION            ; yes, reflect back to VDM
        jmp     short ubf10                     ; if not, call kernel debugger
ELSE
        testb   [_flVdmStatus],VDM_STATUS_VPM_EXEC ; VPM mode?
        jz      short ubf01                     ; no, check V86 IVT

        PCall   VDHCHECKVPMEXCEPT, <EXCBP>      ; see if they hooked exc/int 03
        or      eax,eax
        jnz     short ubf02                     ; yes, reflect it to VDM
ubf01:                                          ; check IVT
        mov     eax,ds:[EXCBP*4]                ; get int 03 vector
        cmp     eax,_vpvdmIRet                  ; hooked?
        jne     short ubf02                     ; yes, reflect exception ;149007
                                                ;149007
        push    ebx                             ; 149007
        PCall   VDHCHECKVPMV86EXCEPT, <EXCBP>   ; see if they hooked exc/int 03
                                                ;149007
        or      ax,ax                           ;149007
        pop     ebx                             ;149007
        jz      short ubfret                    ; no, jump return;149007
ubf02:
        mov     eax,EXCBP                       ; (EAX) = trap num
        jmp     EM86REFLECTEXCEPTION            ; yes, reflect it to VDM
ubfret:
        ret                                     ; otherwise, just return
ENDIF
ubf03:

ifdef INCDEBUG                                  ; D83278
        cmp     [edi].ptda_flDbgEnabled,0       ; debug enabled?
        jz      short ubf10                     ;  no, kill process

        CCALL32 _DbgTrap, <eax>
        cmp     eax,XCPT_CONTINUE_EXECUTION     ; breakpoint handled?
        je      short ubf05                     ;  yes. Continue
        cmp     eax,XCPT_CONTINUE_STOP          ;  yes. Continue
        jne     short ubf10                     ;  no. Kill process

ubf05:  RETC
endif; INCDEBUG                                   D83278

ubf10:  mov     eax,EXCBP                       ; (EAX) = exception number
IFDEF   PMDEBUG
        or      ebx,-1
        cmp     [edi].ptda_flDbgEnabled,0
        je      TRAPDebugFault
ENDIF
        jmp     ProcessFatalFault

EndProc   UserBPFault


;***LP  UserInvalidTSSFault - User invalid TSS fault
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure UserInvalidTSSFault,near,local

LocalVar  uts_p,DWORD

        EnterProc
        CCALL32 _trapQueryRegister, <KM_REG_EFLAG>
        test    eax,F_NT
        jz      utse                            ; jmp if bonafide trap

;       We get here when the trap has been caused as the result of a user
;       incorrectly diddling the flags to set the nested task (NT) bit
;       while the back-link selector in the TSS is invalid. The trap is thus
;       taken when an IRET is executed. The course now is to clear the NT
;       flag from the stack frames (the trap automatically clears the
;       register NT flag) and restart the IRET.

        and     eax,NOT F_NT                    ; clear NT in flags
        CCALL32 _trapSetRegister, <KM_REG_EFLAG, eax>

;       If the faulting instruction is an IRET then clear the NT flag
;       in the IRET frame as well.  If an operand override is seen, the
;       IRET frame is 32 bits.

        mov     edi,[_pTSDCur]                  ; (EDI) = current TSD pointer
        SSToDS  eax,[uts_p]                     ; (EAX) = pointer to put pstk
        PCALL32 KMQUERYCSEIPLADDR, <[_pTCBCur], eax>
        mov     esi,[uts_p]                     ; (ESI) = CS:EIP pointer
        mov     ebx,offset iret16_flag          ; (EBX) = 16 bit IRET offset

uts10:  mov     [edi].TSDpfnFault,offset FLAT:utse
        lodsb                                   ; (AL) = opcode
        mov     [edi].TSDpfnFault,0
        cmp     al,MI_OPERANDSIZE
        jne     short uts20
        mov     ebx,offset iret32_eflag         ; (EBX) = 32 bit IRET offset
        jmp     short uts10

uts20:  cmp     al,MI_IRET
        jne     short utse                      ; jmp if CS:IP != IRET

        SSToDS  eax,[uts_p]                     ; (EAX) = pointer to put pstk
        CCALL32 KMQUERYSSESPLADDR, <[_pTCBCur], eax>,,pascal
        or      eax,eax
        jnz     short utsx                      ; jump if error

        add     ebx,[uts_p]                     ; (EBX) = pointer to flags
        mov     [edi].TSDpfnFault,offset FLAT:utse
        and     [ebx].loword,not F_NT           ; clear NT in user flags
        mov     [edi].TSDpfnFault,0
utsx:   LeaveProc
        RETC                                    ; restart faulting instruction

utse:   LeaveProc NoCheck
        jmp     ProcessFatalFault
EndProc   UserInvalidTSSFault

;***LP  UserNPXNAFault - Processor extension not available exception
;
;       This trap has to be treated specially because it can occur under
;       two very distinct situations. The first situation is if the EM
;       bit is set in the MSW. In this case we treat the trap as any
;       other that is hookable by the user. That is we go off to check
;       for a user exception handler and kill the task if none is available.
;       The second situation is when the TS and MP bits in the MSW are set.
;       In this case we have to perform a NPX context switch.
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure UserNPXNAFault,near,local

;       Switching of the NPX context requires kernel mode as the thread
;       may block (e.g. for allocation of an NPX context buffer.  Note
;       also that the NPX context switch contains a critical section
;       consisting of the following steps:
;
;       *  Save former NPX owner's NPX context (if any)
;       *  Change NPX owner to be current task
;       *  Restore current task's NPX context (if any)

;       Determine what action to take, if EM is set the user handler should
;       be called.  If MP and TS are set, a context switch should occur.

        mov     cx, ss              ; are we using a private lin stk?   ;b726833
        cmp     cx, seg FLAT:DGROUP                                     ;b726833
        jne     unp05               ; no, continue normally             ;b726833
                                    ; yes, switch back to os2Stack      ;b726833
        mov     esi, esp                                                ;b726833
        cli                                                             ;b726833
        mov     [privateStack],esi  ; mark where we switched from       ;b726833
        lss     esp,[os2Stack]      ; restore os2Stack                  ;b726833
        sti                                                             ;b726833
        push    esi                                                     ;b726833
        call    unp05               ; do the NPX stuff                  ;b726833
        cli                                                             ;b726833
        pop     [fpRing0Stack].Offst32  ; restore the linear stack      ;b726833
        mov     [fpRing0Stack].Segmt32,ds                               ;b726833
        lss     esp,[fpRing0Stack]                                      ;b726833
        sti                                                             ;b726833
        jmp     short unp20         ;exit                               ;b726833
                                                                        ;b726833
unp05:                                                                  ;b726833
        push    eax
        CCALL32 SwitchNPX
        pop     eax
        jnc     short unp20                     ; if EM bit is clear, return

        cmp     byte ptr ss:[TKOPTDA.ptda_ProcType],PT_VDM
        je      short unp10                     ; jump if not prot mode proc

        CCALL32 TRAPCheckExceptHandler          ; is there a 16 bit handler?
        jz      ProcessFatalFault               ;  no, terminate process
        CCALL32 TRAPArmExceptHandler            ;  yes, return to it
        jmp     short unp20

;       (EAX) = trap number

unp10:  CCALL32 [pemulahndlr]                   ; vnpx routine to reflect
unp20:  RETC
EndProc   UserNPXNAFault


;***LP  UserSegmentNPFault - User segment not present fault
;
;       This handler gets control for segment not presents faults
;       when faulting CS is not ring 0.
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    eax, ebx, ecx, edx, edi, eflags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure UserSegmentNPFault,near,local

LocalVar  usf_FaultBuf,<CBOBJNAMEBUF+CBERRCODEBUF>

        EnterProc

;       setup the error buffers in the tcb so we can have info in the pop-up

        mov     pksf,ebx        ;save pointer to KSF in case we need it 116367
                                ;to process an error from the load      116367

        SSToDS  ecx,usf_FaultBuf
        mov     ebx,_pTCBCur
        mov     [ebx].TCB_FaultBuf,ecx
        mov     [ebx].TCB_FaultBufL,CBOBJNAMEBUF

;       call the loader to load this thing

        mov     eax,[_pPTDACur]         ; (eax) = our ptda
        mov     edi,[eax].ptda_ldtaddr  ; (edi) = pointer to ldt
        and     edx,RPL_CLR - TABLE_MASK; (edx) = offset in ldt of descriptor
        add     edi,edx                 ; (edi) = pointer to target descriptor
        movzx   eax,[eax].ptda_module   ; (eax) = target mte handle

        cmp     edx,(SEL_64K*1024) shr SEL_LADDR_SHIFT  ; below 64mb?
        jb      usf10                   ; if so, skip VMGetOd

        push    edx                     ; save selector for later use

        call    SegmentNPFaultFindMTE   ; find appropriate MTE
        or      eax,eax                 ;
        .errnz  NO_ERROR                ;
        jnz     short usfx1             ;

        mov     eax,edx                 ; (eax) = appropriate target mte handle
        pop     edx                     ; (edx) = selector

        or      eax,eax                 ; check for mte handle of 0 - do not
        jz      usfx2                   ; call the loader if this is the case

;       do the actual load

usf10:  call    SELLoadSegment

        mov     [ebx].TCB_FaultBufL,0   ; reset fault buffer

        or      eax,eax                 ; error from load?
        jnz     short usfe              ; if error, jump out of line

usfx:   LeaveProc
        RETC

usfx1:  pop     edx                     ; return error code from
        jmp     usfx                    ; from VMGetOd

usfx2:  mov     eax,ERROR_INVALID_HANDLE
        jmp     short usfx


;       we get here if there was an error during the load

usfe:   cmp     eax,ERROR_INTERRUPT     ; if we were interrupted, just get out
        jz      short usfx

        mov     edi,[_pTSDCur]
        mov     [edi].TSDPFErr,eax              ; Set TSDPFErr

;       call    ProcessFatalFault       ; there really was an error     116367
        mov     ebx,pksf                ; load pointer to KSF           116367
        CCALL32 _TRAPProcessFatalFault, <[ebx].ksf_trapnum, [ebx].ksf_errcode>

        jmp     short usfx              ; use common exit

EndProc UserSegmentNPFault


;***LP  UserStackFault - User stack not present or overrun fault
;
;       This handler gets control for stack not presents faults
;       or stack overruns when faulting CS is not ring 0.
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure UserStackFault,near,local
        push    edx
        CCALL32 _trapQueryRegister, <KM_REG_EFLAG>
        pop     edx
        test    eax,F_VM
        jnz     ProcessFatalFault
        or      ebx,-1                          ; for SystemFatalFault
        test    edx,E_SELMASK                   ; is stack not present?
        jnz     SystemFatalFault                ;  yes, jump if stack not pres
        call    CallGateFault
        jc      ProcessFatalFault
        RETC
EndProc   UserStackFault

;***LP  UserPageFault - User code page fault handler
;
;       This handler gets control for page faults when faulting
;       CS is not ring 0.
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDI, ESI
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure UserPageFault,near

LocalVar  upf_FaultBuf,<CBOBJNAMEBUF+CBERRCODEBUF>

        EnterProc

IFDEF   PMDEBUG
IFDEF   PGSTRICT
;       Slide entries in page fault array down to make room for a new entry

        lea     edi,[_apfi + (NPFI * type pfi_s) - 4]
        lea     esi,[edi - type pfi_s]
        mov     ecx,((NPFI - 1) * type pfi_s) SHR 2
.errnz  (((NPFI - 1) * type pfi_s) AND 3)
        std
        rep     movsd
        cld

;       Record address of instruction generating page fault, slot # of current
;       thread, faulting address, and processor error code.

        CCALL32 _trapQueryRegister, <KM_REG_EIP>
        mov     [_apfi].pfi_off,eax
        CCALL32 _trapQueryRegister, <KM_REG_CS>
        mov     [_apfi].pfi_seg,ax
        mov     eax,[_pTCBCur]
        mov     ax,[eax].TCBNumber
        mov     [_apfi].pfi_slot,ax
        mov     eax,cr2
        mov     [_apfi].pfi_fault,eax
        mov     [_apfi].pfi_errcode,edx
ENDIF ; PGSTRICT
ENDIF ; PMDEBUG

;       If user code executing with interrupts off, enter into critical section

        mov     eax,cr2                         ; eax = cr2
        push    eax
        CCALL32 _trapQueryRegister, <KM_REG_EFLAG>
        test    eax,F_INTERRUPT+F_VM            ; Check interrupts
        mov     eax,[_pTCBCur]
        jnz     short upf20                     ; interrupts on, continue

        push    esi
        mov     si,ss:[TKOPTDA.CurrTCB]
        cmp     ss:[si].TCBOrdinal,1            ; Thread 1?
        pop     esi
        jne     short upf10                     ;  no, continue
;        push    esi                                             P725080
;        mov     esi,ss:[TKOPTDA.ptda_pTCBHead]                  P725080
;        cmp     [esi].TCBSig_cur,0          Any current signals P725080
;        pop     esi                                             P725080
;        je      short upf10                 no, continue        P725080

        cmp     ss:[TKOPTDA].ptda_pTCBCritSec,0 ; P723608
        jne     short upf30                     ; no Critsec in effect p723608
upf10:  bts     [eax].TCBEntryActions,TK_IEF_PFCLI ; is this the first time ?
        jc      short upf30                     ; no, we are already in critsec
        CALL16  EnterCritSecHoldSig             ; hold signals and enter CritSec
        jmp     short upf30

upf20:  btr     [eax].TCBEntryActions,TK_IEF_PFCLI ; interrupts are enabled
        jnc     short upf30                     ; we are not in crit sec
        CALL16  ExitCritSecUnHoldSig

;       CR2 is on TOS and in EAX

upf30:  mov     eax,[esp]                       ; eax = cr2
        SSToDS  ecx,upf_FaultBuf
        mov     edi,_pTCBCur
        mov     [edi].TCB_FaultBuf,ecx
        mov     [edi].TCB_FaultBufL,CBOBJNAMEBUF
;        shr     eax,PAGESHIFT                  ; compute page number
                                                ; DCR 1321, pass faulting addr
        CCALL32 _PGPageFault, <eax, edx>        ; load page
        mov     [edi].TCB_FaultBufL,0           ; reset fault buffer
        pop     ecx                             ; ecx = cr2
        mov     cr2,ecx                         ; restore cr2
        or      eax,eax
        jz      short upf40                     ; page fault handled b722559
        cmp     eax,ERROR_INTERRUPT             ; handled in exit kmode
        je      short upf40                     ; b722559

        cmp     ss:[TKOPTDA.ptda_ProcType],PT_VDM ; VDM mode?              1.4
        jne     short upf35                       ;  no, kill process      1.4

        testb   [_flVdmStatus],VDM_STATUS_VPM_APP ; Is VDPMI Host active? GK ;149007
        jz      short upf35                       ;  no, kill VDM   ;149007

        push    ecx                             ; save CR2    ;149007

        testb   [_flVdmStatus],VDM_STATUS_VPM_EXEC ; In VPM mode? ;149007
        jz      short upf31                        ;  no, check VDM V86 ;149007

        PCall   VDHCHECKVPMEXCEPT, <EXCPG>    ; Are VPM page faults hooked? ;149007
        jmp     short upf31_1                 ;149007
upf31:                                        ;149007
        push    ebx                           ;149007
        PCall   VDHCHECKVPMV86EXCEPT, <EXCPG> ; Are V86 page faults hooked?;149007
        pop     ebx                           ;149007
upf31_1:                                      ;149007
        pop     ecx                           ; Restore CR2  ;149007
        or      ax,ax                         ;149007
        jz      short upf35                   ;  no, kill VDM;149007

        push    ecx                           ; save CR2 in stack ;149007

        CCALL32 _trapQueryRegister,<KM_REG_ERRCODE>     ; get error code   1.4
        pop     ecx                             ; pop CR2 from the stack ;149007
                                                ; (ECX) = CR2           ;149007
        mov     edx,eax                         ; (EDX) = error code       1.4
        mov     eax,EXCPG                       ; (EAX) = exception        1.4
        PCall   EM86REFLECTEXCEPTION     ; yes, reflect back to VDM ;149007
        jmp     short upf40              ; exit!                    1.4

;       Fatal Fault: Dispatch to exception handlers.

upf35:  mov     edi,[_pTSDCur]
        mov     [edi].TSDPFErr,eax              ; Set TSDPFErr

        call    ProcessFatalFault

;upfx:   cmp     [_fB1NPX], 0                    ; Deal w/ B1 NPX errata?
;        jne     short upf50                     ;  No.
upf40:  LeaveProc
        RETC

;
; ECX contains CR2
;

;upf50:  and     ecx, PAGEMASK                   ; Errata only hit if at page
;b722559 jnz     short upf40                     ;  boundary.
;b722559 mov     eax, [_pTCBCur]
;b722559 cmp     [eax].TCB_NPX_Buffer, 0         ; Is this thread using the NPX?
;b722559 je      short upf40                     ;  No. Don't check
;b722559 CCALL32 B1NPX_Errata_02                 ;  Yes.
;b722559 jmp     short upf40

EndProc   UserPageFault

;***LP  KernelTraceFault - Kernel single step fault handler
;
;       This handler gets control for single step faults when the
;       faulting CS is ring 0.
;
;       ENTRY   EAX = trap number
;               EBX = KSF stack frame pointer
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, ESI
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure KernelTraceFault,near,local
        mov     esi,edx
        cmp     [InDos],0                       ; if (!InDos
        jnz     short ktf10


        mov     ecx,[_pPTDACur]

ifdef INCDEBUG
        cmp     [ecx].ptda_flDbgEnabled,0       ;     && Debug enabled
        jz      short ktf10
endif ;PMDEBUG

        cmp     [ecx].ptda_ProcType,PT_VDM      ;        (must not be VDM)
        jz      short ktf10

        btr     esi,DR6_BS_BIT_INDEX            ;     && DR6 BS set) {
        jnc     short ktf10

        and     [ebx].ksf_eflag,NOT F_TRACE     ;    clear flags TF bit
        mov     edi,[_pTCBCur]
        and     [edi].TCB_flDbg,NOT DBGT_SSTEP  ;    clear thread SSTEP bit
        test    [edi].TCB_flDbg,DBGT_XCHGOP     ;    if (xchg sstep)
        jz      short ktf10
        or      [edi].TCBEntryActions,TK_EF_TRC ;       set entry trace flag
                                                ; }
ktf10:  and     esi,NOT (DBGDR6IMAGE_ENTERKMODE); if (DR6 is clear)
        jnz     short ktfx
        RETC                                    ;    return

ktfx:   cli                                     ; don't allow DR6 to change
        mov     eax,dr6
        or      eax,esi
        mov     dr6,eax
        mov     [ebx].ksf_errcode,NO_ERROR
        jmp     TRAPDebugFault
EndProc   KernelTraceFault

;***LP  NMIFault - User or kernel NMI fault handler
;
;       ENTRY   EAX = trap number
;               EBX = -1 (stack frame type unknown) or
;                     KSF stack frame pointer for ring 0 faults
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, EDX, ESI, Flags
;
;       CONTEXT User
;               Task
;
;       PSEUDO-CODE
;
;       if watchdogNMI
;          print watchdog nmi message
;       else
;           if abios machine
;              get reason for NMI code
;           else
;              use (0) for NMI code
;           print NMI message
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure NMIFault,near,local

LocalVar pphys,DWORD
LocalVar htoascii,DWORD
LocalVar nmi_pptda,DWORD
LocalVar nmierr,WORD
LocalVar errorlogptr,WORD

        EnterProc
        SaveReg <eax,ebx,edx,edi,esi,ds,es>

;                                                                        122474
; Defect # 122474 moved reference to macro TKCallKrnlExit into procedure 122474
; NWDHandler located in NWD.ASM. This move allows a user routine         122474
; registered with DEVHLP to svc the NMI without doing any TSS editing.   122474
; If the NMI is not handled by the user routine, or if there is none     122474
; registered, then the TSS is edited in NWDHandler to force control into 122474
; here.                                                                  122474
;                                                                        122474

         mov     nmierr,0

;       Was this a watchdog NMI

        test    fNWD,TRUE
        jz      nmi10

        FlatContext <ds,es>
;       yes, format hard err message, and kill process

        mov     fNWD,FALSE
        cmp     ebx,-1                          ; timeout in kernel?
        jne     nmi5                            ; yes, do system fatal fault

        CCALL32 _FormatRegisters, <EXCNMI, 0>
        mov     si,offset DOSGROUP:FaultMsg
        mov     ebx,2
        mov     edx,MSG_INT_TOO_LONG
        mov     eax,TC_GP_TRAP
        mov     ecx,1                           ; one replacement string
        xor     edi,edi                         ; No exception record
        DosContext <ds>
        CALL16  DelayHardErr
        FlatContext <ds>
nmi0:   clc                             ; clear carry so that we know
        jmp     nmixx                   ; that we want to return
;       RestoreReg <es,ds,esi,edi,edx,ebx,eax>
;       LeaveProc
;       ret

nmi5:   CCALL32 _DHGetMessage32, <MSG_INT_TOO_LONG>
        mov     edx,eax
        mov     cx,word ptr [edx]
        add     edx,2
        movzx   ecx,cx
        SaveReg <esi>
        mov     esi,ecx
        add     esi,edx
        std
nmi6:   lodsb
        cmp     al,'%'
        je      nmi7
        dec     ecx
        jmp     nmi6

nmi7:   dec     ecx
        RestoreReg <esi>
        jmp     nmi30

nmi10:  xor     eax,eax
        mov     edx,-1                          ; no address found
        mov     errorlogptr,-1
        cmp     ABIOS_present,0
        stc
        jz      nmixx

        pushad
        push    es
        push    ds
        DosContext <ds>
        call    DHF.DosHlpGetErrorLogPtr
        mov     errorlogptr,ax
        pop     ds
        pop     es
        popad

        CALL16  IntNMI                          ; (EAX) = error code for nmi
        mov     nmierr,ax

        SaveReg <ax>
        mov     dx,0a1h
        mov     al,0ffh
;       out     dx,al
        RestoreReg <ax>

        cmp     ax,1
        jne     nmi20

        CALL16  DisableNMI                      ; disable NMI from occuring again
        SaveReg <ax,dx>
        cli
        RestoreReg <dx,ax>
        DosContext <ds>
        call    DHF.DosHlpDisableCache          ; disable and flush cache
        FlatContext <ds,es>
        CCALL32 _PGTrap2FindPage
        DosContext <ds>
        call    DHF.DosHlpEnableCache
        CALL16  EnableNMI
        FlatContext <ds,es>

        pushad
        push    es
        push    ds
        mov     edx,_NMIPhysAddr
        cmp     edx,-1                          ; address found ?
        je      nmi11                           ; no, don't store addres
        mov     ax,errorlogptr                  ;
        cmp     ax,-1
        je      nmi11

;       EDX = physical address
;        AL = error log ptr

        DosContext <ds>
        call    DHF.DosHlpWriteErrorLog
nmi11:  pop     ds
        pop     es
        popad

        cmp     eax,2                           ; system recovered
        jne     nmi12                           ; no,

        mov     edx,_NMIPhysAddr

        cmp     edx,-1                          ; address not found ?
        je      nmi20                           ; yes, kill system
        jmp     short nmi14                     ; kill process

nmi12:  mov     edx,_NMIPhysAddr
        cmp     edx,-1                          ; address not found ?
        je      nmi20                           ; yes, kill system

        cmp     eax,1                           ; kill system
        jne     nmi20                           ; yes

nmi14:  shld    eax,edx,16              ; (AX) = high part of value
        FlatContext <ds,es>
        SSToDS  edi,htoascii            ; es:[edi]->physical address
        CCALL32 HexToAscii32
        mov     eax,htoascii
        DosContext <ds>
        mov     si,offset DOSGROUP:NMIMsg
        mov     ds:[si],al
        inc     si
        mov     al,ah
        mov     ds:[si],al
        inc     si
        shr     eax,16
        mov     ds:[si],al
        inc     si
        mov     al,ah
        mov     ds:[si],al
        inc     si

        FlatContext <ds,es>
        SSToDS  edi,htoascii            ; es:[edi]->physical address
        mov     eax,edx                 ; (AX) = low part of value
        CCALL32 HexToAscii32
        mov     eax,htoascii
        DosContext <ds>
        mov     ds:[si],al
        inc     si
        mov     al,ah
        mov     ds:[si],al
        inc     si
        shr     eax,16
        mov     ds:[si],al
        inc     si
        mov     al,ah
        mov     ds:[si],al

        mov     esi,offset DOSGROUP:NMIMsg
        mov     ebx,2
        mov     edx,1959
        mov     eax,TC_HARDERR
        mov     ecx,1                           ; one replacement string
        mov     edi,ES:_NMIPTDA
        DosContext <ds>
        CALL16  DelayHardErrTrap2
        FlatContext <ds>
        jmp     nmi0

nmi20:  DosContext es
        SaveReg <edx>
        mov     ax,nmierr
        call    DHF.DosHlpWhyNMI                ; (FS:EDX) = message pointer
        RestoreReg <ebx>
        mov     ax,fs
        mov     ds,ax                           ; (DS) = message selector
        mov     cx,[edx]                        ; (CX) = message length
        movzx   ecx,cx
        add     edx,2
        cmp     ebx,-1
        je      nmi30
        SaveReg <ecx,edx>
        mov     esi,edx
nmi25:  lodsb
        or      al,al                   ; end of string ?
        jz      nmi28                   ; yes, go halt

        cmp     al,'%'                  ; physical address ?
        je      nmi27                   ; yes, go process
        CALL16  ByteToScrnDM
        jmp     short nmi25

        loop    nmi25
        jmp     nmi28
nmi27:  shld    eax,ebx,16              ; (AX) = high part of value
        mov     edi,esi
        SaveReg <ds>
        FlatContext <ds,es>
        SSToDS  edi,htoascii            ; es:[edi]->physical address
        CCALL32 HexToAscii32
        mov     eax,htoascii
        CALL16  ByteToScrnDM
        mov     al,ah
        CALL16  ByteToScrnDM
        shr     eax,16
        CALL16  ByteToScrnDM
        mov     al,ah
        CALL16  ByteToScrnDM


        SSToDS  edi,htoascii            ; es:[edi]->physical address
        mov     eax,ebx                 ; (AX) = low part of value
        CCALL32 HexToAscii32
        mov     eax,htoascii
        CALL16  ByteToScrnDM
        mov     al,ah
        CALL16  ByteToScrnDM
        shr     eax,16
        CALL16  ByteToScrnDM
        mov     al,ah
        CALL16  ByteToScrnDM
        RestoreReg <ds>
        inc     esi                     ; inc past %1
        jmp     nmi25
nmi28:  RestoreReg <edx,ecx>
nmi30:  FlatContext <ds,es>,ax

;       movzx   eax,sp                   ; d25346                      JM_78486
;       mov     dword ptr ss:[eax+20],-1 ; d25346 - change EBX to -1   JM_78486
        stc                              ; carry means return
nmixx:  RestoreReg <es,ds,esi,edi,edx,ebx,eax>
        LeaveProc

        jc      short nmixy             ; do not ret if carry
        ret
nmixy:

;       FlatContext <ds,es>,bx          ; d25346
        FlatContext <ds,es>             ; d25346

;IFDEF PMDEBUG                                                         JM_78486

;       Generate panic here if the current stack frame is neither _criVSF  JM_78486
;       nor _criTSF so that it is easier to determine that an NMI occurred JM_78486

;       push    ebx                     ; d25346                           JM_78486
;       mov     ebx,[_pTCBCur]          ; (EBX) = pointer to current TCB   JM_78486
;       mov     ebx,[ebx].TCB_pcriFrameType ; (EBX) = stack frame CRI      JM_78486
;       cmp     ebx,offset FLAT:_criVSF ; v86 mode stack frame?            JM_78486
;       je      short nmixz             ;  yes, take default action        JM_78486
;       cmp     ebx,offset FLAT:_criTSF ; trap stack frame?                JM_78486
;       je      short nmixz             ;  yes, take default action        JM_78486
;       Panic   <"NMIFault: Trap 2 at unknown physical address">           JM_78486
;nmixz:  pop     ebx                     ; d25346                          JM_78486
;ENDIF                                                                     JM_78486
;       mov     ebx,-1                  ; d25346                           JM_78486
;       FlatContext <ds,es>             ; d25346                           JM_78486

        jmp     SystemFatalFault
EndProc   NMIFault, NoCheck

;***LP  KernelInvalidTSSFault - Kernel invalid TSS fault
;
;       ENTRY   EAX = trap number
;               EBX = KSF stack frame pointer
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure KernelInvalidTSSFault,near,local
        test    [ebx].ksf_eflag,F_NT
        jz      SystemFatalFault                ; jmp if bonafide trap

;       We get here when the trap has been caused as the result of the
;       kernel or device driver incorrectly diddling the flags to set
;       the nested task (NT) bit while the back-link selector in the TSS
;       is invalid.  The trap is thus taken when an IRET is executed. The
;       course now is to clear the NT flag from the stack frames (the trap
;       automatically clears the register NT flag) and restart the IRET.

        and     [ebx].ksf_eflag,NOT F_NT        ; clear NT in trap frame

;       If the faulting instruction is an IRET then clear the NT flag
;       in the IRET frame as well.  If an operand override is seen, the
;       IRET frame is 32 bits.

        mov     ecx,offset iret16_flag          ; (ECX) = 16 bit IRET offset
        lfs     esi,[ebx].ksf_cseip             ; (FS:ESI) = client's CS:EIP
kts10:  lods    byte ptr fs:[esi]               ; (AL) = opcode
        cmp     al,MI_OPERANDSIZE
        jne     short kts20
        mov     ecx,offset iret32_eflag         ; (ECX) = 32 bit IRET offset
        jmp     short kts10

kts20:  cmp     al,MI_IRET
        jne     SystemFatalFault                ; jmp if CS:IP != IRET
        and     [ebx+ecx].ksf_tos.loword,not F_NT ; clear NT on kernel stack
ktsx:   RETC                                    ; restart faulting instruction
EndProc   KernelInvalidTSSFault

;***LP  KernelSegmentNPFault - Kernel segment not present fault handler
;
;       This handler gets control for segment not present fault when
;       the faulting CS is ring 0.
;
;       ENTRY   EAX = trap number
;               EBX = KSF stack frame pointer
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    all
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure KernelSegmentNPFault

;       not present faults when at ring 0 but not in kernel mode not allowed

        cmp     [InDos],0                       ; ring 0/InDos == 0?
        jz      short ksf20                     ;  yes, jump if ring 0/InDos 0

;       call the loader to load this thing

        mov     eax,[_pPTDACur]         ; (eax) = our ptda
        mov     edi,[eax].ptda_ldtaddr  ; (edi) = pointer to ldt
        and     edx,RPL_CLR - TABLE_MASK; (edx) = offset in ldt of descriptor
        add     edi,edx                 ; (edi) = pointer to target descriptor
        movzx   eax,[eax].ptda_module   ; (eax) = target mte handle

        cmp     edx,(SEL_64K*1024) shr SEL_LADDR_SHIFT  ; below 64mb?
        jb      ksf09                   ; if so, skip VMGetOd

        push    edx                     ; save selector for later use

        call    SegmentNPFaultFindMTE   ; find appropriate MTE
        or      eax,eax                 ;
        .errnz  NO_ERROR                ;
        jnz     short ksfx1             ;

        mov     eax,edx                 ; (eax) = appropriate target mte handle
        pop     edx                     ; (edx) = selector

        or      eax,eax                ; check mte handle is not zero - this
        jz      ksfx2                  ; segment not for ldr if hmte is 0

;       do the actual load

ksf09:  call    SELLoadSegment

        or      eax,eax                 ; error from load?
        jnz     short ksf10             ; if error, jump out of line

ksfx:   RETC

ksfx1:  pop     edx                     ; return the error code from
        jmp     ksfx                    ; VMGetOd

ksfx2:  mov     eax,ERROR_INVALID_HANDLE ; bad mte handle
        jmp     short ksfx

ksf10:  cmp     eax,ERROR_INTERRUPT             ; handled in exit kmode
        je      short ksfx

ksf20:  mov     edi,[_pTSDCur]                  ; (EDI) = TSD pointer
        mov     [edi].TSDPFErr,eax              ; error code from loader
        mov     eax,[ebx].ksf_errcode           ; (EAX) = error code
        mov     [edi].TSDerrcFault,eax          ; save error code
        cmp     [edi].TSDpfnFault,0             ; special handler active?
        je      ksf30                           ;  no, jmp

;       If we get here, an invalid segment fault happened at ring 0 or a
;       fatal error occured in processing a segment fault at ring 0 and a
;       local fault handler is in effect.  This implies that we where
;       doing some operation on behalf of the user, and that this operation
;       caused the fault.  Rather than give an internal error, we branch
;       to the address stored in TSDpfnFault.  Since we edit the
;       iret frame's EIP on the stack with the fault handler address, the
;       fault handler has to be in the code segment that the fault happened.
;       On entry to the local fault handler, EAX = error code, DS, ES, FS
;       and GS are cleared.
;       On entry to the local fault handler, EAX = error code, DS, ES = Flat,
;       FS and GS clear, and TSDTrapNum = EXCSNP
;
;       NOTE: The trap handler stored in TSDpfnFault is cleared.

        xor     eax,eax                         ; make sure no bad selectors
        mov     [ebx].ksf_ds,ds                 ; FlatContext(DS)
        mov     [ebx].ksf_es,ds                 ; FlatContext(ES)
        mov     [ebx].ksf_fs,ax
        mov     [ebx].ksf_gs,ax
        mov     [edi].TSDTrapNum,EXCSNP         ; Page Fault
        xchg    eax,[edi].TSDpfnFault           ; (EAX) = fault handler address
        mov     [ebx].ksf_eip,eax               ; put handler addr in EIP
        mov     [ebx].ksf_eax,ERROR_PROTECTION_VIOLATION
        jmp     short ksfx                      ; exit

;       apparently fatal fault

ksf30:  cmp     [InDos],0                       ; ring 0/InDos == 0?
        jnz     SystemFatalFault                ;  no, jump to system panic

;       fault was trying to return to user and it is his fault

        call    RebuildStackFrame
        mov     eax,[ebx].ksf_trapnum           ; (EAX) = trap number
        mov     edx,[ebx].ksf_errcode           ; (EDX) = error code

        mov     edi,[_pPTDACur]                 ;                           6.41
        cmp     [edi].ptda_ProcType,PT_VDM      ; VDM (DPMI) process?       6.41
        jne     UserSegmentNPFault              ; no, now handle page fault 6.41

        mov     ebx,[_pvsfFrameBase]            ; (EBX) = VSF stack frame ptr 6.41
        jmp     EM86ReflectVPMException         ; reflect NP fault to VDM   6.41

EndProc KernelSegmentNPFault

;***LP  SegmentNPFaultFindMTE - Find appropriate MTE handle for segment NP
;                               fault handlers.
;

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure SegmentNPFaultFindMTE

;;;;;LocalVar    lps_oi,<size vmoi_s>
LocalVar    lps_od,<size vmod_s>

        EnterProc

; KDH - Have to find mte handle.
;
;       _VMGetOd(flags, pod)
;       (EBX) = FLAT address past last descriptor
;       (ESI) = ptda pointer
;       (EDI) = descriptor FLAT address

;       pull base address of selector out of descriptor

        mov     ch,[edi].d_extaddr
        mov     cl,[edi].d_hiaddr
        shl     ecx,16
        mov     cx,[edi].d_loaddr       ; (ecx) = base of selector

;;;;;;; mov     eax,[_pPTDACur]         ; (eax) = our ptda
;;;;;;; movzx   edx,[eax].ptda_handle   ; (edx) ptda handle
        mov     edx,[_pPTDACur]         ; (eax) = our ptda
;;;;;;; SSToDS  eax,[lps_oi]            ; address to return info
        SSToDS  eax,[lps_od]            ; address to return info
        mov     [eax].od_laddr, ecx
        mov     [eax].od_pPTDA, edx
;;;;;;; CCALL32 _VMObjectInfo <ecx, edx, eax>
        xor     edx, edx
        CCALL32 _VMGetOd <edx, eax>
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short snpx

;;;;;;; movzx   edx,[lps_oi].oi_hobmte  ; (edx) = mte handle
        mov     edx, [lps_od].od_pob
        movzx   edx,[edx].ob_hobmte     ; (edx) = mte handle

snpx:   LeaveProc
        RETC

EndProc SegmentNPFaultFindMTE


;***LP  KernelGPFault - Kernel GP fault handler
;
;       This handler gets control for GP faults when the faulting
;       CS is ring 0.
;
;       ENTRY   EAX = trap number
;               EBX = KSF stack frame pointer
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure KernelGPFault,near,local
        mov     edi,[_pTSDCur]                  ; (EDI) = TSD pointer
        mov     [edi].TSDerrcFault,edx          ; save error code
        cmp     [edi].TSDpfnFault,0             ; special handler active?
        je      short kgp10                     ;  no, check instruction

;       If we get here, a GP fault happened at ring 0 and a local
;       fault handler is in effect.  This implies that we where doing
;       some operation on behalf of the user, and that this operation
;       caused the fault.  Rather than give an internal error, we branch
;       to the address stored in TSDpfnFault.  Since we edit the
;       iret frame's EIP on the stack with the fault handler address, the
;       fault handler has to be in the code segment that the fault happen.
;       On entry to the local fault handler, EAX = error code, DS, ES = Flat,
;       FS and GS clear, and TSDTrapNum = EXCGP.
;
;       NOTE: The trap handler stored in TSDpfnFault is cleared.

        xor     eax,eax                         ; make sure no bad selectors
        mov     [ebx].ksf_ds,ds                 ; FlatContext(DS)
        mov     [ebx].ksf_es,ds                 ; FlatContext(ES)
        mov     [ebx].ksf_fs,ax
        mov     [ebx].ksf_gs,ax
        mov     [edi].TSDTrapNum,EXCGP          ; General Protection Fault
        xchg    eax,[edi].TSDpfnFault           ; (EAX) = fault handler address
        mov     [ebx].ksf_eip,eax               ; put handler addr in EIP
        mov     [ebx].ksf_eax,ERROR_PROTECTION_VIOLATION
        RETC                                    ; return to fault handler

;       The fault was at ring 0.  If InDos == 0, get the instruction
;       opcode at the CS:EIP.  The CS:EIP is in the kernel and always
;       valid so we don't need to use FuByte.

kgp10:  cmp     [InDos],0                       ; ring 0 and InDos == 0?
        jne     SystemFatalFault                ;  no, system fatal fault

        lfs     esi,[ebx].ksf_cseip             ; (FS:ESI) = CS:EIP
kgp20:  lods    byte ptr fs:[esi]               ; (AL) = [CS:EIP]
        cmp     al,MI_OPERANDSIZE
        je      short kgp20                     ; skip overrides
        cmp     al,MI_ADDRESSSIZE
        je      short kgp20                     ; skip overrides

        cmp     al,MI_POP_DS
        je      short kgp60                     ; jump if seg reg load
        cmp     al,MI_POP_ES
        je      short kgp60                     ; jump if seg reg load

        cmp     al,MI_IRET
        je      short kgp70                     ; jump if IRET
        cmp     al,MI_LONG_RET
        je      short kgp70                     ; jump if far RET
        cmp     al,MI_LONG_RETn
        je      short kgp70                     ; jump if far RET N

        cmp     al,MI_MOV_SEG_MEM_OPCODE        ; MOV seg,r/m16?
        je      short kgp40                     ;  yes, handle mov seg reg
        cmp     al,MI_TWOBYTEOP                 ; is it a two byte opcode?
        jne     short kgp30                     ;  no, don't check for gs,fs

        lods    byte ptr fs:[esi]               ; (AL) = second opcode byte
        cmp     al,MI_POP_GS
        je      short kgp60
        cmp     al,MI_POP_FS
        je      short kgp60

;       Not a mov/pop segment register or return instruction; it is a
;       system fatal fault.

kgp30:  jmp     SystemFatalFault

;       MOV seg,r/m16 instruction (VDM protected mode stack frame)

kgp40:  lods    byte ptr fs:[esi]               ; (AL) = R/M byte
        mov     ah,al                           ; (AH) = R/M byte

        and     al,(MI_MODMASK or MI_RMMASK)
        cmp     al,(MI_MODBYTE or MI_RMSIB)     ; byte displacement/sib byte?
        jne     short kgp30                     ;  no, go fault in kernel

        lods    byte ptr fs:[esi]               ; (AL) = SIB byte
        cmp     al,(MI_SIB_INDEXNONE or MI_SIB_BASEESP) ; disp8[esp] sib byte ?
        jne     short kgp30                     ;  no, go fault in kernel

        and     ah,MI_REGMASK                   ; (AH) = register number
        cmp     ah,MI_REGDS
        je      short kgp50
        cmp     ah,MI_REGES
        je      short kgp50
        cmp     ah,MI_REGFS
        je      short kgp50
        cmp     ah,MI_REGGS                     ; is it the last valid segment?
        jne     short kgp30                     ;  no, go fault in kernel

;       Handle a MOV ds, es, gs, fs in the kernel when InDos == 0
;       that GP faults by zeroing the segment register.  We assume
;       [esp] indexing and a byte displacement.

kgp50:  movzx   eax,byte ptr fs:[esi]           ; (EAX) = 8 bit displacement
        mov     [eax+ebx].ksf_tos.loword,0      ; clear the bad selector
        jmp     short kgpx                      ; restart instruction

;       Handle a pop ds, es, gs or fs in the kernel when InDos == 0
;       that generates a GP fault.  Since we trapped at ring 0 there
;       was no priviledge level transition and SS:ESP wasn't saved.  The
;       top dword on the stack is ksf_tos.

kgp60:  mov     [ebx].ksf_tos.loword,0          ; clear the bad selector
        jmp     short kgpx                      ; restart instruction

;       Handle GP faults on iret in the kernel returning to user code.

kgp70:  call    RebuildStackFrame
        CCALL32 _TRAPProcessFatalFault, <[ebx].ksf_trapnum, [ebx].ksf_errcode>
kgpx:   RETC                                    ; restart instruction
EndProc   KernelGPFault

;***LP  KernelPageFault - Kernel page fault handler
;
;       This handler gets control for page faults when the
;       faulting CS is ring 0.
;
;       ENTRY   EAX = trap number
;               EBX = KSF stack frame pointer
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, FS, GS
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure KernelPageFault,near,local
        EnterProc
IFDEF   PMDEBUG
IFDEF   PGSTRICT

;       Slide entries in page fault array down to make room for a new entry

        lea     edi,[_apfi + (NPFI * type pfi_s) - 4]
        lea     esi,[edi - type pfi_s]
        mov     ecx,((NPFI - 1) * type pfi_s) SHR 2
.errnz  (((NPFI - 1) * type pfi_s) AND 3)
        std
        rep     movsd
        cld

;       Record address of instruction generating page fault, slot # of current
;       thread, faulting address, and processor error code.

        mov     eax,[ebx].ksf_eip
        mov     [_apfi].pfi_off,eax
        movzx   eax,[ebx].ksf_cs
        mov     [_apfi].pfi_seg,ax
        mov     eax,[_pTCBCur]
        mov     ax,[eax].TCBNumber
        mov     [_apfi].pfi_slot,ax
        mov     eax,cr2
        mov     [_apfi].pfi_fault,eax
        mov     [_apfi].pfi_errcode,edx
ENDIF ; PSTRICT
ENDIF ; PMDEBUG
        cmp     [InDos],0                       ; ring 0/InDos == 0?
        jz      short kpf10                     ;  yes, jump if ring 0/InDos 0

        mov     eax,cr2
        push    eax                             ; save cr2
;        shr     eax,PAGESHIFT                  ; compute page number
                                                ; DCR 1321, pass faulting addr
        CCALL32 _PGPageFault, <eax, edx>        ; load page
        pop     ecx
        mov     cr2,ecx                         ; restore cr2
        or      eax,eax                         ; exit if no error
        jz      short kpfx
        cmp     eax,ERROR_INTERRUPT             ; handled in exit kmode
        je      short kpfx

kpf10:  mov     edi,[_pTSDCur]                  ; (EDI) = TSD pointer
        mov     [edi].TSDPFErr,eax              ; error code from PGPageFault
        mov     eax,[ebx].ksf_errcode           ; (EAX) = error code
        mov     [edi].TSDerrcFault,eax          ; save error code
        cmp     [edi].TSDpfnFault,0             ; special handler active?
        je      short kpf20                     ;  no, kernel exit page fault

;       If we get here, an invalid page fault happened at ring 0 or a
;       fatal error occured in processing a page fault at ring 0 and a
;       local fault handler is in effect.  This implies that we where
;       doing some operation on behalf of the user, and that this operation
;       caused the fault.  Rather than give an internal error, we branch
;       to the address stored in TSDpfnFault.  Since we edit the
;       iret frame's EIP on the stack with the fault handler address, the
;       fault handler has to be in the code segment that the fault happened.
;       On entry to the local fault handler, EAX = error code, DS, ES, FS
;       and GS are cleared.
;       On entry to the local fault handler, EAX = error code, DS, ES = Flat,
;       FS and GS clear, and TSDTrapNum = EXCGP.
;
;       NOTE: The trap handler stored in TSDpfnFault is cleared.

        xor     eax,eax                         ; make sure no bad selectors
        mov     [ebx].ksf_ds,ds                 ; FlatContext(DS)
        mov     [ebx].ksf_es,ds                 ; FlatContext(ES)
        mov     [ebx].ksf_fs,ax
        mov     [ebx].ksf_gs,ax
        mov     [edi].TSDTrapNum,EXCPG          ; Page Fault
        xchg    eax,[edi].TSDpfnFault           ; (EAX) = fault handler address
        mov     [ebx].ksf_eip,eax               ; put handler addr in EIP
        mov     [ebx].ksf_eax,ERROR_PROTECTION_VIOLATION
kpfx:   LeaveProc NoCheck
        RETC                                    ; exit

kpf20:  LeaveProc NoCheck
        cmp     [InDos],0                       ; ring 0/InDos == 0?
        jz      short kpf30                     ; yes, rebuild stack frame
        jmp     SystemFatalFault                ;  no, jump to system panic

kpf30:  call    RebuildStackFrame
        mov     eax,[ebx].ksf_trapnum           ; (EAX) = trap number
        mov     edx,[ebx].ksf_errcode           ; (EDX) = error code
        jmp     UserPageFault                   ; now handle page fault
EndProc   KernelPageFault

;***LP  ProcessFatalFault - display trap information and kill process
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure ProcessFatalFault,near,local
        CCALL32 _trapQueryRegister, <KM_REG_TRAPNUM>
        mov     esi,eax
        CCALL32 _trapQueryRegister,<KM_REG_ERRCODE>
        CCALL32 _TRAPProcessFatalFault, <esi, eax>
        ret
EndProc   ProcessFatalFault


;***LP  ProcessFatalFaultV86 - Check if there is an V86 exception handler  ;149007
;                              registered in VDPMI. If yes, call its handler;149007
;                              GK                                           ;149007
;
;       ENTRY   EAX -- Trap Number               ;149007
;               EBX -- CRF                       ;149007
;               ECX -- CR2 if Page Fault         ;149007
;               EDX -- Error Code                ;149007
;                                                ;149007
;       EXIT    NONE                             ;149007
;                                                ;149007
;       USES    EAX, ECX, EDX, ESI, Flags        ;149007
;                                                ;149007
;       CONTEXT Task                             ;149007
;                                                ;149007
;       PSEUDO-CODE                              ;149007
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure ProcessFatalFaultV86,near,local
        testb  [_flVdmStatus], VDM_STATUS_VPM_APP ;If there are no VDPMI client
                                                                ;149007
        jz     pff1                         ; No, go FatalFault ;149007
        push   eax                          ; save registers    ;149007 GK
        push   ebx                                              ;149007
        push   ecx                                              ;149007
        push   edx                                              ;149007

        PCall   VDHCHECKVPMV86EXCEPT, <eax> ; Are V86 exceptions hooked? ;149007
        or     ax,ax                        ; test return result   ;149007
                                                                   ;149007
        pop    edx                          ; restore registers    ;149007GK
        pop    ecx                                                 ;149007
        pop    ebx                                                 ;149007
        pop    eax                                                 ;149007
        jnz    v86handle                    ; Yes, handle exception;149007 GK
                                                                   ;149007
pff1:                                                              ;149007
        call   ProcessFatalFault            ; No, Fatal Processes  ;149007
        ret                                                        ;149007
                                                                   ;149007
v86handle:                                                         ;149007
                                                                   ;149007
        PCall  EM86REFLECTEXCEPTION     ; yes, reflect back to VDM ;149007
        ret                                                        ;149007
                                                                   ;149007
EndProc   ProcessFatalFaultV86                                     ;149007

;***EP  _TRAPProcessFatalFault - display trap information and kill process
;
;       VOID _TRAPProcessFatalFault(ulTrapNum, ulErrCode);
;
;       ENTRY   (ulTrapNum) = trap number to display in register dump
;               (ulErrCode) = error code to display in register dump
;               TSDerridFault and TSDPFErr with appropriate values
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, FS, GS, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _TRAPProcessFatalFault,near

ArgVar  pff_ulTrapNum,DWORD
ArgVar  pff_ulErrCode,DWORD

LocalVar pff_XcptInfo,DWORD             ; ExceptionInfo[0]

        EnterProc
        SaveReg <ds, es, ebx, esi, edi>
IFDEF   PMDEBUG
        mov     esi,[_pTCBCur]          ; (ESI) = current TCB pointer
        mov     ebx,[esi].TCB_pFrameBase; (EBX) = stack frame base
        mov     ecx,[esi].TCB_pcriFrameType  ; (ECX) = PCRI
        mov     eax,[pff_ulTrapNum]     ; (EAX) = trap number
        mov     edx,[pff_ulErrCode]     ; (EDX) = error code

        cmp     ecx,offset FLAT:_criTSF ; is it a trap stack frame?
        je      short tpf10             ;  yes, check if debugger wants it
        cmp     ecx,offset FLAT:_criVSF ; is it a v86 mode stack frame?
        jne     short tpf20             ;  no, display popup

        mov     [ebx].vsf_trapnum,eax
        mov     [ebx].vsf_errcode,edx
        jmp     short tpf15

tpf10:  mov     [ebx].tsf_trapnum,eax
        mov     [ebx].tsf_errcode,edx
tpf15:  or      ebx,-1                  ; (EBX) = no stack frame pointer
        CALL16  _Debug_Fatal            ; is this trap caught by the debugger?
        jc      SystemFatalFault        ;  yes, jump to debugger, no return
ENDIF
tpf20:  mov     esi,[_pTSDCur]          ; (ESI) = current TSD pointer
        mov     eax,ERROR_NOACCESS
        xchg    [esi].TSDerridFault,eax ; (EAX) = PGPageFaultError Id
        cmp     eax,ERROR_NOACCESS      ; genuine trap?
        je      tpf40                   ;  yes, process it

;       There are only two possible cases - ERROR_LDRERROR or ERROR_SWAPERROR
;       Note that ERROR_NOMEMORY cannot happen, since we do overcommit

        mov     ebx,[pff_ulTrapNum]     ; (EBX) = trap number
        mov     esi,[_pTCBCur]
        mov     esi,[esi].TCB_FaultBuf  ; (ESI) = loader error buffer

        cmp     eax,ERROR_LDRERROR      ; is it loader error
        jne     short tpf30             ;  no, must be swapper error
        or      esi,esi                 ; is there a buffer ?
        jz      short tpf40             ;  use default (Trap D/E) if no buffer
        mov     edi,esi
        mov     ecx,CBOBJNAMEBUF + 1
        xor     eax,eax
        repne   scasb                   ; (EDI) = end of load error string + 1
        mov     byte ptr [edi-1],0      ; if string too long, truncate
        mov     eax,_pTSDCur

;       IntToDecAscii(loader error, message buffer pointer)

        CCALL32 _IntToDecAscii <[eax].TSDPFErr, edi>
        mov     ebx,[pff_ulTrapNum]
        DSToSS  esi                     ; (ESI) is now SS relative
        TaskContext <ds>                ; (DS:SI) pointer to buffer on stack
        mov     edx,MSG_DEMAND_LOAD_FAILED
        mov     ecx,2                   ; two replacement strings
        mov     eax,TC_HARDERR          ; hardError
        xor     edi,edi                 ; No exception record
        CALL16  DelayHardErr
        jmp     short tpf50

;       Generate XCPT_IN_PAGE_ERROR here, because XCPTFormatAndSendException
;       will generate a different exception (XCPT_ACCESS_VIOLATION) for this
;       particular trap number.

tpf30:  mov     eax,XCPT_IN_PAGE_ERROR          ; EAX = XcptNum
        mov     ebx,1                           ; EBX = cParameters
        mov     ecx,[pff_ulTrapNum]             ; ECX = trap number
        mov     edx,cr2                         ; EDX = faulting address
        mov     dword ptr [pff_XcptInfo],edx    ; p1 = address
        SSToDS  edx,[pff_XcptInfo]              ; EDX = flat ptr pff_XcptInfo
        CCALL32 _XCPTBuildR3DispatcherStack, <ecx, eax, ebx, edx, FALSE, 0>
        jmp     short tpf50

tpf40:  mov     eax, [pff_ulTrapNum]
        mov     edx, [pff_ulErrCode]
        CCALL32 XCPTFormatAndSendException

tpf50:  RestoreReg <edi, esi, ebx, es, ds>
        LeaveProc
        RETC
EndProc   _TRAPProcessFatalFault

;***LP  SystemFatalFault - display trap info and stop system
;***EP  TRAPDebugFault - direct entry to debugger
;
;       ENTRY   EBX = -1 (stack frame type unknown) or
;                     KSF stack frame pointer for ring 0 faults
;
;       EXIT    Doesn't return
;
;       USES    ALL
;
;       CONTEXT Task
;
;       NOTE    If a device driver switched stacks at interrupt
;               time and faulted since _TKSSBase isn't set correctly,
;               this code will not work properly.
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure SystemFatalFault,near,local
        CALL16  NWDDisableWatchdogNMI   ; disable watchdog nmi
        mov     fNWDEnabled,FALSE       ; make it permanent

        push    ebx                     ;                           83230
        push    ecx                     ;                           83230
; kIOdelay=500ns to allow scsi adapter to                           83230
; finish processing its interrupts.                                 83230
        mov     ecx,0ffffh              ;                           83230
delay0: kIODelay32 ebx                  ;                           83230
        kIODelay32  ebx                 ;                           83230
        loop    delay0                  ;                           83230
        pop     ecx                     ;                           83230
        pop     ebx                     ;                           83230

IFDEF   PMDEBUG
        cmp     fTrapToScreen,0         ; should traps go to debugger?
        jnz     sff30                   ; if not, jmp to print screen message
ENDIF

Entry   TRAPDebugFault,near             ; direct entry to debugger
IFDEF   PMDEBUG

        cmp     ebx,-1                  ; kernel fault?
        jne     short sff_ke            ;  yes, jump to exit kernel trap  83839

        mov     ebx,[_pTCBCur]          ; (EBX) = pointer to current TCB
        mov     esi,[ebx].TCB_pFrameBase; (ESI) = DS relative frame base

;       Adjust ESP to the beginning of the stack frame

        mov     ecx,esi                 ; (ECX) = DS relative frame base
        DSToSS  ecx                     ; (ECX) = SS relative frame base
        mov     esp,ecx                 ; (ESP) = stack pointer back at base

;       We can only handle trap and v86 stack frames

        mov     ecx,[ebx].TCB_pcriFrameType ; (ECX) = stack frame CRI
        mov     ebx,-1                  ; indicate non kernel fault      83839
        cmp     ecx,offset FLAT:_criVSF ; v86 mode stack frame?
        je      short sff_ke            ;  yes, clean off stack          83839
        cmp     ecx,offset FLAT:_criTSF ; trap stack frame?
        je      short sff_ke            ;  yes, clean off stack          83839

        Panic   <"SystemFatalFault: invalid stack frame">

sff_ke:                                 ;                                83839
IF RAS                                  ;                                83839
 ; NOTE:  This code is only for notifing the KERNEL EXIT Information is  83839
 ;        displayed by the Kernel Debugger                               83839
 ;                                                                       83839
        push    esi                     ; ESI is destroyed below         83839
        push    eax                     ; EAX is destroyed below         83839
        push    ebx                     ; EBX is destroyed below         83839

        cmp     ebx,-1                  ; have a valid KSF?              91744
        je      short sff_ke10          ;  no, can't load trapnum        91744
        mov     eax,[ebx].ksf_trapnum   ; (EAX) = trap number            91744

 ; restore the DR6 image we saved away in trap01...
        cmp     eax,KX_SINGLE_STEP_TRAP ; single step trap  0x00000001   83839
        jne     short sff_ke10
	mov	eax,dr6
	or	eax,[ebx].ksf_errcode
	mov	dr6,eax
	jmp	sff_db

sff_ke10:                               ;                                91744
 ; If this is a not a valid trap, it is less than                        83839
 ; 0x00000000 KX_TRAP_NUM_MIN   do not call the kernel exit routine      83839
        cmp     eax,KX_TRAP_NUM_MIN     ;  0x00000000                    83839
        jb      sff_db                  ;                                83839

 ; If this is a not a valid trap, it is greater than                     83839
 ; 0x00000011 KX_TRAP_NUM_MAX   do not call the kernel exit routine      83839
        cmp     eax,KX_TRAP_NUM_MAX     ;  0x00000011                    83839
        ja      sff_db                  ;                                83839

 ; If this is a single step trap do not call the kernel exit routine     83839
        cmp     eax,KX_SINGLE_STEP_TRAP ; single step trap  0x00000001   83839
        je      sff_db                  ;                                83839

 ; If this is a break point trap do not call the kernel exit routine     83839
        cmp     eax,KX_BREAK_POINT_TRAP ; break point trap  0x00000003   83839
        je      sff_db                  ;                                83839

        mov     esi,eax                 ; save trap number               83839
        mov     ebx,ke_Debug_Krnl       ; debug kernel notification      83839
        TKCallKrnlExit SFF_exit         ; by debugger                    83839

sff_db:                                 ;                                83839
        pop     ebx                     ;                                83839
        pop     eax                     ;                                83839
        pop     esi                     ;                                83839
ENDIF                                   ; RAS                            84946

        cmp     ebx,-1                  ; kernel fault?                  83839
        jne     short sff25             ;  yes, jump to exit kernel trap 83839

        cmp     ecx,offset FLAT:_criVSF ; v86 mode stack frame?          83839
        je      short sff10             ;  yes, clean off stack          83839

        cmp     ecx,offset FLAT:_criTSF ; trap stack frame?              83839
        je      short sff20             ;  yes, clean off stack          83839

        Panic   <"SystemFatalFault: invalid code path">       ;          83839

;       Restore the user registers and jump to the debugger.

sff10:  KMExitKmodeVDM v86orvpm,restore ; restore the registers only
        JMP16   _Debug_Trap32           ; jump to the debugger

sff20:  KMExitKmodeTrap restore         ; restore the registers only
        JMP16   _Debug_Trap32           ; jump to the debugger

sff25:  DSToSS_PS ebx                   ; (EBX) = SS relative frame base
        mov     esp,ebx                 ; (ESP) = stack pointer back at base
        KMExitKernelTrap restore        ; restore the registers only
        JMP16   _Debug_Trap32           ; jump to the debugger

ENDIF
sff30:  inc     [_tkPanicActive]        ; already been here?           ;F78693
        jnz     sff60                   ; yes, don't try another print ;F78693

;       Make sure between here and panic that no interrupts come in    ;F78693

        CALL16  ClearAllInts            ; Mask off all interrupts      ;F78693
        cmp     ebx,-1                  ; KSF stack frame (ring 0)?
        jne     short sff35             ;  yes, jump to format KSF stack frame

;       Format all the registers for a non-ring 0 fault

        CCALL32 _trapQueryRegister, <KM_REG_TRAPNUM>
        mov     esi,eax
        CCALL32 _trapQueryRegister,<KM_REG_ERRCODE>
        CCALL32 _FormatRegisters, <esi, eax>
        jmp     sff50

;       Format all the registers except SS:ESP for a ring 0 fault

sff35:  mov     esi,offset FLAT:KFaultFmt
        mov     ecx,KFaultFmtLen
        CCALL32 FormatRegDump           ; format general registers

        mov     esi,offset FLAT:KFaultLimAccFmt
        mov     ecx,KFaultLimAccFmtLen
        CCALL32 FormatLimAccDump        ; put the limit/access byte values in

;       Format SS:ESP for ring 0 kernel faults

        push    ebx                     ; save KSF pointer
        lea     eax,[ebx].ksf_tos       ; (EAX) = real kernel ESP
        DSToSS  eax
        mov     [fpRing0Stack].Offst32,eax
        mov     [fpRing0Stack].Segmt32,ss

        mov     ebx,offset FLAT:fpRing0Stack
        mov     esi,offset FLAT:KFaultFmtSSESP
        mov     ecx,KFaultFmtSSESPLen
        CCALL32 FormatRegDump

        mov     esi,offset FLAT:KFaultLimAccFmtSSESP
        mov     ecx,KFaultLimAccFmtSSESPLen
        CCALL32 FormatLimAccDump        ; put the limit/access byte values in
        pop     ebx                     ; (EBX) = KSF pointer
        CCALL32 FormatControlRegs       ; format the control registers

;       Try and match faulting user CS with a device driver CS
;       and extract the associated name.

        mov     cx,[ebx].ksf_cs         ; (CX) = code selector
        push    ebx
        DosContext es
        mov     di,offset DOSGROUP:GPFDevName
        CALL16  FindDevName
        pop     ebx                     ; (EBX) = KSF pointer
        FlatContext <ds,es>,eax
        jc      short sff40             ; not found

;       Output message containing faulting device name

        mov     edx,offset FLAT:GPFDevMsg
        mov     cx,GPFDevMsgLen
        CALL16  WrtToScrn
sff40:  mov     esi,[ebx].ksf_trapnum   ; (ESI) = trap number

;       (EBX) = -1 for user stack frames (PVSF, PTSF),
;       otherwise, (EBX) is kernel stack frame pointer (PKSF).
;       (ESI) = trap number

sff50:  mov     edx,offset FLAT:RegMsg
        mov     cx,RegMsgLen
IF RAS                                  ;                                78486
 ;
 ; Give the Kernel Exits registered with DEVHLP a chance to look at the error
 ; they can only record information there is no recovery from here
 ;
        push    eax                     ; EAX is destroyed in this call  78486
        TKCallKrnlExit SFF_exit         ; EBX is stack frame or -1       78486
        pop     eax                     ;                                78486
ENDIF                                   ; RAS                            78486

        CALL16  WrtToScrn
        mov     edx,offset FLAT:CrLfMsg
        mov     cx,CrLfMsgLen
        CALL16  WrtToScrn

;       Output message indicating swapper I/O error if appropriate

        cmp     esi,EXCPG               ; page fault?
        jne     short sff51             ;  no, do nothing              ;F78693
        mov     eax,[_pTSDCur]
        cmp     [eax].TSDerridFault,ERROR_SWAPERROR ; swapper I/O error?
        jne     short sff51             ;  no, do nothing              ;F78693

        push    ebx
        sub     ebx,ebx
        mov     eax,MSG_SWAPIN_ATTEMPT_FAILED

;       (ES:EDI) = replacement strings
;       (BX) = number of replacement strings
;       (DX) = message number

        CALL16  OutMessage              ; print swapper I/O error message
        FlatContext <ds>,eax
        pop     ebx

sff51:  dec     [_tkPanicActive]        ; Decrement count              ;F78693

sff60:  Panic   <"SystemFatalFault: exception while in kernel mode">
EndProc   SystemFatalFault

;***LP  _FormatRegisters - format all user level registers
;
;       CCALL32 _FormatRegisters <ulTrapNum, ulErrCode>
;
;       ENTRY   (ulTrapNum) = trap number to display in register dump
;               (ulErrCode) = error code to display in register dump
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, FS, GS, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _FormatRegisters,near,local

ArgVar  frg_ulTrapNum,DWORD
ArgVar  frg_ulErrCode,DWORD

        EnterProc
        SaveReg <ebx, esi, edi>
        mov     eax,[frg_ulTrapNum]     ; (EAX) = trap number
        mov     edi,offset FLAT:TRAPval
        CCALL32 HexToAscii32
        mov     eax,[frg_ulErrCode]     ; (EAX) = error code
        mov     edi,offset FLAT:ERRCval
        CCALL32 HexToAscii32

        SSToDS  ebx,[frg_ulErrCode]     ; (EBX) = address of error code
        mov     esi,offset FLAT:ErrCodeLimAccFmt
        mov     ecx,ErrCodeLimAccFmtLen
        CCALL32 FormatLimAccDump        ; format error code limit/access byte

        xor     ebx,ebx                 ; use register numbers
        mov     esi,offset FLAT:UFaultFmt
        mov     ecx,UFaultFmtLen
        CCALL32 FormatRegDump           ; put the register values in dump msg

        mov     esi,offset FLAT:UFaultLimAccFmt
        mov     ecx,UFaultLimAccFmtLen
        CCALL32 FormatLimAccDump        ; put the limit/access byte values in
        CCALL32 FormatControlRegs       ; format the control registers
        RestoreReg <edi, esi, ebx>
        LeaveProc
        RETC
EndProc   _FormatRegisters

;***LP  FormatRegDump - format the register dump string.
;
;       Formats the register dump string.
;
;       ENTRY   EBX = base pointer for offsets in the format table
;                       if 0, use KMQueryClientRegister
;               ECX = length of table
;               ESI = register dump table, in this format:
;
;               dd      offset relative to EBX of data to be printed
;                         if EBX == 0, reg number for KMQueryClientRegister
;               dd      flat relative offset of the place to put hex string
;               dd      size of register (2 or 4)
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure FormatRegDump,near
        cld                             ; 124620
frd10:  lodsd                           ; (EAX) = offset of value
        or      ebx,ebx                 ; is it a register number?
        jnz     short frd20             ;  no, use as ebx offset

        push    ecx
        CCALL32 _trapQueryRegister, <eax>
        pop     ecx
        mov     edx,eax                 ; (EDX) = register value
        jmp     short frd30

frd20:  mov     edx,[ebx+eax]           ; (EDX) = register value
frd30:  lodsd                           ; (EAX) = offset to put value
        mov     edi,eax                 ; (EDI) = offset to put value

        lodsd                           ; (EAX) = size
        cmp     eax,2                   ; are we printing a dword register?
        jbe     short frd40             ;  no, jump around print high part

        shld    eax,edx,16              ; (AX) = high part of value
        CCALL32 HexToAscii32

frd40:  mov     eax,edx                 ; (AX) = low part of value
        CCALL32 HexToAscii32

        loop    frd10
        RETC
EndProc   FormatRegDump

;***LP  FormatLimAccDump - format the limit and access byte dump string.
;
;       Formats the limit and access byte dump string for the user's
;       segment.
;
;       ENTRY   EBX = base pointer for offsets in the format table
;                       if 0, use KMQueryClientRegister
;               ECX = length of table
;               ESI = register dump table, in this format:
;
;               dd      offset relative to EBX of segment
;                         if EBX == 0, reg number for KMQueryClientRegister
;               dd      flat offset of the place to put access byte hex string
;               dd      flat offset of the place to put limit hex string
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure FormatLimAccDump,near
fla10:  lodsd                           ; (EAX) = offset of segment register
        or      ebx,ebx                 ; is it a register number?
        jnz     short fla15             ;  no, use as ebx offset

        push    ecx
        CCALL32 _trapQueryRegister, <eax>
        pop     ecx
        mov     edx,eax                 ; (EDX) = segment register value
        jmp     short fla20

fla15:  movzx   edx,word ptr [ebx+eax]  ; (EDX) = segment register value
fla20:  lodsd                           ; (EAX) = offset to put access byte str
        mov     edi,eax                 ; (EDI) = offset to put acc byte str

        LOADAR  eax,edx                 ; (EAX) = access byte of segment
        jnz     short fla25

        shr     eax,8                   ; (AX) = attribute/access bytes
        and     ax,0f0ffh               ; (AX) = limit field masked off
        CCALL32 HexToAscii32            ; convert to hex and put in the string
        jmp     short fla30

fla25:  mov     eax,'****'              ; "****" means bad segment
        stosd

fla30:  lodsd                           ; (EAX) = offset to put limit hex string
        mov     edi,eax                 ; (EDI) = offset to put limit hex str

        LOADSL  eax,edx                 ; (EAX) = limit of segment
        jnz     short fla40             ; jump if segment is invalid

        mov     edx,eax                 ; (EDX) = limit
        shr     eax,16                  ; (AX) = high word of limit
        CCALL32 HexToAscii32            ; convert to hex and put in the string

        mov     eax,edx                 ; (AX) = low word of limit
        CCALL32 HexToAscii32            ; convert to hex and put in the string
        jmp     short fla50

fla40:  mov     eax,'****'              ; "********" means bad segment
        stosd
B1EMac  stosd

fla50:  loop    short fla10
        RETC
EndProc   FormatLimAccDump

;***LP  FormatControlRegs - format the control registers
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    EAX, EDX, EDI, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure FormatControlRegs,near,local
        mov     edi,offset FLAT:CR0val
        mov     edx,cr0                 ; (EDX) = cr0
        shld    eax,edx,16              ; (AX) = high part of value
        CCALL32 HexToAscii32
        mov     eax,edx                 ; (AX) = low part of value
        CCALL32 HexToAscii32

        mov     edi,offset FLAT:CR2val
        mov     edx,cr2                 ; (EDX) = cr2
        shld    eax,edx,16              ; (AX) = high part of value
        CCALL32 HexToAscii32
        mov     eax,edx                 ; (AX) = low part of value
        CCALL32 HexToAscii32
        RETC
EndProc   FormatControlRegs

;***LP  CallGateFault - handle stack fault caused by 386 Call Gate
;
;       386 call gates cause dwords to be copied from the user
;       stack to the kernel stack.  OS/2 calls take words on the
;       stack as parameters.  If a particular call requires an
;       odd number of words on the stack, we round that up to
;       the nearest dword, so the call gate will cause an extra
;       word to be copied to the kernel stack.  This is not a
;       problem unless there is no extra word, i.e., there are
;       an odd number of words on the stack and they are all
;       parameters to a call.  We will stack fault on these cases,
;       so we must detect them and emulate the call.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, FS, GS, Flags
;
;       NOTE    This routine assumes that the only stack frame type
;               it will be called with is a TSF (TRAP) type.
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure CallGateFault,near,local
        mov     ebx,[_pTCBCur]
IFDEF   KMSTRICT
        cmp     [ebx].TCB_pcriFrameType,offset FLAT:_criTSF
        je      short cgf10
        Panic   <"CallGateFault: not TSF">
cgf10:
ENDIF
        mov     ebx,[ebx].TCB_pFrameBase; (EBX) = TSF stack frame ptr
        CCALL32 DecodeCallAddress       ; (GS:EDI) = pointer to address
        jc      cgfe                    ;  branch if not a far call

;       There is one problem with fetching the selector: we may have
;       stack-faulted while trying to do an indirect far call because
;       of a limit violation in SS while fetching the operand.  Thus,
;       we must verify that (EDI)+3 does not exceed the segment limit.

        add     edi,2                   ; (EDI) = offset of selector
        jc      cgfe                    ;  true stack fault if wraparound
        mov     eax,gs                  ; (EAX) = selector of pointer to addr
        LOADSL  eax,eax                 ; (EAX) = segment limit
        jnz     short cgfe              ;  jump if invalid selector
        cmp     edi,eax                 ; within limit?
        jae     short cgfe              ;  no, true stack fault

        movzx   ecx,word ptr gs:[edi]   ; (ECX) = selector
        test    cl,TABLE_MASK           ; GDT call gate selector?
        jnz     short cgfe              ;  no, treat as stack fault

        LOADAR  eax,ecx                 ; (AH) = access byte
        jnz     short cgfe              ;  jump if invalid selector

        and     ah,D_SEG+D_TYPEMASK     ; mask off type field
        cmp     ah,D_CALLGATE32         ; is it a call gate?
        jne     short cgfe              ;  no, skip ahead

;       At this point, we know we have faulted on a call to a call gate
;       while copying parameters.  We need to determine if this fault
;       is the result of rounding up the parameter count.

;       BUGBUG  - mikem 01/19/89 - need to verify that the system
;       has odd parameters.  An OS defined bit in the call gate
;       descriptor?

        and     cl,SEL_LOW_MASK         ; Mask off table bit and RPL
        GDTContext <gs>                 ; (GS:ECX) = call gate desc pointer
        movzx   ecx,gs:[ecx].g_parms    ; (ECX) = # of parm dwords
        shl     ecx,2                   ; (ECX) = # bytes of parameters
        sub     ecx,2                   ; (ECX) = # bytes corrected for pad word

        movzx   edx,[ebx].tsf_ss        ; (EDX) = user stack selector
        LOADSL  eax,edx                 ; (EAX) = limit of user stack
        jnz     short cgfe              ;  selector bad - real stack fault
        inc     eax                     ; (EAX) = size of user stack

        movzx   esi,[ebx].tsf_esp.loword; (ESI) = client's ESP (assume 16 bit)
        sub     eax,esi                 ; (EAX) = # bytes on user stack
        and     eax,0fffffffeh          ; Accept byte-aligned stacks
        cmp     eax,ecx                 ; compare stack to parms
        jne     short cgfe              ;  incorrect stack - real stack fault

;       At this point, we know the call faulted because of parameter
;       count rounding.  The parameters on the client's stack are
;       copied down (towards 0) one word and the instruction restarted.
;
;       NOTE    When the system call returns an extra word will be
;               left on the client's stack.  This should not hurt
;               anything and prevents anymore odd word call gate
;               stack faults.
;
;       (EBX) = pointer to tsf frame
;       (ECX) = number of bytes of parameters for call
;       (EDX) = client's SS
;       (ESI) = client's ESP

        mov     ds,edx
        mov     es,edx                  ; (DS,ES) = client's stack selector
    ASSUME  DS:NOTHING, ES:NOTHING
        lea     edi,[esi-2]             ; (ES:EDI) = one word down on stack
        shr     ecx,1                   ; (ECX) = word count
        rep movsw                       ; move parameters one word down
        FlatContext <ds,es>,eax
        sub     [ebx].tsf_esp.loword,2  ; update client's ESP (assume 16 bit)
        clc
        jmp     short cgfx

cgfe:   stc
cgfx:   mov     eax,0                   ; make sure there is nothing in FS, GS
        mov     fs,eax                  ; DON'T AFFECT CARRY! (mov eax,0)
        mov     gs,eax
    ASSUME  GS:NOTHING, FS:NOTHING
        RETC
EndProc CallGateFault

;***LP  DecodeCallAddress - return pointer to far call target address
;
;       Given a pointer to an instruction, this function decodes
;       it,  and  if  it  is  a  far call, it function returns a
;       pointer  to  the  DWORD  containing  the  address of the
;       target function.
;
;       ENTRY   EBX = TSF stack frame pointer
;
;       EXIT    if carry clear
;                   FS:ESI = pointer to next instruction
;                   GS:EDI = pointer to DWORD containing target address
;               if carry set
;                   instruction was not a 16 bit far call
;
;       NOTE    32 bit instructions/code segments not supported
;
;       USES    EAX, ECX, EDX, ESI, EDI, FS, GS, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure DecodeCallAddress,near,local
        CCALL32 QueryOverrides                  ; EDX = selector, AL = opcode
        jc      dcax                            ; jump if error

;       Check for direct far call.  This is the easy case.

        cmp     al,MI_LONG_CALL                 ; direct far call?
        jne     short dca10                     ;  no, skip ahead
        mov     edi,esi
        mov     eax,fs
        mov     gs,eax                          ; GS:EDI = pointer to address
        add     esi,4                           ; skip offset and selector
        clc                                     ; success
        jmp     dcax                            ; exit

;       Check for indirect far call.  This is the tricky one!

dca10:  cmp     al,MI_GROUP5                    ; possible indirect call?
        stc                                     ;  (assume failure)
        jne     short dcax                      ;  no, failure

        lods    byte ptr fs:[esi]               ; AL = Mod-R/M byte
        mov     cl,al                           ; CL = Mod-R/M byte
        and     al,MI_REGMASK                   ; mask off all but REG field
        cmp     al,MI_REG3                      ; REG = 3?
        stc                                     ;  (assume failure)
        jne     short dcax                      ;  no, failure

        mov     al,cl                           ; AL = Mod-R/M byte
        and     al,MI_MODMASK                   ; mask off all but MOD field
        cmp     al,MI_MODREG                    ; MOD = register parm?
        stc                                     ;  (assume failure)
        je      short dcax                      ;  yes, failure

;       At this point, we know we have a far indirect call.

        and     ecx,MI_RMMASK                   ; ECX = R/M field

;       Handle 16-bit unsigned displacement.

        cmp     al,MI_MODWORD                   ; word displacement?
        jne     short dca20                     ;  no, skip ahead
        lods    word ptr fs:[esi]               ; AX = 16-bit displacement
        movsx   eax,ax                          ; sign-extend into EAX
        jmp     short dca50

;       Handle 8-bit signed displacement.

dca20:  cmp     al,MI_MODBYTE                   ; byte displacement?
        jne     short dca30                     ;  no, skip ahead
        lods    byte ptr fs:[esi]               ; AL = displacement
        movsx   eax,al                          ; sign-extend into EAX
        jmp     short dca50

;       We have NO displacement.  Now, if the R/M field selects
;       BP, this really means we have no register part and a one
;       word immediate offset.  Note that ECX still contains our
;       R/M field.

dca30:  cmp     cl,MI_RMBP                      ; [BP]?
        jne     short dca40                     ;  no, no displacement
        lods    word ptr fs:[esi]               ; AX = immediate offset
        movsx   eax,ax                          ; sign-extend into EAX
        jmp     short dca95                     ; done

dca40:  xor     eax,eax                         ; no displacement

;       Decode the various address register combinations.

dca50:  jmp     dword ptr rmtab[ecx*4]          ; jump through table

_DATA   segment
rmtab   dd      offset FLAT:dca55               ; [BX+SI]
        dd      offset FLAT:dca65               ; [BX+DI]
        dd      offset FLAT:dca75               ; [BP+SI]
        dd      offset FLAT:dca85               ; [BP+DI]
        dd      offset FLAT:dca60               ; [SI]
        dd      offset FLAT:dca90               ; [DI]
        dd      offset FLAT:dca80               ; [BP]
        dd      offset FLAT:dca70               ; [BX]
_DATA   ends

dca55:  add     eax,[ebx].tsf_ebx               ; EAX += User EBX
dca60:  add     eax,[ebx].tsf_esi               ; EAX += User ESI
        jmp     short dca95

dca65:  add     eax,[ebx].tsf_edi               ; EAX += User EDI
dca70:  add     eax,[ebx].tsf_ebx               ; EAX += User EBX
        jmp     short dca95

dca75:  add     eax,[ebx].tsf_esi               ; EAX += User ESI
dca80:  add     eax,[ebx].tsf_ebp               ; EAX += User EBP
        or      edx,edx                         ; selector specified?
        jne     short dca95                     ;  yes, skip ahead
        movzx   edx,[ebx].tsf_ss                ; BP defaults to SS
        jmp     short dca100

dca85:  add     eax,[ebx].tsf_ebp               ; EAX += User EBP
        or      edx,edx                         ; selector specified?
        jne     short dca90                     ;  yes, skip ahead
        movzx   edx,[ebx].tsf_ss                ; BP defaults to SS
dca90:  add     eax,[ebx].tsf_edi               ; EAX += User EDI

;       At this point, AX (high word of EAX is ignored) contains the
;       offset of the DWORD containing the target's address.  EDX contains
;       either the selector to be used, or zero if the default selector
;       is to be used.

dca95:  or      edx,edx                         ; selector specified?
        jne     short dca100                    ;  yes, exit (OR clears carry)
        movzx   edx,[ebx].tsf_ds                ; default to DS
dca100: mov     gs,edx
        movzx   edi,ax                          ; GS:EDI = pointer to address
        clc
dcax:   RETC
EndProc DecodeCallAddress

;***LP  QueryOverrides - handle segment override prefix if any
;
;       This function decodes all override prefixes on this instruction
;       and returns the first byte of the instruction opcode and a pointer
;       to the next byte in the instruction.
;
;       ENTRY   EBX = TSF stack frame pointer
;
;       EXIT    if carry clear,
;                       AL = first byte of instruction opcode
;                       FS:ESI = pointer to next instruction byte
;                       EDX = selector to use in addr formation if prefix found
;                           = 0 if no prefix found
;               if carry set, error
;                       too many instruction overrides
;                       address or operand size override
;
;       NOTE    32 bit instructions/code segments not supported
;
;       USES    EAX, ECX, EDX, ESI, FS, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure QueryOverrides,near,local
        xor     edx,edx                         ; sssume no segment prefix
        mov     ecx,15                          ; 386 instruction length limit
        movzx   eax,[ebx].tsf_cs                ; (EAX) = client's CS
        LOADAR  eax,eax                         ; (EAX) = attribute/access byte
        jnz     short qove                      ;  jump if invalid selector
        test    eax,D_COPER32 SHL 16            ; is it a 32 bit code segment
        jnz     short qove                      ;  yes, jump to error exit
        lfs     esi,[ebx].tsf_cseip             ; (FS:ESI) = client's CS:EIP

;       (FS:ESI) = CS:EIP pointer
;       (ECX) = instruction length limit
;       (EDX) = segment value or 0

qov10:  lods    byte ptr fs:[esi]               ; AL = possible segment prefix
        cmp     al,MI_OPERANDSIZE               ; operand size override?
        je      short qove                      ;  yes, return error
        cmp     al,MI_ADDRESSSIZE               ; address size override?
        je      short qove                      ;  yes, return error

        cmp     al,MI_SEGES                     ; ES override?
        jne     short qov40                     ;  no, skip ahead
        movzx   edx,[ebx].tsf_es                ; EDX = User ES
        jmp     short qov90

qov40:  cmp     al,MI_SEGCS                     ; CS override?
        jne     short qov50                     ;  no, skip ahead
        movzx   edx,[ebx].tsf_cs                ; EDX = User CS
        jmp     short qov90

qov50:  cmp     al,MI_SEGSS                     ; SS override?
        jne     short qov60                     ;  no, skip ahead
        movzx   edx,[ebx].tsf_ss                ; EDX = User SS
        jmp     short qov90

qov60:  cmp     al,MI_SEGDS                     ; DS override?
        jne     short qov70                     ;  no, skip ahead
        movzx   edx,[ebx].tsf_ds                ; EDX = User DS
        jmp     short qov90

qov70:  cmp     al,MI_SEGFS                     ; FS override?
        jne     short qov80                     ;  no, skip ahead
        movzx   edx,[ebx].tsf_fs                ; EDX = User FS
        jmp     short qov90

qov80:  cmp     al,MI_SEGGS                     ; GS override?
        clc                                     ;  exit ok
        jne     short qovx                      ;  no, exit
        movzx   edx,[ebx].tsf_gs                ; EDX = User FS
qov90:  loop    short qov10                     ; Loop
qove:   stc
qovx:   RETC
EndProc QueryOverrides

;***EP  RebuildStackFrame - rebuild stack frame for ring 0/InDos == 0 faults
;
;       ENTRY   EBX = pksf (pointer to kernel trap frame)
;
;       EXIT    EBX = pksf (pointer to kernel trap frame)
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
;       disable interrupts
;       move KSF stack frame up above the original stack frame
;       interpret the faulting instruction
;       call the kmode rebuild stack frame routine (KMRebuildStackFrame)
;       set KSF's DS and ES to flat context
;       enter kernel mode (InDos is set, interrupts are enabled, etc)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure RebuildStackFrame,near,local
        cli                                     ; disable interrupts

;       Move the KSF stack frame up above the orignal stack frame
;
;                               +------+ <- esp  (dest)
;                               |------| <- pksf
;                               |      |
;  low  +------+ <- esp (src)   | KFT  |
;       |------| <- pksf  ^     |      |
;       |      |          |     |      |
;       |      |       (count)  |------| <- TCB_pFrameBase
;       | KFT  |          |     |      |
;       |      |          v     |      |
;       |------| <---------     |      | <- pksf + (size KSF)
;       |      |                |      |
;       |      |                |      |
;       |      |                |      |
;  high +------+                +------+
;        Before                  After
;
;       src = SSToDS(esp)
;       dest = TCB_pFrameBase - (size KSF) - (pksf - SSToDS(esp))
;            = TCB_pFrameBase - (size KSF) + SSToDS(esp) - pksf
;       count = (size KSF) + (pksf - SSToDS(esp))
;       pksf =  TCB_pFrameBase - (size KSF)

        mov     eax,esp
        SSToDS  eax                             ; (EAX) = SSToDS(esp)
        mov     edx,[_pTCBCur]                  ; (EDX) = current TCB pointer
        mov     edx,[edx].TCB_pFrameBase        ; (EDX) = pFrameBase
        lea     edi,[edx - size ksf_s + eax]    ; (EDI) = base - size KSF + esp
        sub     edi,ebx                         ; (EDI) -= pksf (destination)
        mov     esi,eax                         ; (ESI) = source pointer
        lea     ecx,[ebx + size ksf_s]          ; (ECX) = size KSF + pksf
        sub     ecx,eax                         ; (ECX) = count
        lea     ebx,[edx - size ksf_s]          ; (EBX) = new pksf
        mov     eax,edi                         ; (EAX) = new stack pointer
        DSToSS  eax                             ;
        cmp     esi,edi                         ; is source < destination  6.31
        jb      short rfs05                     ; yes, move from bottom up 6.31
        mov     esp,eax                         ; (ESP) = stack pointer
        cld                                     ; clear direction flag
        rep movsb                               ; move stack
        jmp     short rfs07                                               ;6.31

rfs05:
;       Copy the data from the bottom up
        lea     edi,[edi + ecx - 1]             ; (EDI) = destination      6.31
        lea     esi,[esi + ecx - 1]             ; (ESI) = source           6.31
        std                                     ; copy up                  6.31
        rep movsb                               ; move stack               6.31
        mov     esp,eax                         ; (ESP) = stack pointer    6.31
        cld                                     ; reset direction flag     6.31
        mov     edx,_pPTDACur                   ; (EDX) = PTDA pointer     6.35
        cmp     [edx].ptda_ProcType,PT_VDM      ; VPM process?             6.35
        jne     short rfs07                     ; no, get faulting inst    6.35
        mov     edx,KM_REG_TOP                  ; else, don't update VSF   6.35
        jmp     rsf60                           ; call KMRebuildStackFrame 6.35

rfs07:
;       Interpret the fault instruction.
        xor     ecx,ecx                         ; (ECX) = cbargs
        lfs     esi,[ebx].ksf_cseip             ; (FS:ESI) = CS:EIP
rsf10:  lods    byte ptr fs:[esi]               ; (AL) = [CS:EIP]
        cmp     al,MI_OPERANDSIZE
        je      short rsf10                     ; skip overrides
        cmp     al,MI_ADDRESSSIZE
        je      short rsf10                     ; skip overrides

        mov     edx,KM_REG_DS                   ; (EDX) = pop ds instruction
        cmp     al,MI_POP_DS
        je      rsf60                           ; jump if pop ds

        mov     edx,KM_REG_ES                   ; (EDX) = pop es instruction
        cmp     al,MI_POP_ES
        je      rsf60                           ; jump if pop es

        mov     edx,KM_REG_RETURN               ; (EDX) = return instruction
        cmp     al,MI_IRET
        je      short rsf60                     ; jump if IRET
        cmp     al,MI_LONG_RET
        je      short rsf60                     ; jump if far RET
        cmp     al,MI_LONG_RETn
        je      short rsf40                     ; jump if far RET N

;       If the instruction is 'MOV AL,[EDX-1]', we are in kmChkStack
;       making the ring 2 stack present.

        cmp     al,MI_MOV_REG8_MEM8             ; is it MOV reg8,mem8
        jne     short rsf20                     ;  no, continue
        lods    byte ptr fs:[esi]               ; (AL) = MOD R/M byte
        mov     edx,KM_REG_TOP                  ; assume top of frame
        cmp     al,(MI_MODBYTE + MI_REGAX + MI_RMEDX)
        je      short rsf60

rsf20:  cmp     al,MI_MOV_SEG_MEM_OPCODE        ; MOV seg,r/m16?
        je      short rsf50                     ;  yes, handle mov seg reg
        cmp     al,MI_TWOBYTEOP                 ; is it a two byte opcode?
        jne     short rsf30                     ;  no, panic

        lods    byte ptr fs:[esi]               ; (AL) = second opcode byte
        mov     edx,KM_REG_GS                   ; (EDX) = pop gs instruction
        cmp     al,MI_POP_GS
        je      short rsf60                     ; jump if pop gs
        mov     edx,KM_REG_FS                   ; (EDX) = pop fs instruction
        cmp     al,MI_POP_FS
        je      short rsf60                     ; jump if pop fs

;       The instruction was not a mov/pop segment register or
;       return/iret instruction in kernel mode exit.

rsf30:  jmp     SystemFatalFault

;       RET n instruction

rsf40:  lods    word ptr fs:[esi]               ; (AX) = cbargs
        movzx   ecx,ax                          ; (ECX) = cbargs
        test    ax,2                            ; cbargs dword aligned ?       ;B729310
        jz      short rsf60                     ; jmp if no!                   ;B729310
        or      ecx,_16B_Gate                   ; turn on 16 bit callgate flag ;B729310
        jmp     short rsf60                                                    ;B729310

;       MOV seg,r/m16 instruction (VDM protected mode stack frame)

rsf50:  lods    byte ptr fs:[esi]               ; (AL) = R/M byte
        mov     ah,al                           ; (AH) = R/M byte

        and     al,(MI_MODMASK or MI_RMMASK)
        cmp     al,(MI_MODBYTE or MI_RMSIB)     ; byte displacement/sib byte?
        jne     short rsf30                     ;  no, go fault in kernel

        lods    byte ptr fs:[esi]               ; (AL) = SIB byte
        cmp     al,(MI_SIB_INDEXNONE or MI_SIB_BASEESP) ; disp8[esp] sib byte ?
        jne     short rsf30                     ;  no, go fault in kernel

        mov     edx,KM_REG_TOP                  ; (EDX) = mov seg instruction
        and     ah,MI_REGMASK                   ; (AH) = register number
        cmp     ah,MI_REGDS
        je      short rsf60
        cmp     ah,MI_REGES
        je      short rsf60
        cmp     ah,MI_REGFS
        je      short rsf60
        cmp     ah,MI_REGGS                     ; is it the last valid segment?
        jne     short rsf30                     ;  no, go fault in kernel

rsf60:  PCALL32 KMREBUILDSTACKFRAME, <edx, ebx, ecx>
        mov     [ebx].ksf_ds,ds                 ; set ksf ds and es to flat
        mov     [ebx].ksf_es,ds
        KMEnterKmodeRebuild                     ; set InDos, enable ints, etc.
        RETC
EndProc   RebuildStackFrame

;***EP  _TRAPCheckExcept16Handler - indicate when a 16-bit handler is installed
;
;       Indicates when an user has installed a 16 bit trap handler for
;       the specified trap.
;
;       ULONG TRAPCheckExcept16Handler(ulTrapNum);
;
;       ENTRY   UlTrapNum = trap number
;
;       EXIT    if zero, no exception handler
;               if non-zero, exception handler exists
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _TRAPCheckExcept16Handler,near

ArgVar  pff_ulTrapNum,DWORD

        EnterProc
        mov     eax,[pff_ulTrapNum]         ; (EAX) = trap number
        call    TRAPCheckExceptHandler      ; is 16 bit except handler present?
        mov     eax,0                       ; assume no
        jz      tce10                       ; you were right
        mov     eax,1                       ; handler is present, return 1
tce10:  LeaveProc
        RETC
EndProc   _TRAPCheckExcept16Handler

;***EP  TRAPCheckExceptHandler - returns if a user installed a handler
;
;       Indicates when an user has installed an exception handler for
;       the specified trap.
;
;       ENTRY   EAX = trap number
;
;       EXIT    if zero flag clear (jz), no exception handler
;               if zero flag set (jnz), exception handler exists
;
;       USES    ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure TRAPCheckExceptHandler,near
        mov     esi,[_pPTDACur]                 ; (ESI) = pointer to PTDA
        mov     edi,TrapNumToIndex[eax*type TrapNumToIndex]
        test    [esi+edi].Exc_vec.Segmt,RPL_CLR ; is there an handler?
        ret                                     ;  yes, clear zero flag
EndProc   TRAPCheckExceptHandler

;***EP  _TRAPArmExcept16Handler - prepare to excecute 16-bit user trap handler
;
;       If a 16-bit user handler exists (set with DOSSETVEC), this routine
;       sets up to execute the handler when we return.
;
;       ULONG TRAPArmExcept16Handler(ulTrapNum);
;
;       ENTRY   ulTrapNum = trap number
;
;       EXIT    SUCCESS, EAX = zero
;               FAILURE, EAX = error code
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _TRAPArmExcept16Handler,near

ArgVar  pff_ulTrapNum,DWORD

        EnterProc
        mov     eax, [pff_ulTrapNum]        ; (EAX) = trap number
        call    TRAPArmExceptHandler
        LeaveProc
        RETC                                ; (EAX) set by TRAPArmExceptHandler
EndProc   _TRAPArmExcept16Handler

;***EP  TRAPArmExceptHandler - prepare to execute the user exception handler
;
;       If a user exception exists (set with DOSSETVEC), this routine
;       sets up to execute the handler when we return.
;
;       ENTRY   EAX = trap number
;
;       EXIT    SUCCESS, EAX = zero
;               FAILURE, EAX = error code
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure TRAPArmExceptHandler,near

LocalVar  aex_iret32, <size iret32_s>

;       Build an iret frame and push it on the client's stack

        EnterProc
        mov     esi,[_pTCBCur]                  ; (ESI) = pointer to TCB
        mov     edi,TrapNumToIndex[eax*type TrapNumToIndex]

        lea     eax,[aex_iret32].iret32_cs      ; put the CS in the IRET frame
        SSToDS  eax
        PCALL32 KMQUERYCLIENTREGISTER, <esi, KM_REG_CS, eax>
        or      eax,eax
        jnz     aexie

        lea     eax,[aex_iret32].iret32_eip
        SSToDS  eax
        PCALL32 KMQUERYCLIENTREGISTER, <esi, KM_REG_EIP, eax>
        or      eax,eax
        jnz     aexie

        lea     eax,[aex_iret32].iret32_eflag
        SSToDS  eax
        PCALL32 KMQUERYCLIENTREGISTER, <esi, KM_REG_EFLAG, eax>
        or      eax,eax
        jnz     aexie

        mov     ecx,size iret32_s               ; (ECX) = size of IRET frame
        movzx   eax,[aex_iret32].iret32_cs      ; (EAX) = client's CS
        xor     edx,edx                         ; (EDX) = zero if invalid CS
        LOADAR  edx,eax                         ; (EAX) = attribute/access byte
        test    edx,D_COPER32 SHL 16            ; is it a 32 bit code segment
        jnz     short aex10                     ;  yes, jump to 32 bit iret

;       Convert the 32 bit iret frame to a 16 bit iret frame

        mov     ax,[aex_iret32].iret32_cs
        mov     ebx,[aex_iret32].iret32_eip
        mov     ecx,[aex_iret32].iret32_eflag

        mov     [aex_iret32].iret16_cs,ax
        mov     [aex_iret32].iret16_ip,bx
        mov     [aex_iret32].iret16_flag,cx
        mov     ecx,size iret16_s               ; (ECX) = size of IRET frame

;       Push the iret frame on client's stack

aex10:  lea     eax,[aex_iret32]                ; (EAX) = pointer to iret
        SSToDS  eax                             ; (EAX) = DS relative
        PCALL32 KMPUSHCLIENTSTACK, <esi, ecx, eax, TK_UM_FATAL>
        .errnz  NO_ERROR
        or      eax,eax                         ; was there an error?
        jnz     short aex20                     ;  yes, don't change the CS:EIP

;       Set user exception handler address
;
;       (ESI) = ptcb
;       (EDI) = Exc_vec index

        mov     ebx,[_pPTDACur]                 ; (EBX) = pointer to PTDA
        movzx   eax,[ebx+edi].Exc_vec.Offst     ; (EAX) = 16 bit offset
        PCALL32 KMSETCLIENTREGISTER, <esi, KM_REG_EIP, eax>
        or      eax,eax
        jnz     short aexie

        movzx   eax,[ebx+edi].Exc_vec.Segmt     ; (EAX) = 16 bit selector
        PCALL32 KMSETCLIENTREGISTER, <esi, KM_REG_CS, eax>
        or      eax,eax
        jnz     short aexie

aex20:  LeaveProc
        RETC
aexie:  Panic   <"TRAPArmExceptHandler">
EndProc   TRAPArmExceptHandler

;***EP  _TRAPGetCR0CR2 - get the contents of cr0 and cr2 register
;
;       VOID TRAPGetCR0CR2(pCR0, pCR2);
;
;       ENTRY   (pCR0) = pointer to storage location for cr0
;               (pCR2) = pointer to storage location for cr2
;
;       EXIT    NONE
;
;       USES    EAX, EBX, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _TRAPGetCR0CR2,near

ArgVar  pff_pCR0,DWORD
ArgVar  pff_pCR2,DWORD

        EnterProc
        SaveReg <ebx>
        mov   eax,[pff_pCR2]            ; get pointer for cr2
        mov   ebx,cr2                   ; can only move cr2 to a register
        mov   [eax],ebx                 ; put cr2 where pointer points
        mov   eax,[pff_pCR0]            ; get pointer for cr0
        mov   ebx,cr0                   ; can only move cr0 to a register
        mov   [eax],ebx                 ; put cr0 where pointer points
        RestoreReg <ebx>
        LeaveProc
        RETC
EndProc    _TRAPGetCR0CR2

;***EP  _TRAPSetCR2 - set the contents of cr2 register
;
;       VOID TRAPSetCR2(ulCR2);
;
;       ENTRY   ulCR2 = CR2 value
;
;       EXIT    NONE
;
;       USES    EAX, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _TRAPSetCR2,near

ArgVar  pff_ulCR2,DWORD

        EnterProc
        mov   eax,[pff_ulCR2]           ; get cr2 value
        mov   cr2,eax                   ; set cr2 value
        LeaveProc
        RETC
EndProc    _TRAPSetCR2

IFDEF MMIOPH
;***LP  MMPHR3PreException MACRO - DEKKO Performance Hooks
;
;       This routine is jump'ed to, rather than called so that performance
;       hooks do not significantly slow down the execution of traps. This
;       routine is include in a macro in dosinc\mmpha.inc. It is basically
;       a copy of the systrace routine "trapPreTrace" below.
;
;       ENTRY   (ESP) -> Entry Frame
;                       <NO RETURN ADDRESS!!!>  (trapR3PreTraceDone implied)
;                       Trap number
;                       Trap Error Code
;                       Trap stack frame        (Eflags, CS, EIP)
;
;       EXIT    Return to traced location.  Only one location is traced,
;               and it uses a jump out-of-line for speed.
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING

        MMPHR3PreException              ; Performance Hooks (0301h - 030Fh)
                                        ;   see dosinc\mmpha.inc

;***LP  MMPHR0PreException MACRO - DEKKO Performance Hooks
;
;       This routine is jump'ed to, rather than called so that performance
;       hooks do not significantly slow down the execution of traps. This
;       routine is include in a macro in dosinc\mmpha.inc. It is basically
;       a copy of the systrace routine "trapPreTrace" below.
;
;       ENTRY   (ESP) -> Entry Frame
;                       <NO RETURN ADDRESS!!!>  (trapR0PreTraceDone implied)
;                       Trap number
;                       Trap Error Code
;                       Trap stack frame        (Eflags, CS, EIP)
;
;       EXIT    Return to traced location.  Only one location is traced,
;               and it uses a jump out-of-line for speed.
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING

        MMPHR0PreException              ; Performance Hooks (0301h - 030Fh)
                                        ;   see dosinc\mmpha.inc
ENDIF   ; MMIOPH


;***LP  _trapQueryRegister - local routine to get a register
;
;       ENTRY   (ulRegNum) = register number
;
;       EXIT    EAX = register
;
;       USES    EAX, ECX, EDX, FS, GS, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _trapQueryRegister,near,local

ArgVar    tqr_ulRegNum,DWORD
LocalVar  tqr_ulReg,DWORD

        EnterProc
        SSToDS  eax,[tqr_ulReg]
        PCALL32 KMQUERYCLIENTREGISTER, <[_pTCBCur], [tqr_ulRegNum], eax>
        or      eax,eax
        jnz     short tqrie1
        mov     eax,[tqr_ulReg]
        LeaveProc
        RETC

tqrie1: Panic   <"_trapQueryRegister">
EndProc   _trapQueryRegister

;***LP  _trapSetRegister - local routine to set a register
;
;       ENTRY   (ulRegNum) = register number
;               (ulReg) = register to set
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, FS, GS, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure _trapSetRegister,near,local

ArgVar    tsr_ulRegNum,DWORD
ArgVar    tsr_ulReg,DWORD

        EnterProc
        PCALL32 KMSETCLIENTREGISTER, <[_pTCBCur], [tsr_ulRegNum], [tsr_ulReg]>
        or      eax,eax
        jnz     short tsrie1
        LeaveProc
        RETC

tsrie1: Panic   <"_trapSetRegister">
EndProc   _trapSetRegister

IF RAS
;***LP  trapPreTrace - Perform Pre-invocation machine exception RAS tracing
;
;       Because of performance requirements, no VDM exceptions are traced.
;       Why: 1) We can't add any clocks to the V86 GP-fault path due to the
;       extremely high frequency of exceptions at this point.  2) Because
;       of the high frequency of exceptions, the STDA would overflow too
;       rapidly, destroying the usefulness of RAS in this area.
;
;       ENTRY   (ESP) -> Entry Frame
;                       Trap number
;                       Trap Error Code
;                       Trap stack frame        (Eflags, CS, EIP)
;
;       EXIT    Return to traced location.
;
;       USES    Flags
;
;       CONTEXT Task
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure trapPreTrace,near

ArgVar  tras_trapnum,DWORD              ; trap entry frame
ArgVar  tras_errcode,DWORD
ArgVar  tras_eip,DWORD
ArgVar  tras_cs,DWORD
ArgVar  tras_eflags,DWORD

        EnterProc
        cmp     [tras_trapnum],EXCPG    ; greater than page fault?
        ja      short tptx              ;  yes, return
        SaveReg <eax, ebx, edx, esi, ds>

;       Use stack to gather data.  (CR2 is not always logged)
;       (ZR) set only if page fault.

        mov     eax,cr2
        push    eax                     ; log CR2 (page fault only)
        push    [tras_eflags]           ; log EFlags
        mov     eax,[tras_cs]
        movzx   eax,ax
        push    eax                     ; log CS (dword, zero-extended)
        push    [tras_eip]              ; log EIP
        push    [tras_errcode]          ; log Error Code
        mov     si,sp
        push    ss
        pop     ds                      ; (DS:SI)-> parameter frame on stack
        mov     eax,RAS_MC_MACHINEEXCEPT; major code for machine exceptions
        sete    bl                      ; (BL) = 1 if page fault, else 0
        movzx   ebx,bl
        shl     ebx,2                   ; (EBX) = 4 if page fault, else 0
        add     ebx,16                  ; (EBX) = data length (16 or 20)
        mov     edx,[tras_trapnum]
        inc     edx                     ; (EDX) = minor code (Trap Number + 1)

;       (EAX) = major code for machine exceptions
;       (EBX) = size of stack frame (16 or 20 bytes, if page fault)
;       (EDX) = Minor Code
;       (DS:SI) -> Parameter frame, on stack

        CALL16  K_S_Systrace
        add     esp,20                  ; remove parameter frame
        RestoreReg <ds, esi, edx, ebx, eax>
tptx:   LeaveProc
        ret                             ; return WITHOUT removing argvars
EndProc trapPreTrace
ENDIF ;RAS

;***LP  XCPTFormatAndSendException - Send proper 32-bit exception
;
;       XCPTFormatAndSendException determines the correct exception
;       for the trap and sets up a Ring 3 dispatcher stack.
;
;       ENTRY   EAX = trap number
;               EDX = error code
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

FPToExcept      label   dword
        dd      0                               ; Invalid Operation bit
        dd      XCPT_FLOAT_DENORMAL_OPERAND     ; Denormal bit
        dd      XCPT_FLOAT_DIVIDE_BY_ZERO       ; Divide by Zero bit
        dd      XCPT_FLOAT_OVERFLOW             ; Overflow bit
        dd      XCPT_FLOAT_UNDERFLOW            ; Underflow bit
        dd      XCPT_FLOAT_INEXACT_RESULT       ; Inexact Result bit

apfnXCPTFormat  label   dword
        dd      offset FLAT:xcptSimple          ; trap 0 - divide by 0
        dd      offset FLAT:xcptSimple          ; trap 1 - single step
        dd      offset FLAT:xcptPanic           ; trap 2 - NMI
        dd      offset FLAT:xcptSimple          ; trap 3 - breakpoint
        dd      offset FLAT:xcptSimple          ; trap 4 - overflow
        dd      offset FLAT:xcptSimple          ; trap 5 - bound inst
        dd      offset FLAT:xcptInOp            ; trap 6 - invalid op
        dd      offset FLAT:xcptPanic           ; trap 7 - NPX not avail
        dd      offset FLAT:xcptPanic           ; trap 8 - double fault
        dd      offset FLAT:xcptAccessViolation ; trap 9 - NPX overrun
        dd      offset FLAT:xcptPanic           ; trap A - invalid TSS
        dd      offset FLAT:xcptAccessViolation ; trap B - seg not pres
        dd      offset FLAT:xcptStackFault      ; trap C - stack fault
        dd      offset FLAT:xcptGPFault         ; trap D - GP fault
        dd      offset FLAT:xcptPageFault       ; trap E - page fault
        dd      offset FLAT:xcptPanic           ; trap F - reserved
        dd      offset FLAT:xcptPanic           ; trap 10 - NPX error
        dd      offset FLAT:xcptPanic           ; trap 11 - alignment
        dd      offset FLAT:xcptPanic           ; trap 12-1F generic

Procedure XCPTFormatAndSendException,near

LocalVar ulTrapNumber, DWORD            ; Trap number
LocalVar aulExceptionInfo, 16           ; 4 ULONGs
LocalVar sFPEnvironment, <SIZE FPENV>   ; 387/487 protect mode environment
LocalVar ulInst, DWORD                  ; First 4 instruction bytes (for GP)

        EnterProc

;
; If we're already dispatching an exception, then don't dispatch another.
;

        mov     ebx, _pTCBCur
        test    [ebx].TCBMiscFlags, TMF_MULT_XCPT
        jnz     xfs60

        mov     [ulTrapNumber], eax     ; Save trap number
        jmp     apfnXCPTFormat[eax * 4] ; Go to the right subsection

;
; The simple case of translation is where a trap maps to exactly
; one exception.
;

Entry   xcptSimple,,local
        mov     eax, TrapNumToExceptNum[eax*4]  ; EAX = XcptNum
xfs1:   xor     ebx, ebx                        ; EBX = cParameters
        jmp     xfs50                           ; Dispatch

;
; We panic if we try to dispatch a trap we should never get here
;

Entry   xcptPanic,,local
        Panic   <"XCPTFormatAndSendException - Trap w/o Exception">

;
; A trap 06 is either an XCPT_ILLEGAL_INSTRUCTION or
; XCPT_INVALID_LOCK_SEQUENCE
;

Entry   xcptInOp,,local
        CCALL32 _trapQueryRegister, <KM_REG_CS>
        mov     ebx,eax
        CCALL32 _trapQueryRegister, <KM_REG_EIP>
        CCALL32 _SELVirtToLin, <eax, ebx>       ; EAX = Linear address of CS:EIP
        SSToDS  ebx, ulInst
        CCALL32 _TKFuBuff, <ebx, eax, 4, 0>
        or      eax,eax                         ; Error fetching instructions?
        jnz     short xio10                     ;  Yes. Assume invalid opcode
        mov     eax, [ulInst]
        cmp     al, 11110000b                   ; LOCK?
        jne     short xio10
        mov     eax, XCPT_INVALID_LOCK_SEQUENCE
        jmp     short xio15

xio10:  mov     eax, XCPT_ILLEGAL_INSTRUCTION
xio15:  xor     ebx, ebx                        ; No parameters
        jmp     xfs50

;
; A stack fault is always an ACCESS_VIOLATION, but the flags can vary.
;

Entry   xcptStackFault,,local
        or      edx,edx                         ; Non-zero error code?
        jz      short xss10
        mov     dword ptr [aulExceptionInfo], XCPT_SPACE_ACCESS
        mov     dword ptr [aulExceptionInfo + 4], edx
        jmp     short xav1

xss10:  mov     dword ptr [aulExceptionInfo], XCPT_LIMIT_ACCESS
        mov     dword ptr [aulExceptionInfo + 4], XCPT_DATA_UNKNOWN
        jmp     short xav1

;
; this is the generic entry for ACCESS_VIOLATIONs when we don't know
; what the bad address is...
;

Entry   xcptAccessViolation,,local
        mov     dword ptr [aulExceptionInfo],XCPT_UNKNOWN_ACCESS ; p1 = access
        mov     dword ptr [aulExceptionInfo + 4], XCPT_DATA_UNKNOWN
xav1:   mov     eax, XCPT_ACCESS_VIOLATION      ; EAX = XcptNum
        mov     ebx, 2                          ; EBX = cParameters
        jmp     xfs50                           ; Dispatch

;
; A GP Fault can be caused by many different actions and can result in
; a number of different exceptions. This one needs to be decoded.
;

Entry   xcptGPFault,,local
        or      edx,edx                         ; Non-Zero Error code
        jz      short xgp1                      ; No. Try other tests

;
; A non-zero error code is returned when a bad selector is loaded.
;
        mov     dword ptr [aulExceptionInfo], XCPT_SPACE_ACCESS
        mov     dword ptr [aulExceptionInfo + 4], edx
        jmp     short xav1                      ; Yes. It's an Access Violation

xgp1:   CCALL32 _trapQueryRegister, <KM_REG_CS>
        mov     ebx,eax
        CCALL32 _trapQueryRegister, <KM_REG_EIP>
        CCALL32 _SELVirtToLin, <eax, ebx>       ; EAX = Linear address of CS:EIP
        SSToDS  ebx, ulInst
        CCALL32 _TKFuBuff, <ebx, eax, 4, 0>
        or      eax,eax                         ; Error fetching instructions?
        jnz     short xcptAccessViolation               ;  Yes. It's an access violation

;
;       CLI = 11111010, STI = 11111011
;
        mov     eax, [ulInst]                   ; EAX = 1st 4 instruction bytes
        and     al, 11111110b
        cmp     al, 11111010b
        je      short xgp30
;
;       IN   = 1110010w or 1110110w
;       OUT  = 1110011w or 1110111w
;       INS  = 0110110w
;       OUTS = 0110111w
;
        mov     eax, [ulInst]                   ; EAX = 1st 4 instruction bytes
        and     al, 11111100b
        cmp     al, 01101100b                   ; INS or OUTS?
        je      short xgp30
        cmp     al, 11100100b                   ; IN or OUT (var port)
        je      short xgp30
        cmp     al, 11101100b                   ; IN or OUT (fixed port)
        je      short xgp30
        cmp     al, 11110010b                   ; REP prefix?
        jne     short xgp5
        and     ah, 11111100b
        cmp     ah, 01101100b                   ; REP INS or REP OUTS?
        je      short xgp30
;
;       Group 6 and Group 7 two-byte opcodes are priveledged
;
xgp5:   mov     eax, [ulInst]                   ; EAX = 1st 4 instruction bytes
        cmp     al, 11110100b                   ; HLT?
        je      short xgp30                     ;  Yes. It's priveledged
        cmp     al, 00001111b                   ; Two byte opcode?
        jne     xcptAccessViolation             ; No. It's an Access Violation
        test    ah, 11111110b                   ; Group 6 or 7?
        jz      short xgp30                     ;  Yes. It's priveledged
        cmp     ah, 00000110b                   ; CLTS?
        je      short xgp30                     ;  Yes. It's priveledged

;
; We get here if the Trap D error code is 0 and it's not a priveledged
; instruction. We'll assume this is therefore a segment overun.
;

        mov     dword ptr [aulExceptionInfo], XCPT_LIMIT_ACCESS
        mov     dword ptr [aulExceptionInfo + 4], XCPT_DATA_UNKNOWN
        jmp     xav1                            ; Yes. It's an Access Violation


xgp30:  mov     eax, XCPT_PRIVILEGED_INSTRUCTION        ; EAX = XcptNum
        xor     ebx, ebx                                ; EBX = cParameters
xfs30:  jmp     short xfs50                     ; Dispatch

;
; A page fault just needs the proper slots of ExceptionInfo filled in.
;

Entry   xcptPageFault,,local
        mov     eax, XCPT_ACCESS_VIOLATION      ; EAX = XcptNum
        mov     ebx, 2                          ; EBX = cParameters
        mov     dword ptr [aulExceptionInfo],XCPT_READ_ACCESS   ; Assume READ
        test    edx,2                           ; R/W bit
        jz      short xpg1                      ;  Jump if read
        mov     dword ptr [aulExceptionInfo],XCPT_WRITE_ACCESS  ; p1 = access
xpg1:   mov     edx,cr2                         ; EDX = faulting address
        mov     dword ptr [aulExceptionInfo+4],edx      ; p2 = address

;
; Central dispatch point.
; EAX = Exception number
; EBX = number of valid parameters
; aulExceptInfo has been filled in as appropriate
;

xfs50:  SSToDS  edx, [aulExceptionInfo] ; EDX = flat ptr aulExceptInfo
        mov     ecx, [ulTrapNumber]     ; ECX = trap number
        CCALL32 _XCPTBuildR3DispatcherStack, <ecx, eax, ebx, edx, FALSE, 0>

xfs60:  LeaveProc
        RETC

EndProc XCPTFormatAndSendException

HIGH32CODE ends

IDTDATA segment

;***    IDT for protected mode
;
;       This is the protected mode interrupt descriptor table.
;

IDTEntry macro  name,access,pub
        ifnb    <pub>
                        public  pub
                pub     label   gate
        endif
        if      (access and D_TYPEMASK) eq D_TASKGATE
                desctab <0, name, 0, access, 0, 0>
        else
                .errnz  g_handler - 0
                ifidn <name>, <0>
                        dd      0
                else
                        dd      offset FLAT:name
                endif
                .errnz  g_parms - 4
                db      0
                .errnz  g_access - 5
                db      access
                .errnz  g_extoffset - 6
                dw      0
                .errnz  (size gate) - 8
        endif
        endm

ALIGN 8
                public  IDT, IDTLEN
IDT             label byte

IDTEntry        trap00, D_TRAP032               ; 0: Divide Error
IDTEntry        trap01, D_INT332                ; 1: Single Step
IDTEntry        <GSEL GDT_NWDTSS>, D_TASK0      ; 2: NMI/NPX Error
IDTEntry        trap03, D_INT332                ; 3: Breakpoint
IDTEntry        trap04, D_TRAP332               ; 4: INTO
IDTEntry        trap05, D_TRAP032               ; 5: BOUND/Print Screen
IDTEntry        trap06, D_TRAP032               ; 6: Invalid Opcode
IDTEntry        trap07, D_TRAP032, NPXNAVec     ; 7: NPX Not Available
IDTEntry        <GSEL GDT_DFTSS>, D_TASK0       ; 8: Double Exception

IDTEntry        trap09, D_TRAP032               ; 9: NPX Segment Overrun
IDTEntry        trap0a, D_TRAP032               ; A: Invalid TSS
IDTEntry        trap0b, D_TRAP032               ; B: Segment Not Present
IDTEntry        trap0c, D_TRAP032               ; C: Stack Fault
IDTEntry        trap0d, D_TRAP032               ; D: General Protection
IDTEntry        trap0e, D_TRAP032, IDTTrap0E    ; E: Page Fault
IDTEntry        trap0f, D_TRAP032               ; F: Intel Reserved

IDTEntry        trap10, D_TRAP032               ;10: [287 Error]/Video
IDTEntry        trap11, D_TRAP032               ;11: 486 alignment
IDTEntry        trap1f, D_TRAP032               ;12: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;13: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;14: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;15: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;16: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;17: Intel Reserved

IDTEntry        trap1f, D_TRAP032               ;18: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;19: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;1A: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;1B: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;1C: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;1D: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;1E: Intel Reserved
IDTEntry        trap1f, D_TRAP032               ;1F: Intel Reserved
IDTEntry        0, 0                            ;20: invalid IDT entry
IDTEntry        0, D_TRAP3, Int21Vec            ;21: (disabled) DOS emulation

; The following entries will include the following IRQs with the
;     noted normal assignments.  These will be initialized after
;     DosHlpInitInterrupts has been called (int.asm).
;
;       50h  -  IRQ 0: Timer
;       51h  -  IRQ 1: Keyboard
;       52h  -  IRQ 2: Misc
;       53h  -  IRQ 3: COM2
;       54h  -  IRQ 4: COM1
;       55h  -  IRQ 5: Parallel
;       56h  -  IRQ 6: Diskette
;       57h  -  IRQ 7: Parallel
;       70h  -  IRQ 8: RTC
;       71h  -  IRQ 9
;       72h  -  IRQ 10
;       73h  -  IRQ 11
;       74h  -  IRQ 12
;       75h  -  IRQ 13
;       76h  -  IRQ 14
;       77h  -  IRQ 15

        rept 80H - (($ - IDT)/size gate)
IDTEntry        0, 0                            ;invalid IDT entry
        endm

IDTLEN          equ     $ - IDT

IDTDATA ends

        end
