;*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
;/*****************************************************************************
;*
;* SOURCE FILE NAME = ENTER.ASM
;*
;* DESCRIPTIVE NAME = Provide structure and entry points for an OS/2 DD.
;*
;*
;* VERSION      V2.0
;*
;* DATE         03/22/87
;*
;* DESCRIPTION  Contains routines called on entering and leaving the driver.
;*              OS/2 device driver.  Provide STRATEGY and IDC entry
;*              points for this device driver.  These small functions
;*              take the registers passed in and push them onto the
;*              stack for C routines to use.
;*
;* FUNCTIONS    Public: enter_driver_sem       
;*                      enter_driver           
;*                      double_enter_driver
;*                      leave_driver           
;*                      req_controller         
;*                      free_controller        
;*                      SeamlessHeartbeat      
;*                      SeamlessRecover        
;*                      SeamlessTerminate      
;*                      SeamlessInitialize     
;*
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   03/22/87                     Charles Whitmer [chuckwh] - Created
;*   03/10/89                     Hock Lee [hockl]  Split hddc and no 
;*   09/07/88                     Hock Lee [hockl] Implemented lazy 
;*   06/24/87                     Charles Whitmer [chuckwh] - Made it 
;*                                 call FSRSemEnter directly in WINTODOS.
;*   03/22/87                     Charles Whitmer [chuckwh] - Wrote enter_driver
;*   10/07/88                     Hock Lee [hockl] - Wrote double_enter_driver
;*   06/24/87                     Charles Whitmer [chuckwh]
;*                                   Made it call FSRSemLeave directly
;*   10/12/89                     Jeff Parsons [jeffpar]
;*                                   Wrote req_controller
;******************************************************************************/

        .xlist
IFDEF   SEAMLESS
INCL_DOSINFOSEG         EQU     1
ENDIF   ;SEAMLESS

        include cmacros.inc
        include pmgre.inc
        include driver.inc
        include assert.mac
        .list

        externFP FSRSemEnter
        externFP FSRSemLeave
        externFP VisRegionCallBack


        IFNDEF CGA
        IFNDEF PP8

        VDD_SUPPORT equ 1                      ;this is required for EGA/VGA drivers only

        externFP init_hw_regs
        externFP DosRequestVDD


sBegin  PtrData
        externB fbShadowFlags
        externB fControllerOwned
        externB fControllerNotify
IFDEF   SEAMLESS
        externB fVDMControllerMine
ENDIF   ;SEAMLESS
sEnd    PtrData

        ENDIF ;!PP8
        ENDIF ;!CGA

        errcode <INV_HDC>

sBegin  Data
        externB semDriver
        externD hVideoVDD
        externB fbOffScreen
        externB fGrimReaper
        IFDEF   VDD_SUPPORT
        externB fControllerMine
        ENDIF

        staticW dsIdent,<"xq">
        staticW enter_flags,?
IFDEF   SEAMLESS
        staticD HeartbeatPIDTID,0
        externW wHeartbeat
        externD hWinVDD
        externB fSeamless
ENDIF   ;SEAMLESS

sEnd    Data


sBegin  Code
        assumes cs,Code

        externW CodeData
        externW MyPtrCodeData

