;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE    ,132
        TITLE   Virtual Video PHYSPAGE I/O Processing

;/*****************************************************************************
;*
;* SOURCE FILE NAME = VVPHYSIO.ASM
;*
;* DESCRIPTIVE NAME = Virtual Video Device Driver PHYSPAGE I/O Processing
;*
;*
;* VERSION      V2.0
;*
;* DATE         10/26/89
;*
;* DESCRIPTION  This module contains the VVD's PHYSPAGE I/O support code.
;*
;* FUNCTIONS
;*              vvSetupShadowPtrs()      Set up shadow pointers
;*              vvSetPhysIOState()       Set I/O state for PHYSPAGE VDM
;*              vvAdjustShadowOutput()   Adjust output based on external shadow data
;*              vvShadowOutput()         Output byte and shadow it uninterruptibly
;*              vvShadowIndxBgnd()       Write index byte and shadow it uninterruptibly
;*              vvShadowDataBgnd()       Write data byte and shadow it uninterruptibly
;*              vvRequestController()    Request video controller
;*              vvLockController()       Lock video controller
;*              vvUnlockController()     Unlock video controller
;*              vvFreeController()       Free video controller
;*
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*
;*   10/26/89                     JTP - Created.
;*
;*   05/05/91                     Avoid deadlock situation by not waiting for the
;*                                some one to post the hevControllerEvent when in
;*                                fact no one else owned it.
;*
;*   06/20/91                     Fix video corruption problem when session switch
;*                                away from PM while window VDMs are in the mist of
;*                                updating the off-screen VRAM through the PMVDMP
;*                                thread.
;*
;*   02/15/92                     B733006 - Performance enhancement to copy routines
;*
;*   02/20/92                     don't trash Sequencer reg 7, non-IBM VGAs use it
;*
;*   02/27/92                     B733211 - Performance enhancement to 320x200 mode
;*
;*   02/29/92                     New vvShadowOutput flag for registers with index
;*                                greater than 80h which weren't handled correctly.
;*
;*   03/27/92                     B735007 - avoid screen corruption on Starlight
;*
;*   10/31/92                     Save/Restore register indices vvShadowOutput.
;*
;*   02/04/93                     More selective index save/restore vvShadowOutput.
;*   05/12/94             F74819  ATIMach32 declare accelerator types globally
;*
;*****************************************************************************/


        .xlist
        include mvdm.inc
        include vddseg.inc
        .list
        include vvd.inc
        include vvdp.inc


        DefData     IMPORT,GLOBAL,C
        FLAGS       flVVD
        VVDRQ       vvdrqVideo
        HVDM        hvdmController
        HEVSEM      hevControllerEvent
       IFDEF VGA                                                        ;          
        PBYTE       pfOEMFlags                                          ;          
       ENDIF                                                            ;          
        EndData     IMPORT,GLOBAL,C


        DefData     IMPORT,SWAP,C
        PBYTE       pbSEQExtShadowIndx
        PBYTE       pbGDCExtShadowIndx
        EndData     IMPORT,SWAP,C


       IFDEF VDDSTRICT
        DefData     EXPORT,SWAP,C
        PVOID       pfnLastLock
        EndData
       ENDIF


        DefCode     IMPORT,SWAP,PASCAL
        DefFn       VDHResetEventSem
        DefFn       VDHPostEventSem
        DefFn       VDHWaitEventSem
        DefFn       VDHArmContextHook
        DefFn       vvSaveLatches
        EndCode     IMPORT,SWAP,PASCAL


        DefCode     EXPORT,SWAP,PASCAL

        DefFn       vvSetupShadowPtrs
        DefFn       vvSetPhysIOState
        DefFn       vvAdjustShadowOutput

       IFDEF SVGA
        DefData     IMPORT,SWAP,C
        ULONG       fSVGASeqFixups
        EndData
       ENDIF

