.386p
page    58,132

;;__BUGFIX__ = 1        ; Fix bug where prefixs on Int hang machine. D1334 DWR
__BUGFIX2__= 1          ; Fix bug where bad overrides are done on LMSW's.
__TONYI__  = 1          ; Speed hacks in GP-fault path.
VCB_FIX_13  = 1  ;Fix to correctly reflect INT D to V86 mode
VCB_FIX_OVR = 1  ;Fix to correctly handle operand and address overrides

;----------------------------- TONYI -------------------------------------
; There were 2 serious bugs in EMM386's instruction emulations.
;
;  1. Seg overrides in an "INT nn" instruction were causing a bogus IRET
;     frame to be built for the client (ouch!)
;
;  2. Multiple seg overrides on an LMSW could result in the wrong override
;     being applied when the instruction was emulated.  This resulted
;     because EMM386 had no concept of override positioning.  In the Intel
;     chips, you can have several overrides, but the *LAST* one is the
;     only one that means anything, the others are essentially NOP's and
;     should be ignored.
;
; Problem #1 is fixed by taking into account the number of prefixes/reps
; encountered before the actual INT instruction such that the IRET frame's
; offset is now X+2 where 'X' is the number of overrides/reps encountered.
; Overrides/reps on an INT are essentially NOP's and have no semantics
; applied.
;
; Problem #2 is fixed by keeping track of the *LAST* override encountered.
; This ensures that only the last one is applied and prior ones are ignored
; once we finally get down to emulating the instructions.
;----------------------------- TONYI -------------------------------------

;******************************************************************************
        title   VMINST.ASM - Virtual Mode GP fault instruction emulation
;******************************************************************************
;
;   (C) Copyright MICROSOFT Corp. 1986-1991
;   (C) Copyright COMPAQ Computer Corp. 1986-1991
;
;   Title:    EMM386.EXE - MICROSOFT Expanded Memory Manager 386 Driver
;
;   Module:   VMINST.ASM - Virtual Mode GP fault instruction emulation
;
;   Version:  2.00
;
;   Date:     February 11, 1986
;
;   Author:     Caldwell Crosswy
;
;******************************************************************************
;
;   Change log:
;
;     DATE    REVISION                  DESCRIPTION
;   --------  --------  -------------------------------------------------------
;   02/12/86  Original
;   04/07/86  A-WCC     Added call to LIM emulation routines for all I/O.
;                       Bit Map set up to trap only LIM related ports. SBP.
;   05/12/86  B-RRH     Cleanup and segment reorganization
;   05/18/86  C-RRH     Added VM privileged instruction emulation
;   06/08/86  D-SBP     Added calls to RRTrap.
;   06/14/86  E-RRH     Changed stack saves from BP, BX, SI to EBP, EBX, ESI
;   06/15/86  F-RRH     Changed inc [bp.VTFOE+VMTF_EIP] to inc SI in Prefix
;                       Handlers
;   06/15/86  G-RRH     Added MOVSB and MOVSW for Rash Rule emulation
;   06/16/86  H-RRH     Added Error Handler interfaces, BugMode conditional
;                       assembly constructs, and Error Handler return logic
;   06/19/86  0.01      Changed call JumpReal to jmp JumpReal (SBP)
;   06/19/86  0.01      Clear TF bit on all INT reflects, but not on emulations.
;   06/19/86  0.01      CLTS now does a clts for the VM client.
;   06/27/86  0.02      INT3 emulation now ALLWAYS reflects(SBP).
;   06/28/86  0.02      Name change from CEMM386 to CEMM (SBP).
;   07/01/86  0.03      Call to IO_Trap instead of LIM_Trap (SBP).
;   07/03/86  0.03      Let IO_Trap handle A20 and Return to real logic (SBP).
;   07/05/86  0.04      JumpReal in R_CODE (SBP).
;   07/06/86  0.04      changed assume to _DATA (SBP).
;   07/10/86  0.05      added MB_Flag check for move block emulator (SBP).
;   01/12/87  0.09      fixed EmHALT to check for user CLI (SBP).
;   05/18/87  2.00      Changed ErrHndlr calls to go to R_CODE (SBP).
;
;******************************************************************************
;
;   Functional Description:
;
;   This module contains the routines that handle GP faults fielded from
;   Virtual Mode.  The offending instruction is either emulated or we exit
;   to the debugger.
;
;   NOTE: The current implementation is sufficient for breadboard - see
;       'to do' notes in various header for holes.  Specifically, the only
;       override that is handled is REP, and only for INS/OUTS.  Segment
;       overrides are not yet supported.
;
;******************************************************************************
.lfcond                                 ; list false conditionals

page
;******************************************************************************
;                       P U B L I C   D E C L A R A T I O N S
;******************************************************************************
;
        public  VmInst                  ; module label
        public  VmFault
        public  EmProtIns               ; just for debug map
        public  EmMovCDTR               ; just for debug map
        public  EmMOVSW                 ; just for debug map
        public  BadVMTrap
;
;******************************************************************************
;                       E X T E R N A L   R E F E R E N C E S
;******************************************************************************
;
ifdef BugMode
DCODE   segment

        extrn _Trap03:far               ; INT 3 handler
        extrn _Trap04:far               ; INTO handler
        extrn _Trap13:far               ; general protection fault

DCODE   ends
endif


_TEXT    segment
        extrn   IO_Trap:near            ; I/O Trap Dispatcher
;LEO    extrn   PortClear:near          ; IOBM trap clear function (VMINIT)
        extrn   ReflectInterrupt:near   ; Interrupt reflection routine (PICTRAP)
        extrn   ErrHndlr:far            ; Handle user's error response
_TEXT    ends

_DATA   segment
        extrn   pLastVMTF:word
        extrn   LastVMTF:word
_DATA   ends

        page
;******************************************************************************
;                       I N C L U D E   F I L E S
;******************************************************************************

include VDMseg.inc
include desc.inc
include VDMsel.inc
include vm386.inc
include oemdep.inc
include emm386.inc
include emmfunct.inc
include emmdata.inc

;******************************************************************************
;                       L O C A L   C O N S T A N T S
;******************************************************************************
;

LOCK_PREFIX     equ     0F0h

;
; bit flags for instruction prefixes
;
REP_FLAG        equ     0001h
REPNE_FLAG      equ     0002h
REPS_FLAG       equ     (REP_FLAG or REPNE_FLAG)
CS_FLAG         equ     0004h
DS_FLAG         equ     0008h
ES_FLAG         equ     0010h
SS_FLAG         equ     0020h
FS_FLAG         equ     0040h
GS_FLAG         equ     0080h
LOCK_FLAG       equ     0100h
OPER_SZ_FLAG    equ     0200h
ADDR_SZ_FLAG    equ     0400h
P0F_FLAG        equ     0800h

SEG_OVR_FLAGS   equ     CS_FLAG+ DS_FLAG+ ES_FLAG+ SS_FLAG + FS_FLAG + GS_FLAG

ifdef __BUGFIX2__
OVR_FLAGS_OFF  equ  (not (CS_FLAG or DS_FLAG or ES_FLAG or SS_FLAG or FS_FLAG or GS_FLAG))
endif

page
;******************************************************************************
;                       L O C A L   D A T A  A R E A
;******************************************************************************
_DATA    segment

PrefixFlag      dw      0               ; flags for Fault Instr Prefixes
RefNum          dw      0               ; Reflect number for RefToRom

_DATA    ends

page
;******************************************************************************
;                            C O D E    A R E A
;******************************************************************************
_TEXT    segment
        assume  cs:_TEXT, ds:_DATA, es:_DATA, ss:_DATA