;/***************************************************************************
;*
;* FUNCTION NAME =  enter_driver_sem, enter_driver
;*                  
;* DESCRIPTION   = 
;*    Takes care of common tasks that must be performed when entering the                       
;*    driver through a major function handler.  Presently takes care of the          
;*    following:                                                                     
;*                                                                                   
;*     1) Waits on the driver's semaphore to make sure that only one thread          
;*        is in the driver at a time.  (We may want to optimize this later           
;*        by gating access to the display hardware and driver data structures        
;*        separately.)                                                               
;*                                                                                   
;*     2) Examines the DDC to make sure that the task noted as the DDC owner         
;*        is the task presently trying to use it.  If not, we need to update         
;*        the screen and private segment selectors in the DDC, and note the          
;*        new owner task as well.                                                    
;*                                                                                   
;*     3) If the vis region is dirty, call pmwin to get new vis region.              
;*                                                                                   
;*    enter_driver_sem should be called if we are only interested in                 
;*    acquiring screen lock.                                                         
;*                                                                                   
;*    leave_driver() is used to free the screen lock acquired by enter_driver()      
;*    and enter_driver_sem().                                                        
;*
;*    Registers Preserved:      
;*          SI,DI,DS,BP         
;*    Registers Destroyed:      
;*          AX,BX,CX,DX,ES,FLAGS
;*
;* INPUT         = DX = hDDC or 0 
;* OUTPUT        = None
;*
;* RETURN-NORMAL = CF = 0 
;* RETURN-ERROR  = CF = 1, DX:AX = 0                  (PMERR_INV_HDC logged) 
;*
;* PSEUDO-CODE   =
;*   enter_driver_sem()
;*   {
;*         SemEnter(Device);
;*         return(SUCCESS);
;*   }
;*  
;*   enter_driver()
;*   {
;*         do {
;*             SemEnter(Device);
;*             if (bad dc) {
;*                 save_error_code(PMERR_INV_HDC);
;*                 SemLeave(Device);
;*                 return(ERROR);                   /* CF = 1; DX:AX = 0 */
;*             }
;*             if (dc is not dirty)
;*                 return(SUCCESS);
;*             SemLeave(Device);
;*             VisRegionCallBack(hdc);              /* call the pmwin function */
;*         } while (TRUE);
;*   }
;*
;****************************************************************************/
 
        assumes ds,Data
        assumes es,nothing

cProc   far_enter_driver_sem,<PUBLIC,FAR,NODATA,NONWIN>
cBegin
        call    enter_driver_sem
cEnd

cProc   enter_driver_sem,<PUBLIC,NEAR>
cBegin
ifdef FIREWALLS
        push    ax
        mov     ax,ds
        cmp     ax,CodeData
        pop     ax
        je      @F
        rip     text,<enter_driver_sem - DS not set correctly>
@@:
endif
IFDEF   SEAMLESS
enter_driver_sem_retry:
ENDIF   ;SEAMLESS
        cCall   FSRSemEnter,<DataOFFSET semDriver>      ; preserves DX
IFDEF   SEAMLESS
        or      ax,ax
        je      @f
        cCall   SeamlessHeartbeat
        jmp     short enter_driver_sem_retry
@@:
ENDIF   ;SEAMLESS
ifdef FIREWALLS
        jz      @F
        rip     text,<Error on acquiring/releasing a semaphore>
@@:
        pushf
        pop     enter_flags
endif
        IFDEF   VDD_SUPPORT
        cCall   req_controller                    ; acquire video controller lock
        ENDIF
cEnd


cProc   far_enter_driver,<FAR,PUBLIC,NODATA,NONWIN>
cBegin
        cCall   enter_driver
cEnd

cProc   enter_driver,<PUBLIC,NEAR>
cBegin

enter_driver_begin:
ifdef FIREWALLS
        push    ax
        mov     ax,ds
        cmp     ax,CodeData
        pop     ax
        je      @F
        rip     text,<enter_driver - DS not set correctly>
@@:
endif
IFDEF   SEAMLESS
enter_driver_retry:
ENDIF   ;SEAMLESS
        cCall   FSRSemEnter,<DataOFFSET semDriver> ; preserves DX
IFDEF   SEAMLESS
        or      ax,ax
        je      @f
        cCall   SeamlessHeartbeat
        jmp     short enter_driver_retry
@@:
ENDIF   ;SEAMLESS
ifdef FIREWALLS
        jz      @F
        rip     text,<Error on acquiring/releasing a semaphore>
@@:
        pushf
        pop     enter_flags
endif

;/*
;**  return success if the ddc is not dirty
;*/

        mov     bx,dx
        cmp     [bx].ddc_usId,DDC_IDENT
        jz      enter_driver_good_ddc            ; good ddc

;/*
;**  there is a collision error
;*/

ifdef FIREWALLS
        or      bx,bx
        jz      null_ddc
        rip     text,<enter_driver: Bad DDC - possible thread collision>
        jmp     short @F
null_ddc:
        rip     text,<enter_driver: Null DDC - can ignore>
@@:
endif
        mov     ax,PMERR_INV_HDC
        save_error_code
        cCall   FSRSemLeave,<DataOFFSET semDriver>
        xor     ax,ax
        cwd
        stc
        jmp     short enter_driver_exit          ; return error (CF = 1, DX:AX = 0)