;/***************************************************************************
;*
;* FUNCTION NAME = vvSetupShadowPtrs()
;*
;* DESCRIPTION   = Set up shadow pointers
;*
;*
;* INPUT         = pvvreg -> vvreg array
;*                 nvvreg == # of vvreg entries
;*                 prle   -> current register list entry
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvSetupShadowPtrs
        ArgVar  pvvregFirst,PVVREG
        ArgVar  nvvreg,ULONG
        ArgVar  prleCur,PRLE

        LocalVar prle_Index,ULONG                                       ;          
        LocalVar prle_Data,ULONG                                       ;          
        EnterProc
        push    ebx
        push    esi
        push    es

        mov     es,pvvregFirst.sel
        movzx   edx,pvvregFirst.off
        mov     esi,prleCur
        mov     ecx,nvvreg
        cmp     ecx, 0
        jz      vvssp_nomatch

        align   4                                                       ;          
;                                       ;                       /*          */
;       movzx   eax,[esi].rle_port.off  ;                       /*          */
        mov     eax,[esi].rle_pple      ;                       /*          */
        mov     ebx,[eax].ppleBRegIndx  ;                       /*          */
        and     ebx,ebx                 ;                       /*          */
        jz      vvssp_nomatch           ;           if not indexed, can't be our port

        mov     prle_Index,ebx          ; 
vvssp_findport:
        mov     ebx, prle_Index
        movzx   eax,[ebx].sTrapBRegInfo.portTrap.off ;          /*          */
        lea     ebx,[edx].vvreg_value
        cmp     es:[edx].vvreg_indx,-1
        jne     vvssp_checkport

        cmp     es:[edx].vvreg_port,PORT_SEQINDX
        jne     short vvssp_checkotherindex
        mov     pbSEQExtShadowIndx.off,bx
        mov     pbSEQExtShadowIndx.sel,es
;        jmp     short vvssp_checkport
        jmp     vvssp_nextport          ;           : just go to the next one in the list

vvssp_checkotherindex:
        cmp     es:[edx].vvreg_port,PORT_GDCINDX
        jne     short vvssp_checkport
        mov     pbGDCExtShadowIndx.off,bx
        mov     pbGDCExtShadowIndx.sel,es
        jmp     vvssp_nextport          ;           : just go to the next one in the list

vvssp_checkport:
        cmp     es:[edx].vvreg_port,ax
        jne     vvssp_nextport

        mov     al,es:[edx].vvreg_indx
        cmp     al,[esi].rle_indx
        jne     vvssp_nextport
        mov     [esi].rle_pbExtShadowData.off,bx
        mov     [esi].rle_pbExtShadowData.sel,es
        ; modify both shadow data and shadow index
        cmp     es:[edx].vvreg_port,PORT_GDCINDX
        jne     short vvssp_seqindx
        mov     bx, pbGDCExtShadowIndx.off
        mov     [esi].rle_pbExtShadowIndx.off,bx
        mov     bx, pbGDCExtShadowIndx.sel
        mov     [esi].rle_pbExtShadowIndx.sel,bx
        jmp     vvssp_nextport
vvssp_seqindx:
        cmp     es:[edx].vvreg_port,PORT_SEQINDX
        jne     vvssp_nextport
        mov     bx, pbSEQExtShadowIndx.off
        mov     [esi].rle_pbExtShadowIndx.off,bx
        mov     bx, pbSEQExtShadowIndx.sel
        mov     [esi].rle_pbExtShadowIndx.sel,bx
vvssp_nextport:
        add     edx,size vvreg_s
        hploop  vvssp_findport
vvssp_nomatch:                          ;          
        pop     es
        pop     esi
        pop     ebx

        ExitProc
EndProc   vvSetupShadowPtrs

