SCCSID equ "@(#)em86io.asm      6.4 91/11/20"

PAGE    ,132
TITLE   EM86IO - I/O Instruction 8086 Emulation
NAME    EM86IO
.386p

;***    EM86IO.ASM
;
;       Title:
;
;       8086 Emulation Support for I/O instructions
;
;       Description:
;
;       This module contains the I/O instruction emulation, VDD
;       I/O trap handler dispatcher and I/O simulation routines.
;
;       Modification History:
;
;     - Bx001   Bill Madden     PTR B727891     10/07/91
;       Changed mpivsf so that the table corresponds to the encoding of the
;       segment registers in the Intel instructions.
;     - Bx002   Bill Madden     PTR B728605     11/20/91
;       Added em86SimVPMStringIO macro to support port string operations in
;       VPM mode.
;     - Bx003   Bill Madden     DEF 25346       09/10/92
;       Added em86DefaultOutWordHook, em86DefaultInWordHook,
;       em86DefaultOtherIOHook, and em86IsPortHooked.  Fixed handling of
;       operand/address overrides for DPMI apps.
;     - Bx004   Bill Madden     DEF 57568       11/21/92
;       Use big bit of code seg to get default addr and op size for VPM apps.
;     -         bjn                             09/19/94
;       Change segment names for page tuning.
;
;
;
;       Design/Implementation Notes:
;
;       This is a call graph of the I/O emulation, VDD handler
;       dispatcher and I/O simulation routines:
;
;       em86opINB
;           VDD byte input handler (ioh_pbihByteInput)
;           em86CallHashPortHdlr
;               VDD byte input handler (ioh_pbihByteInput)
;
;       em86opINW
;           VDD word input handler (ioh_pwihWordInput)
;           em86SimWordInput
;               em86CallPortHdlr
;                   VDD byte input handler (ioh_pbihByteInput)
;           em86CallPortHdlr
;               VDD word input handler (ioh_pbihWordInput)
;               VDD other IO handler (ioh_pothOther) for dword input
;               em86SimWordInput
;                   em86CallPortHdlr
;                       VDD byte input handler (ioh_pbihByteInput)
;               em86SimOtherIO
;                   em86SimDwordInput
;                       em86CallPortHdlr
;                           VDD word input handler (ioh_pwihWordInput)
;                           em86SimWordInput
;                               em86CallPortHdlr
;                                   VDD byte input hdlr (ioh_pbihByteInput)
;           em86CallHashPortHdlr*
;
;       em86opINBimm
;           VDD byte input handler (ioh_pbihByteInput)
;
;       em86opINWimm
;           VDD word input handler (ioh_pwihWordInput)
;           em86SimWordInput
;               em86CallPortHdlr
;                   VDD byte input handler (ioh_pbihByteInput)
;           em86CallPortHdlr
;               VDD other IO handler (ioh_pothOther) for dword input
;               em86SimOtherIO
;                   em86SimDwordInput
;                       em86CallPortHdlr
;                           VDD word input handler (ioh_pwihWordInput)
;                           em86SimWordInput
;                               em86CallPortHdlr
;                                   VDD byte input hdlr (ioh_pbihByteInput)
;
;       em86opOUTB
;           VDD byte output handler (ioh_pbohByteOutput)
;           em86CallHashPortHdlr
;               VDD byte output handler (ioh_pbohByteOutput)
;
;       em86opOUTW
;           VDD word output handler (ioh_pwohWordOutput)
;           em86SimWordOutput
;               em86CallPortHdlr
;           em86CallPortHdlr
;               VDD word output handler (ioh_pwohWordOutput)
;               VDD other IO handler (ioh_pothOther) for dword output
;               em86SimWordOutput
;                   em86CallPortHdlr
;                       VDD byte output handler (ioh_pbohByteOutput)
;               em86SimOtherIO
;                   em86SimDwordOutput
;                       em86CallPortHdlr
;                           VDD word output handler (ioh_pwohWordOutput)
;                           em86SimWordOutput
;                               em86CallPortHdlr
;                                   VDD byte output hdlr (ioh_pbohByteOutput)
;           em86CallHashPortHdlr*
;
;       em86opOUTBimm
;           VDD byte output handler (ioh_pbohByteOutput)
;
;       em86opOUTWimm
;           VDD word output handler (ioh_pwohWordOutput)
;           em86SimWordOutput
;               em86CallPortHdlr
;                   VDD byte output handler (ioh_pbohByteOutput)
;           em86CallPortHdlr
;               VDD other IO handler (ioh_pothOther) for dword output
;               em86SimOtherIO
;                   em86SimDwordOutput
;                       em86CallPortHdlr
;                           VDD word output handler (ioh_pwohWordOutput)
;                           em86SimWordOutput
;                               em86CallPortHdlr
;                                   VDD byte output hdlr (ioh_pbohByteOutput)
;
;       em86opINSB
;           em86StringIO
;               em86CallPortHdlr*
;
;       em86opINSW
;           em86StringIO
;               em86CallPortHdlr*
;
;       em86opOUTSB
;           em86StringIO
;               em86CallPortHdlr*
;
;       em86opOUTSW
;           em86StringIO
;               em86CallPortHdlr*
;
;       em86CallPortHdlr
;           em86CallHashPortHdlr
;               VDD I/O handler
;               em86SimWordInput*
;               em86SimWordOutput*
;               em86SimOtherIO*
;           VDD I/O handler
;           em86SimWordInput
;               em86CallPortHdlr*
;           em86SimWordOutput
;               em86CallPortHdlr*
;           em86SimOtherIO
;               em86SimDwordInput
;                   em86CallPortHdlr*
;               em86SimDwordOutput
;                   em86CallPortHdlr*
;               em86SimStringIO
;                   em86SimByteStringInput
;                       em86CallPortHdlr
;                           VDD byte input hdlr (ioh_pbihByteInput)
;                           em86CallHashPortHdlr
;                               VDD byte input hdlr (ioh_pbihByteInput)
;                   em86SimByteStringOutput
;                       em86CallPortHdlr
;                           VDD byte output hdlr (ioh_pbohByteOutput)
;                           em86CallHashPortHdlr
;                               VDD byte output hdlr (ioh_pbohByteOutput)
;                   em86SimWordStringInput
;                       em86CallPortHdlr*
;                   em86SimWordStringOutput
;                       em86CallPortHdlr*
;                   em86SimDwordStringInput
;                       em86CallPortHdlr*
;                   em86SimDwordStringOutput
;                       em86CallPortHdlr*
;                   em86SimInvalidType
;                       em86CallPortHdlr*
;
;       Public routines:
;
;       em86opINB
;       em86opINW
;       em86opINBimm
;       em86opINWimm
;       em86opOUTB
;       em86opOUTW
;       em86opOUTBimm
;       em86opOUTWimm
;       em86opINSB
;       em86opINSW
;       em86opOUTSB
;       em86opOUTSW
;       em86DefaultIOHook
;       em86DefaultOutWordHook
;       em86DefaultInWordHook
;       em86DefaultOtherIOHook
;       em86ByteInput
;       em86ByteOutput
;       em86SimWordInput
;       em86SimWordOutput
;       em86SimOtherIO
;       em86CHookStubBIH
;       em86CHookStubBOH
;       em86CHookStubWIH
;       em86CHookStubWOH
;       em86CHookStubOTH
;       em86IsPortHooked
;

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

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

DefData IMPORT, SWAPINST, C
PIOH    apiop                                   ; in em86.c
PPHA    appha                                   ; in em86.c
;BD003 BOOL    fAdvHardwareSupport                     ; in em86.c
ULONG   flVdmStatus                             ; in em86vpm.c          ;BN002
EndData

DefData IMPORT, SWAPINST
ULONG   flPrefix                                ; in em86inst.asm
ULONG   pOffset                                 ; in em86inst.asm       ;BN002
EndData

DefData IMPORT, SWAP, C
PIOH    iopDefault                              ; in em86.c
EndData

DefCode IMPORT, SWAP, PASCAL
DefFn   em86GrowIOPM                            ; in em86.c
DefFn   em86SetIOHookState
DefFn   VDHReadUBuf                             ; em86vpmx.c            ;BN002
DefFn   VDHWriteUBuf                            ; em86vpmx.c            ;BN002
EndCode

DefCode EXPORT, SWAP, PASCAL
DefFn   em86DefaultIOInHook
DefFn   em86DefaultIOOutHook
DefFn   em86DefaultOutWordHook                                          ;BN003
DefFn   em86DefaultInWordHook                                           ;BN003
DefFn   em86DefaultOtherIOHook                                          ;BN003
DefFn   em86DefaultIgnoreIOHook
DefFn   em86ByteInput
DefFn   em86ByteOutput
DefFn   em86SimWordInput
DefFn   em86SimWordOutput
DefFn   em86SimOtherIO
DefFn   em86SimDwordIO
DefFn   em86CSimDwordIO
DefFn   em86CHookStubBIH
DefFn   em86CHookStubBOH
DefFn   em86CHookStubWIH
DefFn   em86CHookStubWOH
DefFn   em86CHookStubOTH
EndCode

DefData PRIVATE, SWAP

;***ED+ mpivsf - map index into VSF struc offset
;
;       Maps the segment number index into the offset into
;       the VSF structure for the segment register.
;

        public  mpivsf