VMinst  label   word
;   Index table for opcode emulation dispatch - This is too general-purpose
;   if all we want to handle is INT instructions, but it'll get fleshed out
;   later, when we start emulating IOPL-sensitive stuff
;
OpTable label   word
        dw      offset _TEXT:BadVmTrap   ;  0 - LLDT, LTR, SLDT, STR, VERR, VERW
        dw      offset _TEXT:EmProtIns   ;  1 - LGDT, LIDT, LMSW
        dw      offset _TEXT:BadVmTrap   ;  2 - LAR
        dw      offset _TEXT:BadVmTrap   ;  3 - LSL
        dw      offset _TEXT:BadVmTrap   ;  4
        dw      offset _TEXT:EmLoadal2   ;  5 - 286Loadall
        dw      offset _TEXT:EmCLTS      ;  6 - CLTS
        dw      offset _TEXT:EmLoadal3   ;  7 - 386Loadall
        dw      offset _TEXT:BadVmTrap   ;  8
        dw      offset _TEXT:BadVmTrap   ;  9
        dw      offset _TEXT:BadVmTrap   ;  a
        dw      offset _TEXT:BadVmTrap   ;  b
        dw      offset _TEXT:BadVmTrap   ;  c
        dw      offset _TEXT:BadVmTrap   ;  d
        dw      offset _TEXT:BadVmTrap   ;  e
        dw      offset _TEXT:Prefix_0F   ;  f
        dw      offset _TEXT:BadVmTrap   ; 10
        dw      offset _TEXT:BadVmTrap   ; 11
        dw      offset _TEXT:BadVmTrap   ; 12
        dw      offset _TEXT:BadVmTrap   ; 13
        dw      offset _TEXT:BadVmTrap   ; 14
        dw      offset _TEXT:BadVmTrap   ; 15
        dw      offset _TEXT:BadVmTrap   ; 16
        dw      offset _TEXT:BadVmTrap   ; 17
        dw      offset _TEXT:BadVmTrap   ; 18
        dw      offset _TEXT:BadVmTrap   ; 19
        dw      offset _TEXT:BadVmTrap   ; 1a
        dw      offset _TEXT:BadVmTrap   ; 1b
        dw      offset _TEXT:BadVmTrap   ; 1c
        dw      offset _TEXT:BadVmTrap   ; 1d
        dw      offset _TEXT:BadVmTrap   ; 1e
        dw      offset _TEXT:BadVmTrap   ; 1f
        dw      offset _TEXT:EmMovCDTR   ; 20 - Mov Rn, CRn
        dw      offset _TEXT:EmMovCDTR   ; 21 - Mov Rn, DRn
        dw      offset _TEXT:EmMovCDTR   ; 22 - Mov CRn, Rn
        dw      offset _TEXT:EmMovCDTR   ; 23 - Mov DRn, Rn
        dw      offset _TEXT:EmMovCDTR   ; 24 - Mov Rn, TRn
        dw      offset _TEXT:BadVmTrap   ; 25
        dw      offset _TEXT:ESOverride  ; 26 - ES Override & Mov TRn, Rn
        dw      offset _TEXT:BadVmTrap   ; 27
        dw      offset _TEXT:BadVmTrap   ; 28
        dw      offset _TEXT:BadVmTrap   ; 29
        dw      offset _TEXT:BadVmTrap   ; 2a
        dw      offset _TEXT:BadVmTrap   ; 2b
        dw      offset _TEXT:BadVmTrap   ; 2c
        dw      offset _TEXT:BadVmTrap   ; 2d
        dw      offset _TEXT:CSOverride  ; 2e - CS Override
        dw      offset _TEXT:BadVmTrap   ; 2f
        dw      offset _TEXT:BadVmTrap   ; 30
        dw      offset _TEXT:BadVmTrap   ; 31
        dw      offset _TEXT:EmRDMSR     ; 32 - RDMSR
        dw      offset _TEXT:BadVmTrap   ; 33
        dw      offset _TEXT:BadVmTrap   ; 34
        dw      offset _TEXT:BadVmTrap   ; 35
        dw      offset _TEXT:SSOverride  ; 36 - SS Override
        dw      offset _TEXT:BadVmTrap   ; 37
        dw      offset _TEXT:BadVmTrap   ; 38
        dw      offset _TEXT:BadVmTrap   ; 39
        dw      offset _TEXT:BadVmTrap   ; 3a
        dw      offset _TEXT:BadVmTrap   ; 3b
        dw      offset _TEXT:BadVmTrap   ; 3c
        dw      offset _TEXT:BadVmTrap   ; 3d
        dw      offset _TEXT:DSOverride  ; 3e - DS Override
        dw      offset _TEXT:BadVmTrap   ; 3f
        dw      offset _TEXT:BadVmTrap   ; 40
        dw      offset _TEXT:BadVmTrap   ; 41
        dw      offset _TEXT:BadVmTrap   ; 42
        dw      offset _TEXT:BadVmTrap   ; 43
        dw      offset _TEXT:BadVmTrap   ; 44
        dw      offset _TEXT:BadVmTrap   ; 45
        dw      offset _TEXT:BadVmTrap   ; 46
        dw      offset _TEXT:BadVmTrap   ; 47
        dw      offset _TEXT:BadVmTrap   ; 48
        dw      offset _TEXT:BadVmTrap   ; 49
        dw      offset _TEXT:BadVmTrap   ; 4a
        dw      offset _TEXT:BadVmTrap   ; 4b
        dw      offset _TEXT:BadVmTrap   ; 4c
        dw      offset _TEXT:BadVmTrap   ; 4d
        dw      offset _TEXT:BadVmTrap   ; 4e
        dw      offset _TEXT:BadVmTrap   ; 4f
        dw      offset _TEXT:BadVmTrap   ; 50
        dw      offset _TEXT:BadVmTrap   ; 51
        dw      offset _TEXT:BadVmTrap   ; 52
        dw      offset _TEXT:BadVmTrap   ; 53
        dw      offset _TEXT:BadVmTrap   ; 54
        dw      offset _TEXT:BadVmTrap   ; 55
        dw      offset _TEXT:BadVmTrap   ; 56
        dw      offset _TEXT:BadVmTrap   ; 57
        dw      offset _TEXT:BadVmTrap   ; 58
        dw      offset _TEXT:BadVmTrap   ; 59
        dw      offset _TEXT:BadVmTrap   ; 5a
        dw      offset _TEXT:BadVmTrap   ; 5b
        dw      offset _TEXT:BadVmTrap   ; 5c
        dw      offset _TEXT:BadVmTrap   ; 5d
        dw      offset _TEXT:BadVmTrap   ; 5e
        dw      offset _TEXT:BadVmTrap   ; 5f
        dw      offset _TEXT:BadVmTrap   ; 60
        dw      offset _TEXT:BadVmTrap   ; 61
        dw      offset _TEXT:BadVmTrap   ; 62
        dw      offset _TEXT:ReturnEMM   ; 63 - ARPL (return to Protected mode gateway)
        dw      offset _TEXT:FSOverride  ; 64 - FS override
        dw      offset _TEXT:GSOverride  ; 65 - GS override
        dw      offset _TEXT:OpOverride  ; 66 - Operand size override
        dw      offset _TEXT:AdOverride  ; 67 - Address size override
        dw      offset _TEXT:BadVmTrap   ; 68
        dw      offset _TEXT:BadVmTrap   ; 69
        dw      offset _TEXT:BadVmTrap   ; 6a
        dw      offset _TEXT:BadVmTrap   ; 6b
        dw      offset _TEXT:EmINSB      ; 6c - INSB
        dw      offset _TEXT:EmINSW      ; 6d - INSW
        dw      offset _TEXT:EmOUTSB     ; 6e - OUTSB
        dw      offset _TEXT:EmOUTSW     ; 6f - OUTSW
        dw      offset _TEXT:BadVmTrap   ; 70
        dw      offset _TEXT:BadVmTrap   ; 71
        dw      offset _TEXT:BadVmTrap   ; 72
        dw      offset _TEXT:BadVmTrap   ; 73
        dw      offset _TEXT:BadVmTrap   ; 74
        dw      offset _TEXT:BadVmTrap   ; 75
        dw      offset _TEXT:BadVmTrap   ; 76
        dw      offset _TEXT:BadVmTrap   ; 77
        dw      offset _TEXT:BadVmTrap   ; 78
        dw      offset _TEXT:BadVmTrap   ; 79
        dw      offset _TEXT:BadVmTrap   ; 7a
        dw      offset _TEXT:BadVmTrap   ; 7b
        dw      offset _TEXT:BadVmTrap   ; 7c
        dw      offset _TEXT:BadVmTrap   ; 7d
        dw      offset _TEXT:BadVmTrap   ; 7e
        dw      offset _TEXT:BadVmTrap   ; 7f
        dw      offset _TEXT:BadVmTrap   ; 80
        dw      offset _TEXT:BadVmTrap   ; 81
        dw      offset _TEXT:BadVmTrap   ; 82
        dw      offset _TEXT:BadVmTrap   ; 83
        dw      offset _TEXT:BadVmTrap   ; 84
        dw      offset _TEXT:BadVmTrap   ; 85
        dw      offset _TEXT:BadVmTrap   ; 86
        dw      offset _TEXT:BadVmTrap   ; 87
        dw      offset _TEXT:BadVmTrap   ; 88
        dw      offset _TEXT:BadVmTrap   ; 89
        dw      offset _TEXT:BadVmTrap   ; 8a
        dw      offset _TEXT:BadVmTrap   ; 8b
        dw      offset _TEXT:BadVmTrap   ; 8c
        dw      offset _TEXT:BadVmTrap   ; 8d
        dw      offset _TEXT:BadVmTrap   ; 8e
        dw      offset _TEXT:BadVmTrap   ; 8f
        dw      offset _TEXT:BadVmTrap   ; 90
        dw      offset _TEXT:BadVmTrap   ; 91
        dw      offset _TEXT:BadVmTrap   ; 92
        dw      offset _TEXT:BadVmTrap   ; 93
        dw      offset _TEXT:BadVmTrap   ; 94
        dw      offset _TEXT:BadVmTrap   ; 95
        dw      offset _TEXT:BadVmTrap   ; 96
        dw      offset _TEXT:BadVmTrap   ; 97
        dw      offset _TEXT:BadVmTrap   ; 98
        dw      offset _TEXT:BadVmTrap   ; 99
        dw      offset _TEXT:BadVmTrap   ; 9a
        dw      offset _TEXT:BadVmTrap   ; 9b
        dw      offset _TEXT:BadVmTrap   ; 9c - PUSHF (not for IOPL=3)
        dw      offset _TEXT:BadVmTrap   ; 9d - POPF (not for IOPL=3)
        dw      offset _TEXT:BadVmTrap   ; 9e
        dw      offset _TEXT:BadVmTrap   ; 9f
        dw      offset _TEXT:BadVmTrap   ; a0
        dw      offset _TEXT:BadVmTrap   ; a1
        dw      offset _TEXT:BadVmTrap   ; a2
        dw      offset _TEXT:BadVmTrap   ; a3
        dw      offset _TEXT:BadVmTrap   ; a4 - MOVSB
        dw      offset _TEXT:EmMOVSW     ; a5 - MOVSW
        dw      offset _TEXT:BadVmTrap   ; a6
        dw      offset _TEXT:BadVmTrap   ; a7
        dw      offset _TEXT:BadVmTrap   ; a8
        dw      offset _TEXT:BadVmTrap   ; a9
        dw      offset _TEXT:BadVmTrap   ; aa
        dw      offset _TEXT:BadVmTrap   ; ab
        dw      offset _TEXT:BadVmTrap   ; ac
        dw      offset _TEXT:BadVmTrap   ; ad
        dw      offset _TEXT:BadVmTrap   ; ae
        dw      offset _TEXT:BadVmTrap   ; af
        dw      offset _TEXT:BadVmTrap   ; b0
        dw      offset _TEXT:BadVmTrap   ; b1
        dw      offset _TEXT:BadVmTrap   ; b2
        dw      offset _TEXT:BadVmTrap   ; b3
        dw      offset _TEXT:BadVmTrap   ; b4
        dw      offset _TEXT:BadVmTrap   ; b5
        dw      offset _TEXT:BadVmTrap   ; b6
        dw      offset _TEXT:BadVmTrap   ; b7
        dw      offset _TEXT:BadVmTrap   ; b8
        dw      offset _TEXT:BadVmTrap   ; b9
        dw      offset _TEXT:BadVmTrap   ; ba
        dw      offset _TEXT:BadVmTrap   ; bb
        dw      offset _TEXT:BadVmTrap   ; bc
        dw      offset _TEXT:BadVmTrap   ; bd
        dw      offset _TEXT:BadVmTrap   ; be
        dw      offset _TEXT:BadVmTrap   ; bf
        dw      offset _TEXT:BadVmTrap   ; c0
        dw      offset _TEXT:BadVmTrap   ; c1
        dw      offset _TEXT:BadVmTrap   ; c2
        dw      offset _TEXT:BadVmTrap   ; c3
        dw      offset _TEXT:BadVmTrap   ; c4
        dw      offset _TEXT:BadVmTrap   ; c5
        dw      offset _TEXT:BadVmTrap   ; c6
        dw      offset _TEXT:BadVmTrap   ; c7
        dw      offset _TEXT:BadVmTrap   ; c8
        dw      offset _TEXT:BadVmTrap   ; c9
        dw      offset _TEXT:BadVmTrap   ; ca
        dw      offset _TEXT:BadVmTrap   ; cb
        dw      offset _TEXT:EmINT3      ; cc - INT 3
        dw      offset _TEXT:EmINTnn     ; cd - INT nn
        dw      offset _TEXT:EmINTO      ; ce - INTO
        dw      offset _TEXT:BadVmTrap   ; cf - IRET/EmIRET (not for IOPL=3)
        dw      offset _TEXT:BadVmTrap   ; d0
        dw      offset _TEXT:BadVmTrap   ; d1
        dw      offset _TEXT:BadVmTrap   ; d2
        dw      offset _TEXT:BadVmTrap   ; d3
        dw      offset _TEXT:BadVmTrap   ; d4
        dw      offset _TEXT:BadVmTrap   ; d5
        dw      offset _TEXT:BadVmTrap   ; d6
        dw      offset _TEXT:BadVmTrap   ; d7
        dw      offset _TEXT:BadVmTrap   ; d8
        dw      offset _TEXT:BadVmTrap   ; d9
        dw      offset _TEXT:BadVmTrap   ; da
        dw      offset _TEXT:BadVmTrap   ; db
        dw      offset _TEXT:BadVmTrap   ; dc
        dw      offset _TEXT:BadVmTrap   ; dd
        dw      offset _TEXT:BadVmTrap   ; de
        dw      offset _TEXT:BadVmTrap   ; df
        dw      offset _TEXT:BadVmTrap   ; e0
        dw      offset _TEXT:BadVmTrap   ; e1
        dw      offset _TEXT:BadVmTrap   ; e2
        dw      offset _TEXT:BadVmTrap   ; e3
        dw      offset _TEXT:EmINBimm    ; e4 - INB imm
        dw      offset _TEXT:EmINWimm    ; e5 - INW imm
        dw      offset _TEXT:EmOUTBimm   ; e6 - OUTB imm
        dw      offset _TEXT:EmOUTWimm   ; e7 - OUTW imm
        dw      offset _TEXT:BadVmTrap   ; e8
        dw      offset _TEXT:BadVmTrap   ; e9
        dw      offset _TEXT:BadVmTrap   ; ea
        dw      offset _TEXT:BadVmTrap   ; eb
        dw      offset _TEXT:EmINB       ; ec - INB
        dw      offset _TEXT:EmINW       ; ed - INW
        dw      offset _TEXT:EmOUTB      ; ee - OUTB
        dw      offset _TEXT:EmOUTW      ; ef - OUTW
        dw      offset _TEXT:EmLOCK      ; f0 - LOCK
        dw      offset _TEXT:BadVmTrap   ; f1
        dw      offset _TEXT:EmREPNE     ; f2 - REPNE
        dw      offset _TEXT:EmREP       ; f3 - REP/REPE
        dw      offset _TEXT:EmHALT      ; f4 - HLT
        dw      offset _TEXT:BadVmTrap   ; f5
        dw      offset _TEXT:BadVmTrap   ; f6
        dw      offset _TEXT:BadVmTrap   ; f7
        dw      offset _TEXT:BadVmTrap   ; f8
        dw      offset _TEXT:BadVmTrap   ; f9
        dw      offset _TEXT:BadVmTrap   ; fa - CLI EmCLI (not for IOPL=3)
        dw      offset _TEXT:BadVmTrap   ; fb - STI EmSTI (not for IOPL=3)
        dw      offset _TEXT:BadVmTrap   ; fc
        dw      offset _TEXT:BadVmTrap   ; fd
        dw      offset _TEXT:BadVmTrap   ; fe
        dw      offset _TEXT:BadVmTrap   ; ff - Change P0F_Invalid, if used

        page