;/***************************************************************************
;*
;* FUNCTION NAME = vvSetPhysIOState()
;*
;* DESCRIPTION   = Set I/O state for PHYSPAGE VDM
;*
;*                 This routine walks through the arleMemory table and restores
;*                 VDM's register state for correct physical memory access,
;*                 taking care not to modify any bits in any of the registers
;*                 that do not apply strictly to memory access and/or might
;*                 have an adverse effect on the display.
;*
;*                 As part of the restoration, all I/O that must be externally
;*                 shadowed is is done so uninterruptibly.
;*
;* INPUT         = None
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvSetPhysIOState
        ;FLAG: -- For now, even though it's slower, we'll use vvSetIOState
EndProc   vvSetPhysIOState


;/***************************************************************************
;*
;* FUNCTION NAME = vvAdjustShadowOutput()
;*
;* DESCRIPTION   = Adjust output based on external shadow data
;*
;*
;* INPUT         = prle  -> register entry
;*                 bData == register data to output
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvAdjustShadowOutput
        ArgVar  prleCur,PRLE
        ArgVar  bData,ULONG

        EnterProc
        mov     eax,bData
        mov     edx,prleCur
        mov     ecx,[edx].rle_pbExtShadowData
        jecxz   short vvadj_exit
        push    es
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        mov     dl,[edx].rle_fbMask
        and     al,dl                       ;keep only bits we need to change
        mov     ah,es:[ecx]                 ;get shadow data
        not     dl
        and     ah,dl                       ;keep only bits we don't change
        or      al,ah
        movzx   eax,al
        pop     es

vvadj_exit:
        ExitProc
EndProc   vvAdjustShadowOutput


        EndCode     EXPORT,SWAP,PASCAL


        DefCode     EXPORT,GLOBAL,PASCAL

        DefFn       vvShadowOutput
        DefFn       vvShadowIndxBgnd
        DefFn       vvShadowDataBgnd
        DefFn       vvRequestController
        DefFn       vvLockController
        DefFn       vvUnlockController
        DefFn       vvFreeController


;/***************************************************************************
;*
;* FUNCTION NAME = vvShadowOutput()
;*
;* DESCRIPTION   = Output byte and shadow it uninterruptibly
;*
;*                 We do NOT need to request the controller here, because
;*                 we assume that this routine is called many times by a
;*                 higher-level routine that already made that call,
;*                 assuming it was necessary.
;*
;* INPUT         = hvdmCur   -> VDM
;*                 portIndex == index port
;*                 portData  == data port
;*                 ulIndex   == controller index, -1 if none
;*                 ulData    == data to output
;*                 pbIndex   -> shadow storage, NULL if none
;*                 pbData    -> shadow storage, NULL if none
;*                 flCond    == special condition bits
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvShadowOutput
        ArgVar  hvdmCur,HVDM
        ArgVar  portIndex,PORT
        ArgVar  portData,PORT
        ArgVar  ulIndex,PORT
        ArgVar  ulData,ULONG
        ArgVar  pbIndex,PBYTE
        ArgVar  pbData,PBYTE
        ArgVar  flCond,ULONG

       IFDEF VGA                                                        ;          
        LocalVar ulCRTIndex,ULONG                                       ;          
        LocalVar ulGDCIndex,ULONG                                       ;          
        LocalVar ulSEQIndex,ULONG                                       ;          
       ENDIF                                                            ;          

        EnterProc
        push    ebx
        push    es
        mov     ebx,hvdmCur

       IFDEF VGA                                                        ;          
        mov     edx,PORT_COLRCRTINDX                                    ;          
        in      al,dx                                                   ;          
        mov     ulCRTIndex,eax                                          ;          
        mov     edx,PORT_SEQINDX                                        ;          
        in      al,dx                                                   ;          
        mov     ulSEQIndex,eax                                          ;          
        mov     edx,PORT_GDCINDX                                        ;          
        in      al,dx                                                   ;          
        mov     ulGDCIndex,eax                                          ;          
       ENDIF                                                            ;          

        cmp     flCond,0                    ;any pre-conditions to output?
        je      vvso_output                 ;no

        mov     ah,STATUS1_VERTRTRC
        test    flCond,REGCOND_WAITVERT
        jnz     short vvso_waitretrace
        mov     ah,STATUS1_HORZRTRC
        test    flCond,REGCOND_WAITHORZ
        jz      short vvso_checkseq

vvso_waitretrace:
       IFDEF MONO
        mov     edx,PORT_MONOSTATUS1
       ENDIF
       IFDEF CGA
        mov     edx,PORT_COLRSTATUS1
       ENDIF
       IFDEF EGAVGA
        mov     edx,PORT_COLRSTATUS1
        test    VDMData.regMiscOut[ebx],MISCOUT_COLRPORTS
        jnz     vvso_colorretrace
        mov     edx,PORT_MONOSTATUS1
vvso_colorretrace:
       ENDIF

        mov     ecx,MAX_RTRCLOOP
        align   4                                                       ;          
vvso_waitlow:
        in      al,dx
        test    al,ah
        loopnz  vvso_waitlow

        mov     ecx,MAX_RTRCLOOP
        align   4                                                       ;          
vvso_waithigh:
        in      al,dx
        sti
        test    al,ah
        cli
        loopz   vvso_waithigh

vvso_checkseq:

       IFDEF EGAVGA

        test    flCond,REGCOND_RESETSEQ
        jz      short vvso_output

;/*
;**
;**   Before we drop into the critical section, we must get all the swappable data
;**   items we'll need.
;**
;*/

        mov     al,VDMData.regSEQIndx[ebx]
        mov     ah,VDMData.aregSEQData[REG_SEQRESET][ebx]
        mov     ebx,eax                     ;EBX == swappable data items

        cli
        mov     edx,PORT_SEQINDX
        mov     eax,REG_SEQRESET + SEQRESET_ASYNC SHL 8
        out     dx,ax

       IFDEF VGA