mpivsf  label   byte
        .errnz  IO_SEG_ES SHR IO_SEG_SHIFT - (($ - mpivsf)/type mpivsf)
        db      vsf_es
        .errnz  IO_SEG_CS SHR IO_SEG_SHIFT - (($ - mpivsf)/type mpivsf)
        db      vsf_cs
        .errnz  IO_SEG_SS SHR IO_SEG_SHIFT - (($ - mpivsf)/type mpivsf)
        db      vsf_ss
        .errnz  IO_SEG_DS SHR IO_SEG_SHIFT - (($ - mpivsf)/type mpivsf)
        db      vsf_ds
        .errnz  IO_SEG_FS SHR IO_SEG_SHIFT - (($ - mpivsf)/type mpivsf)
        db      vsf_fs
        .errnz  IO_SEG_GS SHR IO_SEG_SHIFT - (($ - mpivsf)/type mpivsf)
        db      vsf_gs
;end

;***LD+ apfnSimIO - Simulate I/O type handler table
;
;       This table is index by the IO_TYPE_MASK field shifted
;       by IO_TYPE_SHIFT and contains the handlers for the "other"
;       simulation hook in the IOH.
;

apfnSimIO       label   dword
        dd      offset FLAT:em86SimInvalidType      ; 0000  word output
        dd      offset FLAT:em86SimInvalidType      ; 0001  word input
        dd      offset FLAT:em86SimWordStringOutput ; 0010  word string output
        dd      offset FLAT:em86SimWordStringInput  ; 0011  word string input
        dd      offset FLAT:em86SimInvalidType      ; 0100  byte output
        dd      offset FLAT:em86SimInvalidType      ; 0101  byte input
        dd      offset FLAT:em86SimByteStringOutput ; 0110  byte string output
        dd      offset FLAT:em86SimByteStringInput  ; 0111  byte string input
        dd      offset FLAT:em86SimDwordOutput      ; 1000  dword output
        dd      offset FLAT:em86SimDwordInput       ; 1001  dword input
        dd      offset FLAT:em86SimDwordStringOutput; 1010  dword string output
        dd      offset FLAT:em86SimDwordStringInput ; 1011  dword string input
        dd      offset FLAT:em86SimInvalidType      ; 1100
        dd      offset FLAT:em86SimInvalidType      ; 1101
        dd      offset FLAT:em86SimInvalidType      ; 1110
        dd      offset FLAT:em86SimInvalidType      ; 1111
;end


EndData

DefData PRIVATE, SWAPINST
ULONG   ulIOData                                                        ;BN002
EndData

        FARCODE HIGH32
DefCode PRIVATE, SWAP, PASCAL
SWAP32_23 segment
;***LP  em86opINB - Emulate input byte I/O instruction
;
;       This function emulates the input byte instruction.  It called
;       by the opcode decoder for the IN AL,DX instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    Client's AL changed to input value
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       zero high word of EDX
;       if port number is not hashed
;           get I/O hook entry (IOH) from I/O port table (apiop)
;           call byte input handler in IOH selected
;           set the client's AL value to the returned input value
;       else
;           set the I/O handler index to byte input handler
;           set I/O type flags to byte input
;           em86CallHashPortHdlr(I/O type/handler index, port number, crf)
;           set the client's AL value to the returned input value
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opINB,near
        inc     edi
        mov     [ebx].vsf_eip,edi               ; adjust EIP

        movzx   edx,dx                          ; zero high word of EDX
        cmp     edx,PORT_HASH_START             ; is port number hashed?
        jae     short eib10                     ;  yes, jump to port hashing

        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        call    [esi].iop_ioh.ioh_pbihByteInput ; call byte input handler
        em86AssertPVSF ebx
        mov     [ebx].vsf_al,al                 ; set client's AL
        ret

eib10:  mov     ecx,IO_HDLR_BYTE_INPUT + IO_TYPE_BYTE + IO_TYPE_INPUT
        call    em86CallHashPortHdlr
        em86AssertPVSF ebx
        mov     [ebx].vsf_al,al                 ; set client's AL
        ret
EndProc   em86opINB
SWAP32_23 ends

;***LP  em86opINW - Emulate input word/dword I/O instruction
;
;       This function emulates the input word or dword instruction.  It
;       called by the opcode decoder for the IN (E)AX,DX instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    Client's (E)AX changed to input value
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       zero high word of EDX
;       if (real mode and no operand override prefix set) or
;          (16 bit VPM mode and no operand override prefix set) or
;          (32 bit VPM mode and operand prefix set)
;           if port number is not hashed
;               get I/O hook entry (IOH) from I/O port table (apiop)
;               call word input handler in IOH selected
;               set the client's AX value to the returned input value
;           else
;               set the I/O handler index to word input handler
;               set I/O type flags to word input
;               em86CallHashPortHdlr(I/O type/handler index, port number, crf)
;               set the client's AX value to the returned input value
;       else
;           set the I/O handler index to other handler
;           set I/O type flags to dword input
;           em86CallPortHdlr(I/O type/handler index, port number, crf)
;           set the client's EAX value to the returned input value
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opINW,near
        inc     edi
        mov     [ebx].vsf_eip,edi               ; adjust EIP

        movzx   edx,dx                          ; zero high word of EDX
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; APP in DPMI mode?     ;BN003
        jz      short eiw5                              ; no, handle normally   ;BN003
        lar     ecx,dword ptr [ebx].vsf_cs      ; get access rights             ;BN004
        test    ecx,(D_COPER32 SHL 16)          ; check if 32 bit code seg      ;BN004
        jz      short eiw5                              ; no, handle normally   ;BN003
        testb   [flPrefix],PREFIX_OPER32        ; 16 bit operand prefix?        ;BN003
        jnz     short eiw7                      ;  yes, do word input           ;BN003
        jmp     eiw20                           ;  no, do dword input           ;BN003
eiw5:
        testb   [flPrefix],PREFIX_OPER32        ; 32 bit operand prefix?
        jnz     eiw20                           ;  yes, jump to dword input
eiw7:
        cmp     edx,PORT_HASH_START             ; is port number hashed?
        jae     short eiw10                     ;  yes, jump to port hashing

        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        call    [esi].iop_ioh.ioh_pwihWordInput ; call word input handler
        em86AssertPVSF ebx
        mov     [ebx].vsf_ax,ax                 ; set client's AX
        ret

eiw10:  mov     ecx,IO_HDLR_WORD_INPUT + IO_TYPE_WORD + IO_TYPE_INPUT
        call    em86CallHashPortHdlr
        em86AssertPVSF ebx
        mov     [ebx].vsf_ax,ax                 ; set client's AX
        ret

eiw20:  mov     ecx,IO_HDLR_OTHER + IO_TYPE_DWORD + IO_TYPE_INPUT
        call    em86CallPortHdlr
        em86AssertPVSF ebx
        mov     [ebx].vsf_eax,eax               ; set client's AX
        ret
EndProc   em86opINW

SWAP32_24 segment
;***LP  em86opINBimm - Emulate input byte immediate I/O instruction
;
;       This function emulates the input byte with immediate port
;       number instruction.  It called by the opcode decoder for
;       the IN AL,nn instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    Client's AL changed to input value
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The immediate port number is always going to be a
;               direct lookup as long as PORT_HASH_START >= 256.
;
;       PSEUDO-CODE
;
;       get port number from word at client's CS:IP
;       increment client's CS:IP past port number
;       get I/O hook entry (IOH) from I/O port table (apiop)
;       call byte input handler in IOH selected
;       set the client's AL value to the returned input value
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opINBimm,near
        .errnz  (PORT_HASH_START lt 256)
        movzx   edx,byte ptr fs:[esi+edi+1]        ; (EDX) = port number
        add     edi,2
        mov     [ebx].vsf_eip,edi               ; adjust EIP
        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        call    [esi].iop_ioh.ioh_pbihByteInput ; call byte input handler
        em86AssertPVSF ebx
        mov     [ebx].vsf_al,al                 ; set client's AL
        ret
EndProc   em86opINBimm
SWAP32_24 ends

;***LP  em86opINWimm - Emulate input word/dword immediate I/O instruction
;
;       This function emulates the input word/dword with immediate port
;       number instruction.  It called by the opcode decoder for
;       the IN (E)AX,nn instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    Client's (E)AX changed to input value
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The immediate port number is always going to be a
;               direct lookup as long as PORT_HASH_START >= 256.
;
;       PSEUDO-CODE
;
;       get port number from word at client's CS:IP
;       increment client's CS:IP past port number
;       if (real mode and no operand override prefix set) or
;          (16 bit VPM mode and no operand override prefix set) or
;          (32 bit VPM mode and operand prefix set)
;           get I/O hook entry (IOH) from I/O port table (apiop)
;           call word input handler in IOH selected
;           set the client's AX value to the returned input value
;       else
;           set the I/O handler index to other handler
;           set I/O type flags to dword input
;           em86CallPortHdlr(I/O type/handler index, port number, crf)
;           set the client's EAX value to the returned input value
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opINWimm,near
        .errnz  (PORT_HASH_START lt 256)
        movzx   edx,byte ptr fs:[esi+edi+1]        ; (EDX) = port number
        add     edi,2
        mov     [ebx].vsf_eip,edi               ; adjust EIP

        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; APP in DPMI mode?     ;BN003
        jz      short iwi5                              ; no, handle normally   ;BN003
        lar     ecx,dword ptr [ebx].vsf_cs      ; get access rights             ;BN004
        test    ecx,(D_COPER32 SHL 16)          ; check if 32 bit code seg      ;BN004
        jz      short iwi5                              ; no, handle normally   ;BN003
        testb   [flPrefix],PREFIX_OPER32        ; 16 bit operand prefix?        ;BN003
        jnz     short iwi7                      ;  yes, do word input           ;BN003
        jmp     short iwi10                     ;  no, do dword input           ;BN003