enter_driver_good_ddc:
        ddc?    bx
        test    [bx].ddc_fbAbove,DDC_DIRTY_VISRGN
        jz      enter_driver_exit                ; return success (CF = 0)

;/*
;**  the ddc is dirty, we have to call pmwin to get new vis region.
;*/

        push    bx                                ; remember hddc
        push    bx

;/*
;**  free semaphore before calling pmwin
;*/

        cCall   FSRSemLeave,<DataOFFSET semDriver>

;/*
;**  call pmwin to update vis region
;*/

        pop     bx
        farPtr  myhdc,<[bx].ddc_hdc.hi>,<[bx].ddc_hdc.lo>
        cCall   VisRegionCallBack,<myhdc>

;/*
;**   got to do it over again
;*/

        pop     dx                               ; DX = hddc
        jmp     enter_driver_begin               ; let's re-enter again

enter_driver_exit:

        jc      enter_driver_error

        IFDEF   VDD_SUPPORT
        cCall   req_controller                    ; acquire video controller lock
        ENDIF

enter_driver_error:
cEnd


;/***************************************************************************
;*
;* FUNCTION NAME = double_enter_driver
;*
;* DESCRIPTION   = 
;*    Performs the same function as enter_driver except we have to ensure 
;*    two DCs are both clean in this function.                            
;*
;*    The screen lock is acquired ONCE only.
;*   
;*    The DCs can be the same but should not be null.
;*   
;*    leave_driver() is used to free the screen lock acquired by
;*    double_enter_driver().
;*
;*    Registers Preserved:       
;*          SI,DI,DS,BP          
;*    Registers Destroyed:       
;*          AX,BX,CX,DX,ES,FLAGS 
;*
;*
;*
;* INPUT         = SI,DI = hDDCs 
;* OUTPUT        = None
;*
;* RETURN-NORMAL = CF = 0 
;* RETURN-ERROR  = CF = 1, DX:AX = 0                  (PMERR_INV_HDC logged) 
;*
;* PSEUDO-CODE   =
;*   double_enter_driver()
;*   {
;*         do {
;*             SemEnter(Device);
;*             if (bad dcs) {
;*                 save_error_code(PMERR_INV_HDC);
;*                 SemLeave(Device);
;*                 return(ERROR);                   /* CF = 1; DX:AX = 0 */
;*             }
;*             if (both dcs are clean)
;*                 return(SUCCESS);
;*             SemLeave(Device);
;*             VisRegionCallBack(dirty dc);         /* call the pmwin function */
;*         } while (TRUE);
;*   }
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

cProc   double_enter_driver,<PUBLIC,NEAR>,<si,di>
cBegin

double_enter_driver_begin:
ifdef FIREWALLS
        push    ax
        mov     ax,ds
        cmp     ax,CodeData
        pop     ax
        je      @F
        rip     text,<double_enter_driver - DS not set correctly>
@@:
endif
IFDEF   SEAMLESS
double_enter_driver_retry:
ENDIF   ;SEAMLESS
        cCall   FSRSemEnter,<DataOFFSET semDriver>
IFDEF   SEAMLESS
        or      ax,ax
        je      @f
        cCall   SeamlessHeartbeat
        jmp     short double_enter_driver_retry
@@:
ENDIF   ;SEAMLESS
ifdef FIREWALLS
        jz      @F
        rip     text,<Error on acquiring/releasing a semaphore>
@@:
        pushf
        pop     enter_flags
endif

;/*
;**  return success if the ddc is not dirty
;*/

        assert  si,NE,0
        assert  di,NE,0
        cmp     [si].ddc_usId,DDC_IDENT
        jnz     @F                                ;     ddc
        cmp     [di].ddc_usId,DDC_IDENT
        jz      good_ddcs                         ; both ddcs are good

;/*
;**  there is a collision error
;*/

@@:
        rip     text,<double_enter_driver: Bad DDC - possible thread collision>
        mov     ax,PMERR_INV_HDC
        save_error_code
        cCall   FSRSemLeave,<DataOFFSET semDriver>
        xor     ax,ax
        cwd
        stc
        jmp     short double_enter_driver_exit   ; return error (CF = 1, DX:AX = 0)

