;       SCCSID = src/vdev/vcom/vcommain.asm, vcom, r206, 6c.479 92/08/21

        Page    ,132
        NAME    VCOM
        TITLE   VCOM     - Virtual COM device driver
        SUBTTL  VCOMMAIN.ASM - Main Work routines for VCOM
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************

;;;;;;;;;;;;;;;;;;;;;;; START OF FILE SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;
;SOURCE FILE NAME: VCOMMAIN.ASM
;
;DESCRIPTIVE NAME: VCOM Main Work Routines
;
;FUNCTION:
;       This file contains procedures that are the main internal
;       working modules.
;
;SUBROUTINES:
;       Virt_ComIn
;       Virt_ComOut
;       Virt_Req_IIR
;       Virt_Clear_IIR
;       Virt_HWInt
;       Virt_Get_Byte
;       Set_VIIR
;       Get_VMSR
;       Get_PortId_Register
;       Check_PortOwner
;
;INTERNAL REFERENCES:
;       PDDProc_Handler
;       Set_LCR_Request
;       Set_MCR_Request
;       Set_DLLDLM_Request
;       Open_Request
;       Popup_Display
;
;EXTERNAL REFERENCES:
;       VDHSetVIRR
;       VDHClearVIRR
;       VDHQuerySysValue
;       VDHWaitEventSem
;
;;;;;;;;;;;;;;;;;;;;;;; END OF FILE SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;       Jin Kim
;       Yoshihiko Nishida
;       Allen C. Wynn
;       (c) International Business Machines Corporation
;       January 1989
;
;Modification History
;
;@59629  3/23/93  JWK  Add Rx buffer Flush Property
;@73472  8/29/93  JAG  Don't allow writes to IIR - 16450's don't have FIFO
; 80677  3/16/94  RDW  VDM not getting RI or TERI sigs from COM.SYS
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        .386p

.xlist
include STRUC.INC
include VCOM.INC
include mvdm.inc
include basemid.inc
.list

include VDDSEG.INC
include VCOMDATA.INC

DefCode     EXPORT,SWAP,PASCAL              ;Start CSWAP_TEXT segment
         DefFn  Virt_ComIn                  ;Export Definitions
         DefFn  Virt_ComOut
         DefFn  Virt_Get_Byte
         DefFn  Virt_Clear_IIR
         DefFn  Get_VMSR
         DefFn  Get_PortId_Register
         DefFn  Check_PortOwner
         Public  DLL_Handler
         Public  DLM_Handler
         Public  IIR_Handler
         Public  LCR_Handler
         Public  MCR_Handler
         Public  LSR_Handler
         Public  MSR_Handler
         Public  SCR_Handler
         Public  RBR_Handler
         Public  THR_Handler
         Public  IER_Handler
         Public  XXR_Handler
         Public  XLR_Handler
EndCode                                     ;End CSWAP_TEXT segment

DefCode     EXPORT,GLOBAL,PASCAL            ;Start _TEXT segment
         DefFn  Virt_HWInt
         DefFn  Virt_Req_IIR
         DefFn  Set_VIIR
         DefFn  Int14h_Timeout_Proc
EndCode                                     ;End _TEXT segment

DefCode     IMPORT,SWAP,PASCAL              ;Start SWAP_TEXT segment
        DefFn   PDDProc_Handler             ;Import definitions
        DefFn   Set_LCR_Request
        DefFn   Set_MCR_Request
        DefFn   Set_DLLDLM_Request
        DefFn   Open_Request
        DefFn   Popup_Display
EndCode                                     ;End CSWAP_TEXT segment

DefCode     IMPORT,GLOBAL,PASCAL            ;Start _TEXT segment
        DefFn   VDHSetVIRR
        DefFn   VDHClearVIRR
        DefFn   VDHWaitEventSem
        DefFn   VDHQuerySysValue
        DefFn   VDHWakeVIRRs
EndCode                                     ;End _TEXT segment

DefCode     PRIVATE,SWAP,PASCAL             ;Start CSWAP_TEXT segment
                                            ;Actual Code

;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Virt_ComIn
;
;DESCRIPTIVE NAME: "IN" instruction virtualization service routine
;
;INPUT: ECX = Reg_ID: UART register Id
;            0 = DLL, 1 = DLM, 2 = IIR, 3 = LCR, 4 = MCR, 5 = LSR
;            6 = MSR, 7 = SCR, 8 = RBR, *9 = THR, 10 = IER, 11 = XXR, 12 = XLR
;
;       EDX = Port ID (0 = COM1, 1 = COM2, 2 = COM3)
;
;       ESI -> Base address of Port Information Table (PI)
;
;OUTPUT: AL = Value of the register read
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;  Global Data:
;       IN_Jmp_Tab
;
;       Port Information (PI) table structure contains data definitions
;       for each port.
;       Variables in PI table:
;          VLSR, VRBR, VMSR, VLCR, VMCR, VIIR, VDLL, VDLM, VIER
;          flgStatus
;          flgRxOp
;
;FUNCTION:
;       This routine takes Reg_ID from VCOM_PortIn to emulate the
;       "In" operation of UART to the following registers:
;       LSR, RBR, MSR, LCR, MCR, SCR, IIR, DLL, DLM, and VIER.
;
;CALLED BY:
;       VCOM_PortIn
;       Int14h_Rx
;       Int14h_Wait
;
;INTERNAL REFERENCES:
;       Virt_Clear_IIR - clears the serviced interrupt
;       Virt_Get_Byte  - Get a byte/ Line status
;       Get_VMSR
;
;EXTERNAL REFERENCES:
;
;NOTE:
;       AH destroyed.
;********************** END OF SPECIFICATIONS *********************************

Virt_ComIn proc near

        .if <BIT iDOSProp and HW_COM>
           push dx
           mov  dx,iPortAddr
           mov  al,iData
           in   al,dx
           pop  dx
           ret

        .else
           pushfd
           cli
           .if     <BIT [esi].flgRxOp and RXINT_REQD> ;RxInt requested before?
               and     [esi].flgRxOp, NO_RXINT_REQD  ;Turn off the flag
               call    Virt_Get_Byte                 ;Yes, let's take care of it
           .endif
           popfd
        .endif

        jmp     In_Jmp_Tab[ecx*4]               ;jmp into register handler
                                                ;Scaling factor = 4 (dword)