iwi5:
        testb   [flPrefix],PREFIX_OPER32        ; is this a dword input?
        jnz     short iwi10                     ;  yes, jump to dword input
iwi7:
        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        call    [esi].iop_ioh.ioh_pwihWordInput ; call word input handler
        em86AssertPVSF ebx
        mov     [ebx].vsf_ax,ax                 ; set client's AX
        ret

iwi10:  mov     ecx,IO_HDLR_OTHER + IO_TYPE_DWORD + IO_TYPE_INPUT
        call    em86CallPortHdlr
        em86AssertPVSF ebx
        mov     [ebx].vsf_ax,ax                 ; set client's AX
        ret
EndProc   em86opINWimm

SWAP32_23 segment
;***LP  em86opOUTB - Emulate output byte I/O instruction
;
;       This function emulates the output byte instruction.  It called
;       by the opcode decoder for the OUT DX,AL instruction.
;
;       ENTRY   EAX = client's EAX
;               EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       zero high word of EDX
;       if port number is not hashed
;           get I/O hook entry (IOH) from I/O port table (apiop)
;           jump to byte output handler in IOH selected
;       else
;           set the I/O handler index to byte output handler
;           set I/O type flags to byte output
;           em86CallHashPortHdlr(I/O type/handler index, port number, crf)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opOUTB,near
        inc     edi
        mov     [ebx].vsf_eip,edi               ; adjust EIP

        movzx   edx,dx                          ; zero high word of EDX
        cmp     edx,PORT_HASH_START             ; is port number hashed?
        jae     short eob10                     ;  yes, jump to port hashing

        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        jmp     [esi].iop_ioh.ioh_pbohByteOutput; jump to byte output handler

eob10:  mov     ecx,IO_HDLR_BYTE_OUTPUT + IO_TYPE_BYTE + IO_TYPE_OUTPUT
        jmp     em86CallHashPortHdlr
EndProc   em86opOUTB
SWAP32_23 ends

SWAP32_23 segment
;***LP  em86opOUTW - Emulate output word/dword I/O instruction
;
;       This function emulates the output word or dword instruction.  It
;       called by the opcode decoder for the OUT DX,(E)AX instruction.
;
;       ENTRY   EAX = client's EAX
;               EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       zero high word of EDX
;       if (real mode and no operand override prefix set) or
;          (16 bit VPM mode and no operand override prefix set) or
;          (32 bit VPM mode and operand prefix set)
;           if port number is not hashed
;               get I/O hook entry (IOH) from I/O port table (apiop)
;               jump to word output handler in IOH selected
;           else
;               set the I/O handler index to word output handler
;               set I/O type flags to word output
;               em86CallHashPortHdlr(I/O type/handler index, port num, crf)
;       else
;           set the I/O handler index to other handler
;           set I/O type flags to dword output
;           em86CallPortHdlr(I/O type/handler index, port number, crf)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opOUTW,near
        inc     edi
        mov     [ebx].vsf_eip,edi               ; adjust EIP

        movzx   edx,dx                          ; zero high word of EDX
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; APP in DPMI mode?     ;BN003
        jz      short eow5                              ; no, handle normally   ;BN003
        lar     ecx,dword ptr [ebx].vsf_cs      ; get access rights             ;BN004
        test    ecx,(D_COPER32 SHL 16)          ; check if 32 bit code seg      ;BN004
        jz      short eow5                              ; no, handle normally   ;BN003
        testb   [flPrefix],PREFIX_OPER32        ; 16 bit operand prefix?        ;BN003
        jnz     short eow7                      ;  yes, do word output          ;BN003
        jmp     short eow20                     ;  no, do dword output          ;BN003
eow5:
        testb   [flPrefix],PREFIX_OPER32        ; is this a dword output?
        jnz     short eow20                     ;  yes, jump to dword output
eow7:
        cmp     edx,PORT_HASH_START             ; is port number hashed?
        jae     short eow10                     ;  yes, jump to port hashing

        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        jmp     [esi].iop_ioh.ioh_pwohWordOutput; jump to word output handler

eow10:  mov     ecx,IO_HDLR_WORD_OUTPUT + IO_TYPE_WORD + IO_TYPE_OUTPUT
        jmp     em86CallHashPortHdlr

eow20:  mov     ecx,IO_HDLR_OTHER + IO_TYPE_DWORD + IO_TYPE_OUTPUT
        jmp     em86CallPortHdlr
EndProc   em86opOUTW
SWAP32_23 ends

SWAP32_24 segment
;***LP  em86opOUTBimm - Emulate output byte immediate I/O instruction
;
;       This function emulates the output byte with immediate port
;       number instruction.  It called by the opcode decoder for
;       the OUT nn,AL instruction.
;
;       ENTRY   EAX = client's EAX
;               EBX = pointer client register frame (CRF)
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The immediate port number is always going to be a
;               direct lookup as long as PORT_HASH_START >= 256.
;
;       PSEUDO-CODE
;
;       get port number from word at client's CS:IP
;       increment client's CS:IP past port number
;       get I/O hook entry (IOH) from I/O port table (apiop)
;       jump to byte output handler in IOH selected
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opOUTBimm,near
        .errnz  (PORT_HASH_START lt 256)
        movzx   edx,byte ptr fs:[esi+edi+1]        ; (EDX) = port number
        add     edi,2
        mov     [ebx].vsf_eip,edi               ; adjust EIP
        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        jmp     [esi].iop_ioh.ioh_pbohByteOutput; jump to byte output handler
EndProc   em86opOUTBimm
SWAP32_24 ends

;***LP  em86opOUTWimm - Emulate output word/dword immediate I/O instruction
;
;       This function emulates the output word/dword with immediate
;       port number instruction.  It called by the opcode decoder for
;       the OUT nn,(E)AX instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EAX = client's EAX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       NOTE    The immediate port number is always going to be a
;               direct lookup as long as PORT_HASH_START >= 256.
;
;       PSEUDO-CODE
;
;       get port number from word at client's CS:IP
;       increment client's CS:IP past port number
;       if (real mode and no operand override prefix set) or
;          (16 bit VPM mode and no operand override prefix set) or
;          (32 bit VPM mode and operand prefix set)
;           get I/O hook entry (IOH) from I/O port table (apiop)
;           jump to word output handler in IOH selected
;       else
;           set the I/O handler index to other handler
;           set I/O type flags to dword output
;           em86CallPortHdlr(I/O type/handler index, port number, crf)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opOUTWimm,near
        .errnz  (PORT_HASH_START lt 256)
        movzx   edx,byte ptr fs:[esi+edi+1]        ; (EDX) = port number
        add     edi,2
        mov     [ebx].vsf_eip,edi               ; adjust EIP
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; APP in DPMI mode?     ;BN003
        jz      short owi5                              ; no, handle normally   ;BN003
        lar     ecx,dword ptr [ebx].vsf_cs      ; get access rights             ;BN004
        test    ecx,(D_COPER32 SHL 16)          ; check if 32 bit code seg      ;BN004
        jz      short owi5                              ; no, handle normally   ;BN003
        testb   [flPrefix],PREFIX_OPER32        ; 16 bit operand prefix?        ;BN003
        jnz     short owi7                      ;  yes, do word output          ;BN003
        jmp     short owi10                     ;  no, do dword output          ;BN003
owi5:
        testb   [flPrefix],PREFIX_OPER32        ; is this a dword output?
        jnz     short owi10                     ;  yes, jump to dword output
owi7:
        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop
        em86AssertP IOP, esi
        jmp     [esi].iop_ioh.ioh_pwohWordOutput; jump to word output handler

owi10:  mov     ecx,IO_HDLR_OTHER + IO_TYPE_DWORD + IO_TYPE_OUTPUT
        jmp     em86CallPortHdlr
EndProc   em86opOUTWimm

;***LP  em86opINSB - Emulate input string byte I/O instruction
;
;       This function emulates the input string byte
;       instruction.  It called by the opcode decoder
;       for the INSB ES:(E)DI,DX instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       set the I/O handler index to other handler
;       set I/O type flags to byte string input
;       jump to em86StringIO
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opINSB,near
        mov     ecx,IO_HDLR_OTHER+IO_TYPE_BYTE+IO_TYPE_STRING+IO_TYPE_INPUT
        jmp     em86StringIO
EndProc   em86opINSB

;***LP  em86opINSW - Emulate input string word/dword I/O instruction
;
;       This function emulates the input string word or dword
;       instruction. It called by the opcode decoder for the
;       INSW ES:(E)DI,DX instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       set the I/O handler index to other handler
;       set I/O type flags to word/dword string input
;       jump to em86StringIO
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opINSW,near
        mov     ecx,IO_HDLR_OTHER+IO_TYPE_WORD+IO_TYPE_STRING+IO_TYPE_INPUT
        jmp     em86StringIO