;/*
;**  we move the dirty ddc into SI, update its vis region, and then retry
;**  double_enter_driver again.  we only clean one ddc at a time.
;*/

good_ddcs:
        ddc?    si
        ddc?    di
        test    [si].ddc_fbAbove,DDC_DIRTY_VISRGN
        jnz     clean_this_ddc
        test    [di].ddc_fbAbove,DDC_DIRTY_VISRGN
        jz      double_enter_driver_exit           ; return success (CF = 0)
        xchg    si,di                              ; we want the dirty ddc in SI!

;/*
;** the ddc in SI is dirty, we call pmwin to get its updated vis region.
;** free semaphore before calling pmwin
;*/

clean_this_ddc:
        cCall   FSRSemLeave,<DataOFFSET semDriver>

;/*
;**   call pmwin to update vis region
;*/

        farPtr  myhdc,<[si].ddc_hdc.hi>,<[si].ddc_hdc.lo>
        cCall   VisRegionCallBack,<myhdc>

;/*
;**  got to do it over again
;*/

        jmp     double_enter_driver_begin        ; let's re-enter again

double_enter_driver_exit:

        jc      double_enter_driver_error

        IFDEF   VDD_SUPPORT
        cCall   req_controller                    ; acquire video controller lock
        ENDIF

double_enter_driver_error:
cEnd


;/***************************************************************************
;*
;* FUNCTION NAME = leave_driver
;*
;* DESCRIPTION   = Frees up the driver semaphore as we leave. 
;*
;*                 Registers Preserved:    
;*                       AX,DX,SI,DI,DS,BP 
;*                 Registers Destroyed:    
;*                       CX                
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

cProc   far_leave_driver,<FAR,PUBLIC,NODATA,NONWIN>
cBegin
        call    leave_driver
cEnd

cProc   leave_driver,<PUBLIC,NEAR>,<ax,dx>
cBegin

ifdef FIREWALLS
        mov     ax,ds
        cmp     ax,CodeData
        je      @F
        rip     text,<leave_driver - DS not set correctly>
@@:

;/*
;**  The driver IS allowed to clear the direction flag, or to leave it
;**  unchanged, but the driver is NOT allowed to SET it if it was clear
;**  initially.  So if the direction flag in enter_flags is 0 and it is
;**  1 in the cpu flag register, then we have a bug.
;*/

        pushf
        pop     ax
        not     ax
        or      ax,enter_flags
        test    ax,4                              ; direction flag
        jnz     dirflag_okay
        rip     text,<leave_driver - bad direction flag>
dirflag_okay:

endif
        IFDEF   VDD_SUPPORT
        cCall   free_controller                  ; release video controller first
        ENDIF

        cCall   FSRSemLeave,<DataOFFSET semDriver>
cEnd


        IFDEF   VDD_SUPPORT