;/*
;**
;**         IBM lab analysis and simulation revealed a problem in the IBM VGA
;**         due to narrow pulses on DRAM RAS and CAS control signals.  These
;**         pulses violated the DRAM timing specifications, and as a result,
;**         corrupted the memory contents.  The narrow RAS and CAS pulses were
;**         caused when one section of the VGA-LC re-synchronized to the
;**         start of a horizontal line, and another section missed the
;**         re-sync signal.  The two sections involved were the memory cycle
;**         generator, and the the memory cycle arbiter.  The short RAS and
;**         CAS pulses occurred when the memory cycle arbiter saw the reset
;**         command, and the memory cycle generator missed the command.
;**
;**         The following software fix was provided by IBM without any clear
;**         documentation on the registers used.  They say the fix "forces the
;**         memory control circuitry to synchronize in a certain way to the rest
;**         of the video logic."
;**
;**         SVGA: This is a valid register on Tseng chips for CLK selection and
;**               16k/32k ROM enabling/mapping.
;**
;*/

IFDEF  SVGA
        cmp     [fSVGASeqFixups], 0         ;Only do fixups if flag set
        jz      short @F                    ; 
ENDIF ;SVGA
        mov     eax,REG_SEQUNKNOWN_IBM1 + 0 SHL 8
        out     dx,ax                       ;clear horizontal counter
        mov     edx,PORT_COLRCRTINDX
        mov     eax,REG_CRTUNKNOWN_IBM1 + 1 SHL 8
        out     dx,ax                       ;set full instead of half cycles (whatever that means) ***/
        mov     edx,PORT_MONOCRTINDX
        out     dx,ax
IFDEF  SVGA
@@:
ENDIF ;SVGA

       ENDIF ;VGA

       ENDIF ;EGAVGA

vvso_output:
        mov     al,byte ptr ulIndex
        mov     ah,byte ptr ulData
        mov     ecx,pbData                  ;assume we'll need pbData ptr
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        mov     edx,[portIndex]         ;                      ;/*          */
        and     edx,edx                 ;Any index?            ;/*          */
        cli                             ;output and shadow uninterruptibly
        jnz     short vvso_indexdata    ;yes, index            ;/*          */
        mov     edx,portData
        mov     al,ah
        out     dx,al                       ;no, just output data byte
        jmp     short vvso_checkshadow3
vvso_indexdata:
        out     dx,al
        mov     edx,portData
        xchg    al,ah                       ;AL == data, AH == index
        jecxz   short vvso_dodata
        cmp     al,es:[ecx]                 ;data match?
        je      short vvso_checkshadow      ;yes, so skip output
vvso_dodata:
        out     dx,al
vvso_checkshadow:
        push    ecx
        mov     ecx,pbIndex
        jecxz   short vvso_checkshadow2     ;skip index shadowing if no address
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        mov     es:[ecx],ah
vvso_checkshadow2:
        pop     ecx
vvso_checkshadow3:
        jecxz   short vvso_finish           ;skip data shadowing if no address
        mov     es:[ecx],al

vvso_finish:
        test    flCond,REGCOND_RESETSEQ     ;any condition cleanup to perform?
        jz      short vvso_markdirtyandexit ;no

        cmp     edx,PORT_SEQINDX
        jne     short vvso_notseq
        mov     bl,ah                       ;restore using index provided
vvso_notseq:
        mov     al,REG_SEQRESET             ;restore original sequencer setting
        mov     ah,bh
        or      ah,SEQRESET_ASYNC or SEQRESET_SYNC
        mov     edx,PORT_SEQINDX
        out     dx,ax
        mov     al,bl
        out     dx,al

vvso_markdirtyandexit:
        sti
        mov     ecx,vvdrqVideo.vvd_pfbDRQFlags
        jecxz   short vvso_exit
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        or      byte ptr es:[ecx],DRQ_DIRTYREGS

