;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; 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.;
;*****************************************************************************/

;/*************************************************************************
;*                                                                         
;* SOURCE FILE NAME = UTIL2.ASM                                                                   
;*                                                                                                          
;* DESCRIPTIVE NAME = Mouse swapable utility routines.                         
;*                                                                         
;*                                                                         
;* VERSION      V2.0                                                       
;*                                                                         
;* DATE         08/30/91
;*                                                                         
;* DESCRIPTION  This file contains mouse utility routines.                                      
;*              See linker control file for location in link list.
;*
;* FUNCTIONS    GetDeviceParms   
;*              InitSDevice      
;*              AccessCheck      
;*              QueueRead        
;*              PtrDrawCheck                                                                
;*              GetExtModeData   
;*              FlushMonChain    
;*              SaveCfgData      
;*              GetCfgDataOffset 
;*              SaveExtModeData  
;*              EnableMouse      
;*              DisableMouse     
;*              SGCreate                                                                    
;*              SGSwitchStart    
;*              SGSwitchEnd                                                                 
;*              InitFSCB         
;*                                                                         
;* EXTERNAL FUNCTIONS                                                      
;*                                                                         
;*              NONE                                                       
;*                                                                         
;* CHANGE ACTIVITY =                                                       
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION                       
;*   --------  ----------  -----  --------------------------------------   
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx                                  
;**************************************************************************
                                                                    

.xlist
        include basemaca.inc
        include osmaca.inc
        include mouse.inc
        include emiidc.inc                                                ;emi
.list

CPUMODE 386

;*
;* External symbols used in this module
;*

       extrn  DDD              : byte
       extrn  DDD2             : byte
       extrn  DeviceData       : byte
       extrn  SInt_Packet      : byte                                     ;emi
       extrn  AccessTbl        : byte
       extrn  CallSessn        : byte
       extrn  Ptr_Overide      : byte
       extrn  ext_mode_data    : byte
       extrn  Num_Grps         : byte
       extrn  FullScrCBs       : byte
       extrn  TypeOverRider    : byte
       extrn  FgndSessn        : byte
       extrn  EmiFlags         : byte                                     ;emi

       extrn  DDDInit          : word
       extrn  EMaskMax         : word
       extrn  Eq_Length        : word
       extrn  cfg_offset       : word
       extrn  free_space       : word
       extrn  free_offset      : word
       extrn  DevStatus        : word

       extrn  Device_Help      : dword
       extrn  FgndCB           : dword

       extrn  MSEVDDEntry      : fword

       extrn  AllocCB           : near
       extrn  FindCB            : near
       extrn  Emi_far           : far                                     ;emi

       EXTRNFAR CollisionChk

CSEG2   SEGMENT   WORD  PUBLIC  USE16 'SWAPCODE'
        ASSUME    CS:CSEG2, SS:NOTHING, ES:NOTHING, DS:NOTHING

;*
;* Publics defined by this module
;*

       public  GetDeviceParms
       public  InitSDevice
       public  AccessCheck
       public  QueueRead
       public  PtrDrawCheck
       public  GetExtModeData
       public  FlushMonChain
       public  SaveCfgData
       public  GetCfgDataOffset
       public  SaveExtModeData
       public  EnableMouse
       public  DisableMouse
       public  SGSwitchStart
       public  SGSwitchEnd
       public  SGCreate
       public  InitFSCB

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  GetDeviceParms                             
;*                                                               
;*  DESCRIPTION   :  Call the device dependent DD to get the
;*                   device operation parameters.               
;*                                                                     
;*                   This routine is called to get the device parms
;*                   from the device dependent DD.  It will call the   
;*                   dependent DD only if this is a level 0 call.      
;*                   If the Get passes the DDInit flag is set to       
;*                   indicate so,  if it fails then the flag is set to 
;*                   indicate that also.  Data reporting is enabled
;*                   if the device parms are returned with no error.   
;*                                                               
;*  INPUT         :  None, DS is mouse data seg.
;*                                                               
;*  OUTPUT        : DDInit flag is set. 2=Initialized OK, -1=error.
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  GetDeviceParms
;*
;* IF <this is a level 0 request>
;*    call DDDD(Query_Config) to get device specific data
;*    IF <QueryConfig call worked>
;*       call DDDD(Read_Enable) to turn data reporting on
;*       IF <ReadEnable call worked>
;*          call DDDD(Enable_Device) to enable device interrupts
;*          set IDC flag to show IDC init done
;*       ELSE
;*          set IDC init flag to show IDC init failed.  All requests
;*             except opens and closes will fail device not ready
;*       ENDIF
;*    ELSE
;*       set IDC init flag to show IDC init failed.
;*    ENDIF
;* ENDIF
;* return
;*
;*EndSub  GetDeviceParms
;*
;***********************************************************************

GetDeviceParms  proc  near

       push bx
       push es
       mov  ax, cs                         ; put cs in ax
       and  ax, 0003h                      ; save only CPL bits
       .if <ax eq 0>  NEAR                 ; and we are at level 0
          and  DDDInit, NOT DI_IDC         ; show we've tried to do IDC init
          mov  ax, QUERYCONFIG             ; query config function
          mov  di, offset DeviceData       ; offset to returned data
          push ds                          ; save our ds on stack
          push ds                          ; put our ds in es
          pop  es
          mov  ds, DDD.ProtDS              ; get DDs ds value
          call es:DDD.ProtEntry            ; call DD
          pop  ds
          .if <nc>                         ; if there was no error
             mov  al, DeviceData.NumButt   ; get byte value for # butts
             cbw                           ; convert to word value
             mov  bx, 0ffffh               ; init to all 1's
             mov  cx, ax                   ; get number of buttons
             shl  cx,1                     ; double for with and w/o events
             inc  cx                       ; include motion only events
             shl  bx, cl                   ; put 0's in valid event locations
             not  bx                       ; invert to make valid events 1's
             mov  EMaskMax, bx             ; save max event mask allowed
             mov  ax, READENABLE           ; read enable function
             mov  di, offset SInt_Packet   ; offset to shared data area   ;emi
             push ds                       ; save our ds
             push ds                       ; put ds on stack for switch
             pop  es                       ; to es
             mov  ds, DDD.ProtDS           ; get callers DS
             call es:DDD.ProtEntry         ; go do the call
             pop  ds                       ; restore our ds
             .if <nc>                      ; if no error
                push ds                    ; put ds in fs for call
                pop  fs
                call EnableMouse           ; enable the mouse device now
             .else                         ; else there was an error

;*
;* if EMI is active, then continue even though the dependent mouse driver  ;emi
;* has failed. The system can use the EMI device instead.                  ;emi
;*                                                                          ;emi

                .if  <bit EmiFlags z EMI_ACTIVE>                          ;emi
                   or   DDDInit, DI_ERROR  ; fails all commands           ;emi
                .endif                                                    ;emi
             .endif
          .else                            ; else there was an error
             .if  <bit EmiFlags z EMI_ACTIVE>                             ;emi
                or   DDDInit, DI_ERROR  ; fails all commands              ;emi
             .endif                                                       ;emi
          .endif                           ; end error check on query cfg
       .endif

       pop  es
       pop  bx

       ret

GetDeviceParms  endp


;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  InitSDevice                                
;*                                                               
;*  DESCRIPTION   : Initialize the secondary device
;*                                                               
;*                  This routine is called to get the device parms
;*                  from the device dependent DD.  It will call the   
;*                  dependent DD only if this is a level 0 call.      
;*                  If the Get passes the DDInit flag is set to       
;*                  indicate so,  if it fails then the flag is set to 
;*                  indicate that also.  Data reporting is enabled    
;*                  if the device parms are returned with no error.   
;*                                                               
;*  INPUT         :  None, DS is mouse data seg.
;*                                                               
;*  OUTPUT        : DDInit flag is set. 2=Initialized OK, -1=error.
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.
;*                                                               
;***********************************************************************

InitSDevice  proc  near

       push ax
       push di
       push es

       mov  ax, cs                      ; put cs in ax
       .if <bit ax z 3> NEAR            ; If we are at level 0
          push ds                       ; save our ds
          mov  ax, READENABLE           ; read enable function
          mov  di, offset SInt_Packet   ; offset to shared data area      ;emi
          push ds                       ; put ds on stack for switch
          pop  es                       ; to es
          mov  ds, DDD2.ProtDS          ; get callers DS
          call es:DDD2.ProtEntry        ; go do the call
          pop  ds                       ; restore our ds
       .endif

       pop  es
       pop  di
       pop  ax

       ret

InitSDevice  endp

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  AccessCheck                                
;*                                                               
;*  DESCRIPTION   : Checks access to IOCtl parameter and
;*                    data area addresses                        
;*                                                               
;*                  This routine is used to check access to the
;*                  addresses passed in the IOCtl packet.  All        
;*                  selectors are either valid or NULL.  DOSDEVIOCTL  
;*                  checks this level.  A key value that is unique    
;*                  for each IOCtl request is used to determine what  
;*                  kind of access check to do.  Imbedded in the key  
;*                  is the length of the data area to check.  If this 
;*                  length is 0 this indicates that the 1st word in   
;*                  the data area is its length.  In this case 2      
;*                  access checks are required.  1st to access the    
;*                  length value, second to access the entire data    
;*                  area.                                             
;*                                                               
;*  INPUT         :  BX = requested IOCtl # (0 based)
;*                  BX is used to index a table of keys that indicates   
;*                  what access checking is needed.  Each table entry    
;*                  coresponds to a 2 byte entry.  A key is defined as   
;*                  follows:                                             
;*                     2 bytes:                                          
;*                       byte 1: key for parameter area                  
;*                       byte 2: key for data area                       
;*                       key format (X is the defined bit(s))            
;*                          0000 XXXX - length to check                  
;*                          X000 0000 - reserved 0                       
;*                          0X00 0000 - skip check if selector is NULL   
;*                          00X0 0000 - type of check (0-read, 1-r/w)    
;*                          000X 0000 - 1 indicates check is required    
;*                                                               
;*  OUTPUT        :  Carry clear if no error, Set if error.
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.  BX is preserved.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  AccessCheck
;*
;* FOR <both data and parameter addresses> DO
;*    IF <key indicates access check required>
;*       IF <key indicates selector check required>
;*          IF <selector is NULL>
;*             set flag to skip access check
;*          ENDIF
;*       IF <access check still required>
;*          CheckLength <- length from key
;*          IF <CheckLength is 0>
;*             call Device_Help(VerifyAccess) to check the 1st
;*                word (length of data structure)
;*             IF <access is OK>
;*                CheckLength <- 1st word of user data area
;*             ELSE
;*                set access check failure
;*                leave FOR loop
;*             ENDIF
;*          ENDIF
;*          call Device_Help(VerifyAccess) to check CheckLength
;*             bytes for data access
;*          IF <access is NOT OK>
;*             set access check failure status
;*             leave FOR loop
;*          ENDIF
;*       ENDIF
;*    ENDIF
;* ENDFOR
;*
;* IF <access check failed>
;*    return error
;* ELSE
;*    return no error
;* ENDIF
;* return
;*
;*EndSub  AccessCheck
;*
;***********************************************************************


AccessCheck  proc  near

       ?frame = 0
       LocalVar     Count,WORD
       LocalVar     Status,WORD
       EnterProc

;*
;*      This code loops through the access check key for the requested
;*      IOCtl.  This time through the loop the parameter packet is checked.
;*      The second time the data packet is checked.  First it is determined
;*      wether the packet must be checked or not.  If so then the selector
;*      is checked to see if it is NULL.  If so and a NULL is allowed then
;*      the access check is skipped.
;*

       mov  count, 0                             ; init local vars
       mov  Status, 0
       push si                                   ; save si
       push ax                                   ; save function # (IOCtl)
       mov  dl, DevHlp_VerifyAccess              ; set up devhelp function
       mov  si, GIOParaPack                      ; get offset to addresses
       shl  ax, 1                                ; convert to word offset
       mov  di, ax                               ; put in index register
       .while <Count ne 2>                       ; do 2 times
          mov  cl, fs:AccessTbl[di]              ; get key value
          test cl, CHECKREQD                     ; if an access check
          .if <nz>                               ; is required
             mov  dh, cl                         ; get the key
             shl  dh, 2                          ; and clear all bits except
             shr  dh, 7                          ; the type of check (r,r/w)
             and  cx, 0fh                        ; mask off all but len bits
             mov  ax, es:[bx][si]+2              ; get selector
             test fs:AccessTbl[di], NULLOK       ; if a NULL selector is to be
             .if <nz>                            ; ignored
                test ax, 0fff8h                  ; and the selector is null
                .if <z>                          ; then set the status to
                   or   status, 2                ; indicate that the access
                .endif                           ; check is to be skipped
             .endif

;*
;*         Now see if the access check still needs to be done.  If the
;*         length to check from the key is 0 then we will get the length
;*         to check from the 1st word of the user data area.  This
;*         requires us to do an access check on the 1st word before we
;*         can read the value of the length.  A check is then done on the
;*         rest of the data area.
;*

             test status, 2                      ; see if the access check
             .if <z>                             ; is to be done.

;*
;*              If the length to check is 0 then the first word of the
;*              packet is the length of the packet.  For this case we check
;*              for the required access to the first two bytes first.  If
;*              this test passes then we load the actual lenght and do a
;*              check of the entire packet.
;*

                .if <cx eq 0>                    ; if the length key is 0
                   mov  cx, 2                    ; check 1st word
                   push di                       ; save di
                   mov  di, es:[bx][si]          ; get offset of data area
                   call fs:Device_Help           ; go check it
                   .if <c>                       ; if check failed
                      pop  di                    ; restore di
                      or  status, 1              ; set error status for return
                      .leave                     ; leave the loop
                   .else                         ; otherwise access passed
                      push es                    ; save es
                      mov  es, ax                ; get selector to user area
                      mov  cx, word ptr es:[di]  ; read length field
                      pop  es                    ; restore es
                      pop  di                    ; restore di
                   .endif
                .endif

                push di                          ; save di
                mov  di, es:[bx][si]             ; get offset again
                call fs:Device_Help              ; check acc to rest of buffer
                pop  di                          ; restore di
                .if <c>                          ; if access failed
                   or   status, 1                ; set error status
                   .leave                        ; and leave the loop
                .endif
             .endif

          .endif
          mov  status, 0                         ; reset status (no errors)
          inc  di                                ; go to next key
          add  si, 4                             ; go to next user address
          inc  count                             ; update count
       .endwhile

       pop  ax                                   ; restore function #
       pop  si                                   ; restore MSCB offset
       shr  Status, 1                            ; return status in Carry flag

       LeaveProc                                 ; clear local frame
       ret                                       ; return

AccessCheck  EndP

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  QueueRead                                  
;*                                                               
;*  DESCRIPTION   :  Read or peek into the session event queue.
;*                                                               
;*                   This function is used to read an event or just
;*                   peek at the session event queue.  A peek          
;*                   operation will return the current event without   
;*                   removing the event.  This routine maintains the   
;*                   pointers to the FIFO event queue.  This routine   
;*                   can only be called when there is data in the      
;*                   queue.                                            
;*                                                               
;*  INPUT         :  Stack frame as follows:                              
;*                   SS:[SP]    --> Return address                        
;*                   SS:[SP]+2  --> Size of queue record                  
;*                   SS:[SP]+4  --> Type of read (0=peek,<>0=read)        
;*                   SS:[SP]+6  --> offset of where to return event       
;*                   SS:[SP]+8  --> selector of where to return event     
;*                   SS:[SP]+10 --> offset to event queue control area    
;*                                                               
;*  EXIT-NORMAL   : Always
;*                                                               
;*  EXIT-ERROR    :  N/A
;*                                                               
;*  EFFECTS       :  Queue is updated along with queue pointers.
;*                   Stack is cleaned and no registers modified.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  QueueRead
;*
;* move head record in the queue to output event buffer
;* IF <Read function, NOT a PEEK>
;*    QSize <- QSize - 1
;*    HeadPtr <- HeadPtr + RecordSize
;*    IF <HeadPtr is at end of queue memory>
;*       HeadPtr <- start of queue memory
;*    ENDIF
;* ENDIF
;* return
;*
;*EndSub  QueueRead
;*
;************************************************************************

QueueRead  proc  near

;*
;* These equates are used to access the stack frame.
;*

EventOffset   equ     <(word ptr [bp+4])>
EventSelector equ     <(word ptr [bp+6])>

       enter 0,0                    ; reserve no space

       push es                      ; save es
       push di                      ; save di
       push si                      ; save si
       mov  di, EventOffset         ; get offset of where to return event
       mov  es, EventSelector       ; put selector in es
       mov  cx, ElRec_Size          ; get count of bytes in queue record
       mov  si, [si].Eq_Head        ; get offset of head element
       cld                          ; copy forward
       rep  movsb                   ; copy the event
       pop  si                      ; restore queue control block offset
       dec  [si].Eq_Size            ; one less element in queue
       mov  cx, ElRec_Size          ; get size of element
       add  [si].Eq_Head, cx        ; update head pointer to next element
       mov  cx, [si].E_Queue        ; get start of queue memory
       add  cx, fs:Eq_Length        ; get end of queue memory
       .if <[si].Eq_Head eq cx>     ; if head is at end of queue memory
          mov  cx, [si].E_Queue     ; get start of queue memory
          mov  [si].Eq_Head, cx     ; reset head to start of queue memory
       .endif                       ; end head reset test
       pop  di                      ; restore di
       pop  es

       leave
       ret  4                       ; pop 4 bytes parameter data

QueueRead  EndP

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  PtrDrawCheck                               
;*                                                               
;*  DESCRIPTION   : See if a pointer draw drawing operation
;*                    is allowed.                                
;*                                                               
;*                  This routine is used to check the validity of a
;*                  drawing operation (draw or remove) to the pointer 
;*                  draw DD.  In order for a call to be valid the     
;*                  following checks must all pass:                   
;*                     1.  There must be a ptr draw DD registered.    
;*                     2.  The display mode must be supported.        
;*                     3.  Ptr draw operations must not be disabled.  
;*                     4.  The pointer draw overide flag must be OFF. 
;*                     5.  The caller must be in the foreground.      
;*                     6.  The pointer pos must not be in the         
;*                         collision area.                            
;*                                                               
;*  INPUT         :  bl = foreground session #                            
;*                   si = offset to callers MSCB                          
;*                                                               
;*  OUTPUT        :  AX = 0 if call OK, else AX <> 0 if call not OK.
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;*  BeginSub  PtrDrawCheck
;* 
;*  IF <pointer draw DD is registered  AND
;*      display mode is supported      AND
;*      pointer draw routines used     AND
;*      caller is foreground           AND
;*      pointer draw overide is OFF>
;*     call CollisionChk to see if pointer in collision area
;*     IF <pointer not in collision area>
;*        set call OK
;*     ELSE
;*        set call NOT OK
;*     ENDIF
;*  ELSE
;*     set call NOT OK
;*  ENDIF
;*  return
;* 
;* EndSub  PtrDrawCheck
;* 
;***********************************************************************

PtrDrawCheck  proc  near

       test [si].D_Status, PtrDraw+USS_Mode ; If drawing is allowed
       .if <z>               AND            ; and mode supported
       .if <fs:CallSessn eq bl> AND         ; and caller is foreground
       .if <fs:Ptr_Overide eq OFF>          ; and not in a session switch
          CALLFAR CollisionChk              ; then see if a ptr collision
       .else                                ; else
          mov  ax, 1                        ; set error code
       .endif                               ; end checks

       ret                                  ; ax is set here or by CollisionChk

PtrDrawCheck  endp

;***********************************************************************
;*                                                               
;* FUNCTION NAME : GetExtModeData                               
;*                                                               
;* DESCRIPTION   : This routine will copy the extended mode data for
;*                 the specified session to the buffer pointed to by
;*                 es:di.
;*                                                               
;* INPUT         :  AX = session number                                   
;*                  ES:DI = pointer to buffer for extended mode data      
;*                  CX = length of data to copy                           
;*                  FS = selector where extended mode data is stored.     
;*                                                               
;* OUTPUT        : None.
;*                                                               
;* SIDE EFFECTS  : AX, DI are destroyed.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  GetExtModeData  (get extended mode data)
;*
;* calculate offset for current session
;* move extended mode data from this offset to user data area
;* return
;*
;*EndSub  GetExtModeData
;*
;***********************************************************************

GetExtModeData  proc  near

       push dx                          ; save local regs
       push si                          ; 
       push cx                          ; save length of data
       push ds
       mul  cx                          ; calculate session offset
       mov  si, offset ext_mode_data    ; get start of table
       add  si, ax                      ; calculate offset into mode data
       mov  dx, fs
       mov  ds, dx
       rep  movsb                       ; move the extended mode data
       pop  ds
       pop  cx                          ; restore length of data
       pop  si                          ; restore local regs
       pop  dx                          ; 
       ret                              ; return

GetExtModeData  EndP


;***********************************************************************
;*                                                               
;* FUNCTION NAME : FlushMonChain                                
;*                                                               
;* DESCRIPTION   : This routine will flush a monitor chain.  SI must
;*                 point to the session CB.  This routine handles      
;*                 resetting the event queue pointers, issuing the     
;*                 DevHlp_MonFlush, and setting the necessary device   
;*                 status flags.  Any other required checking must be  
;*                 done by the caller.  The caller must disable        
;*                 interrupts befor calling this routine               
;*                                                               
;* INPUT         :  DS:SI = pointer to session CB
;*                                                                       
;* OUTPUT        : carry set if error, clear if no error                 
;*                                                               
;* SIDE EFFECTS  : Registers preserved.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  FlushMonChain  (flush monitor chain and event queue)
;*
;* set device status to QueueBusy and Flush in progress
;* reset event queue head & tail pointer to start of queue
;* call Device_Help(MonFlush) to send a flush record)
;* save status from MonFlush call
;* IF <flush failed>
;*    reset Flush in progress bit in device status
;* ENDIF
;* set device status to NOT QueueBusy
;* get status from MonFlush call
;* return status from MonFlush call
;*
;*EndSub  FlushMonChain
;*
;***********************************************************************

FlushMonChain  proc  near

       push ax                                 ; save ax during call
       or   [si].D_Status, Busy_Mask           ; Set busy bit

       xor  ah, ah                 ; Clear Reg used to
       mov  [si].Eq_Size, ah       ; Reset que element counter
       mov  ax, [SI].E_Queue       ; Get base event que offset
       mov  [si].Eq_Head, ax       ; Reset Head ptr to base
       mov  [si].Eq_Tail, ax       ; Reset Tail ptr to base

;*
;*     Invoke DEVHLP (MonFlush) to clear SG monitor chain data, only if
;*     there are monitors in the chain.
;*     NOTE: Although it is not documented, the DS register must have the
;*           base Mouse device driver's data selector in it. The monitor
;*           dispatcher uses some info from the device header !!!
;*

       .if <[si].Chain_Size gt 0>
          or   [si].D_Status, Q_Flush         ; Set Que Flush and busy bits
          push ds                             ; Save Mouse base data sel
          mov  ax, [si].Chain_Hdle            ; Get SG Monitor Chain Handle
          push fs
          pop  ds                             ; DS MUST have base data sel
          mov  dl, DevHlp_MonFlush            ; Specify function number
          call Device_Help                    ; Invoke MonFlush function
          pop ds                              ; Restore Mouse base data sel
       .else
          clc
       .endif

       pushf                               ; save carry flag state
       .if c                               ; If MonFlush failed then
          and  [si].D_Status, NOT Q_Flush  ; Reset Flush Flag Bit
       .endif                              ; MonFlush Completion Flag

       and  [si].D_Status, NOT Busy_Mask   ; Que nolonger busy
       popf                                ; restore carry flag for DevHlp call
       pop  ax

       ret

FlushMonChain  EndP

;***********************************************************************
;*                                                               
;* FUNCTION NAME : SaveCfgData                                  
;*                                                               
;* DESCRIPTION   : Save display config data in the config data save
;*                 area.  The configuration data is stored in the
;*                 config data segment.
;*                                                               
;* INPUT         :  BX    = configuration #
;*                  DS:SI = address of configuration data                 
;*                  FS    = config data segment                           
;*                                                               
;* OUTPUT        : Carry set if error detected, clear otherwise.
;*                                                               
;* SIDE EFFECTS  :  Regiseter preserved.
;*                                                               
;************************************************************************
;*  PSEUDOCODE :
;*
;* BeginSub  SaveCfgData  (save display configuration data)
;*
;* IF <config # is between 1 and max supported cfg number>
;*    IF <config #'s data not stored already>
;*       IF <enough FreeSpace for config data>
;*          copy config data to free offset
;*          save offset of config data
;*          FreeSpace <- FreeSpace - Size(config data)
;*          FreeOffset <- FreeOffset + Size(config data)
;*          set no error
;*       ELSE
;*          set error
;*       ENDIF
;*    ELSE
;*       set no error
;*    ENDIF
;* ELSE
;*    set error
;* ENDIF
;*
;* return
;*
;*EndSub  SaveCfgData
;*
;***********************************************************************

SaveCfgData  Proc  near

       .if <bx le MaxSupConfigs> AND   ; if the config number is in range
       .if <bx ge 1>                   ; 
          push bx                      ; save config number
          push cx                      ; save local registers
          dec  bx                      ; convert config number to zero base
          shl  bx,1                    ; convert to word offset
          .if <<word ptr fs:cfg_offset[bx]> eq 0>  ; if not stored already
             mov  cx, word ptr ds:[si] ; get length of config data
             .if <cx be fs:free_space> ; if space available for config data
                push si                ; save config data offset
                push di                ; save di for local use
                push cx                ; save length of config data
                mov  di, fs:free_offset  ; get first available free space
                push es
                push fs
                mov  ax, fs
                mov  es, ax
                mov  word ptr fs:cfg_offset[bx], di   ; save offset to data
                rep  movsb             ; copy config data to our segment
                pop  fs
                pop  es
                pop  cx                ; restore length of config data
                pop  di                ; restore local reg used
                pop  si                ; restore offset to config data
                add  fs:free_offset, cx  ; update free space pointer
                sub  fs:free_space, cx   ; update available free space
                clc                      ; clear carry, no error
             .else                       ; not enough space to store data
                stc                      ; set carry, error detected
             .endif                      ; end test for enough space
          .else                          ; cfg data already stored
             clc                         ; so clear carry for no error
          .endif                         ; end data stored already test
          pop  cx                        ; restore local reg used
          pop  bx                        ; restore config number
       .else                             ; else config number out of range
          stc                            ; set error code
       .endif                            ; end range check on config number
       ret                               ; return

SaveCfgData  EndP


;***********************************************************************
;*                                                               
;* FUNCTION NAME : GetCfgDataOffset                             
;*                                                               
;* DESCRIPTION   : Get the offset to the configuration display data
;*                 for a specific display configuration.  This routine 
;*                 returns the offset only.  ES must be set to the     
;*                 data segment that contains the saved configuration  
;*                 data structures.  The caller must know the segment. 
;*                                                               
;* INPUT         :  BX = configuration number to return pointer for.      
;*                  FS = selector of configuration data save segment.     
;*                                                               
;* OUTPUT        : If error detected then carry is set.                  
;*                 If no error detected then carry is clear and          
;*                 DX contains the offset to the config data.            
;*                                                               
;* SIDE EFFECTS  : Registers preserved.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  GetCfgDataOffset  (get stored config data offset)
;*
;* IF <requested cfg # is between 1 and max supported cfg #>
;*    IF <data is stored for cfg #>
;*       get offset
;*       set no error
;*    ELSE
;*       set error
;*    ENDIF
;* ELSE
;*    set error
;* ENDIF
;*
;* return
;*
;*EndSub  GetCfgDataOffset
;*
;***********************************************************************

GetCfgDataOffset  proc  near

       .if <bx le MaxSupConfigs> AND   ; if the config number is in range
       .if <bx ge 1>                   ; 
          push bx                      ; save configuration number
          dec  bx                      ; convert config number to 0 base
          shl  bx, 1                   ; convert to a word offset
          .if <<word ptr fs:cfg_offset[bx]> ne 0>  ; if config data is stored
             mov  dx, fs:cfg_offset[bx]            ; get config data offset
             clc                                   ; clear carry, no error
          .else                        ; else, no data found
             stc                       ; set carry for error
          .endif                       ; end data stored test
          pop  bx                      ; restore config number
       .else                           ; else config number out of range
          stc                          ; set error
       .endif                          ; range check on config #
       ret                             ; return
GetCfgDataOffset  EndP


;***********************************************************************
;*                                                               
;* FUNCTION NAME : SaveExtModeData                              
;*                                                               
;* DESCRIPTION   : This routine is used to save the extended mode data
;*                 for the session specified.  The data is stored in
;*                 the segment pointed to by ES.
;*                                                               
;* INPUT         :  AX = session number                                   
;*                  CX = length of extended mode data                     
;*                  DS:SI = points to extended mode data                  
;*                  FS = selector of segment to store the mode data       
;*                                                               
;* OUTPUT        : None.
;*                                                               
;* SIDE EFFECTS  : Registers preserved.
;*                                                               
;***********************************************************************
;*
;*BeginSub  SaveExtModeData  (save extended mode data)
;*
;* calculate offset for current session
;* move extended mode data to this offset
;* return
;*
;*EndSub  SaveExtModeData
;*
;***********************************************************************

SaveExtModeData  proc  near

       push dx                     ; save local registers
       push di                     ; 
       push cx                     ; save length of data
       push es
       mov  dx, fs
       mov  es, dx
       mul  cx                     ; calculate mode data session offset
       mov  di, offset ext_mode_data ; get start of mode data save buffer
       add  di, ax                 ; add to get offset for session
       rep  movsb                  ; move the data to the save area
       pop  es
       pop  cx                     ; restore data length
       pop  di                     ; restore local registers
       pop  dx                     ; 
       ret                         ; return

SaveExtModeData  EndP

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  EnableMouse                                
;*                                                               
;*  DESCRIPTION   : Call the device dependent DD to Enable
;*                    the mouse device.                          
;*                                                                    
;*                  This routine is called to enable the mouse
;*                  hardware.  It calls the DDDD to do this action.   
;*                  It provides a common point for invoking this      
;*                  function.                                         
;*                                                                    
;*  INPUT         : FS = mouse data seg
;*                                                               
;*  OUTPUT        : None
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  EnableMouse
;*
;* call DDDD(EnableDevice) to enable the mouse
;* return
;*
;*EndSub  EnableMouse
;*
;***********************************************************************

EnableMouse  proc  near
     .if <bit fs:EmiFlags nz EMI_ACTIVE>                                  ;emi
        push ds                                                           ;emi
        push es                                                           ;emi
        pusha                                                             ;emi
                                                                          ;emi
        push fs                                                           ;emi
        pop ds                                                            ;emi
        mov  ax,EMI_ENABLE                                                ;emi
        call Emi_far                                                      ;emi
                                                                          ;emi
        popa                                                              ;emi
        pop  es                                                           ;emi
        pop  ds                                                           ;emi
     .else                                                                ;emi
        .if <fs:TypeOverRider eq TRUE>
           push ax                 ; save input value
           push es                 ; save our es during call
           push ds
           push fs                 ; same with ds (in fs)
           push fs                 ; put our ds in
           pop  es                 ; es for call to DDD
           mov  ds, es:DDD.ProtDS  ; get DDD ds value
           mov  ax, ENABLEDEVICE   ; function
           call es:DDD.ProtEntry   ; do it
           pop  fs                 ; restore our ds (into fs)
           pop  ds
           pop  es                 ; restore our es
           pop  ax                 ; restore ax
        .else
           clc
           or   fs:DevStatus, gREADENABLE   ; We are ready for events now
        .endif
      .endif
      ret

EnableMouse  endp

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  DisableMouse                               
;*                                                               
;*  DESCRIPTION   : Call the device dependent DD to Disable
;*                    the mouse device.                          
;*                                                               
;*             This routine is called to disable the mouse
;*             hardware.  It calls the DDDD to do this action.   
;*             It provides a common point for invoking this      
;*             function.                                         
;*                                                               
;*  INPUT         :  FS=mouse data seg
;*                                                               
;*  OUTPUT        : None
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.
;*                                                               
;************************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  DisableMouse
;*  
;*  call DDDD(DisableDevice) to disable the mouse
;*  return
;*  
;* EndSub  DisableMouse
;*
;***********************************************************************

DisableMouse  proc  near
     .if <bit fs:EmiFlags nz EMI_ACTIVE>                                  ;emi
        push ds                                                           ;emi
        push es                                                           ;emi
        pusha                                                             ;emi
                                                                          ;emi
        push fs                                                           ;emi
        pop  ds                                                           ;emi
        mov  ax,EMI_DISABLE                                               ;emi
        call Emi_far                                                      ;emi
                                                                          ;emi
        popa                                                              ;emi
        pop  es                                                           ;emi
        pop ds                                                            ;emi
     .else
        .if <fs:TypeOverRider eq TRUE>
           push ax                 ; save input value
           push es                 ; save our es during call
           push ds
           push fs                 ; same with ds
           push fs                 ; put our ds in
           pop  es                 ; es for call to DDD
           mov  ds, es:DDD.ProtDS  ; get DDD ds value
           mov  ax, DISABLEDEVICE  ; function
           call es:DDD.ProtEntry   ; do it
           pop  fs                 ; restore our ds
           pop  ds
           pop  es                 ; restore our es
           pop  ax                 ; restore ax
        .else
           clc
           and  fs:DevStatus, NOT gREADENABLE   ; No events for now
        .endif
     .endif                                                               ;emi
     ret

DisableMouse  endp


;***********************************************************************
;*                                                               
;* FUNCTION NAME : SGCreate                                     
;*                                                               
;* FUNCTION      : Allocate a screen group control block.  The session
;*                 type must have been verified by the caller.
;*                                                               
;* NOTES         : This routine should only be called on a CREATION
;*                 notification where the current session type is a
;*                 fullscreen.
;*                                                               
;* INPUT         : bx = incoming session number
;*                                                               
;* OUTPUT        :  DS:SI points to the new CB if no error, cy set if
;*                  error.
;*                                                               
;* SIDE EFFECTS  :
;*                                                               
;***********************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  SGCreate
;*
;* set number of bytes for the new CB
;* call AllocCB to allocate the new CB
;* IF <no error allocating CB>
;*    IF <Fullscreen session type>
;*       call InitFSCB to initialize the fullscreen CB
;*    ELSE
;*       call InitVDMCB to initialize the VDM CB
;*    ENDIF
;* ELSE
;*    set error code
;* ENDIF
;*
;*EndSub  SGCreate
;*
;***********************************************************************

SGCreate  proc  near

;*
;*      First set the size of the requested CB to be allocated.  Then call
;*      AllocCB to allocate the new control block.
;*
       mov  cx, SIZE ScrGp_Data
       add  cx, Eq_Length

       SaveReg <ax,dx>
       call AllocCB
       RestoreReg <dx,ax>

;*
;*      If there was no error then initialize the new control block.
;*
       .if <nc>
          call InitFSCB
       .endif

       ret

SGCreate  endp


;***********************************************************************
;*                                                               
;* FUNCTION NAME : SGSwitchStart                                
;*                                                               
;* DESCRIPTION   : This routine starts a screen group switch.  It
;*                 removes the pointer image if required and sets a    
;*                 flag to disable the mouse until the switch is done. 
;*                                                               
;* NOTE          : This routine should only be called on a PRESWITCH
;*                 notification where the current session type is a        
;*                 fullscreen.                                             
;*                                                               
;* INPUT         : bx = current session number
;*                                                               
;* OUTPUT        :  pointer removed if needed.  Carry set if error.
;*                                                               
;* SIDE EFFECTS  : no regs preserved.
;*                                                               
;************************************************************************
;* PSEUDOCODE :
;*
;* BeginSub  SGSwitchStart
;*
;* set pointer overide flag to ignore all mouse data
;* IF <session type is Full Screen>
;*    call FindCB to get pointer to session CB
;*    IF <CB found>
;*       IF <pointer needs to be removed>
;*          call PointerDraw to remove the pointer image
;*       ENDIF
;*    ELSE
;*       set error
;*    ENDIF
;* ENDIF
;*
;*EndSub  SGSwitchStart
;*
;***********************************************************************

SGSwitchStart  proc  near

;*
;*      Turn the overide flag on so that all mouse data is ignored until
;*      the switch is complete.  If the session switching away is a fullscreen
;*      session then remove the pointer image if needed.
;*

       mov  Ptr_Overide, ON
       call FindCB
       .if <nc>
          .if <[si].Hdle_Cntr gt 0>  AND
          test [si].D_Status, PtrDraw+USS_Mode
          .if <z>                    AND
          CALLFAR CollisionChk
          .if <ax eq 0>
             mov  ax, bx
             mov  ah, 0
             mov  cx, 0
             RemovePointer
          .endif
          clc
       .endif
       ret

SGSwitchStart  endp


;***********************************************************************
;*                                                               
;* FUNCTION NAME : SGSwitchEnd                                  
;*                                                               
;* DESCRIPTION   : This routine ends a screen switch.  The new session
;*                 is now in the foreground.  The pointer image will
;*                 be redrawn for a fullscreen session if needed.
;*                                                               
;* NOTE          : This routine should only be called on a PROSTSWITCH     
;*                 notification where the current session type is a        
;*                 fullscreen.                                             
;*                                                               
;* INPUT         : bl = incoming session number                           
;*                 bh = incoming session type                             
;*                                                               
;* OUTPUT        :  pointer removed if needed.  Carry set if error.
;*                                                               
;* SIDE EFFECTS  : no regs preserved.
;*                                                               
;************************************************************************
;*
;*BeginSub  SGSwitchEnd
;*
;*
;*EndSub  SGSwitchEnd
;*
;***********************************************************************

SGSwitchEnd  proc  near

       call FindCB
       .if <nc>
          mov  word ptr fs:FgndCB, si
          mov  word ptr fs:FgndCB+2, ds
          mov  fs:FgndSessn, bl
             test [si].D_Status, PtrDraw+USS_Mode
             .if <z> AND
             .if <<word ptr [si].Screen_Entp+2> ne 0>  AND
             .if <[si].Hdle_Cntr gt 0>
                CALLFAR CollisionChk
                .if <ax eq 0>
                   mov  al, bl
                   cbw
                   DrawPointer
                .endif
             .endif
       .endif
       mov  fs:Ptr_Overide, OFF
       ret

SGSwitchEnd  endp

;***********************************************************************
;*                                                               
;*  FUNCTION NAME :  InitFSCB                                   
;*                                                               
;*  DESCRIPTION   : Initialize the Full Screen Control Block
;*                                                               
;*                  This routine is called to initial the control
;*                  block used for a Full Screen Group.               
;*                                                               
;*  INPUT         : ds:si points to the FSCB to initialize
;*                                                               
;*  OUTPUT        : ds:si points to the control block
;*                                                               
;*  SIDE EFFECTS  :  stack is clean.
;*                                                               
;************************************************************************
;*
;*BeginSub  InitFSCB
;*
;* call DDDD(DisableDevice) to disable the mouse
;* return
;*
;*EndSub  InitFSCB
;*
;***********************************************************************

InitFSCB  proc  near


        mov [si].Hdle_Cntr, 0        ; Init handle counter to zero

        mov ax, si                   ; Get offset to start of FS CB
        add ax, SRec_Size            ; End of FS CB is start of event queue
        mov [si].E_Queue, ax         ; Get pointer to event queue

        mov [si].Eq_PID, 0           ; 
        mov [si].D_Status, 0
        mov [si].Chain_Size, 0       ; 
        mov [si].Chain_Hdle, 0       ; 
        mov [si].Screen_Entp, 0      ; 
        mov [si].Screen_Tble, 0      ; 
        mov [si].Screen_DOff, 0      ; 
        mov [si].Screen_DSeg, 0      ; 
        mov [si].MLength, 0          ; 
        mov [si].Ptr_Flags, 0        ; 
        mov [si].Cur_Config, 0       ; 
        mov [si].CfgOffset, 0        ; 
        mov [si].CfgSelector, 0      ; 
        ret

InitFSCB  endp

CSEG2  ENDS
       END