RBR_Handler:
        and     [esi].VLSR, not DR              ;Reset VLSR.DR bit
        mov     al, [esi].VRBR                  ;Set AL to VRBR to return
        pushfd                                  ;save flags
        cli
        .if     <BIT [esi].flgRxOp and MORE_TO_COME> ;More data to come?
           call  Virt_Get_Byte                  ;Get the next byte in VRBR.
        .else                                   ;If no more data, then
            mov     ah, RXINT                   ;Clear RXINT request
            call    Virt_Clear_IIR
        .endif
        popfd
        ret                                     ;return

IIR_Handler:
        mov     al, [esi].VIIR
        .if     <[esi].VIIR eq TXINT>           ;TXINT is currently pending?
            mov    ah, TXINT                    ;
            call   Virt_Clear_IIR               ;Then clear it
        .endif
        ret

LSR_Handler:
        mov     al, [esi].VLSR                  ;Set the returning value.
        and     [esi].VLSR, not ERR_BITS        ;Reset error bits now.
        mov     ah, LSINT
        call    Virt_Clear_IIR                  ;Clear LSINT Request
        ret

MSR_Handler:
        call    Get_VMSR                        ;Get current VMSR into al
        ret

IER_Handler:
        mov     al, [esi].VIER
        ret

LCR_Handler:
        mov     al, [esi].VLCR
        ret

MCR_Handler:
        mov     al, [esi].VMCR
        ret

DLL_Handler:
        mov     al, [esi].VDLL
        ret

DLM_Handler:
        mov     al, [esi].VDLM
        ret

SCR_Handler:                                    ;To simulate the INS8250
        mov     al, [esi].VSCR
        ret

THR_Handler:                                    ;Never happens, but a label
        mov     al, 0ffh
        ret                                     ;is cheap, no code needed...

XXR_Handler:
        push    dx
        mov     dx, [esi].Port_Addr
        in      al,dx
        pop     dx
        ret

XLR_Handler:
        push    dx
        mov     dx, [esi].Port_Addr
        add     dx,LSR
        in      al,dx
        pop     dx
        ret

Virt_ComIn endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Virt_Get_Byte
;
;DESCRIPTIVE NAME: Get a byte from PCOM
;
;INPUT: ESI -> Base address of Port Information Table (PI)
;       ECX = Reg_ID
;       EDX = Port ID
;
;OUTPUT: VRBR set to the character
;        VLSR updated.
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;       NONE
;  Global Data:
;       Varible in PI table:
;          VRBR, VLSR, VMCR, VIER
;
;          flgStatus    - IRRSET ( VDHSetVIRR called ),
;                         NO_IRRSET ( VDHClearVIRR called )
;
;          flgRxOp      - MORE_TO_COME (More data to come)
;                         RXINT_REQD (RxInt has been notified)
;
;FUNCTION:
;       This routine gets a byte from PCOM, together with MORE_TO_COME,
;       and LS status indication (Note: only error bits are meaningful).
;       If LS status indicates an error, then LSint is requested.
;       Then, VLSR.DR bit is set, and RxInt is requested.
;
;CALLED BY:
;       Virt_ComIn
;
;INTERNAL REFERENCES:
;       Virt_Req_IIR    - requests to issue interrupt
;       PDDProc_Handler - call PCOM
;
;EXTERNAL REFERENCES:
;
;NOTE:
;       All registers saved
;********************** END OF SPECIFICATIONS *********************************

Virt_Get_Byte   proc    near

        push    eax                           ;Save EAX
        push    ecx                           ;Save Reg_ID

        mov     ax, GETBYTE                   ;dl = Port_ID already.
        pushfd                                ; PCOM returns with int's disabled
        call    PDDProc_Handler               ;Returns ah=char, al=LSR,
        mov     [esi].VRBR, ah                ; cl = more to come.

        .if     <BIT cl and MORE_TO_COME>     ;CL = 1 for MORE_TO_COME
            or    [esi].flgRxOp, MORE_TO_COME
        .else
            and   [esi].flgRxOp, NO_MORE_TO_COME
        .endif
        popfd                                   ; restore ints
        pop     ecx                             ;Restore Reg_ID

        and     al, ERR_BITS                    ;Get LSR error bits only
        or      [esi].VLSR, al                  ;Update VLSR error bits

        or      [esi].VLSR, DR                  ;Set VLSR.DR bit
        mov     ah, RXINT                       ;set up to request RXINT

        .if     <al ne 0> and                   ;Any error bits in LSR
        .if     <BIT [esi].VMCR and OUT2> and   ; and VMCR.OUT2 bit is on,
        .if     <BIT [esi].VIER and ELSI> and   ; and LSR ints enabled,
        .if     <cl ne LSR>                     ; and Reg_ID is not LSR,
        .then
            mov   ah, LSINT
        .endif

        call    Virt_Req_IIR                    ;Request RXINT

        pop     eax                             ;restore EAX
        ret