vvso_exit:
       IFDEF VGA                                                        ;          
        mov     edx,portIndex                                           ;          
        or      edx,edx                                                 ;          
        jz      @F                                                      ;          
        mov     edx,PORT_COLRCRTINDX                                    ;          
        mov     eax,ulCRTIndex                                          ;          
        out     dx,al                                                   ;          
        mov     edx,PORT_SEQINDX                                        ;          
        mov     eax,ulSEQIndex                                          ;          
        out     dx,al                                                   ;          
        mov     edx,PORT_GDCINDX                                        ;          
        mov     eax,ulGDCIndex                                          ;          
        out     dx,al                                                   ;          
@@:                                                                     ;          
       ENDIF                                                            ;          

        pop     es
        pop     ebx
        ExitProc
EndProc   vvShadowOutput


;/***************************************************************************
;*
;* FUNCTION NAME = vvShadowIndxBgnd()
;*
;* DESCRIPTION   = Write index byte and shadow it uninterruptibly
;*
;*
;* INPUT         = AL == data to write
;*                 ECX -> shadow storage
;*                 EDX == port number
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvShadowIndxBgnd

;/*
;**
;**     FLAG:  -- Technically, since we could have lost the controller
;**               between the time the VDM trapped and the time we get here
;**               (due to preemptibility), we should call vvRequestController
;**               instead of vvLockController, but locking is faster.
;**
;*/

        SaveReg <eax>
        CallFn  vvLockController,<>
        RestoreReg <eax>

        push    es
        cli                                 ;output and shadow uninterruptibly
        out     dx,al
        jecxz   short vvsib_finish
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        mov     es:[ecx],al
vvsib_finish:
        sti
        mov     ecx,vvdrqVideo.vvd_pfbDRQFlags
        jecxz   short vvsib_exit
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        or      byte ptr es:[ecx],DRQ_DIRTYREGS
vvsib_exit:
        pop     es

        CallFn  vvUnlockController,<>
        ExitProc
EndProc   vvShadowIndxBgnd


;/***************************************************************************
;*
;* FUNCTION NAME = vvShadowDataBgnd()
;*
;* DESCRIPTION   = Write data byte and shadow it uninterruptibly
;*
;*
;* INPUT         = AL == data to write
;*                 ECX -> RLE data for register
;*                 EDX == port number
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvShadowDataBgnd

;/*
;**
;**     FLAG:  -- Technically, since we could have lost the controller
;**               between the time the VDM trapped and the time we get here
;**               (due to preemptibility), we should call vvRequestController
;**               instead of vvLockController, but locking is faster.
;**
;*/

        SaveReg <eax>
        CallFn  vvLockController,<>
        RestoreReg <eax>

        push    ebx
        push    es
        mov     ah,[ecx].rle_fbMask
        and     al,ah                       ;zero bits we cannot reprogram
        not     ah
        mov     ebx,[ecx].rle_pbExtShadowData
        or      ebx,ebx                     ;is there any external shadow data?
        jz      short vvsdb_noshadow        ;no

        rol     ebx,16
        mov     es,bx
        shr     ebx,16
        and     ah,es:[ebx]                 ;yes, get bits we cannot reprogram
        or      al,ah                       ;OR them into final result

        cmp     al,es:[ebx]                 ;try to avoid output if possible!
        je      short vvsdb_finish2         ;yeah!

        cli
        out     dx,al
        mov     es:[ebx],al                 ;ext. shadow data is also resident!
        sti                                 ;output and shadow
vvsdb_finish2:
        jmp     short vvsdb_finish

vvsdb_noshadow:                             ;get default bits we cannot reprogram
        and     ah,[ecx].rle_bMode[MEMMODE_GRFX]
        or      al,ah                       ;OR them into final result

       IFDEF VDDSTRICT
        cmp     edx,PORT_SEQDATA
        jne     short vvsdb_notseq
        movzx   ecx,VDMData.regSEQIndx
        cmp     cl,4
        ja      short vvsdb_invalid
       IFDEF VGA
        push    eax
        dec     edx
        in      al,dx
        inc     edx
        cmp     al,cl
        pop     eax
        jne     short vvsdb_invalid
       ENDIF
        cmp     byte ptr cs:vvDebugSEQTbl[ecx],1
        je      short vvsdb_doio
        cmp     byte ptr cs:vvDebugSEQTbl[ecx],2
        je      short vvsdb_invalid
        jmp     short vvsdb_finish