;******************************************************************************
;   VmFault - entry point for Virtual Mode GP faults (from routine vm_trap0d
;       in module VMTRAP.ASM).  The appropriate instructions are emulated and
;       control is returned to the Virtual Mode client.  Currently, we assume
;       the client is running WITH IOPL, INT gate DPL = 0, and a truncated
;       IDT, so INT instruction fault to here and are emulated.  All other
;       GP faults enter the debugger.
;
;   The following instructions are invalid in Real or Virtual Mode:
;
;       ARPL, LAR, LSL, VERR, VERW, STR, LTR, SLDT, LLDT
;
;   The following instructions are privileged and are thus invalid in
;   Virtual Mode since VM progs run at CPL 3:
;
;       LIDT, LDGT, LMSW, CLTS, HLT, Debug Register ops, Control
;       Register ops, and Test Register ops
;
;   If client does not have IOPL, the following instructions must be handled
;   (in addition to the INT instructions).  This scenario changes for B0:
;
;       IN, INS, OUT, OUTS, STI, CLI, LOCK, PUSHF, POPF, and IRET
;
;   For B0, the following instructions must be handled when they trap
;   according to the bit map in the TSS.
;
;       IN, INS, OUT, and OUTS
;
;   For Invalid Opcode (vm_trap06) emulation:
;   The following instructions are invalid in Real or Virtual Mode:
;
;       LTR, LLDT, LAR, LSL, ARPL, STR, SLDT, VERR, VERW
;
;   In fielding the exception from Virtual Mode, the 386 interrupt gate
;   switched to the Ring 0 stack and pushed 32-bit values as follows:
;
;        hiword loword  offset (in addition to error code and BP push)
;       +------+------+ <-------- Top of 'kernel' stack
;       | 0000 |  GS  |  +32 (decimal)
;       |------+------|
;       | 0000 |  FS  |  +28
;       |------|------|
;       | 0000 |  DS  |  +24
;       |------|------|
;       | 0000 |  ES  |  +20
;       |------|------|
;       | 0000 |  SS  |  +16
;       |------|------|
;       |     ESP     |  +12
;       |------|------|
;       |    EFLAGS   |  +08
;       |------|------|
;       | 0000 |  CS  |  +04
;       |------|------|
;       |     EIP     |  +00
;       +------|------+
;       | error code  |
;       |------|------| <-------- Ring 0 SS:SP
;       |    (ebp)    |
;       +------+------+ <-------- Ring 0 SS:EBP
;
;
;   ENTRY:  386 Protected Mode - ring 0
;           EBP is on the stack
;           SS:BP -> VM trap frame on stack w/error code (faulting selector)
;           GP exceptions are faults: pushed CS:EIP points to faulting opcode
;   EXIT:   via IRET to VM client, instruction emulated as necessary
;   USED:   (none) (note that DS & ES are free to use - saved during trap)
;   STACK:  n/a
;------------------------------------------------------------------------------
VmFault proc    near
        push    ebx                     ; local registers
        push    esi
        mov     bx,GDTD_GSEL            ; get GDT data alias
        mov     ds,bx                   ; DS -> GDT
;
;   Build a selector (VM1_GSEL) to client's stack.  VM1_GSEL is already set
;   up with limit (0FFFFh) and access (D_DATA0), so just fill in the base.
;
;; ifdef __TONYI__    Remove hack for FOXPRO FIX.DEFECT 5723 ....
;--------------------------------------------------------------
; Touch memory once rather than twice for SS
;--------------------------------------------------------------
;;        mov     si,[bp.VTFOE+VMTF_SS]   ; SI = VM SS (segment form)
;;        mov     bx, si
;;       shl     si,4                    ; SI = low 16 bits of base
;;        shr     bx,4                    ; BH = high 8 bits of base
;;        mov     ds:[VM1_GSEL+2],si      ; place in descriptor
;;        mov     ds:[VM1_GSEL+4],bh      ; place in descriptor
;;else  =====================  DEFECT 5723
        mov     bx,[bp.VTFOE+VMTF_SS]   ; BX = VM SS (segment form)
        shl     bx,4                    ; BX = low 16 bits of base
        mov     ds:[VM1_GSEL+2],bx      ; place in descriptor
        mov     bx,[bp.VTFOE+VMTF_SS]   ; BX = VM SS (again)
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM1_GSEL+4],bh      ; place in descriptor
;;endif ========================  DEFECT 5723
;
;   Build a selector (VM2_GSEL) to client's code segment, as above.
;
ifdef __TONYI__
;--------------------------------------------------------------
; Touch memory once rather than twice for CS
;--------------------------------------------------------------
        mov     si,[bp.VTFOE+VMTF_CS]   ; SI = VM CS (in segment form)
        mov     bx, si
        shl     si,4                    ; SI = low 16 bits of base
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM2_GSEL+2],si      ; place in descriptor
        mov     ds:[VM2_GSEL+4],bh      ; place in descriptor
else
        mov     bx,[bp.VTFOE+VMTF_CS]   ; BX = VM CS (in segment form)
        shl     bx,4                    ; BX = low 16 bits of base
        mov     ds:[VM2_GSEL+2],bx      ; place in descriptor
        mov     bx,[bp.VTFOE+VMTF_CS]   ; BX = VM CS (again)
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM2_GSEL+4],bh      ; place in descriptor
endif
;
;   Reset prefix flags
;
        mov     bx,VDMD_GSEL
        mov     es,bx
        mov     ES:[PrefixFlag],0       ; start with no prefixes
;
;   Jump to appropriate instruction handler
;
        mov     bx,VM2_GSEL
        mov     ds,bx                   ; DS = selector for VM code segment
        mov     si,[bp.VTFOE+VMTF_EIP]  ; DS:SI = VM CS:IP
VmInsHandle:
ifdef __TONYI__
;-----------------------------------------------------
; Break even on 386's, win for 486's.
;-----------------------------------------------------
        movzx   bx, byte ptr ds:[si]
else
        mov     bl,ds:[si]              ; BL = opcode
        mov     bh,0                    ; BX = opcode
endif
        shl     bx,1                    ; BX = BX*2 (word table)
                                        ; DS:SI = VM CS:IP
                                        ; ES pts to local data segment
                                        ; VM1_GSEL pts to VM SS
                                        ; VM2_GSEL pts to VM CS
        jmp     cs:OpTable[bx]          ; enter instruction emulation routine

VmFault endp

page
;******************************************************************************
;   BadVmTrap - unsupported Virtual Mode GP exception - enter the debugger
;
;   ENTRY:  EBP,EBX,ESI are on the stack
;   EXIT:   to the debugger
;   USED:
;   STACK:  n/a
;------------------------------------------------------------------------------
BadVmTrap       proc    near
IFDEF VCB_FIX_13
        cmp     word ptr [bp][VTFO+2],13 ; Exception 13 ?
        jne     Reflect6                ; Reflect to VM illegal opcode handler
        mov     ES:[RefNum],13          ; Vector to VM int 13 handler
        jmp     RefToRom                ; and let the VM OS handle it
ELSE
        jmp     Reflect6                ; Reflect to VM illegal opcode handler
ENDIF

ifdef QLEO
ifndef  BugMode
        jmp     Reflect6                ; Reflect to VM illegal opcode handler
else
        pop     esi                     ;  clean up the stack,
        pop     ebx
        pop     ebp
        db      0EAh                    ; far jump opcode
        dw      offset DCODE:_trap13    ; offset
        dw      DEBC_GSEL               ; selector
endif
endif

BadVmTrap       endp

;******************************************************************************
;   ReturnEMM - user is trying to go back into the protected mode via ARPL
;
;   ENTRY:  EBP,EBX,ESI are on the stack
;   EXIT:   to the debugger
;   USED:
;   STACK:  n/a
;
;       Code ported from EMM386 for PS2 support.
;
;------------------------------------------------------------------------------
ReturnEMM       proc    far
;
; We assume that only EMM can go back to protected mode and therefore
; hardwired this code to go to R_CODE.
; It could use some data checking (by making sure that DS is actually
; pointing to R_CODE) or be more general (by building a new GDT selector
; which is a code selector and contains the faulting code's segment
; and push this instead of RCODE_GSEL)
;
        add     si, 2                   ; "IP" now points to instruction
                                        ;   after "ARPL reg,reg"
        push    RCODE_GSEL              ; push faulting code's CS
        push    si                      ; push faulting code's IP
        retf                            ; back to faulting code in protected mode!
ReturnEMM       endp