;/***************************************************************************
;*
;* FUNCTION NAME = req_controller
;*
;* DESCRIPTION   = Requests video controller ownership, from Video VDD  
;*                 if necessary                                         
;*
;*                 Registers Destroyed:  
;*                       AX,ES           
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = Carry clear 
;* RETURN-ERROR  = None
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

cProc   req_controller,<PUBLIC,NEAR>
cBegin
        mov     es,MyPtrCodeData
        assumes es,PtrData

        test    fbOffScreen,OFFSCR_VDD
        jz      req_owned

        cmp     fGrimReaper,0                     ; is screen access currently allowed?
        jne     req_exit1                         ; no

        cmp     fControllerMine,0                ; do we already have it?
        jne     req_owned                         ; yes
        mov     al,1
        xchg    fControllerOwned,al              ; no, so try to take it
        or      al,al                             ; was it already taken?
        jz      req_owned                         ; no

        sub     dx,dx
        farPtr  hvdd,hVideoVDD.hi,hVideoVDD.lo
        farPtr  cbInput,dx,dx
        farPtr  pInput,dx,dx
        farPtr  cbOutput,dx,dx
        farPtr  pOutput,dx,dx
        cCall   DosRequestVDD,<hvdd,dx,VVDSYSREQ_REQCTRL,cbInput,pInput,cbOutput,pOutput>

ifdef FIREWALLS
        cmp     fControllerOwned,0
        jne     @F
        rip     text,<req_controller - unowned>
 @@:
endif

req_owned:
        test    fbShadowFlags,SHADOW_DIRTYREGS
        jz      req_exit
        push    cx
        push    dx
        stc                                       ; don't init any memory locations
        call    init_hw_regs                      ; just initialize default driver state
        pop     dx
        pop     cx
        and     fbShadowFlags,NOT SHADOW_DIRTYREGS

req_exit:
        inc     fControllerMine                  ; make a note that we own it now

req_exit1:
       IFDEF DEBUGIO
        mov     es,MyPtrCodeData
        assumes es,PtrData

        or      fbShadowFlags,02h
        and     fbShadowFlags,not 04h
       ENDIF
cEnd


;/***************************************************************************
;*
;* FUNCTION NAME = free_controller 
;*
;* DESCRIPTION   = Releases video controller ownership, 
;*                 notifying Video VDD if necessary
;*
;*                 Registers Destroyed:
;*                       AX,ES
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

cProc   free_controller,<PUBLIC,NEAR>
cBegin
ifdef FIREWALLS
        cmp     fControllerMine,0
        jne     @F
        rip     text,<free_controller - unowned>
        jmp     free_exit
@@:

endif
        cmp     fControllerMine,0
        jle     free_exit
        dec     fControllerMine                   ; decrement controller ownership
        jnz     free_exit                         ; not done with it yet, though

        test    fbOffScreen,OFFSCR_VDD
        jz      free_exit

        mov     es,MyPtrCodeData
        assumes es,PtrData

        mov     al,0
        mov     fControllerOwned,al
        xchg    fControllerNotify,al              ; do we need to notify Video VDD?
        or      al,al
        jz      free_exit                         ; no

        sub     dx,dx
        farPtr  hvdd,hVideoVDD.hi,hVideoVDD.lo
        farPtr  cbInput,dx,dx
        farPtr  pInput,dx,dx
        farPtr  cbOutput,dx,dx
        farPtr  pOutput,dx,dx
        cCall   DosRequestVDD,<hvdd,dx,VVDSYSREQ_FREECTRL,cbInput,pInput,cbOutput,pOutput>

free_exit:
cEnd

        ENDIF   ;VDD_SUPPORT

IFDEF   SEAMLESS

;/***************************************************************************
;*
;* FUNCTION NAME = SeamlessHeartbeat
;*
;* DESCRIPTION   = Checks to see if a seamless session has died while
;*                 it owned the drawing semaphore. If it has, VWIN and
;*                 SeamlessRecover are called to cleanup,
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;**************************************************************************/

cProc   SeamlessHeartbeat,<PUBLIC,NEAR>

cBegin
.386
        test    fSeamless,SEAMLESS_ACTIVE       ;are we seamless?
        jne     @f                              ;Yes
        jmp     SeamlessHeartbeatExit           ;No, don't bother
@@:
        IFDEF   FIREWALLS
        color_puts      BLUE,BLACK,<   PMVGA: Semaphore Timeout.>
        ENDIF   ;FIREWALLS

        mov     eax,HeartbeatPIDTID; 
        or      eax,eax
        jnz     CheckHeartbeat

        mov     eax,DWORD PTR semDriver.fsrs_ProcID
        mov     HeartbeatPIDTID,eax             ;Save PID & TID,
        mov     wHeartbeat,0                    ;  reset heartbeat,
        jmp     short SeamlessHeartbeatExit     ;  and return.

CheckHeartbeat:
        lea     bx,semDriver.fsrs_ProcID
        cmp     [bx],eax
        jne     SeamlessHeartbeatCleanup

        IFDEF   FIREWALLS
        push    eax
        color_puts      BLUE,BLACK,<   PMVGA: Same PID & TID second time around.>
        pop     eax
        ENDIF   ;FIREWALLS

        ;/*
        ;**  We timed out on the same PID & TID as the last timeout
        ;*/

        cmp     wHeartbeat,0
        jne     SeamlessHeartbeatCleanup
                                                ;must call VWIN to cleanup
        IFDEF   FIREWALLS
        push    eax
        color_puts      BLUE,BLACK,<   PMVGA: Requesting kill of VDM.>
        pop     eax
        ENDIF   ;FIREWALLS

        push    dx
        shld    ecx,eax,16                      ;Put TID in cx
        sub     dx,dx
        farPtr  hvdd,hWinVDD.hi,hWinVDD.lo
        farPtr  cbInput,dx,ax
        farPtr  pInput,dx,dx
        farPtr  cbOutput,dx,cx
        farPtr  pOutput,dx,dx
        cCall   DosRequestVDD,<hvdd,dx,VWIN_SIGHEARTBEAT,cbInput,pInput,cbOutput,pOutput>
        cmp     ax,1
        ja      @f

        call    SeamlessRecover