vvsdb_notseq:
        cmp     edx,PORT_GDCDATA
        jne     short vvsdb_invalid
        movzx   ecx,VDMData.regGDCIndx
        cmp     cl,8
        ja      short vvsdb_invalid
       IFDEF VGA
        push    eax
        dec     edx
        in      al,dx
        inc     edx
        cmp     al,cl
        pop     eax
        jne     short vvsdb_invalid
       ENDIF
        cmp     byte ptr cs:vvDebugGDCTbl[ecx],1
        je      short vvsdb_doio
        cmp     byte ptr cs:vvDebugGDCTbl[ecx],2
        je      short vvsdb_invalid
        jmp     short vvsdb_finish

        public  vvDebugSEQTbl
        public  vvDebugGDCTbl
vvDebugSEQTbl   db  1,1,1,1,1
vvDebugGDCTbl   db  1,1,1,1,1,1,1,1,1

vvsdb_invalid:
        int 3
       ENDIF

vvsdb_doio:
        out     dx,al                       ;output only

vvsdb_finish:
        mov     ecx,vvdrqVideo.vvd_pfbDRQFlags
        jecxz   short vvsdb_exit
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        or      byte ptr es:[ecx],DRQ_DIRTYREGS

vvsdb_exit:
        pop     es
        pop     ebx

        CallFn  vvUnlockController,<>
        ExitProc
EndProc   vvShadowDataBgnd


;/***************************************************************************
;*
;* FUNCTION NAME = vvRequestController()
;*
;* DESCRIPTION   = Request video controller
;*
;*                 This manages controller requests for (currently) three
;*                 classes of threads:  VDMs, DISPLAY driver requests, and
;*                 external event processing.
;*
;*                 For a VDM to use the controller, the VDD must call this
;*                 function with the VDM handle.  Other (non-VDM) callers
;*                 call with either DISPLAY_OWNER or MISC_OWNER identifiers
;*                 instead of a VDM handle.  When a caller requests ownership
;*                 and the controller is owned by another thread, this service
;*                 will block until the controller is freed.  The one exception
;*                 to this is if a VDM is the owner and the controller is not
;*                 locked.  In that case, ownership is stolen from the VDM, its
;*                 latches are saved, and its PHYSPAGE context hook is rearmed.
;*
;*                 This code is resident so that we do not preempt between the
;*                 time we check the shared OWNED flag and the time we set the
;*                 shared NOTIFY flag.  Note that any global data we touch
;*                 during that time must also be resident, and this is true for
;*                 the shared flags as well as the semaphore handle (which we
;*                 avoid arming unless we have to, for speed's sake).
;*
;* INPUT         = hvdm == Owner ID
;*                 fLocked == TRUE if controller should requested AND locked at
;*                 once.
;*
;* OUTPUT        = Handle of previous owner
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvRequestController
        ArgVar  hvdmID,HVDM
        ArgVar  fLocked,BOOL

       IFDEF VDDSTRICT
        LocalVar ownerController,HVDM
        LocalVar flVVDCur,FLAGS
        LocalVar flDebug,FLAGS
       ENDIF

        EnterProc
        sub     eax,eax                     ;default return value
        cmp     vvdrqVideo.vvd_nbReserved,ALL_RESERVED
        je      vvrc_exit                   ;request can be ignored
        align   4                                                       ;          
vvrc_retry:
        mov     eax,hvdmController
       IFDEF VDDSTRICT
        mov     ownerController,eax
        mov     ecx,flVVD
        mov     flVVDCur,ecx
        mov     flDebug,0
       ENDIF
        or      eax,eax                     ;owned by anyone we know?
        jz      short vvrc_check            ;no, so see if we can take it

        cmp     eax,hvdmID                  ;do we already own controller?
        je      vvrc_lock                   ;yes

        cmp     eax,MAXNONVDM_OWNER         ;no, but could we steal it?
        jbe     short vvrc_check            ;no, it's not a VDM owner

        test    flVVD,VVD_CTLLOCKED         ;a VDM is owner; has he locked it?
       IFDEF VDDSTRICT
        jnz     vvrc_wait                   ;yes, we'll have to wait
       ELSE
        jnz     short vvrc_wait             ;yes, we'll have to wait
       ENDIF

;/*
;**
;**   VDM is owner, but has not locked the controller, so we can steal the
;**   controller from it.  But first, save its latches, and set up the context
;**   hook that will enable it to restore its own state when it runs again.
;**
;*/

        push    eax
        CallFn  vvSaveLatches,<eax, FALSE>
        pop     eax