EndProc   em86opINSW

;***LP  em86opOUTSB - Emulate output string byte I/O instruction
;
;       This function emulates the output string byte
;       instruction.  It called by the opcode decoder
;       for the OUTSB DX,ES:(E)DI instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       set the I/O handler index to other handler
;       set I/O type flags to byte string output
;       jump to em86StringIO
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opOUTSB,near
        mov     ecx,IO_HDLR_OTHER+IO_TYPE_BYTE+IO_TYPE_STRING+IO_TYPE_OUTPUT
        jmp     em86StringIO
EndProc   em86opOUTSB
;***LP  em86opOUTSW - Emulate output string word/dword I/O instruction
;
;       This function emulates the output string word or dword
;       instruction. It called by the opcode decoder for the
;       OUTSW DX,ES:(E)DI instruction.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               EDX = client's EDX
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;               [flPrefix] = instruction prefix flags
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       set the I/O handler index to other handler
;       set I/O type flags to word/dword string output
;       jump to em86StringIO
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86opOUTSW,near
        mov     ecx,IO_HDLR_OTHER+IO_TYPE_WORD+IO_TYPE_STRING+IO_TYPE_OUTPUT
        FallInto em86StringIO
EndProc   em86opOUTSW

;***LP  em86StringIO - emulate string I/O functions
;
;       Common emulation function for the string I/O functions.
;
;       ENTRY   EBX = pointer client register frame (CRF)
;               ECX = I/O type flags
;               EDX = port number
;               ESI+EDI = pointer to faulting opcode
;               Client's CS:IP points to faulting opcode
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       zero high word of EDX
;       or I/O type flag with prefix flags
;       or the client's direction flag into the I/O type flag
;       fall into em86CallPortHdlr
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86StringIO,near,local
        inc     edi
        mov     [ebx].vsf_eip,edi               ; adjust EIP

        movzx   edx,dx
        or      ecx,[flPrefix]
;BD001  and     ecx,(not PREFIX_SEG)
        btrx    ecx,PREFIX_SEG                  ; get and clear PREFIX bit  BN001
        jc      short SIO_10                    ; CY -> segment override    BN001
        or      ecx,(PREFIX_DS and NOT PREFIX_SEG)      ; default to DS     BN001