page
;******************************************************************************
;   EmINTnn - emulate software interrupt
;
;   The emulation of the software INT instructions requires us to massage
;   the trap stack frame (see VmFault header) and build a 'real mode'
;   stack frame for the virtual mode client so that we can transfer
;   control to virtual mode at the address specified in the appropriate
;   real mode IDT vector.  The client's IRET out of the interrupt routine
;   will proceed normally (assuming we're letting him run with IOPL = 3).
;   Since we're fielding the trap from Virtual Mode, we assume the high
;   word of ESP and EIP is 0000.
;
;       +-------+ <-------- Client's current SS:SP
;       | Flags |  +4
;       |-------|
;       |  CS   |  +2
;       |-------|
;       |  IP   |  +0
;       +-------+ <-------- Client's SS:SP when we let him have control
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode = CD nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;           appropriate real mode IRET set up @ client's SS:SP
;   USED:   (none)
;   STACK:
;
;   to do:  Streamline this code - it's on the critical path
;           Decide about how to handle Trace Bit in general
;------------------------------------------------------------------------------

ifdef __TONYI__1
        align   16
endif

EmINTnn proc    near

ifdef __BUGFIX__
;--------------------------------------------------------------------------
; This updates the EIP that we *thought* we had to account for any
; accumulated prefixes.  SI at this point will have been incremented past
; anything that was there.  Since prefixes on an INT are essentially NOP's
; we just skip over them and don't apply any special semantics.
;--------------------------------------------------------------------------
        lea     bx, [si+2]
        shl     ebx, 16                 ; Hi-word EBX == ret for IRET frame
else
;------------------------------------------------------
; Superfluous since SI is reloaded next anyway.
;------------------------------------------------------
        add     si, 2                   ; set IP past the instruction we emulate
        push    si                      ; save on stack for now
endif

;   Adjust client's SP to make room for building his IRET frame
;
ifdef __TONYI__
;----------------------------------------------------------------------
; SUB to memory is slow.  We can do the sub in a reg and trade it for
; a simple/fast MOV.
;----------------------------------------------------------------------
        mov     si,[bp.VTFOE+VMTF_ESP]  ; DS:SI -> client's IRET stack frame
        mov     bx,VM1_GSEL
        sub     si, 6
        mov     ds,bx                   ; DS = VM stack segment
        mov     word ptr [bp.VTFOE+VMTF_ESP], si; adjust client's SP
else
        sub     word ptr [bp.VTFOE+VMTF_ESP],6  ; adjust client's SP
        mov     bx,VM1_GSEL
        mov     ds,bx                   ; DS = VM stack segment
        mov     si,[bp.VTFOE+VMTF_ESP]  ; DS:SI -> client's IRET stack frame
endif
;
;   Move low 16 bits of Flags, CS, and EIP from IRET frame to client stack frame
;
        mov     bx,[bp.VTFOE+VMTF_EFLAGS] ; low word of EFLAGS
ifdef __TONYI__
;----------------------------------------------------------------------
; AND to memory is slow.  We can do the AND in a reg and trade it for
; a simple/fast MOV.
;----------------------------------------------------------------------
        mov     ds:[si.4],bx            ; to client's flags
        and     bx, not (FLAGS_IF OR FLAGS_TF)
        mov     [bp.VTFOE+VMTF_EFLAGS], bx ; low word of EFLAGS
else
;
; *** Clear IF bit on flags for reflect, but leave it unchanged for the
;       flags on the IRET stack we build on the client's stack
; *** Also clear the Trace Flag -> because all software INTs clear the trace
;      flag.
;
        and     [bp.VTFOE+VMTF_EFLAGS],not (FLAGS_IF OR FLAGS_TF)

        mov     ds:[si.4],bx            ; to client's flags
endif
        mov     bx,[bp.VTFOE+VMTF_CS]   ;
ifdef __BUGFIX__
        rol     ebx, 16                 ; Get new CS:IP in proper order
        mov     ds:[si], ebx            ;
else
        mov     ds:[si.2],bx            ; to client's CS
        pop     bx                      ; recover IP
        mov     ds:[si.0],bx            ; to client's IP
endif
;
;   Replace low 16 bits of IRET frame CS:EIP with vector from real mode IDT
;
        mov     si,VM2_GSEL
        mov     ds,si                   ; DS -> Client's code segment
ifdef __TONYI__
        movzx   bx, byte ptr ds:[bx-1]          ; get INTerrupt number
else
        mov     bl,ds:[bx-1]            ; get INTerrupt number
        xor     bh,bh                   ; BX has INT number
endif
        shl     bx,2                    ; BX = BX * 4 (vector table index)
        mov     si,RM_IDT_GSEL          ; get real mode IDT alias
        mov     ds,si                   ; DS -> Real Mode IDT
        mov     esi,ds:[bx]             ;QLEO
;QLEO   mov     si,ds:[bx]              ;
        mov     [bp.VTFOE+VMTF_EIP],si  ; move the IP
        shr     esi,16                  ;QLEO
;QLEO   mov     si,ds:[bx+2]            ;
        mov     [bp.VTFOE+VMTF_CS],si   ; move the CS
;
;   32-bit IRET back to client
;
EmExit:
EmSkipLockExit:                         ; as the label implies...
        pop     esi                     ; restore local regs
EmExitESIUpdated:
        pop     ebx
        pop     ebp
        add     sp,4                    ; throw away error code
        iretd                   ; *** RETURN *** to client
EmINTnn endp

page
;******************************************************************************
;   EmINT3 - emulate the 'breakpoint' interrupt instruction
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode = CD nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;           appropriate real mode IRET set up @ client's SS:SP
;   USED:   (none)
;   STACK:
;
;   to do:  implement the handler
;------------------------------------------------------------------------------
EmINT3  proc    near
        mov     ES:[RefNum], 03h            ; Vector to VM int 3 handler
RefIntN:
        inc     si                          ; fault, not a trap
        mov     [bp.VTFOE+VMTF_EIP], si     ; point beyond int3
        jmp     RefToRom                    ; and let the VM OS handle it
EmINT3  endp

;******************************************************************************
;   EmINTO - emulate overflow interrupt
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode = CD nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;           appropriate real mode IRET set up @ client's SS:SP
;   USED:   (none)
;   STACK:
;------------------------------------------------------------------------------
EmINTO  proc    near
ifndef  BugMode
        mov     ES:[RefNum], 04h            ; Vector to VM int 3 handler
        jmp     RefIntN
else
        inc     si                      ; fault, not a trap
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        pop     esi                     ;  clean up the stack,
        pop     ebx
        pop     ebp
        add     sp,4                    ; throw away error code
        db      0EAh                    ; far jump opcode
        dw      offset DCODE:_trap04    ; offset
        dw      DEBC_GSEL               ; selector
endif
EmINTO  endp

page
;******************************************************************************
;   EmINB - emulate the IN byte instruction
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode =  nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;   USED:
;   STACK:  n/a
;------------------------------------------------------------------------------
EmINB   proc    near
        inc     si
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        push    bx
        xor     bx,bx                   ; IN instruction
        call    IO_Trap                 ; Q: Emulated ?
        jnc     SHORT EINB_Exit         ;   Y: exit
        in      al,dx                   ;   N:do the INput
EINB_Exit:
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmINB   endp

page
;******************************************************************************
;   EmINW - emulate IN word
;
;       This routine emulates the IN word instruction
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;------------------------------------------------------------------------------
EmINW   proc    near
        inc     si
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        push    bx
        xor     bx,bx                   ; IN instruction
        call    IO_Trap                 ; Q: Emulated ?
        cbw                             ; AX= returned value
        jnc     SHORT EINW_Exit         ;   Y: exit
IFDEF VCB_FIX_OVR
        test    ES:[PrefixFlag],OPER_SZ_FLAG   ;Q: Operand-size prefix ?
        jz      short @f                       ;  N: skip
        db      66h                            ;  Y: issue instruction with prefix
@@:
ENDIF
        in      ax,dx                   ;   N:do the word INput
EINW_Exit:                              ;
        pop     bx                      ;
        jmp     EmExit                  ; *** RETURN *** to VM client
EmINW   endp

page
;******************************************************************************
;   EmINBimm - emulate IN word immediate
;
;       This routine emulates the IN word immediate instruction
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;------------------------------------------------------------------------------
EmINBimm        proc    near
        push    bx
        push    dx
        mov     dl,ds:[si+1]            ; get port number
        xor     dh,dh                   ; DX has INT number
        add     si, 2
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        xor     bx,bx                   ; IN instruction
        call    IO_Trap                 ; Q: Emulated ?
        jnc     SHORT EINBi_Exit        ;   Y: exit
        in      al,dx                   ;   N:do the INput
EINBi_exit:
        pop     dx
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmINBimm        endp

page
;******************************************************************************
;***    EmINWimm - emulate IN word immediate
;
;       This routine emulates the IN word immediate instruction
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;------------------------------------------------------------------------------
EmINWimm        proc    near
        push    bx
        push    dx
        mov     dl,ds:[si+1]            ; get port number
        xor     dh,dh                   ; DX has INT number
        add     si, 2
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        xor     bx,bx                   ; IN instruction
        call    IO_Trap                 ; Q: Emulated ?
        cbw                             ; AX = returned value
        jnc     SHORT EINWi_Exit        ;   Y: exit
IFDEF VCB_FIX_OVR
        test    ES:[PrefixFlag],OPER_SZ_FLAG   ;Q: Operand-size prefix ?
        jz      short @f                       ;  N: skip
        db      66h                            ;  Y: issue instruction with prefix
@@:
ENDIF
        in      ax,dx                   ;   N:do the word INput
EINWi_Exit:
        pop     dx
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmINWimm        endp

page
;******************************************************************************
;***    EmOUTB - emulate OUT byte
;
;       This routine emulates the OUT byte instruction
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
EmOUTB  proc    near
        inc     si
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        push    bx
        push    dx
        mov     bx,1                    ; OUT instruction
        call    IO_Trap                 ; Q: Emulated ?
        jnc     SHORT EOUTB_Exit        ;  Y:exit
EOUTB_em:
        out     dx,al                   ;      N:do the byte OUTput
EOUTB_Exit:
        pop     dx
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmOUTB  endp

page
;******************************************************************************
;***    EmOUTW - emulate OUT word
;
;       This routine emulates the OUT word instruction
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
EmOUTW  proc    near
        inc     si
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        push    bx
        mov     bx,1                    ; OUT instruction
        call    IO_Trap                 ; Q: Emulated ?
        jnc     SHORT EOUTW_Exit        ;   Y: exit
                                        ;   N:
IFDEF VCB_FIX_OVR
        test    ES:[PrefixFlag],OPER_SZ_FLAG   ;Q: Operand-size prefix ?
        jz      short @f                       ;  N: skip
        db      66h                            ;  Y: issue instruction with prefix
@@:
ENDIF
        out     dx,ax                   ; do the word OUTput
EOUTW_Exit:
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmOUTW  endp

page
;******************************************************************************
;***    EmOUTBimm - emulate OUT byte immediate
;
;       This routine emulates the OUT byte immediate
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
EmOUTBimm       proc    near
        push    bx
        push    dx
        mov     dl,ds:[si+1]            ; get port number
        xor     dh,dh                   ; DX has INT number
        add     si, 2
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        mov     bx,1                    ; OUT instruction
        call    IO_Trap                 ; Q: Emulated ?
        jnc     SHORT EOUTBi_Exit       ;   Y:exit
EOUTBi_em:
        out     dx,al                   ;      N:do the byte OUTput
EOUTBi_Exit:
        pop     dx
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmOUTBimm       endp

page
;******************************************************************************
;***    EmOUTWimm - emulate OUT word immediate
;
;       This routine emulates the OUT word immediate instruction
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
EmOUTWimm       proc    near
        push    bx
        push    dx
        mov     dl,ds:[si+1]            ; get port number
        xor     dh,dh                   ; DX has INT number
        add     si, 2
        mov     [bp.VTFOE+VMTF_EIP],si  ; set IP past the instruction we emulate
        mov     bx,1                    ; OUT instruction
        call    IO_Trap                 ; Q: Emulated ?
        jnc     SHORT EOUTWi_Exit       ;   Y: exit
IFDEF VCB_FIX_OVR
        test    ES:[PrefixFlag],OPER_SZ_FLAG   ;Q: Operand-size prefix ?
        jz      short @f                       ;  N: skip
        db      66h                            ;  Y: issue instruction with prefix
@@:
ENDIF
        out     dx,ax                   ;   N:do the word OUTput
EOUTWi_Exit:
        pop     dx
        pop     bx
        jmp     EmExit                  ; *** RETURN *** to VM client
EmOUTWimm       endp
page
;******************************************************************************
;***    EmHALT - Emulate HALT command
;
;       This routine is entered if a faulting instruction
;       is a HALT command
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    return to VM client
;               if the user's flags => CLI, then do a CLI,HLT
;               else, skip the instruction
;
EmHALT  proc    near

ifdef QLEO
        test    [bp.VTFOE+VMTF_EFLAGS],FLAGS_IF ;Q: user flags => CLI ?
        jnz     SHORT EmH_skip                  ;  N: just skip hlt
        cli                                     ;  Y: do the CLI,HLT
        hlt

EmH_skip:
        inc     si                              ; inc VM CS:IP past command
        mov     [bp.VTFOE+VMTF_EIP], si         ;  we emulate
        jmp     EmExit                          ; and leave
endif
;
; Need to return to instruction follwing HLT
;
        inc     si                              ; inc VM CS:IP past HLT command
        mov     [bp.VTFOE+VMTF_EIP], si
;
; Check if client had interrupts on/off
;
        test    [bp][VTFOE].VMTF_EFLAGS,FLAGS_IF;Q: HLT with CLI?
        jz      short EmHhlt                    ; Y: HLT with CLI

;
; Set up the stack so that preflectinterrupt can handle this case.
; Also indicate in Genglags to preflectinterrupt.
;
        push    ds
        push    RCODEA_GSEL
        pop     ds
        assume  ds:R_CODE
        or      ds:[GenFlags],fEMMhlt
        pop     ds
        assume  ds:_DATA

        pop     esi                     ; restore local regs
        pop     ebx
        pop     ebp
        add     sp,4                    ; throw away error code
        push    ebp                     ; put ebp on top of error code

        sti                             ; HLT with STI
EmHhlt:
        hlt

;##### NOTE WE WILL NOT COME HERE IF STI HLT IS EXECUTED ######

        cli                                     ; clear interrupts
        jmp     EmExit                          ; and leave
EmHALT  endp

page
;******************************************************************************
;***    EmLoadal2 - Emulate 286 Loadall command
;
;       This routine is entered if a faulting instruction
;       is a Loadall 286 command
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    return to VM client
;
EmLoadal2  proc    near

        jmp     BadVmTrap                   ;  N: Vector to VM illegal opcode
EmLoadal2  endp

;******************************************************************************
;***    EmLoadal3 - Emulate 386 Loadall command
;
;       This routine is entered if a faulting instruction
;       is a Loadall 386 command
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    return to VM client
;
EmLoadal3  proc    near

        jmp     BadVmTrap                   ;  N: Vector to VM illegal opcode
EmLoadal3  endp

;******************************************************************************
;   EmCLTS - emulate the CLTS instruction
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode =  nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;   USED:
;   STACK:  n/a
;------------------------------------------------------------------------------
EmCLTS  proc    near

        test    ES:[PrefixFlag],P0F_FLAG
        jz      SHORT Not_CLTS
        clts                                    ; go ahead and CLTS
        inc     si                              ; inc VM CS:IP past command
        mov     [bp.VTFOE+VMTF_EIP], si         ;  we emulate
        jmp     EmExit                          ; and leave

Not_CLTS:
        jmp     BadVmTrap

EmCLTS  endp

;******************************************************************************
;   EmProtIns - emulate the protection control instructions
;   Currently this throws LIDT and LGDT to the error handler and only emulates
;   LMSW
;   NOTE:  The Stack configuration is critical!!!  If it is changed, the
;   offsets to the register images must be updated accordingly.
;   (RRH)
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode =  nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;           bp.VTFOE+VMTF_EIP points beyond offender to next client instruction
;           VM2_GSEL, DS, and SI may be modified at exit, this should not be a
;           problem for the IRET
;   USED:
;   STACK:  n/a
;------------------------------------------------------------------------------
EmProtIns proc    near

        test    ES:[PrefixFlag],P0F_FLAG
        jnz     SHORT EmPI1
        jmp     Not_ProtIns1
EmPI1:
        push    dx
        push    ax
        push    es
        mov     dx, ES:[PrefixFlag]
        mov     bx,VDMCA_GSEL           ; Load CODE (writeable) alias
        mov     es, bx
        assume  es:_TEXT
        inc     si                      ; set si past the opcode
        mov     bl,[si]                 ; BL = modR/M
        mov     bh, bl                  ; BH = modR/M
        and     bl, 38h
        cmp     bl, 30h
        je      SHORT EmLMSW            ; Emulate LMSW
        cmp     bl, 18h
        jne     SHORT LGDTerr           ; VM guy not allowed to LGDT
        mov     bx, ErrLIDT
        jmp     ExitPIer                ; VM guy not allowed to LIDT
LGDTerr:
        mov     bx, ErrLGDT
        jmp     ExitPIer
EmLMSW:
        inc     si                      ; set SI past the modR/M
        mov     [bp.VTFOE+VMTF_EIP],si  ; VM CS:IP = DS:SI
        mov     bl, bh                  ; BL = modR/M
        cmp     bl, 0C0h                ; is MSW being loaded from a register?
        JAE     MoveReg                 ; Y: go do a register move

MoveData:
;   Because of all the possible addressing modes this is pretty nasty.  Like
;   MoveReg, it is complicated by the requirement to not clear the PE bit.
;   The general approach is to find the client's data value, put it in AX,
;   set the PE bit in AX, then load the MSW from AX.  To keep from having
;   to know about all the modR/M combinations, we yank the client's possible
;   offset from his code, and put his slightly modified modR/M byte in our
;   home-made MOV AX, MemData instruction.

;   If there is a data offset, yank it from the VM instruction and put it
;   in our instruction.

        and     bl, 0C7h                ; Force AX to be the MOV destination
        mov     byte ptr es:[LMSWmod],bl
        mov     ax, 09090h
        mov     word ptr es:[LMSWoff],ax ; initialize offset to NOPS
        cmp     bl, 06h                 ; special case for DS:d16
        je      SHORT load16off
        and     bl, 0C0h
        cmp     bl, 040h
        je      SHORT load8off
        cmp     bl, 080h
        jne     SHORT BldDesc           ; No data offset, so go build desc
Load16off:
        mov     ax,[si]                 ; AX = 16 bit offset
        mov     word ptr es:[LMSWoff],ax
        add     si, 2
        mov     [bp.VTFOE+VMTF_EIP],si  ; VM CS:IP = DS:SI
        jmp     SHORT BldDesc
Load8off:
        mov     al,[si]                 ; AL = 8 bit offset
        mov     byte ptr es:[LMSWoff],al
        inc     si
        mov     [bp.VTFOE+VMTF_EIP],si  ; VM CS:IP = DS:SI

BldDesc:
;   Build a descriptor to the client's data segment

        mov     bl, bh
        mov     ax, [bp.VTFOE+VMTF_DS]  ; Assume DS is the data segment

;   Check for segment override
        and     dx, SEG_OVR_FLAGS       ; strip all but segment overrides
        cmp     dx, CS_FLAG
        jl      SHORT GetmodBase        ; no override, check base
        je      SHORT CS_data
        cmp     dx, ES_FLAG
        je      SHORT ES_data
        jl      SHORT GetSel            ; ds is override
        cmp     dx, FS_FLAG
        jl      SHORT SS_data
        je      SHORT FS_data
GS_data:
        mov     ax, [bp.VTFOE+VMTF_GS]  ; GS is the data segment
        jmp     SHORT GetSel
FS_data:
        mov     ax, [bp.VTFOE+VMTF_FS]  ; FS is the data segment
        jmp     SHORT GetSel
SS_data:
        mov     ax, VM1_GSEL
        mov     ds, ax
        jmp     SHORT RestoreRegs
CS_data:
        mov     ax, VM2_GSEL
        mov     ds, ax
        jmp     SHORT RestoreRegs
ES_data:
        mov     ax, [bp.VTFOE+VMTF_ES]  ; ES is the data segment
        jmp     SHORT GetSel

GetmodBase:
;   We have no Segment override, so we need to look at the modR/M byte to
;   see whether or not the data index/offset is based on DS (assumed) or SS
        and     bl, 0C7h                ; clear instruction bits
        cmp     bl, 46h
        je      SS_Data                 ; EA = SS:[BP+d8]
        and     bl, 7
        cmp     bl, 2
        je      SS_Data                 ; EA = SS:[BP+SI+?]
        cmp     bl, 3
        je      SS_Data                 ; EA = SS:[BP+DI+?]

GetSel:
;   Build a selector (VM2_GSEL) to client's data.  VM2_GSEL is already set
;   up with limit (0FFFFh) and access (D_DATA0), so just fill in the base.
;   The Descriptor base VM segment value is in AX
        push    es
        push    bx
        mov     bx,GDTD_GSEL            ; get GDT data alias
        mov     es,bx                   ; DS -> GDT
        mov     bx, ax                  ; copy the VM data segment value
        shl     ax,4                    ; BX = low 16 bits of base
        mov     es:[VM2_GSEL+2],ax      ; place in descriptor
        shr     bx,4                    ; BH = high 8 bits of base
        mov     es:[VM2_GSEL+4],bh      ; place in descriptor
        mov     ax, VM2_GSEL
        mov     ds, ax
        pop     bx
        pop     es

RestoreRegs:
;   Since BX, SI, DI, and BP can all be used to form an effective address we
;   blindly restore BX, SI, and BP from the stack so that we don't
;   have to know what the instruction is using for its effective address.
;   DI does not need to be restored, because it should still be the client's

        push    bp
        mov     bp, sp

;   Now the Stack had better look like PROT_INS_FRAME. (vm386.inc)
        mov     si, [bp.PIF_ESI]
        mov     bx, [bp.PIF_EBX]
        mov     bp, [bp.PIF_EBP]

;   Move the VM data operand to AX
        db      3Eh                     ; ds overide (for bp & di)
        db      8Bh                     ; MOV opcode
LMSWmod db      00h                     ; modR/M
LMSWoff db      90h, 90h                ; possible offset (NOPS otherwise)

        jmp     SHORT ExLMSW            ; Finally... go do the LMSW

MoveReg:
;   Here we have a LMSW from one of the general registers.  This is pretty
;   ugly because many of the possible registers the client might have used
;   are currently saved on the stack.  It is also complicated by the
;   requirement to not clear the PE bit.  The general approach is to find
;   the client's register value/image, put it in AX, set the PE bit in AX,
;   then load the MSW from AX.  To keep from having to know all the modR/M
;   combinations, we again use a slightly modified client's modR/M byte in
;   our home-made MOV AX, RegData instruction.

        push    bp
        mov     bp, sp

;   Now the Stack had better look like PROT_INS_FRAME. (vm386.inc)

        and     bh, 07h                 ; If Src is AX, it hasn't been changed
        jz      SHORT ExLMSW            ; so just go do the the LMSW
CkSrcBX:                                ; otherwise, find it and move it to AX
        cmp     bh, 3
        jne     SHORT CkSrcBP
        mov     ax, [bp.PIF_EBX]        ; src was BX, get from stack
        jmp     SHORT ExLMSW
CkSrcBP:
        cmp     bh, 5
        jne     SHORT CkSrcSI
        mov     ax, [bp.PIF_EBP]        ; src was BP, get from stack
        jmp     SHORT ExLMSW
CkSrcSI:
        cmp     bh, 6
        jne     SHORT CkSrcDX
        mov     ax, [bp.PIF_ESI]        ; src was SI, get from stack
        jmp     SHORT ExLMSW
CkSrcDX:
        cmp     bh, 2
        jne     SHORT CkSrcSP
        mov     ax, [bp.PIF_DX]         ; src was DX, get from stack
        jmp     SHORT ExLMSW
CkSrcSP:
        cmp     bh, 4
        jne     SHORT GetReg
        mov     ax, [bp.PIF_VMESP]      ; src was SP, get from stack
        jmp     SHORT ExLMSW
GetReg:
        or      bh, 0C0h                ; set register to register move bits
        mov     es:[Lmod], bh           ; setup move from client's src register
        jmp     SHORT GetRn             ; clear prefetch so that bh gets there
GetRn:
;   Execute MOV AX, Rn
        db      08Bh
Lmod    db      0C0h

;   Finally Execute the LMSW
ExLMSW:

;   At this point we could check for the PE bit and notify the user that
;   he must switch to real mode... but because an old app might do a SMSW,
;   which copies the PE bit (?!!), then set a bit in that image and do a LMSW,
;   (not caring about the PE bit), we just let it go... under the assumption
;   that if he really were trying to enter Protected mode, he would have
;   failed trying to LIDT and LGDT.

        or      ax, 0001h               ;  N: we want to stay in Prot mode
        LMSW    ax                      ; So we must set it (use BTS above)

        pop     bp
        pop     es
        assume  es:_DATA
        pop     ax
        pop     dx
        jmp     EmExit                  ; *** RETURN *** to VM client

ExitPIer:
        pop     es
        assume  es:_DATA
        pop     ax
        pop     dx
        mov     [bp+4],bx       ; save minor error # over error code
        pop     esi
        pop     ebx
        pushad          ; -> entry condition for ErrHndlr

ifndef  BugMode
        mov     bx,[bp+4]               ; restore minor error #
        mov     ax, PrivErr
        jmp     ErrHndlr
else
        popad
        push    ebx
        push    esi
        jmp     BadVmTrap
endif

Not_ProtIns1:
        jmp     BadVmTrap

EmProtIns endp

;******************************************************************************
;   EmMovCDTR - emulate the - MOV Rn, C/D/TRn & MOV C/D/TRn, Rn - instructions
;     This is done by copying the MOV instruction from the VM to our
;     code and then executing it.  If CR0 is being stored, the PE
;     bit is masked clear before completing the instruction.  Execution speed
;     should not be critical for these instructions, so I have lumped them
;     all together to save memory that can be better used for more time
;     critical emulations.
;     NOTE:  The Stack configuration is critical!!!  If it is changed, the
;     offsets to the register images must be updated accordingly.
;     (RRH)
;     Also NOTE: The TR register opcodes have been removed from the legal 0F
;     prefix list, (P0F_OpTab(x)), and the EmMovCDTR address removed from their
;     opcode table ,(OpTable), vectors, so that they don't come here anymore.
;
;   ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting opcode =  nn  (DS = VM2_GSEL)
;   EXIT:   via IRET to VM client
;           bp.VTFOE+VMTF_EIP points beyond offender to next client instruction
;   USED:
;   STACK:  n/a
;------------------------------------------------------------------------------
EmMovCDTR proc    near

        test    ES:[PrefixFlag],P0F_FLAG
        JZ      Not_MovCDTR             ; Didn't come from a 0F prefix

MovCDTR1:
        push    eax
        push    es
        mov     bx,VDMCA_GSEL           ; Load CODE (writeable) alias
        mov     es, bx
        assume  es:_TEXT
        mov     bl,[si]                 ; BL = opcode
        mov     es:[CDTRopc], bl
        inc     si                      ; set SI past the opcode
        mov     bl,[si]                 ; BL = modR/M
        mov     bh, bl                  ; Copy modR/M
        and     bl, 0F8h                ; For control, we reroute move thru AX
        mov     es:[CDTRmod], bl
        inc     si                      ; set SI past the modR/M
        mov     [bp.VTFOE+VMTF_EIP],si  ; VM CS:IP = DS:SI

        push    bp
        mov     bp, sp

;   Now the Stack had better look like MOV_CDTR_FRAME. (vm386.inc)

        mov     bl, es:[CDTRopc]        ; load opcode
        cmp     bl, 024h                ; Check TRn load/store
        jb      SHORT NotTRErr          ; N: continue
        mov     bx, ErrMovTR            ; Y: go flag error
        jmp     CTRErr

NotTRErr:
        and     bh, 7                   ; strip all but src reg bits
        cmp     bh, 0
        je      SHORT ExCDTR            ; reg is AX, so just do move

        cmp     bl, 020h                ; Check CRn store
        je      SHORT ExCDTR1           ; Y: go do it
        cmp     bl, 021h                ; Check DRn store
        je      SHORT ExCDTR1           ; Y: go do it

ChkSrcBX:
        cmp     bh, 3
        jne     SHORT ChkSrcBP
        mov     eax,dword ptr [bp.MCF_EBX]      ; mov EBX from stack
        jmp     SHORT ExCDTR
ChkSrcBP:
        cmp     bh, 5
        jne     SHORT ChkSrcSI
        mov     eax,dword ptr [bp.MCF_EBP]      ; mov EBP from stack
        jmp     SHORT ExCDTR
ChkSrcSI:
        cmp     bh, 6
        jne     SHORT ChkSrcSP
        mov     eax,dword ptr [bp.MCF_ESI]      ; mov ESI from stack
        jmp     SHORT ExCDTR
ChkSrcSP:
        cmp     bh, 4
        jne     SHORT GetFmReg
        mov     eax,dword ptr [bp.MCF_VMESP]    ; mov ESP from stack
        jmp     SHORT ExCDTR
GetFmReg:
        or      bh, 0C0h                ; set register to register move bits
        mov     es:[CDTRreg], bh        ; setup move from client's src register
        jmp     SHORT GetERn            ; clear prefetch so that bh gets there
GetERn:
;   Execute MOV EAX, ERn
        db      66h
        db      08Bh
CDTRreg db      0C0h

ExCDTR:
        cmp     bl, 022h                ; Check CRn load
        jne     SHORT FltrDRL3          ; N: destination not CRn
        cmp     byte ptr es:[CDTRmod], 0C0h ; Check for CR0 load
        je      SHORT CR0FltrL          ; Y: filter it
        mov     bx, ErrMovCR
        jmp     CTRErr                  ; N: go tell user he did a NoNo
FltrDRL3:
        cmp     byte ptr es:[CDTRmod], 0C3h ; Check for DR0-3 load
        ja      SHORT FltrGDbit         ; N: continue
        jmp     SHORT ExCDTR1           ;  linear map, then continue
FltrGDbit:
        cmp     byte ptr es:[CDTRmod], 0C7h ; Check for DR7 load
        jne     SHORT ExCDTR1           ; N: continue
        and     ax, 0DFFFh              ; Y: don't let client set the GD bit
        jmp     SHORT ExCDTR1           ; continue

;   For the reason below, we don't bug the user about setting the PE bit
;   through LMSW.  To be consistent, and because the client would die
;   in his attmept to LGDT or Mov CR3,Reg before this, we let him go here
;   also.
;   LMSW Reason:
;   At this point we could check for the PE bit and notify the user that
;   he must switch to real mode... but because an old app might do a SMSW,
;   which copies the PE bit (?!!), then set a bit in that image and do a LMSW,
;   (not caring about the PE bit), we just let it go... under the assumption
;   that if he really were trying to enter Protected mode, he would have
;   failed trying to LIDT and LGDT.

CR0FltrL:
        or      eax,80000001h           ; set PE bit
                                        ; and PG bit if they weren't
ExCDTR1:
;   Execute MOV CDTRn, EAX  (Finally!)
        db      0Fh
CDTRopc db      020h
CDTRmod db      0C0h

;   The special register move has now been executed, but we altered it to
;   use AX.  If the move was a load, we are done. If it was a store to one of
;   the registers on the stack, we need to stuff the register's stack image,
;   otherwise we need to move AX to the proper register.

        cmp     bl, 022h                ; Check CRn load
        je      SHORT Exit_MovCDTR      ; Y: we're done
        cmp     bl, 023h                ; Check DRn load
        je      SHORT Exit_MovCDTR      ; Y: we're done
FltrDRS3:
NotDRS3:
        cmp     bl, 020h                ; Check CRn store
        jne     short ChkDstBX          ; N: source not CRn
        cmp     byte ptr es:[CDTRmod], 0C0h ; Check for CR0 store
        je      short CR0FltrS          ; Y: filter it
        cmp     byte ptr es:[CDTRmod],0 ; Check for CR0 store           ;QLEO
        je      short CR0FltrS          ; Y: filter it                  ;QLEO
        jmp     short ChkDstBX          ; N: check if store was on EBX  ;QLEO
;QLEO   jmp     short Exit_MovCDTR      ; N: we're done                 ;QLEO
;QLEO   mov     bx, ErrMovCR
;QLEO   jmp     SHORT CTRErr            ; N: go tell user he did a NoNo

;   Because SMSW shows the PE bit, we let it go through here also
CR0FltrS:
        and     eax,7FFFFFFFh           ; Y: clear
                                        ; PG bit if it was set
ChkDstBX:
        cmp     bh, 3
        jne     SHORT ChkDstBP
        mov     dword ptr [bp.MCF_EBX],eax      ; mov EBX to stack
        jmp     SHORT Exit_MovCDTR
ChkDstBP:
        cmp     bh, 5
        jne     SHORT ChkDstSI
        mov     dword ptr [bp.MCF_EBP],eax      ; mov EBP to stack
        jmp     SHORT Exit_MovCDTR
ChkDstSI:
        cmp     bh, 6
        jne     SHORT ChkDstAX
        mov     dword ptr [bp.MCF_ESI],eax      ; mov ESI to stack
        jmp     SHORT Exit_MovCDTR
ChkDstAX:
        cmp     bh, 0
        jne     SHORT ChkDstSP
        mov     dword ptr [bp.MCF_EAX],eax      ; mov EAX to stack
        jmp     SHORT Exit_MovCDTR      ;   at exit
ChkDstSP:
        cmp     bh, 4
        jne     SHORT PutInReg
        mov     dword ptr [bp.MCF_VMESP],eax      ; mov ESP to stack
        jmp     SHORT Exit_MovCDTR      ;   at exit
PutInReg:
;   Execute MOV ERn, EAX
        or      bh, 0C0h                ; set register to register move bits
        mov     es:[regCDTR], bh        ; setup load to client's dest register
        jmp     SHORT PutERn            ; clear prefetch so that bh gets there
PutERn:
        db      66h
        db      089h
regCDTR db      0C0h


Exit_MovCDTR:
        pop     bp
        pop     es
        assume  es:_DATA
        pop     eax
        jmp     EmExit                  ; *** RETURN *** to VM client
CTRErr:
        pop     bp
        sub     [bp.VTFOE+VMTF_EIP],3   ; VM CS:IP = faulting instruction
        pop     es
        assume  es:_DATA

        mov     [bp+4],bx       ; save minor error # over error code
        pop     eax
        pop     esi
        pop     ebx
        pushad          ; -> entry condition for ErrHndlr

ifndef  BugMode
        mov     bx,[bp+4]               ; restore minor error #
        mov     ax, PrivErr
        jmp     ErrHndlr
else
        popad
        push    ebx
        push    esi
        jmp     BadVmTrap
endif

Not_MovCDTR:
        jmp     BadVmTrap

EmMovCDTR endp

;******************************************************************************
;***    EmRDMSR - Emulate Pentium RDMSR instruction (Read Model Specific Reg)
;

EmRDMSR proc    near

        test    ES:[PrefixFlag],P0F_FLAG
        JZ      Not_MovCDTR             ; Didn't come from a 0F prefix

        db      0Fh, 32h                ; RDMSR - ecx in, edx:eax out

     ;;;jmp     short EmIncIP           ; go inc VM CS:IP past RDMSR & exit

        if2
        .errnz  $ - EmIncIP             ; fall through to EmIncIP
        endif

EmRDMSR endp


;******************************************************************************
;***    EmLOCK - Emulate LOCK prefix
;
;       This routine is entered if a faulting instruction
;       is a LOCK prefix.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;
EmLOCK  proc    near

EmIncIP:
        inc     si                              ; set IP past the
        mov     [bp.VTFOE+VMTF_EIP], si         ; instruction we emulate
        jmp     EmExit                          ; and leave

EmLOCK  endp

;******************************************************************************
;***    EmREPNE - handle REPNE prefix
;
;       We come here if the trapping instruction has
;       a REPNE prefix. We just pass it on to the handler
;       for the next opcode.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
EmREPNE proc    near

        or      ES:[PrefixFlag],REPNE_FLAG      ; set appropriate flag
        inc     si                              ; inc VM CS:IP past prefix
        jmp     VmInsHandle                     ; handle next part of instr

EmREPNE endp

;***    EmREP - handle REP prefix
;
;       We come here if the trapping instruction has
;       a REP or REPE prefix. We just pass it on to the handler
;       for the next opcode.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
EmREP   proc    near

        or      ES:[PrefixFlag],REP_FLAG        ; set appropriate flag
        inc     si                              ; inc VM CS:IP past prefix
        jmp     VmInsHandle                     ; handle next part of instr

EmREP   endp

;******************************************************************************
;***    Prefix_0F - handle 0F overrides
;
;       This routine is entered if an instruction begins with a 0F prefix.
;         It checks the current opcode against a table of valid 0F-opcodes,
;         and if the opcode is valid, it indexes back into OpTable to jump
;         to the proper opcode handler.
;         (NOTE: This scheme works fine as long as the valid 0F-opcodes
;           continue to have low values allowing this P0F_OpTabx to stay
;           relatively small.  If this changes, we probably should go back
;           to using the P0F_OpTab scheme.)
;
;       ENTRY:  386 Protected Mode
;           EBP,EBX,ESI pushed on stack
;           VM1_GSEL = VM client's stack segment
;           VM2_GSEL = VM client's code segment
;           DS:SI -> faulting prefix =  nn  (DS = VM2_GSEL)
;       EXIT    xfer directly to handler for next opcode
;
;***
P0F_Invalid equ     0FFh    ; OpTable vector offset to BadVmTrap

P0F_OpTabx  label   byte
        db  00h             ; 00h - LLDT, LTR, SLDT, STR, VERR, VERW
        db  02h             ; 01h - LIDT, LGDT, LMSW
        db  04h             ; 02h - LAR
        db  06h             ; 03h - LSL
        db  P0F_Invalid     ; 04h - BadVmTrap
        db  0Ah             ; 05h - 286 Loadall
        db  0Ch             ; 06h - CLTS
        db  0Eh             ; 07h - 386 Loadall
        db  P0F_Invalid     ; 08h - BadVmTrap
        db  P0F_Invalid     ; 09h - BadVmTrap
        db  P0F_Invalid     ; 0Ah - BadVmTrap
        db  P0F_Invalid     ; 0Bh - BadVmTrap
        db  P0F_Invalid     ; 0Ch - BadVmTrap
        db  P0F_Invalid     ; 0Dh - BadVmTrap
        db  P0F_Invalid     ; 0Eh - BadVmTrap
        db  P0F_Invalid     ; 0Fh - BadVmTrap
        db  P0F_Invalid     ; 10h - BadVmTrap
        db  P0F_Invalid     ; 11h - BadVmTrap
        db  P0F_Invalid     ; 12h - BadVmTrap
        db  P0F_Invalid     ; 13h - BadVmTrap
        db  P0F_Invalid     ; 14h - BadVmTrap
        db  P0F_Invalid     ; 15h - BadVmTrap
        db  P0F_Invalid     ; 16h - BadVmTrap
        db  P0F_Invalid     ; 17h - BadVmTrap
        db  P0F_Invalid     ; 18h - BadVmTrap
        db  P0F_Invalid     ; 19h - BadVmTrap
        db  P0F_Invalid     ; 1Ah - BadVmTrap
        db  P0F_Invalid     ; 1Bh - BadVmTrap
        db  P0F_Invalid     ; 1Ch - BadVmTrap
        db  P0F_Invalid     ; 1Dh - BadVmTrap
        db  P0F_Invalid     ; 1Eh - BadVmTrap
        db  P0F_Invalid     ; 1Fh - BadVmTrap
        db  40h             ; 20h - CR moves
        db  42h             ; 21h - DR moves
        db  44h             ; 22h - CR moves
        db  46h             ; 23h - DR moves
        db  48h             ; 24h - TR moves
        db  P0F_Invalid     ; 25h - BadVmTrap
        db  4Ch             ; 26h - TR moves
        db  P0F_Invalid     ; 27h - BadVmTrap
        db  P0F_Invalid     ; 28h - BadVmTrap
        db  P0F_Invalid     ; 29h - BadVmTrap
        db  P0F_Invalid     ; 2Ah - BadVmTrap
        db  P0F_Invalid     ; 2Bh - BadVmTrap
        db  P0F_Invalid     ; 2Ch - BadVmTrap
        db  P0F_Invalid     ; 2Dh - BadVmTrap
        db  P0F_Invalid     ; 2Eh - BadVmTrap
        db  P0F_Invalid     ; 2Fh - BadVmTrap
        db  P0F_Invalid     ; 30h - BadVmTrap
        db  P0F_Invalid     ; 31h - BadVmTrap
        db  32h + 32h       ; 32h - RDMSR

P0F_OpTabx_Size equ $-P0F_OpTabx

;***
Prefix_0F       proc    near

        inc     si                          ; inc DS:SI past prefix
        mov     bl,[si]                     ; BL = opcode
        cmp     bl, P0F_OpTabx_Size
        jae     SHORT Bad_0F
        mov     bh,0                        ; BX = opcode
        mov     bl, cs:P0F_OpTabx[bx]
        cmp     bl, P0F_Invalid
        je      SHORT Bad_0F
        or      es:[PrefixFlag],P0F_FLAG    ; set appropriate flag
        jmp     cs:OpTable[bx]              ; enter instr emulation routine

Bad_0F:
        jmp     BadVmTrap

Prefix_0F       endp

;******************************************************************************
;***    CSOverride - handle CS overrides
;
;       This routine is entered if a faulting instruction
;       has a CS override.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;
CSOverride      proc    near
ifdef __BUGFIX2__
        and     ES:[PrefixFlag], OVR_FLAGS_OFF  ; Nullify prior prefixes
endif

        mov     bx, CS_FLAG

XXOverride:

        and     es:[PrefixFlag], NOT SEG_OVR_FLAGS ; clr any prior seg override
        or      ES:[PrefixFlag], bx             ; set appropriate flag
        inc     si                              ; inc VM CS:IP past prefix
        jmp     VmInsHandle                     ; handle next part of instr

CSOverride      endp

;******************************************************************************
;***    DSOverride - handle DS overrides
;
;       This routine is entered if a faulting instruction
;       has a DS override.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;
DSOverride      proc    near
ifdef __BUGFIX2__
        and     ES:[PrefixFlag], OVR_FLAGS_OFF  ; Nullify prior prefixes
endif
        mov     bx, DS_FLAG
        jmp     short XXOverride

DSOverride      endp

;******************************************************************************
;***    ESOverride - handle ES overrides
;
;       This routine is entered if a faulting instruction
;       has a ES override or a MOV TRn, Rn opcode
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;

ESOverride      proc    near

        test    ES:[PrefixFlag],P0F_FLAG        ; only for TR emulation
        jz      SHORT ESO1                      ; only for TR emulation
        jmp     EmMovCDTR                       ; only for TR emulation
ESO1:
ifdef __BUGFIX2__
        and     ES:[PrefixFlag], OVR_FLAGS_OFF  ; Nullify prior prefixes
endif
        mov     bx, ES_FLAG
        jmp     short XXOverride

ESOverride      endp

;******************************************************************************
;***    SSOverride - handle SS overrides
;
;       This routine is entered if a faulting instruction
;       has a SS override.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;

SSOverride      proc    near
ifdef __BUGFIX2__
        and     ES:[PrefixFlag], OVR_FLAGS_OFF  ; Nullify prior prefixes
endif
        mov     bx, SS_FLAG
        jmp     short XXOverride

SSOverride      endp

;******************************************************************************
;***    FSOverride - handle FS overrides
;
;       This routine is entered if a faulting instruction
;       has a FS override.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;

FSOverride      proc    near
ifdef __BUGFIX2__
        and     ES:[PrefixFlag], OVR_FLAGS_OFF  ; Nullify prior prefixes
endif
        mov     bx, FS_FLAG
        jmp     short XXOverride

FSOverride      endp

;******************************************************************************
;***    GSOverride - handle GS overrides
;
;       This routine is entered if a faulting instruction
;       has a GS override.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    xfer on to handler for next opcode
;

GSOverride      proc    near
ifdef __BUGFIX2__
        and     ES:[PrefixFlag], OVR_FLAGS_OFF  ; Nullify prior prefixes
endif
        mov     bx, GS_FLAG
        jmp     short XXOverride

GSOverride      endp

IFDEF VCB_FIX_OVR
OpOverride      proc    near
        or      ES:[PrefixFlag],OPER_SZ_FLAG    ; set appropriate flag
        inc     si                              ; inc VM CS:IP past prefix
        jmp     VmInsHandle                     ; handle next part of instr
OpOverride      endp

AdOverride      proc    near
        or      ES:[PrefixFlag],ADDR_SZ_FLAG    ; set appropriate flag
        inc     si                              ; inc VM CS:IP past prefix
        jmp     VmInsHandle                     ; handle next part of instr
AdOverride      endp
ENDIF   ; VCB_FIX_OVR

;******************************************************************************
;***    EmINSB - emulate IN byte string
;
;       This routine emulates the IN byte string instruction
;               *** this routine emulates REP instructions entirely     ***
;               *** within protected mode.  This effectively masks out  ***
;               *** interupts between bytes in the operation, even      ***
;               *** if the VM code had interrupts on.                   ***
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
;       USES    ES,BX,DI
;

EmINSB  proc    near
;
        inc     si                      ; set IP past the
        mov     [bp.VTFOE+VMTF_EIP], si ; instruction we emulate
;
;
;   Build a selector (VM1_GSEL) to client's ES segment.  VM1_GSEL is already set
;   up with limit (0FFFFh) and access (D_DATA0), so just fill in the base.
;
        mov     bx,GDTD_GSEL
        mov     ds,bx                   ; DS = GDT selector
        mov     bx,[bp.VTFOE+VMTF_ES]   ; BX = VM ES (segment form)
        shl     bx,4                    ; BX = low 16 bits of base
        mov     ds:[VM1_GSEL+2],bx      ; place in descriptor
        mov     bx,[bp.VTFOE+VMTF_ES]   ; BX = VM ES (again)
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM1_GSEL+4],bh      ; place in descriptor
        mov     bx,VM1_GSEL
        test    ES:[PrefixFlag],REP_FLAG        ;Q: REP prefix active ?
        mov     es,bx                           ; ES = VM ES
        jnz     SHORT EINSB_loop                ;  Y: go do rep instruction
        insb                                    ;  N: do single instruction
        jmp     SHORT EINSB_exit                        ;     and leave