;/*
;**
;**   Note that this bit of code is actually preemptible, but since we've
;**   already marked the controller as unowned and plan on leaving the shared
;**   controller flag set, no other VDM thread will be able to request the
;**   controller until this thread leaves and frees the controller at some later
;**   point.
;**
;*/

        test    VDMData.flVDMVideo[eax],VDM_PHYSHOOK
       IFDEF VDDSTRICT
        jnz     vvrc_take                   ;context hook already armed
       ELSE
        jnz     short vvrc_take             ;context hook already armed
       ENDIF
        or      VDMData.flVDMVideo[eax],VDM_PHYSHOOK
       IFDEF VDDSTRICT
        test    VDMData.flVDMVideo[eax],VDM_PHYSPAGE
        ASSERTNZ <"VDM does not own physical pages",10>,<00>
       ENDIF
        CallFn  VDHArmContextHook,<VDMData.hhookUpdatePhysContext[eax], eax>
        jmp     short vvrc_take

vvrc_check:
       IFDEF VDDSTRICT
        or      flDebug,0001h               ;make a note we came through here
       ENDIF

        cmp     fLocked,LOCKBUTDONOTBLOCK   ;no block requested?        ;          
        je      short vvrc_take             ;yes, go for it             ;          

        mov     ecx,vvdrqVideo.vvd_pfCtrlOwned
        jecxz   short vvrc_take

        push    es
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        mov     al,1
        xchg    es:[ecx],al                 ;set shared OWNED flag
        or      al,al                       ;was it already set?
        jz      short vvrc_take0            ;no

        movzx   ecx,vvdrqVideo.vvd_pfCtrlNotify.off
        mov     es:[ecx],al                 ;otherwise, set shared NOTIFY flag
        pop     es

;/*
;**
;**   Note that it's OK to avoid arming the semaphore until we absolutely
;**   have to, since we are in NON-PREEMPTIBLE code (or at least, we better be)
;**
;*/

vvrc_wait:
        or      flVVD,VVD_CTLWAITING
        CallFn  VDHResetEventSem,<hevControllerEvent>
        CallFn  VDHWaitEventSem,<hevControllerEvent, SEM_INDEFINITE_WAIT>
        jmp     vvrc_retry

vvrc_take0:
        pop     es

vvrc_take:
        mov     eax,hvdmID
       IFDEF VDDSTRICT
        or      eax,eax
        ASSERTNZ <"Invalid controller owner",10>,<00>
       ENDIF
        xchg    hvdmController,eax          ;record who's taking ownership

vvrc_lock:
       IFNDEF SVGA                                                      ;           
       IFDEF VGA                                                        ;          
        mov     ecx,pfOEMFlags              ;get pointer to the         ;          
        jecxz   short vvrc_lockck           ;  Starlight state flag     ;          
                                            ;                           ;          
        push    eax                         ;                           ;          
        push    es                          ;                           ;          
        rol     ecx,16                      ;extract to sel:offset      ;          
        mov     es,cx                       ;                           ;          
        shr     ecx,16                      ;                           ;          
        align   4                           ;                           ;          
vvrc_ck:                                    ;                           ;          
        test    byte ptr es:[ecx],00000010b ;is the Blt engine on?      ;          
        jz      short vvrc_restreg      ;no--never mind                 ;          
        mov     dx,33CEh                ;Control Code Register 1        ;          
        in      al,dx                   ;wait until the Blt engine      ;          
        test    al,00000001b            ;is done (check start/stop bit) ;          
        jnz     short vvrc_ck           ;                               ;          
        cli                             ;keep pointer from interrupting ;          
        test    byte ptr es:[ecx],00000010b ;is the Blt engine still on?;          
        jz      short vvrc_restreg      ;no--ptr must have turned it off;          
                                                                        ;          
        mov     dx,3CEh                                                 ;          
        mov     ax,050Fh                ;unlock Starlight               ;          
        out     dx,ax                                                   ;          
                                                                        ;          
        mov     al,10h                  ;disable Starlight              ;          
        out     dx,al                                                   ;          
        inc     dx                                                      ;          
        in      al,dx                                                   ;          
        and     al,0F7h                                                 ;          
        mov     ah,al                                                   ;          
        or      al,40h                  ;reset the Blt engine           ;          
        out     dx,al                                                   ;          
                                                                        ;          
        in      al,84h                  ;delay for 486                  ;          
        mov     al,ah                                                   ;          
        out     dx,al                   ;turn off the reset             ;          
        dec     dx                                                      ;          
                                                                        ;          
        mov     ax,0Fh                  ;lock Starlight                 ;          
        out     dx,ax                                                   ;          
        and     byte ptr es:[ecx],11111101b  ;show Blt engine disabled  ;          
                                                                        ;          
