PAGE    ,132
TITLE   EM86INST - 8086 Emulation Instruction Decoder
NAME    EM86INST
.386p

;***    EM86INST.ASM
;
;       Version 1.16.1.1, for src/dos/mvdm/em86inst.asm, em86, mer.krnl, 960610.1
;
;       SCCSID = @(#)em86inst.asm       6.10 92/01/06
;
;       Title:
;
;       8086 Emulation Instruction Decoder
;
;       Description:
;
;       This module contains the instruction decoder for the
;       instruction trapped in v86 mode.
;
;       Design/Implementation Notes:
;
;       The 8086 emulation routines have the following
;       restrictions/warnings:
;
;           * The high word of ESP in V86 mode is trash and not
;             zero.   This means any place that needs the VDM's
;             SP must zero extend the low word of ESP.
;
;           * All the code that manipulates the client's stack
;             must handle stack wrap.  A lot of DOS applications
;             have a 64k stack and either start SP at 0 or some
;             thing low like 6 (EasyCad).  This means as things
;             are pushed ESP wraps from 00000000 to 0000FFFE
;             (not 00000000 to FFFFFFFE).  Either use VDHPushStack
;             or VDHPopStack or be careful to word wrap the client's
;             stack.
;
;           * The high word of EIP is always zero so the places
;             that use EIP don't need to zero extend.  The places
;             that decrement/increment or set IP can set it as a
;             dword (EIP) as long as the high word is zero.
;
;           * There is no need to worry about IP wrapping.  The 386
;             faults if the high word is non-zero when returing to
;             V86 mode.
;
;       Public interfaces:
;
;       em86opInvalid
;       flPrefix
;
;       Modification History:
;
;     - Bx001   Bill Madden     PTR B719295
;       Changed em86opLMSW and em86opSetCRx to ignore if the TS bit is
;       different.
;     - Bx002   Bill Madden     08/06/91
;       Added em86opLxS, em86opPOPSeg, and em86opMOVSeg to handle DPMI apps
;       that directly access selector 40.
;     - Bx003   Bill Madden     PTR B726303     08/27/91
;       Added em86opGPFault and changed apfnOpcode to call GPFault instead of
;       OpInvalid, so that DPMI apps get trap D's, not trap 6's.
;     - Bx004   Bill Madden     PTR B727891     10/07/91
;       Changed em86opMovSeg to not destroy client's source register.
;     - Bx005   Bill Madden     PTR B730957     01/02/92
;       Changed em86opGPFault to call em86opInvalid for V86 mode apps, since we
;       hook trap 6, but not trap D.
;     - Bx006   Bill Madden     PTR B735444     04/23/92
;       Changed em86GetBIOSSel to em86GetValidSel, so that we can fix up video
;       segment b800 and segment 40 for DPMI apps.
;     - Bx007   Bill Madden     DEF 24741       07/10/92
;       Changed em86opSetCR0 to update virtual EM bit for DPMI apps.
;     - Bx008   Bill Madden     DEF 55264       10/26/92
;       Changed em86opSetCR0, em86opGetCRx to use virtual EM bit for all apps.
;     - Bx009   Dave Medina     DEF 59704       02/06/93
;       Don't allow apps to clear EM bit if there isn't an NPX.
;     - Bx010   Bill Madden     DEF 61782       02/20/93
;       Use em86AllocSel to allocate selectors.
;     - Bx011   Bill Madden     DEF 63842       03/24/93
;       Added P5 support.
;     - Bx012   Bill Madden     DEF 73423       08/31/93
;       Added P5 support.
;     - Bx014   Bob Zybach      DEF 73166       10/15/93
;       Added P5 support.
;     -         bjn                             09/19/94
;       Changed segment names for page tuning.
;

INCL_NONE       equ     1                       ; don't include everything
INCL_TYPES      equ     1                       ; include basic types
INCL_DEF        equ     1                       ; include typedef macros
INCL_VDHALL     equ     1                       ; include VDH services

.xlist
        include dosseg.inc
        include osmaca.inc
        include osconst.inc
        include infoseg.inc
        include dossymc.inc
        include sigdef.inc
        include except.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include tkexport.inc
        include dirent.inc
        include curdir.inc
        include find.inc
        include screen.inc
        include sft.inc
        include sem.inc
        include tkptda.inc
        include kmode.inc
        include seldesc.inc
        include mi.inc
        include mvdm.inc
        include mvdmexp.inc
        include vdmvdh.inc
        include em86.inc
        include error.inc
        include vpm.inc
.list
        CpuMode 386
        ModName em86inst

        ReferGlobal     mathCop, BYTE                                   ;BN009

;  Assert that the CRF is the same as the VSF.

.errnz  crf_eax - vsf_eax
.errnz  crf_ebx - vsf_ebx
.errnz  crf_ecx - vsf_ecx
.errnz  crf_edx - vsf_edx
.errnz  crf_ebp - vsf_ebp
.errnz  crf_esi - vsf_esi
.errnz  crf_edi - vsf_edi
.errnz  crf_padesp - vsf_padesp
.errnz  crf_pad2 - vsf_trapnum
.errnz  crf_eip - vsf_eip
.errnz  crf_cs - vsf_cs
.errnz  crf_eflag - vsf_eflag
.errnz  crf_esp - vsf_esp
.errnz  crf_ss - vsf_ss
.errnz  crf_es - vsf_es
.errnz  crf_ds - vsf_ds
.errnz  crf_fs - vsf_fs
.errnz  crf_gs - vsf_gs
.errnz  crf_alteip - vsf_alteip
.errnz  crf_altcs - vsf_altcs
.errnz  crf_alteflag - vsf_alteflag
.errnz  crf_altesp - vsf_altesp
.errnz  crf_altss - vsf_altss
.errnz  crf_altes - vsf_altes
.errnz  crf_altds - vsf_altds
.errnz  crf_altfs - vsf_altfs
.errnz  crf_altgs - vsf_altgs

;  Flag used in em86DecodeOperand to indicate that a SIB byte
;  exists.  Since the low word of ECX is used in this code, the
;  flag is in the high word.

EM86_SIB_FLAG           equ     10000h

;  The set of CR0 flags a VDM is allowed to change.  If the VDM tries
;  to change any other CR0 flags, it is terminated.

EM86_CR0_FLAGS          equ     (CR0_EM or CR0_MP or CR0_ET)

DefCode IMPORT, SWAP
DefFn   em86opINB                       ; in em86io.asm
DefFn   em86opINW                       ; in em86io.asm
DefFn   em86opOUTB                      ; in em86io.asm
DefFn   em86opOUTW                      ; in em86io.asm
DefFn   em86opINBimm                    ; in em86io.asm
DefFn   em86opINWimm                    ; in em86io.asm
DefFn   em86opOUTBimm                   ; in em86io.asm
DefFn   em86opOUTWimm                   ; in em86io.asm
DefFn   em86opINSB                      ; in em86io.asm
DefFn   em86opINSW                      ; in em86io.asm
DefFn   em86opOUTSB                     ; in em86io.asm
DefFn   em86opOUTSW                     ; in em86io.asm
DefFn   em86opINTO                      ; in em86int.asm
DefFn   em86opINTnn                     ; in em86int.asm
DefFn   em86opPUSHF                     ; in em86if.asm
DefFn   em86opPOPF                      ; in em86if.asm
DefFn   em86opSTI                       ; in em86if.asm
DefFn   em86opCLI                       ; in em86if.asm
DefFn   em86opIRET                      ; in em86if.asm
DefFn   em86opHLT                       ; in em86misc.asm
DefFn   EM86ReflectVPMException         ; em86int.asm
EndCode

DefCode IMPORT, SWAP, PASCAL
DefFn   EM86ReflectException            ; in em86int.asm
DefFn   VDHSwitchToVPM                  ; em86misc.asm
DefFn   VDHBeginUseVPMStack             ; em86vpmx.c
DefFn   VDHReadUBuf                     ; em86vpmx.c
DefFn   VDHWriteUBuf                    ; em86vpmx.c
DefFn   em86EnabledInterrupts           ; em86if.asm
EndCode

DefCode IMPORT, GLOBAL, C
DefFn   SELAllocGDT                     ; memory\selmgra.asm
DefFn   SELFreeGDT                      ; memory\selmgra.asm
DefFn   SELSetDescInfo                  ; memory\selmgra.asm
DefFn   SELGetDescInfo                  ; memory\selmgra.asm
DefFn   em86AllocSel                    ; em86vpm.c                     ;BN013
EndCode

DefData IMPORT, SWAP
_BYTE   mpivsf
EndData

DefData IMPORT, SWAPINST, C
ULONG   flVdmStatus                     ; in em86vpm.c
PVPMD   pvpmData                        ; em86vpm.c
EndData

DefData EXPORT, SWAPINST

;***LD+ flPrefix - instruction prefix flags
;
;       Instruction prefix flags.
;

ULONG   flPrefix, 0
;end

;*** pOffset, selClient                                                  ;BN002
;
; These are used by the routines that use VDHRead/WriteUBuf to access DPMI
; application's data area.  pOffset holds the offset of where to read or
; write to.  selClient is used to either hold the information to write or
; the word that is read in.
;
ULONG   pOffset                                                          ;BN002
SEL     selClient                                                        ;BN002

EndData

DefData PRIVATE, SWAP

;***    mprmvsf - map R/M number to VSF offset
;

mprmvsf label   byte                            ; R/M
        db      vsf_eax                         ; 000
        db      vsf_ecx                         ; 001
        db      vsf_edx                         ; 010
        db      vsf_ebx                         ; 011
        db      vsf_esp                         ; 100
        db      vsf_ebp                         ; 101
        db      vsf_esi                         ; 110
        db      vsf_edi                         ; 111

;***    dtBEGIN - BEGIN dispatch table
;
;       ENTRY   name = name of dt table
;               badaddr = local label for unspecified entries
;
;       EXIT    ?sindex = specific entry index
;               ?badaddr = default handler for unspecified entries
;

dtBEGIN macro   name,badaddr            ;; Beginning of dispatch table
        ?sindex  = 0
        ?badaddr = badaddr
name    label   dword                   ;; Start of dispatch table
endm

;***    dtS - SPECIFIC dispatch table entry
;
;       ENTRY   index = index of entry
;               addr  = address of handler
;               ?badaddr = unspecified entry handler
;               ?sindex = specific entry index
;
;       EXIT    Unspecified entries prior to "index" filled in;
;               Specified entry filled in.
;               ?sindex = next index to fill in;
;

dtS     macro   index,addr              ;; Specific entry in dispatch table
        if ?sindex gt index
            %out dtS: index out of order
            .err
        else
            if ?sindex lt index         ;; Fill unspecified entries
                rept index - ?sindex
                   dd   offset FLAT:?badaddr
                endm
            endif
            dd    offset FLAT:addr      ;; Specified entry
            ?sindex = index+1           ;; Set new start index
        endif
endm

;***    dtEND - END dispatch table
;
;       ENTRY   index = highest entry in table
;               ?sindex = specific entry index
;
;       EXIT    rest of the table filled in
;

dtEND   macro   index                   ;; End of dispatch table
        if ?sindex lt index             ;; Fill in rest of table
            rept index - ?sindex
                dd    offset FLAT:?badaddr
            endm
        endif
endm

;***LD  apfnOpcode - Instruction Emulation Routine Table
;
;       The instruction emulation decoder uses this table of
;       emulation routine addresses indexed by the first opcode
;       in the instruction.
;
        public  apfnOpcode
dtBEGIN apfnOpcode,em86opGPFault                                         ;BC003
        dtS      07h, em86opPOPSeg                                       ;BN002
        dtS      0fh, em86op0F
        dtS      1fh, em86opPOPSeg                                       ;BN002
        dtS      26h, em86opESPrefix
        dtS      2eh, em86opCSPrefix
        dtS      36h, em86opSSPrefix
        dtS      3eh, em86opDSPrefix
        dtS      64h, em86opFSPrefix
        dtS      65h, em86opGSPrefix
        dtS      66h, em86opOPER32Prefix
        dtS      67h, em86opADDR32Prefix
        dtS      6ch, em86opINSB
        dtS      6dh, em86opINSW
        dtS      6eh, em86opOUTSB
        dtS      6fh, em86opOUTSW
        dtS      8eh, em86opMOVSeg                                       ;BN002
        dtS      9ch, em86opPUSHF
        dtS      9dh, em86opPOPF
        dtS     0c4h, em86opLxS                                          ;BN002
        dtS     0c5h, em86opLxS                                          ;BN002
        dtS     0cdh, em86opINTnn
        dtS     0ceh, em86opINTO
        dtS     0cfh, em86opIRET
        dtS     0e4h, em86opINBimm
        dtS     0e5h, em86opINWimm
        dtS     0e6h, em86opOUTBimm
        dtS     0e7h, em86opOUTWimm
        dtS     0ech, em86opINB
        dtS     0edh, em86opINW
        dtS     0eeh, em86opOUTB
        dtS     0efh, em86opOUTW
        dtS     0f0h, em86opLOCKPrefix
        dtS     0f2h, em86opREPNEPrefix
        dtS     0f3h, em86opREPPrefix
        dtS     0f4h, em86opHLT
        dtS     0fah, em86opCLI
        dtS     0fbh, em86opSTI
dtEND   NUM_OPCODE


EndData

        FARCODE HIGH32
DefCode PRIVATE, SWAP, PASCAL

;***LP  em86opLOCKPrefix - Emulate LOCK instruction prefix
;
;       This function emulates the LOCK instruction prefix
;       by ignoring the prefix.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       do nothing and return so that the next instruction is executed
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86opLOCKPrefix,near,local
;
; BUGBUG 24-Jul-90 JeffPar  This routine used to just return.  I think
; we should track LOCKs like any other prefix (eg, PREFIX_LOCK) so that we
; can account for it if we eventually fall into em86opInvalid.  I trust
; that *invalid* LOCKs will always go through the trap06 handler, and that
; em86opARPL will in turn route control straight to em86opInvalid....
;
Entry em86opGenericPrefix,near,local
        inc     edi
        mov     [ebx].vsf_eip,edi
        movzx   ecx,byte ptr fs:[esi+edi]       ; (ECX) = opcode
        jmp     apfnOpcode[ecx*type apfnOpcode] ; jump to emulation routine
        ret
EndProc   em86opLOCKPrefix

;***LP  em86opPrefix - Emulate instruction prefix macro
;
;       This macro is used to generate the prefix override
;       code.   The routine name generated is EmxxxxPrefix
;       and the flPrefix flag used is PREFIX_xxxx where "xxxx"
;       is the "name" parameter.
;
;       ENTRY   (name) = name of prefix
;               EBX = pointer client register frame (CRF)
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    ESI+EDI = pointer to byte after prefix
;               Client's CS:IP points to byte after prefix
;               [flPrefix] set
;
;       USES    ECX, ESI, Eflags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       set name prefix flag
;       get opcode at client's CS:IP
;       increment client's CS:IP past opcode
;       jump to entry in opcode table (apfnOpcode)
;

em86opPrefix    macro   name
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86op&name&Prefix,near,local
        orb     [flPrefix],PREFIX_&name
        jmp     em86opGenericPrefix
EndProc   em86op&name&Prefix
                endm

;***LP  em86opREPPrefix - Emulate REP instruction prefix
;
;       This function emulates the REP instruction prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix REP

;***LP  em86opREPNEPrefix - Emulate REPNE instruction prefix
;
;       This function emulates the REPNE instruction prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix REPNE

;***LP  em86opCSPrefix - Emulate CS override prefix
;
;       This function emulates the CS override prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix CS

;***LP  em86opDSPrefix - Emulate DS override prefix
;
;       This function emulates the DS override prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix DS

;***LP  em86opESPrefix - Emulate ES override prefix
;
;       This function emulates the ES override prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix ES

;***LP  em86opSSPrefix - Emulate SS override prefix
;
;       This function emulates the SS override prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix SS

;***LP  em86opFSPrefix - Emulate FS override prefix
;
;       This function emulates the FS override prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix FS

;***LP  em86opGSPrefix - Emulate GS override prefix
;
;       This function emulates the GS override prefix
;       by setting the prefix flag for use in the other
;       emulation routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix GS

;***LP  em86opOPER32Prefix - Emulate operand size override prefix
;
;       This function emulates the operand size override prefix
;       by setting the prefix flag for use in the other emulation
;       routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix OPER32

;***LP  em86opADDR32Prefix - Emulate address size override prefix
;
;       This function emulates the address size override prefix
;       by setting the prefix flag for use in the other emulation
;       routines.
;
;       ENTRY   See em86opPrefix macro
;
;       EXIT    See em86opPrefix macro
;
;       USES    See em86opPrefix macro
;

em86opPrefix ADDR32

;***LP  em86op0F - handle the two byte opcode
;
;       This function handles the "0Fh" two-byte opcodes.
;       The only 0Fh opcodes emulated are LMSW and MOV to/from CRn
;       control registers.  We no longer use an undocumented 0Fh opcode
;       to transfer control from the DOS emulation layer (via the SVC
;       macro).
;
;       ENTRY   EAX = client's EAX register
;               EDX = client's EDX register
;               EBX = pointer client register frame (CRF)
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, EBP, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       get second opcode byte
;       if lmsw
;          do lmsw opcode
;       if mov crx,rr
;          do mov crx,rr opcode
;       if mov rr,crx
;          do mov rr,crx opcode
;       invalid opcode exception
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

xRxDisp dd      FLAToffset em86opGetCRx         ; 20h
        dd      FLAToffset em86opGetDRx         ; 21h
        dd      FLAToffset em86opSetCRx         ; 22h
        dd      FLAToffset em86opSetDRx         ; 23h
        dd      FLAToffset em86opGetDRx         ; 24h - BUGBUG: should be GetTRx
        dd      FLAToffset em86opInvalid        ; 25h
        dd      FLAToffset em86opSetDRx         ; 26h - BUGBUG: should be SetTRx

Procedure em86op0F,near,local
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC
        jnz     short e0f10                     ; if VPM, FS already set
        push    ds
        pop     fs

e0f10:  add     esi,edi                         ; (ESI) = linear EIP
;
;       (FS:ESI) = pointer to instruction for all modes
;
        mov     cx,word ptr fs:[esi+1]          ; (CX)  = next opcode bytes
        cmp     cl,MI_LMSW_OPCODE
        je      em86opLMSW

        cmp     cl,CLTS_OPCODE
        je      em86opCLTS

        cmp     cl,MI_GET_CRx_OPCODE
        jb      short em86opInvalid
        cmp     cl,MI_SET_TRx_OPCODE
        ja      short em86opInvalid

        mov     al,ch
        and     al,MI_MODMASK
        cmp     al,MI_MODREG                    ; register operand?
        jne     short em86opInvalid             ;  no, invalid opcode

        movzx   eax,ch
        and     al,MI_RMMASK                    ; (EAX) = R/M field
        movzx   edi,mprmvsf[eax*type mprmvsf]   ; (EDI) = VSF offset to reg

        mov     al,cl
        sub     al,MI_GET_CRx_OPCODE
        jmp     xRxDisp[eax*4]                  ; dispatch to sub-handler
EndProc   em86op0F

;***LP  em86opInvalid - Generate invalid opcode trap
;***LP  em86opGPFault - Generate a general protection trap
;
;       This is for instructions that need to generate invalid
;       opcode trap or GP fault in a VDM.
;
;       ENTRY   EBX = pointer client reg frame (CRF)
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       if in V86 mode or invalid opcode trap then
;          EM86ReflectException(6)
;       else
;          DX = error code
;          EM86ReflectException(13)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86opGPFault,near

        test    [flVdmStatus], VDM_STATUS_VME   ; if P5 machine         ;BN011 begin
        jz      short opgp0

;       check whether both VIP and VIF are set

        mov     eax, [ebx].vsf_eflag
        test    eax, F_VIP                      ; check for VIP
        jz      short opgp0                     ; No - normal GP fault
        test    eax, F_VIF                      ; chack for VIF
        jz      short opgp0
        call    em86EnabledInterrupts           ; dispatch sti hook
        mov     edi,[ebx].vsf_eip               ; (EDI) = EIP           ;BN012
        call    adjustUserEIP                   ; back up over prefixes ;BN012
        mov     [ebx].vsf_eip,edi               ; update client's EIP   ;BN012
        jmp     opinvexit                                               ;BN011 end

opgp0:
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; DPMI mode?    ;BN005
        jz      short   em86opInvalid                   ; no, do inv op ;BN005
        push    EXCGP                           ; GP Fault              ;BN003
        mov     edx,[ebx].vsf_errcode           ; (EDX) = error code    ;BN003
        jmp     short opinv                                             ;BN003

Entry   em86opInvalid,near

        test    [flVdmStatus], VDM_STATUS_VME   ; VME supported?        ;BN014
        jz      short opinv0                    ; No - normal Inv Op    ;BN014
        mov     eax, [ebx].vsf_eflag                                    ;BN014
        test    eax, F_VIP                      ; check for VIP         ;BN014
        jz      short opinv0                    ; No - normal GP fault  ;BN014
        test    eax, F_VIF                      ; check for VIF         ;BN014
        jz      short opinv0                                            ;BN014
        call    em86EnabledInterrupts           ; dispatch sti hook     ;BN014
        mov     edi,[ebx].vsf_eip               ; (EDI) = EIP           ;BN014
        call    adjustUserEIP                   ; back up over prefixes ;BN014
        mov     [ebx].vsf_eip,edi               ; update client's EIP   ;BN014
        jmp     opinvexit                                               ;BN014
opinv0:                                                                 ;BN014

        push    EXCOP                           ; invalid opcode trap   ;BN003
opinv:
        mov     edi,[ebx].vsf_eip               ; (EDI) = EIP

        call    adjustUserEIP                   ; back up over prefixes ;BC002

        mov     [ebx].vsf_eip,edi               ; update client's EIP

        pop     eax                             ; get trap type         ;BC003
        call    EM86ReflectException

opinvexit:
        ret
EndProc   em86opGPFault

;***LP  em86opLMSW - decode the LMSW instruction
;
;       ENTRY   CH = MOD-R/M byte
;               EBX = VSF stack frame pointer
;               FS:ESI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, FS, GS, Flags
;
;       NOTE    We only allow DOS applications to set the EM, ET, and
;               MP bits on this instruction, we also ignore if the TS
;               bit is different (but don't allow them to set it).
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure em86opLMSW,near,local
        mov     al,ch                           ; (AL) = MOD-R/M byte
        and     al,MI_REGMASK                   ; mask off all but REG field
        cmp     al,MI_REG6                      ; REG = 6?
        jne     em86opInvalid                   ;  no, invalid opcode

        push    esi                             ; save old inst pointer
        add     esi,3                           ; skip 0Fh+opcode+MOD-R/M bytes
        call    em86DecodeOperand               ; (EAX) = zero-extended operand
        pop     edi                             ; (EDI) = old inst pointer
        jc      em86opInvalid                   ; jump if decode error
        mov     edx,cr0                         ; (EDX) = current CR0
        mov     dx,ax                           ; get new value for msw
        mov     eax,edx                         ; (EAX) = new 32-bit CR0
        sub     esi,edi                         ; (ESI) = instruction length

;       (EAX) = new CR0 value
;       (ESI) = instruction length

        jmp     em86opSetCR0                                            ;BN007

EndProc   em86opLMSW

;***LP  em86DecodeOperand - decode the address or register operand
;
;       ENTRY   CH = MOD-R/M byte
;               EBX = VSF stack frame pointer
;               FS:ESI = pointer to after MOD-R/M byte
;               Client's CS:IP points to faulting opcode
;
;       EXIT    if carry clear,
;                  In V86 mode:
;                       EAX = operand
;                       ESI = pointer after instruction
;                  In VPM mode:                                         ;BN002
;                       EAX = operand
;                       EDX = client selector
;                       EDI = offset to selector
;                       ESI = pointer after instruction
;               if carry set,
;                       error decoding instruction
;
;       USES    EAX, ECX, EDX, ESI, EDI, FS, GS, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure em86DecodeOperand,near,local
        mov     cl,ch
        and     cl,MI_RMMASK                    ; (CL) = R/M field
        and     ch,MI_MODMASK                   ; (CH) = MOD field
        movzx   edx,[ebx].vsf_ds                ; default to DS

        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; VPM mode?
        jz      short dco5                              ; no, continue

        lar     eax,dword ptr [ebx].vsf_cs      ; get access rights
        test    eax,(D_COPER32 SHL 16)          ; check if 32 bit code seg
        jz      short dco5                      ; nope, continue

        orb     [flPrefix],PREFIX_SEG32         ; 32 bit code segment
        testb   [flPrefix],PREFIX_ADDR32        ; 16 bit addr override?
        jz      dco75                           ; no, go handle 32 bit addr
        jmp     short dco6                      ; yes, 16 bit addr
dco5:
        testb   [flPrefix],PREFIX_ADDR32        ; is there an address override?
        jnz     dco75                           ;  yes, 32 bit address
dco6:
;       (CL) = R/M field
;       (CH) = MOD field
;       (EBX) = pvsf
;       (EDX) = default segment
;       (FS:ESI) = instruction pointer

        cmp     ch,MI_MODNONE                   ; MOD = no displacement?
        je      short dco15
        cmp     ch,MI_MODBYTE                   ; MOD = byte displacement?
        je      short dco20
        cmp     ch,MI_MODWORD                   ; MOD = word displacement?
        je      short dco25

;       Register operand

dco10:  movzx   eax,cl                          ; (EAX) = R/M number
        movzx   eax,mprmvsf[eax*type mprmvsf]
        movzx   eax,word ptr [eax+ebx]          ; (EAX) = word operand
        jmp     dco150

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

dco15:  xor     edi,edi                         ; no displacement
        cmp     cl,MI_RMBP                      ; [BP]?
        jne     short dco30                     ;  no, no displacement
        lods    word ptr fs:[esi]               ; (AX) = immediate offset
        movzx   edi,ax                          ; (EDI) = zero-extended disp
        jmp     dco140                          ; done

;       Handle 8-bit signed displacement

dco20:  lods    byte ptr fs:[esi]               ; (AL) = displacement
        movsx   edi,al                          ; (EDI) = sign-extended disp
        jmp     short dco30

;       Handle 16-bit unsigned displacement

dco25:  lods    word ptr fs:[esi]               ; (AX) = 16-bit displacement
        movzx   edi,ax                          ; (EDI) = zero-extended disp
        jmp     short dco30

;       Decode the various address register combinations.

dco30:  movzx   eax,cl                          ; (EAX) = R/M number
        jmp     dword ptr rmtab2[eax*4]         ; jump through table

_DATA   segment
rmtab2  dd      offset FLAT:dco35               ; [BX+SI]
        dd      offset FLAT:dco45               ; [BX+DI]
        dd      offset FLAT:dco55               ; [BP+SI]
        dd      offset FLAT:dco65               ; [BP+DI]
        dd      offset FLAT:dco40               ; [SI]
        dd      offset FLAT:dco70               ; [DI]
        dd      offset FLAT:dco60               ; [BP]
        dd      offset FLAT:dco50               ; [BX]
_DATA   ends

dco35:  add     edi,[ebx].vsf_ebx               ; (EDI) += User EBX
dco40:  add     edi,[ebx].vsf_esi               ; (EDI) += User ESI
        jmp     dco135

dco45:  add     edi,[ebx].vsf_edi               ; (EDI) += User EDI
dco50:  add     edi,[ebx].vsf_ebx               ; (EDI) += User EBX
        jmp     dco135

dco55:  add     edi,[ebx].vsf_esi               ; (EDI) += User ESI
dco60:  add     edi,[ebx].vsf_ebp               ; (EDI) += User EBP
        movzx   edx,[ebx].vsf_ss                ; BP defaults to SS
        jmp     dco135

dco65:  add     edi,[ebx].vsf_ebp               ; (EDI) += User EBP
        movzx   edx,[ebx].vsf_ss                ; BP defaults to SS
dco70:  add     edi,[ebx].vsf_edi               ; (EDI) += User EDI
        jmp     dco135

;       Decode a 32 bit address or register operand
;
;       (CL) = R/M field
;       (CH) = MOD field
;       (EBX) = pvsf
;       (ECX) = R/M field
;       (EDX) = default segment
;       (FS:ESI) = instruction pointer

dco75:  and     ecx,not EM86_SIB_FLAG           ; clear SIB flag in ECX
        cmp     cl,MI_RMSIB                     ; is there a SIB byte?
        jne     short dco80                     ;  no, don't skip it
        lods    byte ptr fs:[esi]               ;  yes, get the SIB byte
        mov     cl,al                           ; (CL) = SIB byte
        or      ecx,EM86_SIB_FLAG               ; set the SIB flag in ECX

dco80:  cmp     ch,MI_MODNONE                   ; MOD = no displacement?
        je      short dco90
        cmp     ch,MI_MODBYTE                   ; MOD = byte displacement?
        je      short dco95
        cmp     ch,MI_MODWORD                   ; MOD = word displacement?
        je      short dco100
        jmp     dco10                           ; MOD is register parmeter

;       We have NO displacement.  Now, if the R/M field selects
;       BP, this really means we have no register part and a one
;       32 bit immediate offset.

dco90:  xor     edi,edi                         ; no displacement
        test    ecx,EM86_SIB_FLAG               ; is there a SIB byte?
        jnz     short dco115                    ;  yes, jump to SIB cracking

        cmp     cl,MI_RMDISP                    ; 32 bit displacement only?
        jne     short dco110                    ;  no displacement
        lods    dword ptr fs:[esi]              ; (EAX) = immediate offset
        mov     edi,eax                         ; (EDI) = displacement
        jmp     short dco140                    ; done

;       Handle 8-bit signed displacement

dco95:  lods    byte ptr fs:[esi]               ; (AL) = displacement
        movsx   edi,al                          ; (EDI) = sign-extended disp
        jmp     short dco105

;       Handle 32-bit unsigned displacement

dco100: lods    dword ptr fs:[esi]              ; (EAX) = 32-bit displacement
        mov     edi,eax                         ; (EDI) = displacement

;       Decode the various address register combinations.

dco105: test    ecx,EM86_SIB_FLAG               ; is there a SIB byte?
        jnz     short dco115                    ;  yes, jump to SIB cracking

dco110: movzx   eax,cl                          ; (EAX) = R/M number
        movzx   eax,mprmvsf[eax*type mprmvsf]
        add     edi,dword ptr [eax+ebx]
        cmp     cl,MI_RMEBP                     ; [EBP]?
        jne     short dco140
        movzx   edx,[ebx].vsf_ss                ; EBP defaults to SS
        jmp     short dco140

;       Crack the SIB byte
;
;       (CL) = SIB byte
;       (CH) = MOD field
;       (EBX) = pvsf
;       (EDX) = default segment
;       (FS:ESI) = instruction pointer
;       (EDI) = displacement

dco115: mov     al,cl                           ; (AL) = SIB byte

;       Get base register

        and     eax,MI_SIB_BASEMASK             ; (EAX) = base register number
        .errnz  MI_SIB_BASESHIFT
        cmp     eax,MI_SIB_BASEESP              ; is the base ESP?
        je      short dco120                    ;  yes, jump to set default seg

        cmp     eax,MI_SIB_BASENONE             ; is there a base register?
        jne     short dco125                    ;  yes, jump to get base

        cmp     ch,MI_MODNONE                   ; does MOD == NONE?
        jne     short dco120                    ;  no, EBP is the base register

;       Get a 32 bit displacement

        lods    dword ptr fs:[esi]              ; (EAX) = 32-bit displacement
        mov     edi,eax                         ; (EDI) = displacement
        jmp     short dco130

dco120: movzx   edx,[ebx].vsf_ss                ;  yes, set default seg to SS
dco125: movzx   eax,mprmvsf[eax*type mprmvsf]
        add     edi,dword ptr [eax+ebx]         ; (EAX) = add in base

;       Get index register

dco130: mov     al,cl                           ; (AL) = SIB byte
        and     eax,MI_SIB_INDEXMASK            ; (EAX) = index
        cmp     eax,MI_SIB_INDEXNONE            ; is there an index register
        je      short dco140                    ;  no, we are done

        shr     eax,MI_SIB_INDEXSHIFT
        movzx   eax,mprmvsf[eax*type mprmvsf]
        mov     eax,dword ptr [eax+ebx]         ; (EAX) = add in base

;       Scale index register

        and     ecx,MI_SIB_SSMASK               ; (ECX) = scale
        shr     ecx,MI_SIB_SSSHIFT              ; (ECX) = shift index
        shl     eax,cl                          ; (EAX) = shifted index register
        add     edi,eax                         ; add index register to total
        jmp     short dco140

;       (EBX) = pvsf
;       (EDX) = default segment register
;       (EDI) = 32 bit offset

dco135:
        movzx   edi,di                          ; (EDI) = low word of offset

dco140:
        mov     eax,[flPrefix]                  ; (EAX) = prefix flags
        test    eax,PREFIX_SEG                  ; is there a seg prefix?
        jz      short dco145                    ;  no, skip prefix

        and     eax,IO_SEG_MASK
        shr     eax,IO_SEG_SHIFT                ; (EAX) = segment number index
        movzx   eax,mpivsf[eax*type mpivsf]     ; (EAX) = VSF struc offset
        movzx   edx,word ptr [eax+ebx]          ; (EDX) = client's seg register

dco145:
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; VPM mode?     ;BN002
        jnz     short dcoVPM                            ; yes, load selector ;BN002

        cmp     edi,_64K                        ; limit violation?
        jae     short dco155                    ;  yes, error if >= 64K

        shl     edx,4                           ; (EDX) = segment to address
        add     edi,edx                         ; (EDI) = address
        movzx   eax,word ptr [edi]              ; (EAX) = word operand
        jmp     short dco150

dcoVPM:
        testb   [flPrefix], PREFIX_SEG32        ; 32 bit code segment?
        jnz     short dcoVPM10                  ; yes -> check for override  ;BN002

        testb   [flPrefix], PREFIX_OPER32       ; 32 bit operand override?   ;BN002
        jnz     short dcoVPM20                  ; yes -> skip a dword        ;BN002
        jmp     short dcoVPM30                  ; no  -> skip a word

dcoVPM10:                                                                    ;BN002
        testb   [flPrefix], PREFIX_OPER32       ; 16 bit operand override?   ;BN002
        jnz     short dcoVPM30                  ; yes -> only skip a word    ;BN002

dcoVPM20:
        add     edi,2                           ; skip an extra word         ;BN002
dcoVPM30:                                                                    ;BN002
        add     edi,2                           ; advance to segment         ;BN002
        mov     pOffset, edi                    ; store pointer for ReadUBuf ;BN002
        push    edx                             ; save client selector       ;BN002
        PCall   VDHReadUBuf, <FLAToffset selClient, 2, edx, \
                              FLAToffset pOffset, 0>                         ;BN002
        pop     edx                             ; restore client selector    ;BN002
        cmp     eax,FALSE                       ; any errors                 ;BN002
        je      short dco155                    ;  yes -> exit               ;BN002
        mov     ax, selClient                   ; put selector in ax         ;BN002
        jmp     short dco150                    ; return success             ;BN002

dco150: clc
        ret

dco155: stc                                     ; error exit
        ret
EndProc   em86DecodeOperand

;***LP  em86opGetCRx - decode the MOV r32,CRx instructions
;
;       This routine only decodes the CR0 instructions.
;
;       ENTRY   CH = MOD-R/M byte
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to reg operand
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, Flags
;
;       NOTE    We only allow DOS applications to see or set the
;               EM bit on these instructions.  Return applications the setting
;               of the "virtual" EM bit (from flVdmStatus).
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure em86opGetCRx,near,local
        .ERRNZ  MI_REGCR0
        test    ch,MI_REGMASK                   ; REG = 0 (CR0)?
        jnz     em86opInvalid                   ; no, invalid request

        add     [ebx].vsf_eip,3                 ; update client's EIP

        mov     eax,cr0                         ; (EAX) = CR0 register
        and     eax, not CR0_EM                         ; clear EM bit   BN008 begin
        testb   [flVdmStatus],VDM_STATUS_VPM_EM_FLAG    ; virt EM set?
        jz      short GetCRx10                          ;  no, skip ahead
        or      eax, CR0_EM                             ;  yes, set EM bit

GetCRx10:                                                               ;BN008 end
        mov     dword ptr [ebx+edi],eax         ; set client's register
        ret
EndProc   em86opGetCRx

;***LP  em86opSetCRx - decode the MOV CRx,r32 instructions
;
;       This routine only decodes the CR0 instructions that are changing
;       only the EM bit.
;
;       ENTRY   CH = MOD-R/M byte
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to reg operand
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, Flags
;
;       NOTE    We only allow DOS applications to set the EM, ET, and
;               MP bits on this instruction, we also ignore if the TS
;               bit is different (but don't allow them to set it).
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure em86opSetCRx,near,local
        .ERRNZ  MI_REGCR0
        test    ch,MI_REGMASK                   ; REG = 0 (CR0)?
        jnz     em86opInvalid                   ; no, invalid request

        mov     eax,dword ptr [ebx+edi]         ; (EAX) = new CR0 value
        mov     esi,3                           ; instruction length

;       (EAX) = new CR0 value
;       (ESI) = instruction length
Entry   em86opSetCR0,near,local
        mov     ecx,cr0                         ; (ECX) = CR0
        and     ecx,not (EM86_CR0_FLAGS)        ; (ECX) = clear bits

        mov     edx,eax
        and     edx,not (EM86_CR0_FLAGS)        ; (EDX) = everything else

        sub     edx,ecx                         ; see which bits changed  BC001
        and     edx,not CR0_TS                  ; ignore TS Bit           BN001

        jnz     em86opInvalid                   ; inv op if any other bits set

        add     [ebx].vsf_eip,esi               ; update client's EIP

        and     eax,EM86_CR0_FLAGS              ; mask out everything else
        cmp     mathCop,0                       ; coprocessor present?  ;BN009
        jz      short SetCRx5                   ;  no, don't update CR0 ;BN009

        or      ecx,eax                         ; (ECX) = new CR0
        mov     cr0,ecx                         ; turn off/on allowed bits

;BD008  testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; VPM mode?     ;BN007
;BD008  jz      short SetCRxExit                                        ;BN007

SetCRx5:                                                                ;BN009
        test    eax, CR0_EM                                             ;BC009
        jz      short SetCRx10                                          ;BN007

        orb     [flVdmStatus],VDM_STATUS_VPM_EM_FLAG                    ;BN007
        jmp     short SetCRxExit                                        ;BN007

SetCRx10:                                                               ;BN007
        clrb    [flVdmStatus],VDM_STATUS_VPM_EM_FLAG                    ;BN007

SetCRxExit:
        ret
EndProc   em86opSetCRx

;***LP  em86opGetDRx - decode the MOV r32,DRx instructions
;
;       This routine decodes instructions that attempt to get DRx registers.
;       Currently, we emulate by setting the target register to zero.
;
;       ENTRY   CH = MOD-R/M byte
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to reg operand
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, Flags
;
;       NOTE    We only allow DOS applications to see or set the
;               EM bit on these instructions.
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure em86opGetDRx,near,local
        mov     dword ptr [ebx+edi],0           ; zero client's register

        FallInto em86opSetDRx
EndProc   em86opGetDRx

;***LP  em86opSetDRx - decode the MOV DRx,r32 instructions
;
;       This routine decodes instructions that attempt to set DRx registers.
;       Currently, we emulate by ignoring the access.
;
;       ENTRY   CH = MOD-R/M byte
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to reg operand
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, Flags
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure em86opSetDRx,near,local
        FallFrom em86opGetDRx

        add     [ebx].vsf_eip,3                 ; update client's EIP
        ret
EndProc   em86opSetDRx

;***LP  em86opCLTS - decode the CLTS instruction
;
;       This routine decodes the CLTS instruction.
;       Currently, we emulate by ignoring the access.
;
;       ENTRY
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to reg operand
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, Flags
;

Procedure em86opCLTS,near,local

        add     [ebx].vsf_eip,2                 ; update client's EIP
        ret
EndProc   em86opCLTS


;***LP  em86opLxS - decode the LDS or LES instruction (VPM mode only)
;
;       This routine decodes the LDS or LES instructions, and sees if the app
;       is trying to load in selector 40 or B800.  If so it creates a new
;       LDT selector that points to the appropriate data area.
;
;       ENTRY
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to instruction pointer (EIP)
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, EDI, ESI, Flags
;
Procedure em86opLxS,near,local

        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; DPMI app?
        jz      em86opInvalid                           ; no, go to inv op

        mov     ch, fs:[edi+1]                  ; get mod r/m byte
        mov     esi,edi
        add     esi,2                           ; (ESI) = after mod r/m byte
        push    edi                             ; save CRF EIP

        call    em86DecodeOperand               ; decode the instruction

        mov     esi,pvpmData                    ; (ESI) = ptr to vpm data

        cmp     ax, 40h                         ; selector 40?
        jne     short LxS05                     ; no, try video selector

        mov     cx,[esi.vpmd_selBIOSarea]       ; (CX) = BIOS selector
        cmp     cx,0                            ; if nonzero we've already got it
        jnz     short LxS10                     ; yes, don't need to get one

        call    em86GetValidSel                 ; allocate selector
        jc      short LxSerr                    ; if error, jump

        mov     [esi.vpmd_selBIOSarea],cx       ; save away selector
        jmp     short LxS10

LxS05:
        cmp     ax, 0B800h                      ; video selector (B800)
        jne     short LxSerr                    ; no, go to invalid op

        mov     cx,[esi.vpmd_selVIDEOarea]      ; (CX) = video selector
        cmp     cx,0                            ; if nonzero we've already got it
        jnz     short LxS10                     ; yes, don't need to get one

        call    em86GetValidSel                 ; allocate selector
        jc      short LxSerr                    ; if error, jump

        mov     [esi.vpmd_selVIDEOarea],cx      ; save away selector

LxS10:
        verw    dx                              ; can we write to the selector?
        jz      short LxS20                     ; yes, it's writable

        ;       If we can't write to the selector, get a writable alias to it
        call    em86CreateWrtSel                ; create an alias
        jc      short LxSerr                    ; if error, exit

LxS20:
        mov     selClient, cx                   ; get selector alias

        mov     pOffset, edi
        push    edx
        PCall   VDHWriteUBuf, <FLAToffset selClient, 2, edx, \
                               FLAToffset pOffset, VPM_FAULT_IF_SU_SET>

        pop     edx
        pop     edi                             ; restore CRF EIP
        call    adjustUserEIP                   ; back up past any prefixes
        mov     [ebx].vsf_eip, edi              ; store in user EIP
        test    dx, RPL_MASK                    ; did we allocate alias SEL?
        jnz     short LxSexit                   ; if not, exit
        CCall   SELFreeGDT, <edx>               ; free alias selector
        jmp     short LxSexit
LxSerr:
        pop     edi                             ; pop edi off stack
        jmp     em86opGPFault                   ; jump to invalid op
LxSexit:
        ret
EndProc   em86opLxS


;***LP  adjustUserEIP - Adjust client EIP back to beginning of instruction
;
;       This routine backs up the client's EIP register, after it has been
;       advanced past any prefix bytes.
;
;       ENTRY
;               EDI = VSF EIP
;
;       EXIT    EDI points to beginning of instruction
;
;       USES    EAX, EDI, Flags
;
Procedure adjustUserEIP,near,local

        mov     eax,[flPrefix]                  ; (EAX) = overrides
        test    eax,PREFIX_SEG
        jz      short auip10
        dec     edi                             ; backup to segment override

auip10:
        test    eax,PREFIX_ADDR32
        jz      short auip20
        dec     edi                             ; backup to address override

auip20:
        test    eax,PREFIX_OPER32
        jz      short auip30
        dec     edi                             ; backup to operand override

auip30:
        test    eax,PREFIX_REP
        jz      short auip40
        dec     edi                             ; backup to rep override

auip40:
        ret

EndProc   adjustUserEIP


SWAP32_26 segment
;***LP  em86GetValidSel - Allocate an LDT alias selector for V86 segment
;
;       This routine allocates a new selector that can be used by the client
;       to access a DOS segment (b800: or 40:). Some DPMI applications have
;       hard coded segemts that we need to replace with a valid selector.
;
;       ENTRY
;               EAX = segment to allocate descriptor for
;
;       EXIT
;               carry set indicates failure
;               carry clear indicates success
;                  CX = new selector for data area
;
;       USES    CX, Flags
;
Procedure em86GetValidSel,near,local
LocalVar        NewSel, SEL

        EnterProc

        SaveReg <edx, ebx, eax>
        mov     NewSel,0ffffh                   ; allocate a new selector
        lea     ebx, NewSel                     ; get addr of NewSel
        SSToDS  ebx                             ; convert to flat
        shl     eax, 4                          ; convert segment to base addr
        CCall   em86AllocSel, <eax, 0ffffh, ebx, D_DATA3, 0>            ;BN013
        or      eax,eax                         ; error?
        jz      short gbserr                    ; if zero -> error
        clc
        mov     cx,NewSel                       ; (AX) = new selector
        jmp     short gbsexit
gbserr:
        stc
gbsexit:
        RestoreReg <eax, ebx, edx>
        ExitProc

EndProc   em86GetValidSel
SWAP32_26 ends

;***LP  em86opPOPSeg - Check if app is popping ring 0 sel into seg register
;
;       This routine checks if the client application is trying to pop in
;       a hard coded selector 40 or B800 into a segment register.  If so it
;       replaces the selector with a valid LDT selector that points to the
;       appropriate data area.
;
;       ENTRY
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to instruction pointer (EIP)
;               Client's CS:IP points to faulting opcode
;
;       EXIT
;               if successful, returns
;               if not (sel 40 or b800) or unsucessful jumps to em86opInvalid
;
;       USES    EAX, EDI, ES, ESI, Flags
;

Procedure em86opPOPSeg,near,local

        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; DPMI app?
        jz      em86opInvalid                           ; no, go to inv op

        mov     edi,[ebx].vsf_esp                       ; get client ESP
        mov     pOffset, edi                            ; set up ptr for read
        movzx   eax, [ebx].vsf_ss                       ; convert SS to dword
        PCall   VDHReadUBuf, <FLAToffset selClient, 2, eax, \
                              FLAToffset pOffset, 0>

        mov     esi,pvpmData                    ; (ESI) = ptr to host data

        mov     ax,selClient
        cmp     ax,40h                          ; trying to load BIOS sel?
        jne     short pop05                     ; if not, try B800

        mov     cx,[esi.vpmd_selBIOSarea]       ; (CX) = BIOS selector
        cmp     cx,0                            ; if nonzero, already allocated
        jnz     short pop10

        call    em86GetValidSel                 ; allocate LDT BIOS selector
        jc      short poperr                    ; if error, jump

        mov     [esi.vpmd_selBIOSarea],cx
        jmp     short pop10

pop05:
        cmp     ax,0b800h                       ; trying to load video sel?
        jne     short poperr                    ; if not, exit

        mov     cx,[esi.vpmd_selVIDEOarea]      ; (CX) = video selector
        cmp     cx,0                            ; if nonzero, already allocated
        jnz     short pop10

        call    em86GetValidSel                 ; allocate LDT BIOS selector
        jc      short poperr                    ; if error, jump

        mov     [esi.vpmd_selVIDEOarea],cx      ; save selector to video mem

pop10:
        mov     selClient, cx                   ; put in buffer
        mov     pOffset, edi                    ; write at client ESP
        movzx   eax, [ebx].vsf_ss               ; convert SS to dword
        PCall   VDHWriteUBuf, <FLAToffset selClient, 2, eax, \
                               FLAToffset pOffset, VPM_FAULT_IF_SU_SET>
        jmp     short popret                    ; and return

poperr:
        jmp     em86opGPFault

popret:
        ret

EndProc   em86opPOPSeg

SWAP32_25 segment
;***LP  em86opMOVSeg - Check if app is moving ring 0 sel into seg register
;
;       This routine checks if the client application is trying to pop in
;       a hard coded selector 40 or B800 into a segment register.  If so it
;       replaces the selector with a valid LDT selector that points to the
;       appropriate data area.
;
;       ENTRY
;               EBX = VSF stack frame pointer
;               EDI = VSF offset to instruction pointer (EIP)
;               Client's CS:IP points to faulting opcode
;               FS =  VSF CS
;
;       EXIT
;               if successful, returns
;               if not (sel 40 or B800) or unsucessful jumps to em86opInvalid
;
;       USES    EAX, EDI, ESI, ES, Flags
;

Procedure em86opMOVSeg,near,local

        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; DPMI app?
        jz      TUNEBN                                  ; no, go to inv op

        ; The bottom three bits tell us what the source register is:
        ; 000 = ax, 001 = cx, 010 = dx, 011 = bx, 110 = SI, 111 = DI
        ; The next three tell us the destination register:
        ; 000 = ds, 001 = cs, 010 = ss, 011 = es, 100 = fs, 101 = gs
        movzx   eax, fs:[edi][1]                ; get mod r/m byte
        mov     edi,eax                         ; save copy in DI                  BN004
        and     eax, MI_RMMASK                  ; get source register
        and     edi, MI_REGMASK                 ; get destination register         BN004

        movzx   eax,mprmvsf[eax*type mprmvsf]   ; (EAX) = VSF offset to source reg BN004
        mov     ax,word ptr [ebx+eax]           ; (AX) = contents of source reg    BN004

        mov     esi,pvpmData                    ; (ESI) = ptr to host data

        cmp     ax,40h                          ; are they getting 40?
        jne     short mov05                     ; if not, try B800

getsel:
        mov     cx,[esi.vpmd_selBIOSarea]       ; (CX) = BIOS selector
        cmp     cx,0                            ; if nonzero, selector is alloc
        jnz     short mov10

        call    em86GetValidSel                 ; allocate an LDT selector
        jc      short moverr                    ; jump if error

        mov     [esi.vpmd_selBIOSarea],cx       ; save away BIOS selector
        jmp     short mov10

mov05:
        cmp     ax,0b800h                       ; video buffer?
        jne     short moverr                    ; if not, exit

        mov     cx,[esi.vpmd_selVIDEOarea]      ; (CX) = video selector
        cmp     cx,0                            ; if nonzero, selector is alloc
        jnz     short mov10

        call    em86GetValidSel                 ; allocate an LDT selector
        jc      short moverr                    ; jump if error

        mov     [esi.vpmd_selVIDEOarea],cx      ; store away video selector

mov10:
        shr     edi,MI_REGSHIFT                 ; (EDI) = segment number index     BN004
        movzx   edi,mpivsf[edi*type mpivsf]     ; (EDI) = VSF struc offset         BN004

        mov     word ptr [edi+ebx],cx           ; store new selector               BN004
        add     [ebx].vsf_eip,2                 ; increment past this instruction  BN004

        jmp     short movret                    ; and return

moverr:
        jmp     em86opGPFault
movret:
        ret
TUNEBN:
        jmp     em86opInvalid                           ; no, go to inv op

EndProc   em86opMOVSeg
SWAP32_25 ends


;***LP  em86CreateWrtSel - Create writeable alias to a selector
;
;       This routine creates a GDT writeable alias selector to an existing
;       selector.
;
;       ENTRY
;               EDX = selector to create alias for
;
;       EXIT
;               if successful:
;                       carry clear
;                       DX = new selector
;
;               if not successful, returns carry set
;
;       USES    EAX, EBX, Flags
;

Procedure em86CreateWrtSel,near,local
LocalVar        OrigDesc, <SIZE seldesc_s>
LocalVar        NewSel, SEL

        EnterProc
        SaveReg <ebx, ecx>
        lea     ebx,OrigDesc                    ; get addr of OrigDesc
        SSToDS  ebx                             ; convert to flat
        CCall   SELGetDescInfo, <edx, HOBNULL, ebx>     ; get orig desc info
        cmp     ax, NO_ERROR                    ; error?
        jne     short CWSerr                    ; yes -> exit
        lea     ebx, NewSel                     ; get addr of NewSel
        SSToDS  ebx                             ; convert to flat
        CCall   SELAllocGDT, <ebx>              ; get the new selector
        cmp     ax, NO_ERROR                    ; error?
        jne     short CWSerr                    ; yes -> exit
        movzx   eax, NewSel
        CCall   SELSetDescInfo, <SELSD_ALL, D_DATA3, eax, OrigDesc.desc_cb, \
                                 OrigDesc.desc_laddr, HOBNULL, HOBNULL, 0>
        mov     dx, NewSel                      ; return new selector
        clc                                     ; indicate success
        jmp     short CWSexit                   ; exit

CWSerr:
        stc
CWSexit:
        RestoreReg <ecx, ebx>
        ExitProc
EndProc   em86CreateWrtSel

EndCode
        end