EINSB_loop:
        cld                                     ; assume cld
        test    word ptr [bp.VTFOE+VMTF_EFLAGS],FLAGS_DF ;Q: client's DF bit is CLD?
        jz      SHORT EINSB_rep                 ;  Y: go ahead
        std                                     ;  N: set it
EINSB_rep:
        rep insb                                ;   rep version

EINSB_Exit:                     ;
        jmp     EmExit          ; *** RETURN *** to VM client
;
EmINSB  endp


;******************************************************************************
;***    EmINSW - emulate IN word string
;
;       This routine emulates the IN word string instruction
;               *** this routine emulates REP instructions entirely     ***
;               *** within protected mode.  This effectively masks out  ***
;               *** interupts between bytes in the operation, even      ***
;               *** if the VM code had interrupts on.                   ***
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
;       USES    ES,BX,DI
;

EmINSW  proc    near
;
        inc     si                      ; set IP past the
        mov     [bp.VTFOE+VMTF_EIP], si ; instruction we emulate
;
;
;
;   Build a selector (VM1_GSEL) to client's ES segment.  VM1_GSEL is already set
;   up with limit (0FFFFh) and access (D_DATA0), so just fill in the base.
;
        mov     bx,GDTD_GSEL
        mov     ds,bx                   ; DS = GDT selector
        mov     bx,[bp.VTFOE+VMTF_ES]   ; BX = VM ES (segment form)
        shl     bx,4                    ; BX = low 16 bits of base
        mov     ds:[VM1_GSEL+2],bx      ; place in descriptor
        mov     bx,[bp.VTFOE+VMTF_ES]   ; BX = VM ES (again)
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM1_GSEL+4],bh      ; place in descriptor
        mov     bx,VM1_GSEL