vvrc_restreg:                                                           ;          
        sti                                                             ;          
        pop     es                                                      ;          
        pop     eax                                                     ;          
                                                                        ;          
vvrc_lockck:                                                            ;          
       ENDIF                                                            ;          
       ENDIF                                                            ;           

        cmp     fLocked,FALSE               ;was a lock also requested?
        je      short vvrc_exit             ;no

       IFDEF VDDSTRICT
        mov     edx,[ebp+4]
        mov     pfnLastLock,edx
       ENDIF

        or      flVVD,VVD_CTLLOCKED         ;lock the controller then

vvrc_exit:
        ExitProc
EndProc   vvRequestController


;/***************************************************************************
;*
;* FUNCTION NAME = vvLockController()
;*
;* DESCRIPTION   = Lock video controller
;*
;*                 This locks the controller (assuming a VDM owns it),
;*                 essentially inhibiting anyeone from stealing the controller
;*                 away from the owning VDM.  See vvRequestController for more
;*                 details on stealing.
;*
;* INPUT         = None
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvLockController
        cmp     vvdrqVideo.vvd_nbReserved,ALL_RESERVED
        je      short vvlc_exit             ;request can be ignored

       IFDEF VDDSTRICT
        cmp     hvdmController,MAXNONVDM_OWNER
        ASSERTA <"Invalid controller lock request",10>,<00>

        mov     eax,[esp]
        mov     pfnLastLock,eax
       ENDIF

        or      flVVD,VVD_CTLLOCKED         ;lock the controller then
vvlc_exit:
        ExitProc
EndProc   vvLockController


;/***************************************************************************
;*
;* FUNCTION NAME = vvUnlockController()
;*
;* DESCRIPTION   = Unlock video controller
;*
;*                 This unlocks the controller (assuming it was locked),
;*                 and wakes up anyone waiting for it.
;*
;* INPUT         = None
;*
;* OUTPUT        = None (controller unlocked)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvUnlockController
        and     flVVD,NOT VVD_CTLLOCKED     ;unlock the controller

        test    flVVD,VVD_CTLWAITING        ;ownership notification required?
        jz      short vvuc_exit             ;no
        and     flVVD,NOT VVD_CTLWAITING
        CallFn  VDHPostEventSem,<hevControllerEvent>

vvuc_exit:
        ExitProc
EndProc   vvUnlockController


;/***************************************************************************
;*
;* FUNCTION NAME = vvFreeController()
;*
;* DESCRIPTION   = Free video controller
;*
;*                 This is the counterpart to vvRequestController.  it
;*                 basically just clears our private owner as well as the
;*                 shared OWNED flag if the given ID matches the owner,
;*                 and also checks for anyone waiting for ownership
;*                 and wakes them up.
;*
;* INPUT         = hvdm == Owner ID
;*
;* OUTPUT        = None (controller ownership freed)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvFreeController
        ArgVar  hvdmID,HVDM

        EnterProc
        mov     eax,hvdmController
        or      eax,eax                     ;is there an owner?
        jz      short vvfc_check            ;no, ignore the request

       IFDEF VDDSTRICT
        cmp     eax,hvdmID                  ;assert that owners match
        ASSERTE <"Controller owner mismatch",10>,<00>
       ENDIF

        sub     eax,eax
        mov     ecx,vvdrqVideo.vvd_pfCtrlOwned
        jecxz   short vvfc_skip

        push    es
        rol     ecx,16
        mov     es,cx
        shr     ecx,16
        mov     es:[ecx],al                 ;release controller ownership
        pop     es
vvfc_skip:
        mov     hvdmController,eax

vvfc_check:
        call    vvUnlockController
        ExitProc
EndProc   vvFreeController


        EndCode     EXPORT,GLOBAL,PASCAL


        END