Virt_Get_Byte   endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Virt_ComOut
;
;DESCRIPTIVE NAME: "OUT" instruction virtualization service routine
;
;INPUT: ECX = Reg_ID: UART register Id
;            0 = DLL, 1 = DLM, 2 = IIR, 3 = LCR, 4 = MCR, 5 = LSR
;            6 = MSR, 7 = SCR, 8 = RBR, 9 = THR, 10 = IER, 11 = XXR, 12 = XLR
;
;       EDX = Port ID (0 = COM1, 1 = COM2, 2 = COM3)
;       AL = value to write
;       ESI -> Base address of Port Information Table (PI)
;
;OUTPUT: NONE
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;  Global Data:
;       Variables in PI Table -
;          VLCR, VMCR, VDLL, VDLM, VIER, VLSR
;
;FUNCTION:
;       This routine takes Reg_ID and Val from VCOM_PortOut to
;       emulate the "Out" operation of INS8250 to the following
;       registers: THR, LCR, MCR, DLL, DLM, IER.
;
;CALLED BY:
;       VCOM_PortOut
;       Int14h_Init
;       Int14h_xInit
;       Int14h_Tx
;       Int14h_Wait
;
;INTERNAL REFERENCES:
;       PDDProc_Handler - call PCOM
;       Virt_Req_IIR    - requests to issue interrupt
;       Virt_Clear_IIR  - clears the serviced interrupt
;       Set_LCR_Request
;       Set_MCR_Request
;       Set_DLLDLM_Request
;
;EXTERNAL REFERENCES:
;
;NOTE:
;       EAX, EBX, ECX, EDX destroyed
;********************** END OF SPECIFICATIONS *********************************