IFDEF VCB_FIX_OVR
        test    ES:[PrefixFlag],REP_FLAG        ;Q: REP prefix active ?
        jnz     SHORT EINSW_loop                ;  Y: go do rep instruction
        test    ES:[PrefixFlag],OPER_SZ_FLAG    ;Q: Operand-size prefix ?
        mov     es,bx                           ; ES = VM ES
        jz      short @f                        ;  N: skip
        db      66h                             ;  Y: issue instruction with prefix
@@:     insw                                    ;  N: do single instruction
        jmp     SHORT EINSW_exit                ;     and leave
EINSW_loop:
        cld                                     ; assume cld
        test    word ptr [bp.VTFOE+VMTF_EFLAGS],FLAGS_DF ;Q: client's DF bit is CLD?
        jz      SHORT EINSW_rep                 ;  Y: go ahead
        std                                     ;  N: set it
EINSW_rep:
        test    ES:[PrefixFlag],OPER_SZ_FLAG    ;Q: Operand-size prefix ?
        mov     es,bx                           ; ES = VM ES
        jz      short @f                        ;  N: skip
        db      66h                             ;  Y: issue instruction with prefix
@@:     rep insw                                ;   rep version
ELSE
        test    ES:[PrefixFlag],REP_FLAG        ;Q: REP prefix active ?
        mov     es,bx                           ; ES = VM ES
        jnz     SHORT EINSW_loop                ;  Y: go do rep instruction
        insw                                    ;  N: do single instruction
        jmp     SHORT EINSW_exit                ;     and leave