SIO_10:
        .errnz PREFIX_OPER32 - IO_TYPE_DWORD
        .errnz PREFIX_ADDR32 - IO_TYPE_ADDR32
        .errnz PREFIX_REP - IO_TYPE_REP
        .errnz (PREFIX_DS and (not PREFIX_SEG)) - IO_SEG_DS
        .errnz (PREFIX_CS and (not PREFIX_SEG)) - IO_SEG_CS
        .errnz (PREFIX_SS and (not PREFIX_SEG)) - IO_SEG_SS
        .errnz (PREFIX_ES and (not PREFIX_SEG)) - IO_SEG_ES
        .errnz (PREFIX_FS and (not PREFIX_SEG)) - IO_SEG_FS
        .errnz (PREFIX_GS and (not PREFIX_SEG)) - IO_SEG_GS

        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; APP in DPMI mode?
        jz      short SIO_40                            ; no, handle normally

        SaveReg <eax>                           ; save EAX (client's EAX)       ;BN004  begin
        lar     eax,dword ptr [ebx].vsf_cs      ; get access rights
        test    eax,(D_COPER32 SHL 16)          ; check if 32 bit code seg
        jz      short SIO_35                    ; no, handle normally

        ; for 32 bit segments DWORD and ADDR32 mean 16 bit operations
        mov     eax,ecx                         ; get I/O type flags
        not     eax                             ; invert flags
        and     ecx,not (IO_TYPE_DWORD OR IO_TYPE_ADDR32)       ; clear them
        test    ecx,IO_TYPE_BYTE                        ; byte size op?
        jnz     SIO_20                                  ; yes, invert addr32
        and     eax, (IO_TYPE_DWORD OR IO_TYPE_ADDR32)  ; no, invert addr & op
        jmp     short SIO_30
SIO_20:
        and     eax, IO_TYPE_ADDR32
SIO_30:
        or      ecx,eax                         ; update I/O type flags
SIO_35:
        RestoreReg <eax>                        ; restore EAX                   ;BN004  end

SIO_40:
        testb   [ebx].vsf_eflag,F_DIRECTION
        jz      TUNER3;  em86CallPortHdlr
        or      ecx,IO_TYPE_REVERSE
  ;;      FallInto em86CallPortHdlr      ; for tuning
TUNER3:   jmp     em86CallPortHdlr
EndProc   em86StringIO

SWAP32_24 segment
;***LP  em86CallPortHdlr - calls the VDD I/O port handler
;
;       This function is the common I/O port handler dispatcher.
;
;       ENTRY   EAX = non-string output data
;               EBX = pointer client register frame (CRF)
;               ECX = I/O handler index and I/O type flags
;               EDX = port number
;
;       EXIT    EAX = non-string input data
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       if port number is not hashed
;           get I/O hook entry (IOH) from I/O port table (apiop)
;           if other I/O type handler
;               call to I/O port handler in IOH
;               if simulate return indication
;                   jump to SimOtherIO
;           else
;               jump to I/O port handler in IOH
;       else
;           em86CallHashPortHdlr()
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86CallPortHdlr,near,local
        cmp     edx,PORT_HASH_START             ; is port number hashed?
        jae     TUNER2   ; em86CallHashPortHdlr      ;  yes, jump to port hashing
        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop

;       (ESI) = pointer to IOH entry
;       (ECX) = I/O handler index and I/O type flags

Entry   em86CallIOH,near,local
        em86AssertP IOP, esi
        mov     edi,ecx
        and     edi,IO_HDLR_MASK                ; (EDI) = ioh handler offset
        .errnz  IO_HDLR_SHIFT
        cmp     edi,IO_HDLR_OTHER
        je      short cph10
        jmp     dword ptr [esi].iop_ioh[edi]    ; jump to handler
TUNER2: jmp em86CallHashPortHdlr
;       Handle the simulate return indication for "other" I/O type handler

cph10:  push    ecx
        call    [esi].iop_ioh.ioh_pothOther     ; call handler
        pop     ecx
        jc      short cph20                     ; jump if simulate the I/O
        ret

cph20:  mov     eax,[ebx].vsf_eax               ; (EAX) = output data
        movzx   edx,[ebx].vsf_dx                ; (EDX) = port number
        jmp     em86SimOtherIO
EndProc   em86CallPortHdlr
SWAP32_24 ends

;***LP  em86CallHashPortHdlr - call the hashed VDD I/O port handlers
;
;       This function is the common port handler router for
;       port numbers above the non-hashed limit.  For these
;       ports hashing is required.
;
;       ENTRY   EAX = non-string output data
;               EBX = pointer client register frame (CRF)
;               ECX = I/O handler index and I/O type flags
;               EDX = port number above PORT_HASH_START
;
;       EXIT    EAX = non-string input data
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       get port hash table index by hashing port number (appha)
;       search the port hash entry (PHA) list for the port number
;       get IOH from the PHA entry
;       jump to I/O port handler in IOH selected by the I/O handler index
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CallHashPortHdlr,near,local
        mov     esi,edx
        and     esi,PORT_HASH_MASK
        mov     esi,appha[esi*type appha]       ; (ESI) = ppha
chp10:  or      esi,esi                         ; end of linked list?
        jnz     short chp20                     ;  no, found the pha
        mov     esi,offset FLAT:iopDefault      ;  yes, use the default IOH
TUNER1: jmp     em86CallIOH

chp20:  em86AssertP PHA, esi
        cmp     edx,[esi].pha_port              ; is this the port?
        je      TUNER1 ;  em86CallIOH                     ;  yes, jump to IOH handler
        .errnz  pha_iop                         ; (ESI) = IOH in PHA entry
        .errnz  iop_ioh
        mov     esi,[esi].pha_ppha              ; (ESI) = next entry in list
        jmp     short chp10
EndProc   em86CallHashPortHdlr

;***EP  em86DefaultOtherIOHook - no VDD handler I/O trap
;***EP  em86DefaultInWordHook - no VDD handler I/O trap
;***EP  em86DefaultOutWordHook - no VDD handler I/O trap
;***LP  em86DefaultIOInHook  - no VDD handler I/O trap
;***LP  em86DefaultIOOutHook - no VDD handler I/O trap
;
;       This routine is called when there is no VDD handler
;       installed for a port.  Each entry in the apioh table
;       initially points to a default ioh containing these
;       routines for the handlers.
;
;       ENTRY   EAX = non-string output data
;               EBX = pointer client register frame (CRF)
;               ECX = I/O handler index and I/O type flags
;               EDX = port number
;
;       EXIT    EAX = non-string input data
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       if advanced hardware support is enabled
;               perform the in/out instruction
;               grow the tss to accomodate the new port (em86GrowIOPM)
;               turn trapping off for the port (em86SetIOHookState)
;       else
;               return -1
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86DefaultOtherIOHook,near
        SaveReg <esi, edi>
        mov     esi,ecx
        and     esi,IO_TYPE_MASK                ; get IO type
        test    esi,IO_TYPE_DWORD               ; dword in or out?
        jz      short doh30                     ; no, call simulation routine

        SaveReg <ecx>                           ; save flags
        mov     ecx,4                           ; indicate word input
        call    em86IsPortHooked                ; are ports hooked?
        RestoreReg <ecx>                        ; restore flags
        jc      short doh30                     ; yes, simulate it

        cmp     esi,IO_TYPE_OUTPUT or IO_TYPE_DWORD     ; dword output?
        jne     short doh10                             ; no, mut be an in

        out     dx,eax                          ; perform actual output
        jmp     short doh20                     ; call default io hook
doh10:
        in      eax,dx                          ; perform actual input
doh20:
        mov     ecx,4                           ; indicate dword operation
        call    em86DefaultIOHook               ; unhook the ports
        jmp     short dohret
doh30:
        shr     esi,IO_TYPE_SHIFT
        call    apfnSimIO[esi*type apfnSimIO]   ; call emulation routine
dohret:
        RestoreReg <edi, esi>
        clc                                     ; clear carry, don't chain
        ret
EndProc   em86DefaultOtherIOHook

Procedure em86DefaultInWordHook,near
        mov     ecx,2                           ; indicate word input
        call    em86IsPortHooked                ; are ports hooked?
        jc      em86SimWordInput          ; yes, simulate it
        in      ax,dx                           ; perform actual input
        mov     ecx,2                           ; indicate word operation
        jmp     em86DefaultIOHook
EndProc   em86DefaultInWordHook



SWAP32_26 segment
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4
Procedure em86DefaultOutWordHook,near
        mov     ecx,2                           ; indicate word input
        call    em86IsPortHooked                ; are ports hooked?
        jc      TUNE  ; em86SimWordOutput               ; yes, simulate it
        out     dx,ax                           ; perform actual output
        mov     ecx,2                           ; indicate word operation
        jmp     em86DefaultIOHook
EndProc   em86DefaultOutWordHook

Procedure em86DefaultIOInHook,near
;BD003  cmp     fAdvHardwareSupport,FALSE       ; is hardware support enabled?
;BD003  je      dioh10                    ; yes
        call    em86ByteInput                   ; perform actual input
        mov     ecx,1                           ; indicate byte operation
        jmp     em86DefaultIOHook
TUNE:   jmp     em86SimWordOutput
EndProc   em86DefaultIOInHook


Procedure em86DefaultIOOutHook,near
;BD003  cmp     fAdvHardwareSupport,FALSE       ; is hardware support enabled?
;BD003  je      dioh10                    ; yes
        call    em86ByteOutput                  ; perform actual output
        mov     ecx,1                           ; indicate byte operation
        jmp     em86DefaultIOHook

;BD003 dioh10: sub     eax, eax                 ; hardware support disabled
;BD003 dec     eax                              ; return 0xffffffff
;BD003 ret
EndProc   em86DefaultIOOutHook


Procedure em86DefaultIOHook,near
        LocalVar dih_nPort,DWORD
        EnterProc
        push    eax
        mov     dih_nPort,edx                   ; save port number
        SaveReg <ecx>                           ; save size
;       em86GrowIOPM(hvdm,port)
        PCall   em86GrowIOPM,<0,edx>
        RestoreReg <ecx>                        ; restore size
        or      ax,ax
        jz      short dih10

        mov     edx,dih_nPort
;       em86SetIOHookState(hvdm,port,count,FALSE)
        PCall   em86SetIOHookState,<0,edx,ecx,FALSE>

        pop     eax
dih10:  LeaveProc
        ret
EndProc   em86DefaultIOHook
SWAP32_26 ends


;***LP  em86DefaultIgnoreIOHook - no VDD handler I/O trap
;
;       This routine is called when there is no VDD handler
;       installed for a port.  Each entry in the apioh table
;       initially points to a default ioh containing this
;       routine for all the handlers.
;
;       ENTRY   EAX = non-string output data
;               EBX = pointer client register frame (CRF)
;               ECX = I/O handler index and I/O type flags
;               EDX = port number
;
;       EXIT    EAX = non-string input data
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       pop up message about unsupported port access
;       terminate the VDM
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86DefaultIgnoreIOHook,near
        sub     eax,eax                         ; return 0xffffffff
        dec     eax
        ret
EndProc   em86DefaultIgnoreIOHook

SWAP32_23 segment
;***LP  em86ByteInput - do byte input instruction
;
;       This function is used in NULL pbihByteInput IOH entries.
;
;       ENTRY   EDX = port number
;
;       EXIT    AL = input data
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86ByteInput,near
        in      al,dx
        ret
EndProc   em86ByteInput
SWAP32_23 ends

SWAP32_23 segment
;***LP  em86ByteOutput - do byte output instruction
;
;       This function is used in NULL pbohByteOuput IOH entries.
;
;       ENTRY   AL = output data
;               EDX = port number
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86ByteOutput,near
        out     dx,al
        ret
EndProc   em86ByteOutput
SWAP32_23 ends

;***LP  em86SimWordInput - Simulate word input
;
;       This routine simulates word input with the byte input
;       function in the IOH.  The address of this function is
;       put in NULL pwihWordInput handler entries in IOHs.
;
;       ENTRY   EBX = pointer to client register frame (CRF)
;               EDX = port number
;
;       EXIT    AX = input data
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       em86CallPortHdlr(byte input, port number, crf)
;       save input byte
;       increment port number
;       em86CallPortHdlr(byte input, port number, crf)
;       combine input bytes
;       return word
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimWordInput,near
        SaveReg <esi, edi>
        mov     ecx,IO_HDLR_BYTE_INPUT + IO_TYPE_BYTE + IO_TYPE_INPUT
        SaveReg <edx>
        call    em86CallPortHdlr        ; (AL) = low byte
        RestoreReg <edx>
        inc     edx                     ; increment port number
        mov     ecx,IO_HDLR_BYTE_INPUT + IO_TYPE_BYTE + IO_TYPE_INPUT
        push    eax                     ; save low input byte
        call    em86CallPortHdlr        ; (AL) = high byte
        pop     ecx                     ; (CH) = low byte
        mov     ah,al
        mov     al,cl                   ; (AX) = high and low byte
        RestoreReg <edi, esi>
        ret
EndProc   em86SimWordInput

SWAP32_24 segment
;***LP  em86SimWordOutput - Simulate word output
;
;       This routine simulates word outputs with the byte output
;       function in the IOH.  The address of this function is
;       put in NULL pfnWordOutput handler entries in IOHs.
;
;       ENTRY   AX = output data
;               EBX = pointer to client register frame (CRF)
;               EDX = port number
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       save output word
;       em86CallPortHdlr(byte output, port number, crf)
;       restore output word
;       increment port number
;       em86CallPortHdlr(byte output, port number, crf)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimWordOutput,near
        SaveReg <esi, edi>
        mov     ecx,IO_HDLR_BYTE_OUTPUT + IO_TYPE_BYTE + IO_TYPE_OUTPUT
        SaveReg <eax, edx>              ; (AL) = low byte
        call    em86CallPortHdlr        ; output low byte
        RestoreReg <edx, eax>
        inc     edx                     ; increment port number
        mov     al,ah                   ; (AL) = high byte
        mov     ecx,IO_HDLR_BYTE_OUTPUT + IO_TYPE_BYTE + IO_TYPE_OUTPUT
        call    em86CallPortHdlr        ; output high byte
        RestoreReg <edi, esi>
        ret
EndProc   em86SimWordOutput
SWAP32_24 ends

;***LP  em86SimOtherIO - Simulate all other I/O operations
;
;       This routine simulates all the other I/O (dword, string, etc)
;       with the byte/word input/output functions in the IOH.  The address
;       of this function is put in NULL pfnOther handler entries in IOHs.
;
;       ENTRY   EAX = non-string output data
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type flags
;               EDX = port number
;
;       EXIT    EAX = non-string input data
;               Carry clear, I/O is done
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       dispatch thru apfnSimIO index by I/O type
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimOtherIO,near
        SaveReg <esi, edi>

        mov     esi,ecx
        and     esi,IO_TYPE_MASK
        shr     esi,IO_TYPE_SHIFT
        call    apfnSimIO[esi*type apfnSimIO]
        clc
        RestoreReg <edi, esi>
        ret
EndProc   em86SimOtherIO

;***LP  em86SimDwordInput - Simulate dword input
;
;       This routine simulates dword input with the word input
;       function in the IOH.
;
;       ENTRY   EBX = pointer to client register frame (CRF)
;               EDX = port number
;
;       EXIT    EAX = input data
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       em86CallPortHdlr(word input, port number, crf)
;       increment port number to next word port
;       save input data word
;       em86CallPortHdlr(word input, port number, crf)
;       combine input words
;       return dword
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimDwordInput,near,local
        mov     ecx,IO_HDLR_WORD_INPUT + IO_TYPE_WORD + IO_TYPE_INPUT
        SaveReg <edx>
        call    em86CallPortHdlr        ; (AX) = low word
        RestoreReg <edx>
        inc     edx                     ; increment port number
        inc     edx
        mov     ecx,IO_HDLR_WORD_INPUT + IO_TYPE_WORD + IO_TYPE_INPUT
        push    eax                     ; save low word
        call    em86CallPortHdlr        ; (AX) = high word
        pop     ecx                     ; (CX) = low word
        shl     eax,16                  ; (EAX) = shifted high word
        mov     ax,cx                   ; (EAX) = high and low word
        clc                             ; must clear the carry as
        ret                             ; em86callPortHdlr depends on it.
EndProc   em86SimDwordInput

;***LP  em86SimDwordOutput - Simulate dword output
;
;       This routine simulates dword output with the word output
;       function in the IOH.
;
;       ENTRY   EAX = output data
;               EBX = pointer to client register frame (CRF)
;               EDX = port number
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       save output dword
;       em86CallPortHdlr(word output, port number, crf)
;       restore output dword
;       increment port number to next word port
;       em86CallPortHdlr(word output, port number, crf)
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimDwordOutput,near,local
        mov     ecx,IO_HDLR_WORD_OUTPUT + IO_TYPE_WORD + IO_TYPE_OUTPUT
        SaveReg <eax, edx>              ; (AX) = low word
        call    em86CallPortHdlr        ; output low word
        RestoreReg <edx, eax>
        inc     edx                     ; increment port number
        inc     edx
        shr     eax,16                  ; (AX) = high word
        mov     ecx,IO_HDLR_WORD_OUTPUT + IO_TYPE_WORD + IO_TYPE_OUTPUT
        jmp     em86CallPortHdlr        ; output high word
EndProc   em86SimDwordOutput

;***LP  em86SimVPMStringIO - Simulate string I/O macro                  ;BN002
;
;       This routine simulates all the string I/O types for VPM applications
;       provided em86SimOtherIO using byte/word/dword input/output functions.
;
;       ENTRY   (iosize) = Byte, Word or Dword
;               (iotype) = Input or Output
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type flags
;               EDX = port number
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;

em86SimVPMStringIO macro   iosize,iotype
        local   svpm5,svpm10,svpm20,svpm30,svpm35,svpm37,svpm40,svpm50,svpmx,iohdlrequ,iotypeequ,iosizeequ,svpmerr
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimVPM&iosize&String&iotype,near,local

ifidni <iotype>,<Input>                 ; if input type

        mov     esi,[ebx].vsf_edi       ; get client's EDI
        test    ecx,IO_TYPE_ADDR32      ; 32 bit address?               ;BN003
        jnz     short svpm5             ; yes, use 32 bit offset        ;BN003

        movzx   esi,si
svpm5:
        mov     pOffset,esi             ; store client's EDI
        movzx   esi,[ebx].vsf_es        ; (SI) = client's ES

else
        mov     eax,[ebx].vsf_esi       ; get client's ESI
        mov     esi,ecx                 ; (ESI) = I/O type flags
        and     esi,IO_SEG_MASK
        shr     esi,IO_SEG_SHIFT        ; (ESI) = segment number index
        movzx   esi,mpivsf[esi]         ; (ESI) = VSF struc offset
        .errnz  type mpivsf - 1
        movzx   esi,word ptr [ebx+esi]  ; (SI) = client's segment register
        test    ecx,IO_TYPE_ADDR32      ; 32 bit address?               ;BN003
        jnz     short svpm10            ;  yes, use 32 bit offset       ;BN003

        movzx   eax,ax
svpm10:
        mov     pOffset,eax             ; store in pOffset

endif   ;if output

        mov     edi, FLAToffset ulIOData        ; (EDI) = offset of buffer
ifidni <iosize>,<Byte>                  ; get the size of the operation
        mov     eax, 1
elseifidni <iosize>,<Word>
        mov     eax, 2
elseifidni <iosize>,<Dword>
        mov     eax, 4
endif
        test    ecx,IO_TYPE_REVERSE     ; is direction flag clear?
        jz      short svpm20            ;  yes, don't reverse the count
        neg     eax                     ; (EAX) = negative increment count

svpm20: test    ecx,IO_TYPE_REP         ; was there a REP prefix?
        jz      short svpm30            ;  no, jump around client's CX check
        cmp     [ebx].vsf_cx,0          ; is client's CX zero?
        je      short svpmx             ;  yes, exit

;       Begining of the loop for REP string operations
;
;       (EAX) = index register increment
;       (EBX) = client stack frame pointer
;       (ECX) = I/O type flags
;       (EDX) = port number
;       (EDI) = buffer address

svpm30:
        SaveReg <eax>                   ; save index register increment

ifidni <iotype>,<Output>                ; if output type
        ; read in the data from the client's data area
        SaveReg <ecx, edx>
        PCall   VDHReadUBuf, <edi, eax, esi, FLAToffset pOffset, 0>
        RestoreReg <edx, ecx>
        or      eax,eax                 ; any errors
        jz      short svpmerr           ;  yes -> exit
 ifidni <iosize>,<Byte>
        mov     al,[edi]                ; (AL) = output byte
 elseifidni <iosize>,<Word>
        mov     ax,[edi]                ; (AX) = output word
 elseifidni <iosize>,<Dword>
        mov     eax,[edi]               ; (EAX) = output dword
 endif
endif

ifidni  <iotype>,<Input>
        iotypeequ equ IO_TYPE_INPUT
else
        iotypeequ equ IO_TYPE_OUTPUT
endif

ifidni <iosize>,<Byte>
        iosizeequ equ IO_TYPE_BYTE
 ifidni  <iotype>,<Input>
        iohdlrequ equ IO_HDLR_BYTE_INPUT
 else
        iohdlrequ equ IO_HDLR_BYTE_OUTPUT
 endif
elseifidni <iosize>,<Word>
        iosizeequ equ IO_TYPE_WORD
 ifidni  <iotype>,<Input>
        iohdlrequ equ IO_HDLR_WORD_INPUT
 else
        iohdlrequ equ IO_HDLR_WORD_OUTPUT
 endif
elseifidni <iosize>,<Dword>
        iosizeequ equ IO_TYPE_DWORD
        iohdlrequ equ IO_HDLR_OTHER
endif

        SaveReg <ecx, edx, esi, edi>
        mov     ecx,iohdlrequ + iotypeequ + iosizeequ
        call    em86CallPortHdlr        ; call VDD I/O handler
        RestoreReg <edi, esi>           ; don't restore edx and ecx till later

ifidni <iotype>,<Input>                 ; if input type
 ifidni <iosize>,<Byte>
        mov     [edi],al                ; (AL) = input byte
        PCall   VDHWriteUBuf, <edi, 1, esi, FLAToffset pOffset, \
                               VPM_FAULT_IF_SU_SET OR VPM_PROT_WRITE>
 elseifidni <iosize>,<Word>
        mov     [edi],ax                ; (AX) = input word
        PCall   VDHWriteUBuf, <edi, 2, esi, FLAToffset pOffset, \
                               VPM_FAULT_IF_SU_SET OR VPM_PROT_WRITE>
 elseifidni <iosize>,<Dword>
        mov     [edi],eax               ; (EAX) = input dword
        PCall   VDHWriteUBuf, <edi, 4, esi, FLAToffset pOffset, \
                               VPM_FAULT_IF_SU_SET OR VPM_PROT_WRITE>
 endif
        or      eax,eax                 ; any errors
        RestoreReg <edx,ecx>            ; restore original edx and ecx
        jz      short svpmerr           ;  yes -> exit
else
        RestoreReg <edx,ecx>            ; restore original edx and ecx
endif

        RestoreReg <eax>                ; (EAX) = index register increment
svpm35:
        test    ecx,IO_TYPE_ADDR32      ; is this a 32 bit address?     ;BN003
        jz      short svpm40            ;  no, use 16 bit offset        ;BN003
svpm37:

ifidni <iotype>,<Input>                 ; if input type
        add     [ebx].vsf_edi,eax       ; increment index extended register
        jmp     short svpm50
svpm40:
        add     [ebx].vsf_di,ax         ; increment index register
else
        add     [ebx].vsf_esi,eax       ; increment index extended register
        jmp     short svpm50
svpm40:
        add     [ebx].vsf_si,ax         ; increment index register
endif

svpm50: test    ecx,IO_TYPE_REP         ; was there a REP prefix?
        jz      short svpmx             ;  no, jump around client's CX dec
        dec     [ebx].vsf_cx            ; decrement client's CX
        jnz     short svpm30            ; jump to begining of loop
svpmx:
        ret

svpmerr:
        pop     eax
        ret

EndProc   em86SimVPM&iosize&String&iotype
        endm

;***LP  em86SimVPMByteStringInput - Simulate byte string input          ;BN002
;
;       Simulates byte string input with the byte input handler
;       in the IOH.
;
;       ENTRY   See em86SimVPMStringIO macro
;
;       EXIT    See em86SimVPMStringIO macro
;
;       USES    See em86SimVPMStringIO macro
;

em86SimVPMStringIO Byte, Input

;***LP  em86SimVPMByteStringOutput - Simulate byte string output        ;BN002
;
;       Simulates byte string output with the byte output handler
;       in the IOH.
;
;       ENTRY   See em86SimVPMStringIO macro
;
;       EXIT    See em86SimVPMStringIO macro
;
;       USES    See em86SimVPMStringIO macro
;

em86SimVPMStringIO Byte, Output

;***LP  em86SimVPMWordStringInput - Simulate word string input          ;BN002
;
;       Simulates word string input with the word input handler
;       in the IOH.
;
;       ENTRY   See em86SimVPMStringIO macro
;
;       EXIT    See em86SimVPMStringIO macro
;
;       USES    See em86SimVPMStringIO macro
;

em86SimVPMStringIO Word, Input

;***LP  em86SimVPMWordStringOutput - Simulate word string output        ;BN002
;
;       Simulates word string output with the word output handler
;       in the IOH.
;
;       ENTRY   See em86SimVPMStringIO macro
;
;       EXIT    See em86SimVPMStringIO macro
;
;       USES    See em86SimVPMStringIO macro
;

em86SimVPMStringIO Word, Output

;***LP  em86SimVPMDwordStringInput - Simulate dword string input        ;BN002
;
;       Simulates dword string input with the dword input handler
;       in the IOH.
;
;       ENTRY   See em86SimVPMStringIO macro
;
;       EXIT    See em86SimVPMStringIO macro
;
;       USES    See em86SimVPMStringIO macro
;

em86SimVPMStringIO Dword, Input

;***LP  em86SimVPMDwordStringOutput - Simulate dword string output      ;BN002
;
;       Simulates dword string input with the dword output handler
;       in the IOH.
;
;       ENTRY   See em86SimVPMStringIO macro
;
;       EXIT    See em86SimVPMStringIO macro
;
;       USES    See em86SimVPMStringIO macro
;

em86SimVPMStringIO Dword, Output

;***LP  em86SimStringIO - Simulate string I/O macro
;
;       This routine simulates all the string I/O types provided
;       em86SimOtherIO using byte/word/dword input/output functions.
;
;       ENTRY   (iosize) = Byte, Word or Dword
;               (iotype) = Input or Output
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type flags
;               EDX = port number
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       if (iotype) == Input
;            get client's ES:EDI
;       else
;            get appropriate client segment register
;            get client's ESI index
;       if no address override
;            zero high part of index
;       set index increment count based on I/O size
;       if reverse prefix set
;           make index increment negative
;       if rep I/O type
;           if client's CX is zero
;               stop loop
;       loop forever
;           if (iotype) == Output
;               if if (iosize) == Byte
;                    get byte at address
;               else if (iosize) == Dword
;                    get dword at address
;               else
;                    get word at address
;           set I/O handler index and I/O type flags
;           em86CallPortHdlr(I/O type/handler index, port number, crf)
;           if (iotype) == Input
;               if (iosize) == Byte
;                   put byte at address
;               else if (iosize) == Dword
;                   put dword at address
;               else
;                   put word at address
;           add index increment count to address
;           if (iotype) == Input
;               if address override
;                   add index increment count to client's EDI
;               else
;                   add index increment count to client's DI
;           else
;               if address override
;                   add index increment count to client's ESI
;               else
;                   add index increment count to client's SI
;           if rep I/O type
;               decrement client's CX
;               if client's CX is zero
;                   stop loop
;           else
;               stop loop
;

em86SimStringIO macro   iosize,iotype
        local   ssi1,ssi2,ssi3,ssi4,ssi5,ssix,iohdlrequ,iotypeequ,iosizeequ
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86Sim&iosize&String&iotype,near,local
        em86AssertPVSF ebx
        testb   [flVdmStatus],VDM_STATUS_VPM_EXEC       ; DPMI app?             ;BN002
        jnz     em86SimVPM&iosize&String&iotype         ; yes, jump to VPM hdlr ;BN002
ifidni <iotype>,<Input>                 ; if input type
        movzx   esi,[ebx].vsf_es        ; (ESI) = client's ES
        shl     esi,4                   ; (ESI) = client's linear ES
        mov     edi,[ebx].vsf_edi       ; get client's EDI
else
        mov     esi,ecx                 ; (ESI) = I/O type flags
        and     esi,IO_SEG_MASK
        shr     esi,IO_SEG_SHIFT        ; (ESI) = segment number index
        movzx   esi,mpivsf[esi]         ; (ESI) = VSF struc offset
        .errnz  type mpivsf - 1
        movzx   esi,word ptr [ebx+esi]  ; (ESI) = client's segment register
        shl     esi,4                   ; (ESI) = linear value of seg reg
        mov     edi,[ebx].vsf_esi       ; get client's ESI
endif
        test    ecx,IO_TYPE_ADDR32      ; is there an address override?
        jnz     short ssi1              ;  yes, jump around zero high word
        movzx   edi,di                  ; (EDI) = low word of index register
ssi1:

ifidni <iosize>,<Byte>
        mov     eax,1                   ; (EAX) = byte size increment count
elseifidni <iosize>,<Word>
        mov     eax,2                   ; (EAX) = word size increment count
elseifidni <iosize>,<Dword>
        mov     eax,4                   ; (EAX) = dword size increment count
endif
        test    ecx,IO_TYPE_REVERSE     ; is direction flag clear?
        jz      short ssi2              ;  yes, don't reverse the count
        neg     eax                     ; (EAX) = negative increment count

ssi2:   test    ecx,IO_TYPE_REP         ; was there a REP prefix?
        jz      short ssi3              ;  no, jump around client's CX check
        cmp     [ebx].vsf_cx,0          ; is client's CX zero?
        je      short ssix              ;  yes, exit

;       Begining of the loop for REP string operations
;
;       (EAX) = index register increment
;       (EBX) = client stack frame pointer
;       (ECX) = I/O type flags
;       (EDX) = port number
;       (ESI+EDI) = index register address

ssi3:   SaveReg <eax>                   ; save index register increment

ifidni <iotype>,<Output>                ; if output type
 ifidni <iosize>,<Byte>
        mov     al,[esi+edi]            ; (AL) = output byte
 elseifidni <iosize>,<Word>
        mov     ax,[esi+edi]            ; (AX) = output word
 elseifidni <iosize>,<Dword>
        mov     eax,[esi+edi]           ; (EAX) = output dword
 endif
endif

ifidni  <iotype>,<Input>
        iotypeequ equ IO_TYPE_INPUT
else
        iotypeequ equ IO_TYPE_OUTPUT
endif
ifidni <iosize>,<Byte>
        iosizeequ equ IO_TYPE_BYTE
 ifidni  <iotype>,<Input>
        iohdlrequ equ IO_HDLR_BYTE_INPUT
 else
        iohdlrequ equ IO_HDLR_BYTE_OUTPUT
 endif
elseifidni <iosize>,<Word>
        iosizeequ equ IO_TYPE_WORD
 ifidni  <iotype>,<Input>
        iohdlrequ equ IO_HDLR_WORD_INPUT
 else
        iohdlrequ equ IO_HDLR_WORD_OUTPUT
 endif
elseifidni <iosize>,<Dword>
        iosizeequ equ IO_TYPE_DWORD
        iohdlrequ equ IO_HDLR_OTHER
endif

        SaveReg <ecx, edx, esi, edi>
        mov     ecx,iohdlrequ + iotypeequ + iosizeequ
        call    em86CallPortHdlr        ; call VDD I/O handler
        RestoreReg <edi, esi, edx, ecx>

ifidni <iotype>,<Input>                 ; if input type
 ifidni <iosize>,<Byte>
        mov     [esi+edi],al            ; (AL) = input byte
 elseifidni <iosize>,<Word>
        mov     [esi+edi],ax            ; (AX) = input word
 elseifidni <iosize>,<Dword>
        mov     [esi+edi],eax           ; (EAX) = input dword
 endif
endif
        RestoreReg <eax>                ; (EAX) = index register increment
        test    ecx,IO_TYPE_ADDR32      ; is there an address override?
        jz      short ssi4              ;  no, jump to normal index register

ifidni <iotype>,<Input>                 ; if input type
        add     edi,eax                 ; (EDI) = incremented index pointer
        add     [ebx].vsf_edi,eax       ; increment index extended register
        jmp     short ssi5

ssi4:   add     di,ax                   ; (DI) = incremented index pointer
        add     [ebx].vsf_di,ax         ; increment index register
else
        add     edi,eax                 ; (EDI) = incremented index pointer
        add     [ebx].vsf_esi,eax       ; increment index extended register
        jmp     short ssi5

ssi4:   add     di,ax                   ; (DI) = incremented index pointer
        add     [ebx].vsf_si,ax         ; increment index register
endif

ssi5:   test    ecx,IO_TYPE_REP         ; was there a REP prefix?
        jz      short ssix              ;  no, jump around client's CX dec
        dec     [ebx].vsf_cx            ; decrement client's CX
        jnz     short ssi3              ; jump to begining of loop
ssix:   ret
EndProc   em86Sim&iosize&String&iotype
        endm

;***LP  em86SimByteStringInput - Simulate byte string input
;
;       Simulates byte string input with the byte input handler
;       in the IOH.
;
;       ENTRY   See em86SimStringIO macro
;
;       EXIT    See em86SimStringIO macro
;
;       USES    See em86SimStringIO macro
;

em86SimStringIO Byte, Input

;***LP  em86SimByteStringOutput - Simulate byte string output
;
;       Simulates byte string output with the byte output handler
;       in the IOH.
;
;       ENTRY   See em86SimStringIO macro
;
;       EXIT    See em86SimStringIO macro
;
;       USES    See em86SimStringIO macro
;

em86SimStringIO Byte, Output

;***LP  em86SimWordStringInput - Simulate word string input
;
;       Simulates word string input with the word input handler
;       in the IOH.
;
;       ENTRY   See em86SimStringIO macro
;
;       EXIT    See em86SimStringIO macro
;
;       USES    See em86SimStringIO macro
;

em86SimStringIO Word, Input

;***LP  em86SimWordStringOutput - Simulate word string output
;
;       Simulates word string output with the word output handler
;       in the IOH.
;
;       ENTRY   See em86SimStringIO macro
;
;       EXIT    See em86SimStringIO macro
;
;       USES    See em86SimStringIO macro
;

em86SimStringIO Word, Output

;***LP  em86SimDwordStringInput - Simulate dword string input
;
;       Simulates dword string input with the dword input handler
;       in the IOH.
;
;       ENTRY   See em86SimStringIO macro
;
;       EXIT    See em86SimStringIO macro
;
;       USES    See em86SimStringIO macro
;

em86SimStringIO Dword, Input

;***LP  em86SimDwordStringOutput - Simulate dword string output
;
;       Simulates dword string input with the dword output handler
;       in the IOH.
;
;       ENTRY   See em86SimStringIO macro
;
;       EXIT    See em86SimStringIO macro
;
;       USES    See em86SimStringIO macro
;

em86SimStringIO Dword, Output

;***LP  em86SimInvalidType - Invalid I/O type number
;
;       ENTRY   EAX = output data
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type
;               EDX = port number
;
;       EXIT    NONE
;
;       USES    NONE
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       em86InternalError <"em86SimInvalidType">
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86SimInvalidType,near,local
        em86InternalError <"em86SimInvalidType">
        ret
EndProc   em86SimInvalidType

;***LP  em86SimDwordIO - always simulate dword I/O for asm hooks
;
;       Called from em86CallPortHdlr for the last three ports of
;       the range a VDD installs.  For the last three ports, dword
;       I/O is always simulated.
;
;       ENTRY   EAX = output data
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    EAX = input data
;               if carry set,
;                       simulate the I/O operation
;               if carry clear,
;                       I/O is done
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86SimDwordIO,near
        test    ecx,IO_TYPE_DWORD               ; dword I/O?
        jnz     em86SimOtherIO                  ;  yes, simulate dword I/O
        jmp     [esi].iop_iohVDD.ioh_pothOther  ; call handler
EndProc   em86SimDwordIO

;***LP  em86CSimDwordIO - always simulate dword I/O for "C" hooks
;
;       Called from em86CallPortHdlr for the last three ports of
;       the range a VDD installs.  For the last three ports, dword
;       I/O is always simulated.
;
;       ENTRY   EAX = output data
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    EAX = input data
;               if carry set,
;                       simulate the I/O operation
;               if carry clear,
;                       I/O is done
;
;       USES    EAX, ECX, EDX, ESI, EDI, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CSimDwordIO,near
        test    ecx,IO_TYPE_DWORD               ; dword I/O?
        jnz     em86SimOtherIO                  ;  yes, simulate dword I/O
        jmp     em86CHookStubOTH                ; call "C" handler
EndProc   em86CSimDwordIO

SWAP32_24 segment
;***LP  em86CHookStubBIH - "C" interface byte input hook stub
;
;       Calls the "C" interface byte input hook routine in the
;       iop_iohVDD field of the IOP structure.
;
;       BYTE HOOKENTRY ByteInput(port, pcrf)
;
;       ENTRY   EBX = pointer to client register frame (CRF)
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    AL = input data
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CHookStubBIH,near
        em86AssertPVSF ebx
        em86AssertP IOP, esi
        PCall   [esi].iop_iohVDD.ioh_pbihByteInput, <edx, ebx>
        ret
EndProc   em86CHookStubBIH
SWAP32_24 ends

SWAP32_24 segment
;***LP  em86CHookStubBOH - "C" interface byte output hook stub
;
;       Calls the "C" interface byte output hook routine in the
;       iop_iohVDD field of the IOP structure.
;
;       VOID HOOKENTRY ByteOutput(bOutputData, port, pcrf)
;
;       ENTRY   AL = output data
;               EBX = pointer to client register frame (CRF)
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CHookStubBOH,near
        em86AssertPVSF ebx
        em86AssertP IOP, esi
        movzx   eax,al
        PCall   [esi].iop_iohVDD.ioh_pbohByteOutput, <eax, edx, ebx>
        ret
EndProc   em86CHookStubBOH
SWAP32_24 ends

;***LP  em86CHookStubWIH - "C" interface word input hook stub
;
;       Calls the "C" interface word input hook routine in the
;       iop_iohVDD field of the IOP structure.
;
;       WORD HOOKENTRY WordInput(port, pcrf)
;
;       ENTRY   EBX = pointer to client register frame (CRF)
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    AX = input data
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CHookStubWIH,near
        em86AssertPVSF ebx
        em86AssertP IOP, esi
        PCall   [esi].iop_iohVDD.ioh_pwihWordInput, <edx, ebx>
        ret
EndProc   em86CHookStubWIH

;***LP  em86CHookStubWOH - "C" interface word output hook stub
;
;       Calls the "C" interface word output hook routine in the
;       iop_iohVDD field of the IOP structure.
;
;       VOID HOOKENTRY WordOutput(usOutputData, port, pcrf)
;
;       ENTRY   AX = output data
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CHookStubWOH,near
        em86AssertPVSF ebx
        em86AssertP IOP, esi
        movzx   eax,ax
        PCall   [esi].iop_iohVDD.ioh_pwohWordOutput, <eax, edx, ebx>
        ret
EndProc   em86CHookStubWOH

;***LP  em86CHookStubOTH - "C" interface other I/O hook stub
;
;       Calls the "C" interface other I/O hook routine in the
;       iop_iohVDD field of the IOP structure.
;
;       BOOL HOOKENTRY Other(ulOutputData, pulInputData, port, flType, pcrf)
;
;       ENTRY   EAX = output data
;               EBX = pointer to client register frame (CRF)
;               ECX = I/O type
;               EDX = port number
;               ESI = PIOP
;
;       EXIT    EAX = input data
;               if carry set,
;                       simulate the I/O operation
;               if carry clear,
;                       I/O is done
;
;       USES    EAX, ECX, EDX, GS, FS, Flags
;
;       CONTEXT VDM Task
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING
        ALIGN   4

Procedure em86CHookStubOTH,near

LocalVar  chs_ulInputData,ULONG

        EnterProc
        SaveReg <edi>
        em86AssertPVSF ebx
        em86AssertP IOP, esi
        SSToDS  edi,[chs_ulInputData]
        PCall   [esi].iop_iohVDD.ioh_pothOther, <eax, edi, edx, ecx, ebx>
        cmp     eax,1                   ; sets carry if FALSE
        mov     eax,[chs_ulInputData]   ; (EAX) = input data
        RestoreReg <edi>
        LeaveProc
        ret
EndProc   em86CHookStubOTH

SWAP32_26 segment
;***LP  em86IsPortHooked - checks if a range of ports are hooked        ;BN003
;
;       This function checks if any ports in the specified range have
;       been hooked by any VDDs.
;
;       ENTRY
;               EBX = pointer client register frame (CRF)
;               ECX = number of ports
;               EDX = port number
;
;       EXIT
;               Carry Set:  a port in the range IS hooked
;               Carry Clear:  NO ports in the range are hooked
;
;       USES    ECX, Flags
;
;       CONTEXT VDM Task
;
;       PSEUDO-CODE
;
;       for each port in the range
;          if port number is not hashed
;             get I/O hook entry (IOH) from I/O port table (apiop)
;             if not default handler
;                return with carry set
;             else
;                check next port
;          else
;             get port hash table index by hashing port number (appha)
;             search the port hash entry (PHA) list for the port number
;             if not default handler
;                return with carry set
;             else
;                check next port
;       return with carry clear
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, GS:NOTHING, FS:NOTHING

Procedure em86IsPortHooked,near,local                                   ;BN003
        SaveReg <edi,esi,edx>
IPH10:  cmp     edx,PORT_HASH_START             ; is port number hashed?
        jae     short IPH30                     ;  yes, jump to port hashing
        mov     esi,apiop[edx*type apiop]       ; (ESI) = piop

;       (ESI) = pointer to IOH entry
;       (ECX) = I/O handler index and I/O type flags

        em86AssertP IOP, esi
IPH20:  cmp     esi,offset FLAT:iopDefault      ; is default handler?
        jne     short IPH70                     ;  no, indicate port is hooked
        jmp     short IPH60                     ;  yes, port is not hooked

IPH30:
        mov     esi,edx
        and     esi,PORT_HASH_MASK
        mov     esi,appha[esi*type appha]       ; (ESI) = ppha
IPH40:  or      esi,esi                         ; end of linked list?
        jnz     short IPH50                     ;  no, found the pha
        jmp     short IPH60                     ;  yes, port is not hooked

IPH50:  em86AssertP PHA, esi
        cmp     edx,[esi].pha_port              ; is this the port?
        je      short IPH20                     ;  yes, check if hooked
        mov     esi,[esi].pha_ppha              ; (ESI) = next entry in list
        jmp     short IPH40
IPH60:
        inc     edx
        loop    IPH10
        clc
        jmp     short IPHret
IPH70:
        stc
IPHret:
        RestoreReg<edx,esi,edi>
        ret
EndProc   em86IsPortHooked
SWAP32_26 ends

EndCode

        end