Virt_ComOut     proc    near
        .if <BIT iDOSProp and HW_COM>
           push dx
           mov  dx,iPortAddr
           out  dx,al
           pop  dx
           ret
        .endif

        .if     <cl eq THR>                     ;THR?
            and    [esi].VLSR, not (THRE+TEMT)  ;Reset THRE, TEMT bits
            mov    ah, TXINT
            call   Virt_Clear_IIR               ;Clear TXINT request
            mov    bl, al                       ;
            mov    ax, PUTBYTE                  ;Now bl = char, dl = Port_Id.
            pushfd                              ; save interrupt state
            call   PDDProc_Handler              ;Put a byte into PCOM's Queue
                                                ;(returns with int's disabled)
            .if    <BIT cl and MORE_ROOM> near  ;More room in PCOM's TxQueue?
                or    [esi].VLSR, (THRE+TEMT)   ;Set THRE, TEMT bits
                popfd                           ;restore interrupt state
                mov   ah, TXINT
                call  Virt_Req_IIR              ;Request TXINT
            .else
                popfd                           ;restore interrupt state
            .endif

        .elseif   <cl eq IER> near              ;IER?
            and   al, (ERXI+ETXI+ELSI+EMSI)     ;Clean unnecessary bits
            mov   ah, [esi].VIER                ;Set AH to old VIER
            mov   [esi].VIER, al                ;Set new VIER to AL
            mov   ecx, eax                      ;AH=old,AL=new. Save it to CX
            and   ah, ERXI                      ;Get Enable RxInt bit
            and   al, ERXI
            .if    <ah eq 0> and                ;ERXI was off in old VIER
            .if    <al ne 0>                    ; and is on now
                push    ecx                             ;@59629
                .if <BIT iDOSProp and COM_FLUSH_Rx>     ;@59629
                    .while <BIT [esi].VLSR and DR>      ;@59629
                        mov ecx,RBR                     ;@59629
                        call Virt_ComIn                 ;@59629
                    .endwhile                           ;@59629
                .elseif <BIT [esi].VLSR and DR>         ;@59629
                    mov    ah, RXINT                    ;@59629
                    call   Virt_Req_IIR                 ;@59629
                .endif                                  ;@59629
                pop    ecx                              ;@59629
            .else
                .if    <ah ne 0> and            ;ERXI was on in old VIER
                .if    <al eq 0>                ; and is off now
                    mov    ah, RXINT
                    call   Virt_Clear_IIR       ;Clear RXINT request
                .endif
            .endif

            mov   eax, ecx                      ;Set AH=old, AL=new.
            and   ah, ETXI                      ;Get Enable TxInt bit
            and   al, ETXI
            .if    <ah eq 0> and                ;ETXI was off in old VIER
            .if    <al ne 0> and                ; and is on now
            .if    <BIT [esi].VLSR and THRE>    ; and currently THRE is set
                mov    ah, TXINT
                call   Virt_Req_IIR             ;Virtualize TXINT. EBX destroyd
            .else
                .if    <ah ne 0> and            ;ETXI was on in old VIER
                .if    <al eq 0>                ; and is off now
                    mov    ah, TXINT
                    call   Virt_Clear_IIR       ;Clear TXINT request
                .endif
            .endif

            mov   eax, ecx                      ;Set AH=old, AL=new.
            and   ah, ELSI                      ;Get Enable LSInt bit
            and   al, ELSI
            .if    <ah ne 0> and                ;ELSI was on in old VIER
            .if    <al eq 0>                    ; and is off now
                mov    ah, LSINT
                call   Virt_Clear_IIR           ;Clear LSINT request
            .endif

            mov   eax, ecx                      ;Set AH=old, AL=new.
            and   ah, EMSI                      ;Get Enable MSInt bit
            and   al, EMSI
            .if    <ah ne 0> near and           ;EMSI was on in old VIER
            .if    <al eq 0> near               ; and is off now
                mov    ah, MSINT
                call   Virt_Clear_IIR           ;Clear MSINT request
            .endif

        .elseif   <cl eq LCR>                   ;LCR?
            push  eax
            mov   ah,[esi].VLCR                 ;Save old VLCR
            mov   [esi].VLCR, al                ;Set VLCR
            push  ax                            ; Save ax
            and   ax,DLAB_EXP                   ; Exclude DLAB bit
            .if    <al ne ah>                   ;If change in LCR
                call  Set_LCR_Request           ;Request PCOM to set LCR
            .endif
            pop   ax                            ; Restore ax
            pushfd
            cli
            .if <BIT ah and DLAB> and           ;if DLAB was on
            .if <BIT al nand DLAB> and          ;but is off now
            .if <BIT [esi].VMCR and OUT2>       ;and interrupts are enabled
                call    Set_VIIR                ;Set up VIIR register
                .if  <nc> and                   ;if an interrupt was pending
                .if <BIT [esi].flgStatus nand IRRSET>  ; but not reflected
                    or      [esi].flgStatus,IRRSET     ;reflect to VDM
                    push    ecx
                    push    edx
                    CallFn  VDHSetVIRR,<[esi].hVDM_PI, [esi].hIRQ_PI>
                    pop     edx
                    pop     ecx
                .endif
            .elseif <BIT al and DLAB> and       ;if DLAB was off
            .if <BIT ah nand DLAB>              ;but is on now
                 mov    al,[esi].VIIR_Req       ;save current state of VIIR_Req
                 mov    ah,ALLINT               ;clear all interrupts
                 call   Virt_Clear_IIR
                 mov    [esi].VIIR_Req,al       ;restore state of VIIR_Req so
            .endif                              ;we can reflect them after baud
            popfd                               ;change is complete
            pop   eax
        .elseif   <cl eq MCR>                   ;MCR?
            and   al, (DTR+RTS+OUT1+OUT2+LOOPBIT)   ;Clean unnecessary bits
            .if   <al ne [esi].VMCR>

                  mov   [esi].VMCR, al              ;Set VMCR
                  .if    <BIT [esi].VMCR nand OUT2> ;OUT2 bit is off?
                      mov    ah, ALLINT
                      call   Virt_Clear_IIR         ;Clear ALL interrupt requests
                  .endif
                  call   Set_MCR_Request            ;Request PCOM to set MCR
            .endif
        .elseif   <cl eq DLL>                   ;DLL?
            push  ax
            mov   ah,[esi].VDLL
            .if   <al ne ah>
            mov   [esi].VDLL, al                ;Set VDLL
            call  Set_DLLDLM_Request            ;Request PCOM to set DLL, DLM
            .endif
            pop   ax
        .elseif   <cl eq DLM>                   ;DLM?
            push  ax
            mov   ah,[esi].VDLM
            .if   <al ne ah>
            mov   [esi].VDLM, al                ;Set VDLM
            call  Set_DLLDLM_Request            ;Request PCOM to set DLL, DLM
            .endif
            pop   ax
        .elseif   <cl eq XXR>                   ;DLM?
            push  dx
            mov   dx,[esi].Port_Addr
            out   dx,al
            pop   dx
        .elseif   <cl eq SCR>                   ;SCR?
            mov   [esi].VSCR, al
        .endif                                  ;End of Reg_ID check
        ret
Virt_ComOut     endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Virt_Clear_IIR
;
;DESCRIPTIVE NAME: Clear the requested interrupt type from VIIR
;
;INPUT: AH = Int_ID: Interrupt type identifier
;               LSINT  = 6 (Receiver Line Status Interrupt)
;               RXINT  = 4 (Receive Data Ready)
;               TXINT  = 2 (Transmit Holding register empty)
;               MSINT  = 0 (Modem Status Interrupt)
;               ALLINT = 0FFh (Clear all interrupt requests)
;       ESI -> Base address of Port Information Table (PI)
;
;OUTPUT: NONE
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;
;  Global Data:
;       Variables in PI Table:
;          VMCR, VIIR, VIIR_Req
;          flgStatus
;
;FUNCTION:
;       This routine frees the requested interrupt entry in VIIR_Req table,
;       and update VIIR to the next highest interrupt request.
;       If all the entries of VIIR_Req are free, then this routine
;       disable the interrupt pending bit of VIIR and calls VDHClearVIRR
;       to clear the interrupt request to VPIC
;
;CALLED BY:
;       Virt_ComIn
;       Virt_ComOut
;       Get_VMSR
;
;INTERNAL REFERENCES:
;       Set_VIIR
;EXTERNAL REFERENCES:
;       VDHClearVIRR
;
;NOTE:
;       Registers saved
;********************** END OF SPECIFICATIONS *********************************

Virt_Clear_IIR  proc    near
        pushfd
        cli                                     ;Disable interrupts
        .if     <ah eq ALLINT>
            mov    [esi].VIIR_Req, 0            ;Clear all Int requests
            call   Set_VIIR                     ;Reset VIIR
            .if    <BIT [esi].flgStatus and IRRSET> ;IRQ is on now?
                and   [esi].flgStatus, NO_IRRSET    ;Reset the flag
                push    eax                     ;Save registers
                push    ecx                     ; that might be destroyed
                push    edx                     ;   by CallFn
                CallFn VDHClearVIRR, <[esi].hVDM_PI, [esi].hIRQ_PI>
                                                ;Turn it off
                pop     edx                     ;Restore registers
                pop     ecx
                pop     eax
            .endif
        .else

            .if   <ah eq LSINT>
                and [esi].VIIR_Req, not LSINT_POS ;Clear LSINT request bit
            .elseif  <ah eq RXINT>
                and [esi].VIIR_Req, not RXINT_POS
            .elseif  <ah eq TXINT>
                and [esi].VIIR_Req, not TXINT_POS
            .else
                and [esi].VIIR_Req, not MSINT_POS
            .endif

            .if   <ah eq [esi].VIIR>            ;Int_ID is pending in VIIR now?
                call   Set_VIIR                 ; then, service the next int
                .if  c  and                     ;No more requested interrpt,
                .if  <BIT [esi].flgStatus and IRRSET> ; and IRRSET ?
                    and   [esi].flgStatus, NO_IRRSET
                    push    eax                 ;Save registers
                    push    ecx                 ; that might be destroyed
                    push    edx                 ;   by CallFn
                    mov     ecx, CURRENT_VDM
                    CallFn VDHClearVIRR, <ecx, [esi].hIRQ_PI> ;VDH request
                    pop     edx                 ;Restore registers
                    pop     ecx
                    pop     eax
                .endif
            .endif
        .endif
        popfd                                   ;Restore state of flags
        ret
Virt_Clear_IIR  endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Get_VMSR
;
;DESCRIPTIVE NAME: Get VMSR Request
;
;INPUT:
;       ESI -> Base address of PI for this port
;
;OUTPUT:
;       AL = VMSR + correct delta bits
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;       None
;  Global Data:
;       Variables in Port_Info_Tab (PI table):
;          VMSR
;
;FUNCTION:
;       Get VMSR value in AL.
;       Set correct delta bits in AL.
;
;CALLED BY:
;       Int14h_Init
;       Int14h_Status
;       Int14h_xInit
;       Int14h_xCtl
;       Virt_ComIn
;
;INTERNAL REFERENCES:
;       Virt_Clear_IIR
;
;EXTERNAL REFERENCES:
;
;NOTE:
;       All registers saved except AL (returned)
;********************** END OF SPECIFICATIONS *********************************

Get_VMSR proc    near

       .if <BIT [esi].VMCR and LOOPBIT>
           push    dx
           mov     dx, [esi].Port_Addr
           add     dx,MSR
           in      al,dx
          .if <BIT [esi].VMCR and OUT2>
          .else
              and  al,NOT DCD
          .endif
           pop     dx
       .else
           push    ebx
           mov     bh,ah                    ;save ah value
           pushfd                           ;save state of flags
           cli                              ;shield from another interrupt
           mov     al, [esi].VMSR           ;AL = MSR value
           or      al, [esi].VMSR_Delta     ;put in delta bits
           mov     [esi].VMSR_Delta, 0      ;clear delta bits
           and     [esi].VMSR, NOT RI       ;RDW 80677
           mov     ah, MSINT
           call    Virt_Clear_IIR           ;Clear MSINT Request
           popfd                            ;finished, OK for other MSInts
                                            ; so restore state of flags
           mov     ah,bh                    ;restore ah value
           pop     ebx
       .endif
        ret
Get_VMSR endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Get_PortId_Register
;
;DESCRIPTIVE NAME: Determine the com port and UART register from Port addr.
;
;INPUT: EDX = Port_address (03F8 - 03FF, 02F8 - 02FF, 3220 - 3227 )
;       AH  = Caller_id: = VPORTIN if caller is VCOM_PortIn
;                        = VPORTOUT if caller is VCOM_PortOut
;                           This is used to determine between RBR or THR.
;
;OUTPUT:
;       EDX = COM port ID ( 0 = COM1, 1 = COM2, 2 = COM3 )
;       ECX = UART Register ID
;           0 = DLL   1 = DLM     2 = IIR    3 = LCR    4 = MCR    5 = LSR
;           6 = MSR   7 = SCR     8 = RBR    9 = THR    10 = IER
;       ESI -> Base address of PI for this port
;       Carry Bit is set if Failure
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;
;  Global Data:
;       Variables in PI (Port Information table):
;           VLCR
;           Port_Addr
;
;FUNCTION:
;       Convert the port address to port id.
;       Determine the register id (e.g. RBR or THR)
;
;CALLED BY:
;       VCOM_PortIn
;       VCOM_PortOut
;
;INTERNAL REFERENCES:
;       NONE
;
;EXTERNAL REFERENCES:
;
;NOTE:
;      The only registers that change are the registers returned.
;
;********************** END OF SPECIFICATIONS *********************************


Get_PortId_Register  proc    near

       push    ebx                     ;Save registers that are used but
                                       ; not returned
       mov     ecx,edx                 ;Place port address in ECX
       mov     ebx,edx                 ;Place port address in EBX


       .if <BIT iDOSProp and HW_COM>
           mov  iData,al
           mov  iPortAddr,dx
       .endif

       and     bx,BASE_ADDR_BITS       ;Strip off last 3 bits
                                       ;to get base port address

       lea     esi, PI

        .for edx = 0 to (MAXNUMPORT-1) by 1;for each slot in PI,
            .if <bx eq  [esi].Port_Addr>   ;if 40:0 address is same as in PI,
                .leave                     ;  the we found the correct port
            .endif                         ; (DX and ESI are set up correctly)
            add  esi,size Port_Info_Tab    ;otherwise, get next PI slot
        .next   edx                        ;and try again (DX is incremented)

        .if <bx ne [esi].Port_Addr>     ;If 40:0 address is not same as PI,
            jmp Bad_PortId              ; then we don't support the port
        .endif                          ; in 40:0 area or we fell out of the
                                        ; loop above (off end of PI)

       and     ecx,COM_REG_BITS        ;Strip off all but the last three
                                       ; bits of ECX
                                       ; This leave a number 0 - 7 which
                                       ; VCOM_PortIn and VCOM_PortOut will
                                       ; map to a UART Register ID.

       mov     bl,iDOSProp
       shr     bl,4

       mov     bh,dl
       inc     bh
       .if     <bl eq ALL_SEL> or
       .if     <bl eq bh>
           jmp SHORT Existing_PortId
       .endif
       jmp SHORT Non_Existing_PortId

Existing_PortId:
 ; /* SPECIAL CASES

       .if     <ecx eq DLL>              ;If ECX is DLL (=0)
           .if  <BIT [esi].VLCR nand DLAB> ; and VLCR.DLAB is not set,
               .if <ah eq VPORTIN>      ; and caller was VCOM_PortIn
                   .if <BIT [esi].VMCR and LOOPBIT>
                       mov      ecx,XXR
                   .else
                       mov      ecx,RBR ;Return register value RBR
                   .endif
               .else                    ; caller was VCOM_PortOut
                   .if <BIT [esi].VMCR and LOOPBIT>
                       mov      ecx,XXR
                   .else
                       mov      ecx,THR ;Return register value THR
                   .endif
               .endif
           .endif
       .elseif <ecx eq DLM>             ;If ECX is DLM (=1)
           .if  <BIT [esi].VLCR nand DLAB> ; and VLCR.DLAB is not set,
               mov     ecx,IER          ;Return register value THR
           .endif
       .endif

       .if  <ecx eq LSR>
            .if <BIT [esi].VMCR and LOOPBIT>
                mov      ecx,XLR
            .endif
       .endif

       pop     ebx                      ;Restore value of EBX
       clc                              ;Clear the carry biut
       ret                              ;and return

Non_Existing_PortId:
       mov     al,NO_PORT


Bad_PortId:                             ;if portid is out of range
        pop     ebx                     ;Restore value of EBX
        stc                             ;Set the carry bit
        ret                             ;and return

Get_PortId_Register    endp




;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Check_PortOwner
;
;DESCRIPTIVE NAME: Determination of the owner of this com port
;
;INPUT: EDX  = COM port ID ( 0 = COM1, 1 = COM2, 2 = COM3 )
;       ESI -> Base address of PI for this COM port
;
;OUTPUT: SUCCESS
;          Carry bit is clear
;        FAILURE
;          Carry bit is set when the user has chosen terminate or ignore
;          AH = TERMINATE or IGNORE
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;       ihVDM       - Current VDM handle
;       iflgIgnore  - ignore flags
;
;  Global Data:
;       Variables in PI (Port Information table):
;           hVDM_PI (owner of port)
;
;FUNCTION:
;       Validate the ownership of this com port
;       If this is the owner, the return
;       If the first access, call Open_Request
;       If a collision, call Popup_Display
;
;CALLED BY:
;       VCOM_PortIn
;       VCOM_PortOut
;       VCOM_Int14h
;
;INTERNAL REFERENCES:
;       Open_Request
;       Popup_Display
;
;EXTERNAL REFERENCES:
;       VDHWaitEventSem
;       VDHQuerySysValue
;
;NOTE:
;      EAX value is not changed if success.  EAX (AH) value is only changed
;      if a failure occurs.
;
;********************** END OF SPECIFICATIONS *********************************


Check_PortOwner    proc    near


       push        ecx
       mov         ecx,[esi].hVDM_PI   ;; PERFORMANCE FIX
      .if     <ecx eq ihVDM>           ;If I am the owner, then
          pop      ecx                 ; restore reg
          mov      ah,RETURNGOOD       ; set success indicator
          clc                          ; clear the carry
          ret                          ; quick return
      .endif

       push        edi                 ;Save values of registers that I use
       push        ebx
       push        eax

        mov     eax,VDHGSV_SECONDS1970
        mov     ecx,CURRENT_VDM
        CallFn VDHQuerySysValue,<ecx, eax>;Get time now
        mov     ebx,eax                     ;put time in EBX

       .repeat                         ;Keep looping until success or
                                       ;User chooses Terminate or Ignore
           bt     iflgIgnore,dx        ;Test if we are in ignore mode
           .if     < c >               ;If we are in ignore mode (carry set)
               mov     ah,VDHP_IGNORE  ;    Set AH to IGNORE
               mov     al,-1           ;    pretend we don't have the port(-1)
               .leave near             ;    leave loop
           .endif

           mov     ecx,[esi].hVDM_PI   ;Get owner of the port

           .if     <ecx eq ihVDM>      ;If I am the owner, then
               mov     ah,RETURNGOOD   ;  set success indicator
               .leave  near            ;  leave loop

           .elseif <ecx eq PORT_NOT_OWNED>;If the port is not owned, then
               call    Open_Request       ;  try to open it
               .if  <ah eq RETURNGOOD>    ;  (Open_Request returns
                    .leave near           ;  ah = RETURNGOOD or error )
              .endif                      ;  leave loop
           .endif

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   If we are still in the REPEAT-UNTIL loop, then a collision has occured,
;   either because PCOM detected it (When we called Open_Request), or
;   because we fell thru the IF-ELSIF statement above.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


            mov     eax,VDHGSV_SECONDS1970
            mov     ecx,CURRENT_VDM
            CallFn VDHQuerySysValue,<ecx, eax>;Get time now

            .if     <eax ge ebx>             ;If time now is greater than
                                             ;30 sec past last popup
                                             ;(or it's the first time thru)
                mov     ax,MSG_VCOM_DEVICE_IN_USE
                                             ;Put error message number in AX
                call    Popup_Display        ;(Port_ID is in DL)
                .if <ah eq VDHP_IGNORE>      ;If the user response is IGNORE
                    mov     cl,dl            ;
                    mov     bx,01h           ;     then set ignore flag
                    shl     bx,cl            ;     Move to correct bit
                    or      iflgIgnore,bx    ;     Set ignore flag
                    .leave                   ;   leave loop
                .elseif <ah eq VDHP_TERMINATE_SESSION>
                                             ;Else if user chose terminate
                    .leave                   ;   leave loop
                .else                        ;else user chose retry
                    mov     eax,VDHGSV_SECONDS1970
                    mov     ecx,CURRENT_VDM
                    CallFn VDHQuerySysValue,<ecx, eax>;Get time now
                    mov     ebx,eax          ;Set time here, to account
                    add     ebx,30d          ;for delay in reading message,
                .endif                       ;consulting manuals, etc.
           .endif                            ;(30 sec after RETRY is selected,
                                             ;not when popup is put up)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   We are at this point, either because the user chose RETRY on this
;   pass thru the loop, or because he chose retry in a previous loop,
;   and it is not time to put up another popup.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

            push    eax                     ;Save registers that may
            push    ecx                     ;change.
            push    ebx
            push    edx                     ;

            mov     ebx,ihComSem
            mov     eax,DELAY_5SEC
            CallFn  VDHWaitEventSem,<ebx,eax> ;Wait 5 seconds (no one clears it
                                              ; until VDM Termination)
            pop     edx                       ;Restore registers
            pop     ebx
            pop     ecx                     ;
            pop     eax                     ;

       .until                              ;Keep looping until success or
                                           ;User chooses Terminate or Ignore

       .if     <ah eq RETURNGOOD>          ;If we were the port owner or we
                                           ;successfully opened the port
           pop         eax
           pop         ebx
           pop         edi
           pop         ecx                 ;   Restore registers that I used
           clc                             ;   clear the carry
           ret                             ;   Return

       .else                               ;Else User chose Terminate or ignore
           pop         ecx                 ;  Get rid of EAX value on stack
                                           ;  I'm returning a value in EAX
           pop         ebx
           pop         edi
           pop         ecx                 ;  Restore registers that I used
           mov         al,-1               ;  pretend we don't have the port(-1)
           stc                             ;   Set carry
           ret                             ;   Return
       .endif

Check_PortOwner    endp

EndCode                                 ;end CSWAP_TEXT segment

;============================================================================
;============================================================================
;     Interrupt time code follows.  It cannot be swapped.
;============================================================================

DefCode PRIVATE,GLOBAL,PASCAL           ;Start _TEXT segment


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Virt_HWInt
;
;DESCRIPTIVE NAME: Hardware Interrupt virtualization service routine
;
;INPUT: AH = Int_ID: Interrupt type. There are four flavors of UART interrupts.
;               Among them, Receiver Line Status interrupt information
;               is associated with each character received.  So, only the
;               rest of the three interrupts will be passed from PCOM/VDMM.
;               RxInt = 4 (Receive Data Ready)
;               TxInt = 2 (Transmit Holding register empty)
;               MSInt = 0 (Modem Status Interrupt)
;       ESI -> Base address of Port Information Table (PI)
;       BL  =  contents of MSR excepts for delta bits, if MSINT.
;              LSR error bits (or 0) if RXINT.
;
;OUTPUT:
;       flgRxOp set at RxInt request
;       flgStatus set when VDHSetVIRR is called
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;       Variables in PI table:
;          VLSR, VMSR, VIIR, VMSR_Delta
;          flgRxOp
;  Global Data:
;       NONE
;
;FUNCTION:
;       This routine handles the interrupt notifications passed from PCOM
;       by calling Virt_Req_IIR routine.
;
;
;CALLED BY:
;       VCOM_PCOMNotify
;
;INTERNAL REFERENCES:
;       Virt_Req_IIR   - requests to issue interrupt
;
;EXTERNAL REFERENCES:
;
;NOTE:
;       EAX, EBX, EDX destroyed
;********************** END OF SPECIFICATIONS *********************************

Virt_HWInt      proc    near

        pushfd                                  ;save flags
        cli                                     ;Disable interrupts

        .if     <ah eq RXINT>                   ;RXINT?
        .then
            .if    <[esi].VIIR eq RXINT> or     ;RXINT is pending in VIIR, or
            .if    <BIT [esi].flgRxOp and RXINT_REQD> ;RXINT requested before
            .then
                or   [esi].flgRxOp, MORE_TO_COME ;Set MORE_TO_COME flag
            .else
                or   [esi].flgRxOp, RXINT_REQD  ;Set RXINT_REQD flag
            .endif

            and     bl,ERR_BITS                 ;Check for error bits
            .if     <bl ne 0>                   ;if there were error bits,
                mov     ah, LSINT               ;Then generate LSINT instead
            .endif

        .elseif <ah eq TXINT>                   ;TXINT?
        .then
            or     [esi].VLSR, (THRE+TEMT)      ;Set THRE, TEMT bits
        .elseif <ah eq MSINT>                   ;MSINT?
        .then

            .if <BIT bl and TERI>               ;RDW 80677  If TERI Set from
                or  bl, RI                      ;RDW 80677  intrpt the Set
            .endif                              ;RDW 80677  RI

                                                ;BL = new MSR value
            and     bl, not DELTA_BITS          ;Get new non-delta bits
            mov     bh, [esi].VMSR              ;BH = old MSR value
            mov     [esi].VMSR, bl              ;Update MSR value
            xor     bh,bl                       ;figure delta bits
                                                ;Now test for TERI delta bit
;RDW 80677  .if <BIT bh and RI> and             ;If RI was on last time
;RDW 80677  .if <BIT bl nand RI>                ;but is off now, (trailing edge)
;RDW 80677      or      bh,RI                   ; Set RI (TERI) delta bit
;RDW 80677  .else                               ;otherwise
;RDW 80677      and     bh, NOT RI              ; Reset RI (TERI) delta bit
;RDW 80677  .endif

            .if <BIT bl and RI>                 ;RDW 80677  RI High indicates
                or  bh, RI                      ;RDW 80677  that TERI is high
            .endif                              ;RDW 80677  now. So Set TERI on

            shr   bh, 4                         ;Move to delta bit position
            or      [esi].VMSR_Delta, bh        ;Update with new delta bits
                                                ; (preserve old delta bits)
        .endif                                  ;End checking Int_ID

        call    Virt_Req_IIR                    ;call Virt_Req_IIR AH=Int ID

        push  ax
        mov   al,[esi].VMSR
        mov   ah,[esi].VLSR

        .if <al ne [esi].XMSR> or
        .if <ah ne [esi].XLSR>
            mov [esi].XMSR,al
            mov [esi].XLSR,ah

            test   [esi].flgStatus, SLEEPING
            .if <nz>
                CallFn  VDHWakeVIRRs, <[esi].hVDM_PI>
            .endif

        .endif
        pop   ax

        popfd                                   ;restore state of flags

        ret
Virt_HWInt      endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Virt_Req_IIR
;
;DESCRIPTIVE NAME: Request the VIIR register to identify the interrupt type
;
;INPUT: AH = Int_ID: Interrupt type. There are four flavors of UART interrupts.
;               LSINT  = 6 (Receiver Line Status Interrupt)
;               RXINT  = 4 (Receive Data Ready)
;               TXINT  = 2 (Transmit Holding register empty)
;               MSINT  = 0 (Modem Status Interrupt)
;       ESI -> Base address of Port Information Table (PI)
;
;OUTPUT: NONE
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;
;  Global Data:
;       Variables in PI table:
;          VMCR, VIER, VIIR, VIIR_Req, flgStatus
;          For the structure of VIIR_Req, see Set_VIIR sublogue
;
;FUNCTION:
;       This routine only simulates an interrupt when VMCR.OUT2 is on.
;       By managing the VIIR_Req table, this routine simulates INS8250
;       interrupt handling logic of prioritizing interrupt requests
;       in a limited fashion in order not to allow the starvation
;       of interrupt types that have lower priorities.
;       If there were no pending interrupt request prior to this
;       interrupt, VDHSetVIRR is called to VPIC to request the simulation.
;
;CALLED BY:
;       Virt_ComOut
;       Virt_HWInt
;       Virt_Get_Byte
;
;INTERNAL REFERENCES:
;       Set_VIIR
;EXTERNAL REFERENCES:
;       VDHSetVIRR
;
;NOTE:
;
;********************** END OF SPECIFICATIONS *********************************

Virt_Req_IIR    proc    near

        .if     <BIT [esi].VMCR and OUT2> near  ;OUT2 is on
            push    eax                         ;Save EAX
            mov   al, [esi].VIER                ; al = VIER
            .if   <ah eq LSINT>                 ;If LSINT
                and    al, ELSI                 ; get ELSI bit
                .if    nz                       ; if set, then
                    or  [esi].VIIR_Req, LSINT_POS ; mark LSINT as requested
                    or  [esi].VIIR_Req, RXINT_POS ; mark RXINT as requested
                .else                               ;LSINT not in IIR, but
                    mov   al, [esi].VIER            ;must check RXINT.
                    and    al, ERXI                 ; get ERXI bit
                    .if    nz                       ; if set, then
                       or [esi].VIIR_Req, RXINT_POS ; mark RXINT as requested
                    .endif
                .endif
            .elseif   <ah eq RXINT>             ;If RXINT
                and    al, ERXI                 ; get ERXI bit
                .if    nz                       ; if set, then
                    or  [esi].VIIR_Req, RXINT_POS ; mark RXINT as requested
                .endif
            .elseif  <ah eq TXINT>              ;If TXINT
                and    al, ETXI                 ; get ETXI bit
                .if    nz                       ; if set, then
                    or  [esi].VIIR_Req, TXINT_POS ; mark TXINT as requested
                .endif
            .elseif  <ah eq MSINT>              ;If MSINT
                and    al, EMSI                 ; get EMSI bit
                .if    nz                       ; if set, then
                    or  [esi].VIIR_Req, MSINT_POS ; mark MSINT as requested
                .endif
            .endif

            pushfd                              ;save flags
            cli                                 ;Clear interrupts
                                                ; (must be done before
                                                ;  Set_VIIR)
            .if <BIT [esi].VLCR nand DLAB>      ;if we are not in the middle
                                                ;of a Baud rate change
               call Set_VIIR                    ;Identify the interrupt in VIIR

               .if nc and                       ;Int enable bit is on in AL,
               .if <BIT [esi].flgStatus nand IRRSET>  ;No IRRSET yet?
                   or     [esi].flgStatus, IRRSET  ;Set the IRRSET bit
                   push    edx                     ;Save EDX
                   push    ecx                     ;Save ECX
                   CallFn VDHSetVIRR,<[esi].hVDM_PI, [esi].hIRQ_PI>
                                                   ;call VDHSetVIRR
                   pop     ecx                     ;Restore ECX
                   pop     edx                     ;Restore EDX
               .endif
            .endif
            popfd                               ;restore flags
            pop     eax                         ;Restore EAX

        .endif                                  ;End of OUT2 check
        ret
Virt_Req_IIR    endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Set_VIIR
;
;DESCRIPTIVE NAME: Set the VIIR register to identify the interrupt type
;
;INPUT:
;       ESI -> Base address of Port Information Table (PI)
;
;OUTPUT
;       If cannot find any more interrupts to be identified, then
;          Set carry bit.
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;
;  Global Data:
;       Variable in PI table:
;          VIIR
;          VIIR_Req
;
;          The Bit map of the VIIR_Req is:
;           7    6   5   4   3   2   1   0
;         +---+---+---+---+---+---+---+---+
;         | 0 | 0 | 0 | 0 |   |   |   |   |
;         +---+---+---+---+---+---+---+---+
;                           |   |   |   |
;                           |   |   |   +----- LSint Request bit
;                           |   |   +--------- Rxint Request bit
;                           |   +------------- Txint Request bit
;                           +----------------- MSint Request bit
;
;
;FUNCTION:
;       Sets the interrupt id field of VIIR according to VIIR_Req
;       using AL.
;
;CALLED BY:
;       Virt_Req_IIR
;       Virt_Clear_IIR
;
;INTERNAL REFERENCES:
;       NONE
;EXTERNAL REFERENCES:
;       NONE
;
;NOTE:
;       Interrupts should be disabled by the caller when called.
;
;********************** END OF SPECIFICATIONS *********************************

Set_VIIR        proc    near
        .if     <[esi].VIIR_Req eq 0>           ;No interrupts are requested
            mov    [esi].VIIR, NO_INT_PENDING   ;Set VIIR to no int pending
            stc                                 ;Set carry bit
            ret                                 ;and return
        .endif

        .if     <BIT [esi].VIIR_Req and LSINT_POS>    ;If LSINT requested,
            mov  [esi].VIIR, LSINT              ; set VIIR to it
        .elseif <BIT [esi].VIIR_Req and RXINT_POS>
            mov  [esi].VIIR, RXINT
        .elseif <BIT [esi].VIIR_Req and TXINT_POS>
            mov  [esi].VIIR, TXINT
        .else                                   ;Else MSINT
            mov  [esi].VIIR, MSINT
        .endif
        clc
        ret
Set_VIIR        endp


;********************** START OF SPECIFICATIONS ******************************
;SUBROUTINE NAME: Int14h_Timeout_Proc
;
;DESCRIPTIVE NAME: Timeout Procesdure for Int14
;
;INPUT:
;        ESI -> Base address of PI for this port
;
;OUTPUT:
;
;
;RELATED DATA OBJECTS:
;  Instance Data per VDM:
;        iClients_DX
;        Variables in PI (Port_Info_Tab
;           Wait_TimeOut
;
;  Global Data:
;        NONE
;
;FUNCTION:
;       Figure timeout value to be used by Tx or Rx
;
;CALLED BY:
;       Int14h_Wait
;
;INTERNAL REFERENCES:
;       NONE
;
;EXTERNAL REFERENCES:
;       VDHQuerySysVal
;
;NOTE:
;
;********************** END OF SPECIFICATIONS *********************************

Int14h_Timeout_Proc proc    near

        push    ebp
        mov     ebp, esp

        mov     eax, DWORD PTR [ebp+12]
        CallFn  VDHWakeVIRRs, <DWORD PTR [eax]>

        leave
        ret     8

Int14h_Timeout_Proc endp


EndCode                                     ;End _TEXT segment

        END