EINSW_loop:
        cld                                     ; assume cld
        mov     bx,FLAGS_DF
        test    bx,word ptr [bp.VTFOE+VMTF_EFLAGS] ;Q: client's DF bit is CLD?
        jz      SHORT EINSW_rep                 ;  Y: go ahead
        std                                     ;  N: set it
EINSW_rep:
        rep insw                                ;   rep version
ENDIF   ; VCB_FIX_OVR

EINSW_Exit:                     ;
        jmp     EmExit          ; *** RETURN *** to VM client
;
EmINSW  endp


;******************************************************************************
;***    EmOUTSB - emulate OUT byte string
;
;       This routine emulates the OUT byte string instruction
;               *** this routine emulates REP instructions entirely     ***
;               *** within protected mode.  This effectively masks out  ***
;               *** interupts between bytes in the operation, even      ***
;               *** if the VM code had interrupts on.                   ***
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
;       USES    DS,BX,SI
;

EmOUTSB proc    near
;
        inc     si                      ; set IP past the
        mov     [bp.VTFOE+VMTF_EIP], si ; instruction we emulate
;
;   restore ESI
;
        pop     esi
;
;   Build a selector (VM1_GSEL) to client's DS segment.  VM1_GSEL is already set
;   up with limit (0FFFFh) and access (D_DATA0), so just fill in the base.
;
        mov     bx,GDTD_GSEL
        mov     ds,bx                   ; DS = GDT selector
        mov     bx,[bp.VTFOE+VMTF_DS]   ; BX = VM DS (segment form)
        shl     bx,4                    ; BX = low 16 bits of base
        mov     ds:[VM1_GSEL+2],bx      ; place in descriptor
        mov     bx,[bp.VTFOE+VMTF_DS]   ; BX = VM DS (again)
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM1_GSEL+4],bh      ; place in descriptor
        mov     bx,VM1_GSEL
        mov     ds,bx                   ; DS = VM DS

        test    ES:[PrefixFlag],REP_FLAG ;Q: REP prefix active ?
        jnz     SHORT EOUTSB_loop               ;  Y: go do rep instruction
        outsb                                   ;  N: do single instruction
        jmp     SHORT EOUTSB_exit               ;     and leave