@@:
        pop     dx

SeamlessHeartbeatCleanup:
        mov     HeartbeatPIDTID,0

SeamlessHeartbeatExit:
.286

cEnd

EXTRN   DOSLOCALINFO:ABS
        externFP FSRSemExit

;/***************************************************************************
;*
;* FUNCTION NAME = SeamlessRecover
;*
;* DESCRIPTION   = A seamless session has died while it had hardware access.
;*                 We need to free up and reset the appropriate resources.
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

cProc   SeamlessRecover,<PUBLIC,NEAR>

cBegin
        push    es
        mov     es,MyPtrCodeData
        assumes es,PtrData

        cmp     fVDMControllerMine,0    ; does the VDM own the controller?
        je      @f                      ; no
        mov     fVDMControllerMine,0    ; free the controller
        mov     fControllerOwned,0      ; 
        or      fbShadowFlags,SHADOW_DIRTYREGS  ;Force the init_hw to take
                                                ; place at req_controller

        test    fbOffScreen,OFFSCR_VDD
        jz      @f

        xor     al,al
        mov     fControllerOwned,al
        xchg    fControllerNotify,al             ; do we need to notify Video VDD?
        or      al,al
        jz      @f                      ; no

        sub     dx,dx
        farPtr  hvdd,hVideoVDD.hi,hVideoVDD.lo
        farPtr  cbInput,dx,dx
        farPtr  pInput,dx,dx
        farPtr  cbOutput,dx,dx
        farPtr  pOutput,dx,dx
        cCall   DosRequestVDD,<hvdd,dx,VVDSYSREQ_FREECTRL,cbInput,pInput,cbOutput,pOutput>
@@:
        mov     ax,DOSLOCALINFO
        mov     es,ax
        assume  es:nothing
.386
        mov     ax,es:[lis_tidCurrent]  ; Set current thread ID
        shl     eax,16                  ; tidCurrent in highword
        mov     ax,es:[lis_pidCurrent]  ; and current process

        mov     dword ptr [semDriver].fsrs_ProcID,eax
.286
        pop     es

        cCall   FSRSemExit,<DataOFFSET semDriver>      ; preserves DX

        IFDEF   FIREWALLS
        color_puts      BLUE,BLACK,<   PMVGA: Cleared the semaphore.>
        ENDIF   ;FIREWALLS

cEnd

;/***************************************************************************
;*
;* FUNCTION NAME = SeamlessTerminate
;*
;* DESCRIPTION   = A seamless session has died, and we have been called
;*                 by the PM Shield to clean up.
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

cProc   SeamlessTerminate,<PUBLIC,FAR,PASCAL>,<ds>
        parmD   ulPid

cBegin
        mov     ds,CodeData
        assumes ds,Data
        IFDEF   FIREWALLS
        color_puts      BLUE,BLACK,<   PMVGA: Called by SHIELD because a Seamless VDM went down.>
        ENDIF   ;FIREWALLS
        mov     ax,WORD PTR ulPid       ;Get the PID
        cmp     word ptr [semDriver].fsrs_ProcID,ax
        jne     @f

        cCall   SeamlessRecover

.386
        mov     HeartbeatPIDTID,0
.286

        IFDEF   FIREWALLS
        color_puts      BLUE,BLACK,<   PMVGA: Cleaned up from termination of an errant Seamless VDM.>
        ENDIF   ;FIREWALLS
@@:
cEnd

cProc   SeamlessInitialize,<PUBLIC,FAR,PASCAL>,<ds>
        parmD   ulPid

cBegin
        IFDEF   FIREWALLS
        color_puts      BLUE,BLACK,<   PMVGA: Called by SHIELD because a Seamless VDM is coming up.>
        ENDIF   ;FIREWALLS
cEnd

ENDIF   ;SEAMLESS

sEnd    Code
end