EOUTSB_loop:
        cld                                     ; assume cld
        test    word ptr [bp.VTFOE+VMTF_EFLAGS],FLAGS_DF ;Q: client's DF bit is CLD?
        jz      SHORT EOUTSB_rep                ;  Y: go ahead
        std                                     ;  N: set it
EOUTSB_rep:
        rep outsb                               ;   rep version

EOUTSB_Exit:
        jmp     EmExitESIUpdated                ; *** RETURN *** to VM client
;
EmOUTSB endp


;******************************************************************************
;***    EmOUTSW - emulate OUT word string
;
;       This routine emulates the OUT word string instruction
;               *** this routine emulates REP instructions entirely     ***
;               *** within protected mode.  This effectively masks out  ***
;               *** interupts between bytes in the operation, even      ***
;               *** if the VM code had interrupts on.                   ***
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
;       USES    DS,BX,SI
;

EmOUTSW proc    near
;
        inc     si                      ; set IP past the
        mov     [bp.VTFOE+VMTF_EIP], si ; instruction we emulate
;
;   restore ESI
;
        pop     esi
;
;
;   Build a selector (VM1_GSEL) to client's DS segment.  VM1_GSEL is already set
;   up with limit (0FFFFh) and access (D_DATA0), so just fill in the base.
;
        mov     bx,GDTD_GSEL
        mov     ds,bx                   ; DS = GDT selector
        mov     bx,[bp.VTFOE+VMTF_DS]   ; BX = VM DS (segment form)
        shl     bx,4                    ; BX = low 16 bits of base
        mov     ds:[VM1_GSEL+2],bx      ; place in descriptor
        mov     bx,[bp.VTFOE+VMTF_DS]   ; BX = VM DS (again)
        shr     bx,4                    ; BH = high 8 bits of base
        mov     ds:[VM1_GSEL+4],bh      ; place in descriptor
        mov     bx,VM1_GSEL
        mov     ds,bx                   ; DS = VM DS

IFDEF VCB_FIX_OVR
        test    ES:[PrefixFlag],REP_FLAG ;Q: REP prefix active ?
        jnz     SHORT EOUTSW_loop               ;  Y: go do rep instruction
        test    ES:[PrefixFlag],OPER_SZ_FLAG    ;Q: Operand-size prefix ?
        jz      short @f                        ;  N: skip
        db      66h                             ;  Y: issue instruction with prefix
@@:     outsw                                   ;  N: do single instruction
        jmp     SHORT EOUTSW_exit               ;     and leave
EOUTSW_loop:
        cld                                     ; assume cld
        test    word ptr [bp.VTFOE+VMTF_EFLAGS],FLAGS_DF ;Q: client's DF bit is CLD?
        jz      SHORT EOUTSW_rep                ;  Y: go ahead
        std                                     ;  N: set it
EOUTSW_rep:
        test    ES:[PrefixFlag],OPER_SZ_FLAG    ;Q: Operand-size prefix ?
        jz      short @f                        ;  N: skip
        db      66h                             ;  Y: issue instruction with prefix
@@:     rep outsw                               ;   rep version
ELSE
        test    ES:[PrefixFlag],REP_FLAG ;Q: REP prefix active ?
        jnz     SHORT EOUTSW_loop               ;  Y: go do rep instruction
        outsw                                   ;  N: do single instruction
        jmp     SHORT EOUTSW_exit               ;     and leave
EOUTSW_loop:
        cld                                     ; assume cld
        mov     bx,FLAGS_DF
        test    bx,word ptr [bp.VTFOE+VMTF_EFLAGS] ;Q: client's DF bit is CLD?
        jz      SHORT EOUTSW_rep                ;  Y: go ahead
        std                                     ;  N: set it
EOUTSW_rep:
        rep outsw                               ;   rep version
ENDIF   ; VCB_FIX_OVR

EOUTSW_Exit:
        jmp     EmExitESIUpdated                ; *** RETURN *** to VM client
;
EmOUTSW endp

;******************************************************************************
;***    EmMOVSW - emulate MOV word string
;
;       This routine emulates the MOV word string instruction
;       specifically to handle the RASH Rules (Jokes) emulation.
;       For now, the only reason for doing this is to allow a bug
;       in PC Week's benchmark tests to FAIL "properly" so that the test
;       works.  (Unbelievable!)
;
;       NOTE:   After testing, if the ROM properly emulates the RASH
;       requirements, this routine should be reduced to the Reflect6
;       function.
;
;       ENTRY   386 PROTECTED MODE
;               see common description at top
;
;       EXIT    IRET back to VM program
;
;       USES    DS,BX,SI
;
EmMOVSW proc    near
        mov     bx, sp
        cmp     di, 0FFFFh                  ; Q:Does either Di
        je      SHORT EmWRASH
        cmp     ss:[bx.GPF_ESI], 0FFFFh     ;   or SI = FFFF?
        je      SHORT EmWRASH               ;  Y:Assume RASH GPfault
Reflect6:
        mov     es:[RefNum], 06h
RefToROM:
        pop     esi                         ;  N: clean up the stack,
        pop     ebx
        pop     ebp
        add     sp,4                        ; throw away error code,
        push    ebp
        mov     bp,sp                       ; reset BP to stack frame
        push    es:[RefNum]                 ; push the trap number
        jmp     ReflectInterrupt            ; and, Reflect to ROM
EmWRASH:
        test    es:[PrefixFlag], REPS_FLAG  ; Q:Is this a REP of REPNE?
        jz      SHORT EMWCXok               ;  N: don't change CX
        inc     cx
EMWCXok:
        mov     ax, 2                       ; assume up counter
        test    [bp.VTFOE+VMTF_EFLAGS], FLAGS_DF
        jz      SHORT EMWUpdtSI
        not     ax
EMWUpdtSI:
        mov     si, ss:[bx.GPF_ESI]
        add     ss:[bx.GPF_ESI], ax         ; verify neg???
        cmp     si, 0FFFFh                  ; Q:Did SI cause the GP fault
        JE      SHORT EMW_Exit              ;  Y:We're done

EMWUpdtDI:
        test    es:[PrefixFlag], REPS_FLAG  ; Q:Is this a REP of REPNE?
        jz      SHORT EMWCXok2              ;  N: don't change CX
        inc     cx
EMWCXok2:
        add     di, ax                      ; verify neg???
EMW_Exit:
        jmp     EmExit          ; *** RETURN *** to VM client
;
EmMOVSW endp

_TEXT    ends

        end
