;       SCCSID = @(#)devhlp.asm 6.5 91/11/13
SCCSID  equ     "@(#)devhlp.asm 6.5 91/11/13"
PAGE    ,132
TITLE   DevHlp - Dos routines to help device drivers

;**     DEVHLP - Device Driver Helper Routines
;
;
;       This module provides common DOS functions for device drivers.  DOS
;       4.0 device drivers are more complicated due to multitasking.  To
;       simplify the driver writer's task, save memory (installable drivers
;       can get at these routines too), and prevent a multitude of random
;       implementations of the functions, they are now provided in the DOS.
;
;       Some subfunctions are only valid at task-time, some at interrupt
;       time, and some both.  Most of these subfunctions are offered as
;       a convenience, it may be required that some be used... see the
;       individual documentation.
;
;       Modifications:
;
;       14 July 1986    MZ  Remove dh_ConsInpFilt
;
;       29 Jan  1987    JH  add vectors for dekko device
;
;       21 Sep  1988    JSF 1.2 PTM 2168 Correct pause operation on 3xbox
;
;       28 Feb  1991    TCW Keyboard Hot Plug/Reset   DCR1187
;
;       06 Nov  1991    ACW Add support for DHGETDOSV_DEVICECLASSTABLE ;DCR1535
;                           parameter for GetDosVar devhelp add        ;DCR1535
;                           RegisterDeviceClass devhelp                ;DCR1535
;
;       14 Jul  1992    ACW Add support for DHGETDOSV_DMQS_INFO        ;26214
;                           parameter for GetDosVar devhelp            ;26214
;
;       21 Sep  1992        Add support for CTL-ALT-DEL to flush       ;FEATURE
;                           lazy write file buffers.                   ;53227
;
;       26 Sep  1992        Fix for problem with 53227 where memory    ;FIX
;                           is swapped out                             ;54743
;
;       28 Sep  1992    ACW Add support for DHGETDOSV_APM_INFO         ;26087
;                           parameter for GetDosVar devhelp            ;26087
;
;       11 Mar  1994    FJT If coming from panic reboot immediately    ;F78693
;       02 Apr  1996    TPL Use _ShutdownFlag instead                  ;150101
;
;       10 Apr  1996    FJS Added APM11Info to GetDOSVar call          ;d151440
;       30 Apr  1996    TPL Actual fix required for SETBOOT            ;152084
;       02 May  1996    FJS Added POWEROFF_EVENT to SendEvent call     ;d153441

INCL_NOREFERS   equ     1       ; for kmode.inc

.xlist
.xcref
include dosseg.inc
include basemaca.inc
include osmaca.inc
include fnodev.inc
include defdbug.inc
include devsym.inc
include sigdef.inc
include sysserv.inc
include devhlp.inc
include sched.inc
include mi.inc
include pmode.inc
include infoseg.inc
include kmode.inc
include devclass.inc                                            ;DCR1535
include shutdown.inc                                            ;#54743
.cref
.list

        ModName , FNO_DEVHLP_ASM

        ReferTask       CurrTCB,WORD

        ReferGlobal     nPanic,DWORD                                   ;F78693
        ReferGlobal     CtrlNumLkQ,BYTE
        ReferGlobal     CtrlPrtScQ,BYTE
        ReferGlobal     DOSTable,BYTE
        ReferGlobal     InterruptLevel,BYTE
        ReferGlobal     PendingSigArg,WORD
        ReferGlobal     PendingSignals,WORD
        ReferGlobal     SMHotKeyQ,BYTE
        ReferGlobal     Resched,BYTE
        ReferGlobal     TCResched,BYTE
        ReferGlobal     SysInfoSeg,WORD
        ReferGlobal     rwSysInfoSeg,WORD
        ReferGlobal     LocInfoSeg,DWORD
        ReferGlobal     CodePgBuff,WORD
        ReferGlobal     BClock,DWORD
        ReferGlobal     _flGlobalActions,DWORD
        ReferGlobal     DMQS_Sel,WORD               ;26214
        ReferGlobal     _InitCodeInvalid,BYTE       ;#55189
        ReferGlobal     APM_CodeSeg,WORD            ;APM

        ReferGlobal     APM_Length,WORD             ;d151440

        ReferGlobal     _DDInitTime,BYTE

        ReferFlat       _pSigGCH,DWORD

if RAS
        EXTRN           RASRST:FAR
endif

        EXTRN           strp_common:FAR
        EXTRN           TCYield:NEAR
        EXTRN           ProcBlock:NEAR
        EXTRN           ProcRun:NEAR
        EXTRN           ProcRun2:NEAR
        EXTRN           Reboot:far
        extrn           SemClear:near
        extrn           SemRequest:near
        extrn           SysServQueueEvent:near
        EXTRN           Yield:NEAR
        EXTRN           dhFrameCS:abs

        EXTRN32         KMARMCONTEXTHOOK
        EXTRNFAR        SysServReboot
        EXTRNFAR        syiAPM


CONSTANTS       SEGMENT

        EVEN

;*      SchedPtr - Address of SchedClock when scheduler is initialized

        public  SchedPtr
SchedPtr        dd      SchedRet         ; default is just return


;*      The following variables are those accessible via GetDOSVar

        public  ScrnIoOk,CurProcSG,CurProcSW,CurProcPID

ScrnIoOk        DB      1       ; Default is screen writes ok.
CurProcSG       DB      0       ; See GetDOSVar for info
CurProcSW       DW      0
CurProcPID      DW      0
PrintArb        DD      0       ; pointer to print arb. device

        PUBLIC  VectorReboot, VectorMSATS, VectorSDF, VectorDEKKO, VectorRIPL
VectorReboot    DD      DOSCODE:Reboot
VectorMSATS     DD      2 dup (?)

if RAS
VectorSDF       DD      DOSCODE:RASRST
else
VectorSDF       DD      0
endif

VectorDEKKO     DD      DOSHIGH4CODE:strp_common     ;@@ add for deco device
VectorRIPL      DD      0

; Table of pointers to variables returned by GetDOSVar.  Note that the last
; entry, RWSYSINFOSEG, is returned only to the clock driver when it requests
; the SysInfoSeg, entry #1.  (The clock driver gets a r/w selector, everybody
; else gets a r/o selector.)

DOSVarTab LABEL WORD
        DW      0
        DW      DOSGROUP:SysInfoSeg
        DW      DOSGROUP:LocInfoSeg
        DW      0
        DW      DOSGROUP:VectorSDF
        DW      DOSGROUP:VectorReboot
        DW      DOSGROUP:VectorMSATS
        DW      DOSGROUP:Resched
        DW      DOSGROUP:TCResched
        DW      DOSGROUP:DOSTable
        DW      DOSGROUP:VectorDEKKO               ;@@ add for deco device
        DW      DOSGROUP:CodePgBuff
        DW      DOSGROUP:VectorRIPL
        DW      DOSGROUP:InterruptLevel
        DW      DOSGROUP:DevClassTables                 ;DCR1535
        DW      DOSGROUP:DMQS_Sel                       ;feature 26214
        DW      DOSGROUP:APM_CodeSeg                    ;APM
        DW      DOSGROUP:APM_Length                     ;APM 1.1 ;d151440
NDOSVAR EQU ($-DOSVarTab) / 2

        DW      DOSGROUP:rwSysInfoSeg           ; must immediately follow table
RWSYSINFOSEG EQU (($-DOSVarTab) / 2) - 1

Public          DevClassTables                            ;DCR1535
DevClassTables LABEL WORD                                 ;DCR1535
        DW      DOSGROUP:DiskDCTable                      ;DCR1535
        DW      DOSGROUP:MouseDCTable                     ;DCR1535

CONSTANTS       ENDS

CpuMode 386

DATA segment

;  Control-alt-delete handling

Public          CADCount
CADCount        dw                  0   ; init to 0

; Handle to context hook for Control-alt-delete handling

Public          CADHookHandle
CADHookHandle   db size PHOOK dup (0)   ; init to 0

; Table to store Device Class Table entries                  ;DCR1535
                                                             ;DCR1535
DD_table macro   name,maxentries                             ;DCR1535
   public name                                               ;DCR1535
   name LABEL word                                           ;DCR1535
        dw      (0)                                          ;DCR1535
        dw      maxentries                                   ;DCR1535
        db      size DevClassTableEntry * maxentries dup (0) ;DCR1535
endm                                                         ;DCR1535
                                                             ;DCR1535
DD_table   DiskDCTable ,MAXDISKDCENTRIES;                    ;DCR1535
DD_table   MouseDCTable,MAXMOUSEDCENTRIES;                   ;DCR1535

DATA ends

CpuMode reset


        FARCODE LOW
CODE    SEGMENT

;***    SchedAddr - return address of SchedClock pointer
;***    SchedRet - far return
;
;       SchedAddr returns the address of a double word which conntains
;       the address of SchedClock when the scheduler is initialized.
;       The clock device driver should far call the routine at this
;       address once per clock tic.
;
;       The double word at the address returned is 0 until the scheduler
;       is initialized; the device driver must not use the address until
;       it becomes non-zero.
;
;       ENTRY:  NONE
;
;       EXIT:   (ES:BX) = address of double word
;
;       USES:   BX, ES, Flags
;
;               FS and GS must be preserved (DevHelp requirement)

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure SchedAddr
        DosContext es
        mov     bx,offset DOSGROUP:SchedPtr
        ret
EndProc   SchedAddr

Procedure SchedRet,FAR,LOCAL
        pusha                           ;@@     hook the timer services
        push    ds                      ;@@     interrupt handler in so
        push    es                      ;@@     timer services will be
        DosContext <es,ds>,bx           ;@@     available as soon as the
        sti                             ;@@     clock device driver is
        xor     ah,ah                   ;@@     loaded and initialized.
        extrn   Tick_Handler:near       ;@@     Other device drivers need
        call    Tick_Handler            ;@@     timer services at init time.
        pop     es                      ;@@
        pop     ds                      ;@@     took this straight from
        popa                            ;@@     SUTIL.ASM:SchedClock
        ret
EndProc   SchedRet


;**     PullRequest - Pull request packet from linked list
;
;       PullRequest pulls the next waiting packet address from the selected
;       device queue. If there is no packet (such as when characters are typed
;       on the keyboard before they are read), then the zero flag is set on
;       return.
;
;       ENTRY:  DS:SI   Head of device list (should match PushRequest value)
;
;       EXIT:   Zero    Set if there is no request to fulfil
;               Carry   Set if there is no request to fulfil
;               ES:BX   Pointer to device packet
;
;       USES:   BP, Flags
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure PullRequest
;       DBPRT   40h,80h,<PullReq @ $x:$x\n>,<ds,si>
        xor     bp,bp                           ; set the "Z" flag for return
        stc                                     ; set carry (goes with zero)
        pushf                                   ; save interrupt state
        cli                                     ; and disable interrupts
        les     bx,ds:[si]                      ; get 1st in queue
        mov     bp,es
        or      bp,bp                           ; Is there a request?
        jz      plr1                            ; No

        mov     bp,es:[bx].DevLink.Offst
        mov     ds:[si].Offst,bp
        mov     bp,es:[bx].DevLink.Segmt
        mov     ds:[si].Segmt,bp                ; Advance head to next request
;       DBPRT   40h,80h,<Got $x:$x\n>,<es,bx>
        popf                                    ; Restore the interrupt flag
        or      bp,1                            ; Clear the "Z" & carry flags
        ret                                     ; And return

plr1:   ;DBPRT  40h,80h,<NONE\n>,<>
        popf                                    ; Restore the interrupt state
        ret
EndProc   PullRequest


;**     PullParticular - Pull particular request packet from list
;
;       PullParticular pulls the specified packet address from the selected
;       device queue. If the packet is not found, then the zero flag is set
;       on return.
;
;       ENTRY:  DS:SI   Head of device list (should match PushRequest value)
;               ES:BX   Pointer to device packet
;
;       EXIT:   Zero    Set if the specified request is not found
;               Carry   Set if the specified request is not found
;
;       USES:   BP, Flags
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure PullParticular
        xor     bp,bp                           ; set the "Z" flag for return
        stc
        pushf                                   ; save interrupt state
        cli                                     ; disable interrupts
        SaveReg <ax,si,ds>                      ; save registers
        sub     si,DevLink                      ; back up SI as if it were
                                                ; pointing to an actual request
ppt1:   cmp     bx,ds:[si].DevLink.Offst        ; Are the pointers equal??
        jne     ppt2                            ; I guess not.
        mov     ax,es
        cmp     ax,ds:[si].DevLink.Segmt        ; segment registers must match
        je      ppt3

ppt2:   lds     si,ds:[si].DevLink
        mov     bp,ds
        or      bp,bp                           ; Are there any (more) requests?
        jnz     ppt1                            ; Yes. Check for equality

        RestoreReg <ds,si,ax>                   ; restore registers
        popf                                    ; restore the interrupt
        ret

ppt3:   mov     bp,es:[bx].DevLink.Offst
        mov     ds:[si].DevLink.Offst,bp
        mov     bp,es:[bx].DevLink.Segmt
        mov     ds:[si].DevLink.Segmt,bp        ; advance head to next request

        RestoreReg <ds,si,ax>                   ; restore registers
        popf                                    ; restore the interrupt flag
        or      bp,1                            ; clear the "Z" and carry flags
        ret
EndProc   PullParticular,NoCheck


;**     PushRequest - Push request packet on linked list
;
;       PushRequest adds the current device request packet to the list of
;       packets to be executed by the device interrupt routine. When the
;       device routine finds that the request can not be immediately done,
;       it calls this routine which adds the packet to a list. Since the
;       device is active at this point, the caller must have turned off
;       interrupts before checking to see if the device was busy (otherwise
;       a window exists in which the device finishes before the packet is
;       put on the list).
;
;       ENTRY:  DS:SI   Pointer to DWORD head of device list (next request
;                       to do). It should be initialized to 0.
;               ES:BX   Pointer to device request packet
;
;       EXIT:   NONE
;
;       USES:   NONE
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  PushRequest
;       DBPRT   40h,80h,<PushReq $x:$x into $x:$x\n>,<es,bx,ds,si>
        pushf                                   ; save interrupt state
        cli                                     ; must not let device interrupt
        SaveReg <ax,si,ds>                      ; save registers

        mov     es:[bx].DevLink.Segmt,0         ; set this is last entry
        lds     si,ds:[si]                      ; get 1st request in queue
        mov     ax,ds
        or      ax,ax                           ; is this 1st request?
        jnz     prq1                            ; nope, find the end of the que

        RestoreReg <ds,si,ax>                   ; restore registers
        mov     ds:[si].Offst,bx
        mov     ds:[si].Segmt,es                ; set head = new
        popf                                    ; restore interrupt state
        ret

prq1:   cmp     ds:[si].DevLink.Segmt,0         ; reached end of list?
        jz      prq2                            ; yes
        lds     si,ds:[si].DevLink
        jmp     short prq1

prq2:   mov     ds:[si].DevLink.Offst,bx
        mov     ds:[si].DevLink.Segmt,es        ; set next(last) = new
        RestoreReg <ds,si,ax>                   ; restore registers
        popf                                    ; restore interrupt state
        ret
EndProc   PushRequest,NoCheck


;**     SortRequest - Insert request packet on linked list in sorted order
;
;       SortRequest inserts the current device request packet into the list
;       of packets to be executed by the device interrupt routine.  This
;       function is similar to PushRequest except the request packet is
;       inserted in sorted order by starting record number.
;
;       ENTRY:  DS:SI   Pointer to DWORD head of device list (next request
;                       to do). It should be initialized to 0.
;               ES:BX   Pointer to device request packet
;
;       EXIT:   NONE
;
;       USES:   NONE
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure SortRequest
;       DBPRT   40h,80h,<srq10 $x:$x into $x:$x\n>,<es,bx,ds,si>
        pushf                                   ; save interrupt state
        cli                                     ; must not let device interrupt
        SaveReg <ax,dx,si,ds>                   ; save registers
        lds     si,ds:[si]                      ; get 1st request in queue
        mov     ax,ds
        or      ax,ax                           ; is this 1st request?
        jnz     srq10                           ; nope. insert in sorted order

        RestoreReg <ds,si,dx,ax>                ; restore registers
        mov     ds:[si].Offst,bx
        mov     ds:[si].Segmt,es                ; set head = new
        mov     es:[bx].DevLink.Segmt,0         ; set this is last entry
        popf                                    ; restore interrupt state
        ret

srq10:  cmp     ds:[si].DevLink.Segmt,0         ; reached end of list?
        jz      srq70                           ; yes

        mov     dx,es:[bx].IOstart.HighWord     ; (DX:AX) = start sector
        mov     ax,es:[bx].IOstart.LowWord

        cmp     dx,ds:[si].IOstart.HighWord     ; is new.hi < this request?
        jb      srq40                           ; jump if new.hi < this request
        ja      srq20                           ; jump if new.hi > this request
        cmp     ax,ds:[si].IOstart.LowWord      ; is new.lo < this request?
        jb      srq40                           ; jump if new.lo < this request

srq20:  SaveReg <si,ds>                         ; save request packet address
        lds     si,ds:[si].DevLink              ; (DS:SI) = next request in list

        cmp     dx,ds:[si].IOstart.HighWord     ; is new.hi < next request?
        jb      srq60                           ; yes, insert request in list
        ja      srq30                           ; new.hi > next request
        cmp     ax,ds:[si].IOstart.LowWord      ; is new.lo < next request?
        jb      srq60                           ; yes, insert request in list

srq30:  add     sp,4                            ; remove the ds and si on stack
        jmp     short srq10                     ; go to begining of loop

srq40:  SaveReg <si,ds>                         ; save request packet address
        lds     si,ds:[si].DevLink              ; (DS:SI) = next request in list

        cmp     dx,ds:[si].IOstart.HighWord     ; is new.hi > this request?
        ja      srq60                           ; yes, insert request in list
        jb      srq50                           ; jump if new.hi < request
        cmp     ax,ds:[si].IOstart.LowWord      ; is new.lo > this request?
        ja      srq60                           ; yes, insert request in list

srq50:  add     sp,4                            ; remove request packet address
        jmp     short srq10                     ; go to begining of loop

srq60:  RestoreReg <ds,si>                      ; restore request packet address

srq70:  mov     ax,bx                           ; insert request in list
        xchg    ds:[si].DevLink.Offst,ax        ; following request at DS:SI
        mov     es:[bx].DevLink.Offst,ax
        mov     ax,es
        xchg    ds:[si].DevLink.Segmt,ax
        mov     es:[bx].DevLink.Segmt,ax
        RestoreReg <ds,si,dx,ax>                ; restore registers
        popf                                    ; restore interrupt state
        ret
EndProc   SortRequest,NoCheck


;**     QueueFlush - Flush characters in queue
;
;       QueueInit and QueueFlush are functionally equivalent.  They
;       initialize the queue structure variables to indicate the buffer
;       is empty.
;
;       ENTRY   DS:BX   Points to the queue structure to be initialized
;               (The Qsize field must be set up)
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  QueueFlush
        DBPRT   40h,80h,<QueueFlush @ $x:$x\n>,<ds,bx>
        pushf                           ;Save the flags
        cli                             ;And disable interrupts for a bit
        mov     ds:[bx].Qchrout,0       ;Reset the output index
        mov     ds:[bx].Qcount,0        ;...and count of characters.
        popf                            ;Restore interrupts
        ret                             ;And return
EndProc    QueueFlush


;**     QueueWrite - Put character into queue structure
;
;       QueueWrite is called to insert a character onto the end of the
;       specified queue. If the queue is full, the zero flag is set.
;
;       ENTRY   DS:BX   Points to the queue structure
;               AL      The character to insert at the end of the queue
;       EXIT    Z set
;               carry set
;                       queue is full
;               Z clear
;               carry clear
;                       character stored successfully, index updated
;       USES    Flags          @@
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  QueueWrite
        DBPRT   40h,80h,<QueWrite $b into $x:$x\n>,<ax,ds,bx>
        push    bp                      ; save register   @@
        xor     bp,bp                   ; set zero
        stc                             ; set carry
        pushf                           ;Save the flags (Z is reset)
        cli                             ;Disable interrupts for a bit
        mov     bp,DS:[BX].Qcount       ;Get the size of the queue
        cmp     bp,DS:[BX].Qsize        ;Is the queue full??
        jae     QueueFull               ;Yes. Leave
        add     BP,DS:[BX].Qchrout      ;Get the offset of the next char in
        cmp     BP,DS:[BX].Qsize        ;Have we reached the end of the buffer??
        jb      SaveChar                ;Nope. Save the new character
        sub     bp,ds:[bx].Qsize        ;Adjust the input index
SaveChar:
        add     bp,bx                   ;make index into a pointer
        mov     DS:[BP].Qbase,AL        ;Store the character specified
        inc     DS:[BX].Qcount          ;And bump the character count

        popf                            ;Restore the flags
        or      bp,1                    ;Clear the "Z" flag
        pop     bp                      ; restore register @@
        ret

QueueFull:
        popf                            ;Restore the flags
        pop     bp                      ; restore register @@
        ret
EndProc    QueueWrite


;**     QueueRead - Read a character from a queue
;
;       QueueRead is called to remove a character from the beginning of the
;       specified queue. If the queue is empty, the zero flag is set.
;
;       ENTRY   DS:BX   Points to the queue structure
;
;       EXIT    Zero    Set if the queue is empty, otherwise cleared
;               carry   Set if the queue is empty, otherwise cleared
;               AL      The character read from the queue
;       USES    Flags   @@
;
;       FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  QueueRead
        push    bp                   ; save register       @@
        DBPRT   40h,80h,<QueRead from $x:$x\n>,<ds,bx>
        xor     BP,BP                   ; Set zero
        stc                             ; Set carry
        Pushf                           ;Save the flags
        Cli                             ;Disable interrupts for a bit
        Cmp     DS:[BX].Qcount,0        ;Are there any characters in the queue??
        Je      QueueEmpty              ;No. Leave
        Mov     BP,DS:[BX].Qchrout      ;Get the index to the output char
        add     bp,bx                   ;make index into a pointer
        Mov     AL,DS:[BP].Qbase        ;Get the character from the queue
        Mov     BP,DS:[BX].Qchrout      ;Get the index to the output char
        Inc     BP                      ;Increment the output index
        Cmp     BP,DS:[BX].Qsize        ;Have we reached the end of the buffer??
        Jb      SetOutput               ;Nope. Set the new output index
        Xor     BP,BP                   ;Reset the output index
SetOutput:
        Mov     DS:[BX].Qchrout,BP      ;Store the new output index
        Dec     DS:[BX].Qcount          ;And decrement the character count

        popf                            ;Restore the flags
        Or      BP,1
        DBPRT   40h,80h,<Got $b\n>,<ax>
        pop     bp                      ; restore register @@
        ret

QueueEmpty:
        DBPRT   40h,80h,<Q empty.\n>,<>
        popf                            ;Restore the flags...
        pop     bp                      ; restore register @@
        ret
EndProc    QueueRead


;**     GetDOSVar - get address of important DOS variables
;
;       GetDOSVar will return the address of important DOS
;       variables.  The list of variables available is subject
;       to change with different versions of the system.
;
;
;       Index   Value
;
;       1       SysInfoSeg:WORD  Selector for the system (GDT) INFO segment.
;               Valid at both task-time and interrupt-time.  The clock device
;               driver gets a read/write selector, everybody else gets a
;               read/only selector.
;
;       2       LocInfoSeg:DWORD  Selector/Segment address of the local (LDT)
;               INFO segment.  Valid only at task-time.
;
;       3       Reserved
;
;       4       VectorSDF:DWORD  Vector to the stand-alone dump facility.
;               Valid at both task-time and interrupt-time
;
;       5       VectorReboot:DWORD  Vector to reboot the DOS.  Valid at both
;               task-time and interrupt-time.
;
;       6       VectorMSATS:QWORD  Vector to the MSATS facility, protect mode
;               and real mode.  Valid at both task-time and interrupt-time.
;
;       7       YieldFlag:BYTE  Indicator for performing yields.  Valid only
;               at task-time.
;
;       8       TCYieldFlag:BYTE  Indicator for performing time-critical yields.
;               Valid only at task-time.
;
;       9       Reserved
;
;       10      Reserved
;
;       11      CodePgBuff:DWORD  3x Box's current code page tag.
;
;       12      VectorRIPL:DWORD  Vector to the boot environment descriptor
;               table.  Valid at both task-time and interrupt-time.
;               0:0 if not remote IPL.
;
;       13      InterruptLevel:BYTE  Interupt level indicator.  If the value
;               is -1, then it is task time.  If it is !0, then it is the
;               number of nested interrupts.
;
;       14      DCTable:DWORD Segment/offset address of the DeviceClassTable
;               For the specified class.  Table entries are filled in by
;               calling DevHelp REGISTERDEVICECLASS. DCR1535
;               CX = Device Class
;
;       15      DMQS_Sel:WORD  Selector for the XGA DMQS information.
;               Valid at both task-time and interrupt-time. 26214
;
;       16      APM_Info:WORD Information for Advance Power Management.
;               If the first call is from device driver initialization,
;               the APM information is retrieved from BIOS and returned.
;               All subsequent calls just return the APM_Info structure.
;               CX = 0, query APM BIOS presence
;               CX = 1, query APM BIOS presence and establish connection
;               NOTE: If the first call to get this DOS Var is made after
;                     device driver initialization time, the APM_Info will
;                     contain all zeros (no APM info available).
;
;       17      APM11Info:WORD Information for APM 1.1 Provides everything
;               provided with APM_Info plus provides for limits of the APM
;               BIOS code and data segments. New legth field to determine
;               segment version
;
;
;       ENTRY   AL = index of variable wanted, see table above
;                 CX = Device Class if AL=14 or AL=16
;
;       EXIT    if CF clear,
;                       BX:AX is the address of the variable
;               else something is wrong:
;                       AL out of range
;
;       USES    Flags        @@
;
;               FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure GetDOSVar
        LocalVar gv_callerBP,BP         ; caller's BP
        EnterProc
        SaveReg <di,ds>

        cmp     al,NDOSVAR
        jae     gdv6
        or      al,al
        jz      gdv6
        cmp     al,3                    ; if al = 3 set carry and return
        je      gdv6
        cmp     al,14                   ; if al = 14, special  ;DCR1535
        je      gdv14                   ; processing required  ;DCR1535
        cmp     al,16                   ; if al = 16, need to fetch
        je      gdv16                   ; APM info from BIOS

        cmp     al,17                   ; if al = 17, need to fetch ;d151440
        je      gdv16                   ; APM 1.1 info from BIOS    ;d151440

        cmp     al,1
        jne     gdv4

;       Request is for the SysInfoSeg.  Check if the requestor is the clock
;       device driver, and if so, return a read-write selector.  Otherwise,
;       return a read-only selector.

        DosContext ds,bx
        lds     bx,[BClock]             ; ds:si = ptr to clock device header
        mov     bx,[bx].SDevProtCS      ; get clock driver's cs
        mov     di,[gv_callerBP]
        mov     di,ss:[di+dhFrameCS]    ; (di) = caller's CS
        cmp     bx,di                   ; were we called by the clock driver?
        jne     gdv4                    ; no, go handle normal case
        mov     al,RWSYSINFOSEG         ; yes, set index to return r/w infoseg

gdv4:   DosContext ds,bx                ; address dos data area
        xor     ah,ah
        mov     bx,ax                   ; put into index position
        shl     bx,1                    ; convert into word offset (CLEAR CARRY)
        mov     bx,DOSVarTab[bx]
        mov     ax,ds                   ; return pointer in ax:bx
    ASSUME   DS:NOTHING
        jmp short gdv8                  ; get out

;       Request is for the DeviceClassTable.  Check to make sure class is
;       within limits, then return the table entry.

gdv14:
        cmp     cx,0
        jle     gdv6
        cmp     cx,MAXDEVCLASSTABLES
        jg      gdv6
        DosContext ds,ax                ; address dos data area
        xor     bh,bh
        mov     bl,cl
        dec     bx                      ; was 1 based, make it 0 based
        shl     bx,1                    ; convert into word offset (CLEAR CARRY)
        mov     bx,DevClassTables[bx]
        mov     ax,ds                   ; return pointer in ax:bx
    ASSUME   DS:NOTHING
        jmp short gdv8                  ; get out

gdv16:
        DosContext DS
        cmp     _DDInitTime,0            ; Device Driver init time?
        je      gdv4                     ; no, just return var
        CALLFAR syiAPM                   ; yet, have SysInit retrieve APM info
        jmp short gdv4

gdv6:   stc
gdv8:   RestoreReg <ds,di>
        LeaveProc
        ret
EndProc   GetDOSVar


;***    dh_Yield - Yield the CPU
;
;       DOS is designed so that we never pre-emptively schedule the CPU
;       while in kernel mode (indos != 0).  This works well because
;       in general the kernel code either gets its job done and gets out
;       quickly, or it blocks waiting for (usually) I/O or (occasionally)
;       a resource.
;
;       The one part of the kernel that can take a lot of CPU time is in
;       device drivers, typically those that handle memory-mapped screens.
;
;       These drivers should periodically (every 30 milliseconds or so)
;       call the DevHlp_Yield function to yield the CPU if someone else
;       needs it.  Note that most of the time the context won't switch;
;       DevHlp_Yield switches context only if a higher priority task is
;       scheduled to run.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    Flags
;
;       FS and GS must be preserved (DevHelp requirement)

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  dh_Yield
        SaveReg <fs,gs>
        NullContext <fs,gs>             ; Zero FS and GS since we may block
        call    Yield
        RestoreReg <gs,fs>
        ret                             ; exit
EndProc   dh_Yield


;***    dh_YieldTC - Time Critical Yield the CPU if TCResched Set
;
;       DOS is designed so that we never pre-emptively schedule the CPU
;       while in kernel mode (indos != 0).  This works well because
;       in general the kernel code either gets its job done and gets out
;       quickly, or it blocks waiting for (usually) I/O or (occasionally)
;       a resource.
;
;       The one part of the kernel that can take a lot of CPU time is in
;       device drivers, typically those that handle memory-mapped screens.
;
;       These drivers should periodically (every 30 milliseconds or so)
;       call the DevHlp_Yield function to yield the CPU if someone else
;       needs it.  Note that most of the time the context won't switch;
;       DevHlp_Yield switches context only if a higher priority task is
;       scheduled to run.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    Flags
;
;       FS and GS must be preserved (DevHelp requirement)

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  dh_YieldTC
        SaveReg <ds,fs,gs>
        NullContext <fs,gs>             ; Zero FS and GS since we may block
        DosContext ds                   ; Use DS to access DOSGROUP
        test    TCResched,0ffh
        jz      yie1                    ; nobody else wants to run
        call    TCYield
yie1:   RestoreReg <gs,fs,ds>
    ASSUME  DS:NOTHING
        ret                             ; exit
EndProc   dh_YieldTC


;***    dh_ProcBlock - ProcBlock if not at Interrupt Level
;
;       This is the devhlp router for the ProcBlock function.  The
;       sole purpose of this router is to preserve FS and GS.  See
;       ProcBlock for more details.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    Flags
;
;       FS and GS must be preserved (DevHelp requirement)

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  dh_ProcBlock
        SaveReg <fs,gs>
        NullContext <fs,gs>             ; zero FS and GS since we may block
        call    ProcBlock               ; do the block
        RestoreReg <gs,fs>
        ret                             ; exit
EndProc   dh_ProcBlock


;***    dh_ProcRun - DevHelp ProcRun
;
;       This is the devhlp router for the ProcRun function.  The
;       sole purpose of this router is to preserve FS and GS.  See
;       ProcRun for more details.
;
;       ENTRY   (AX:BX) = event identifier
;       EXIT    (AX) = Count of those woken
;              'Z' set according to (ax)
;               CARRY CLEAR
;       USES    NONE
;
;       FS and GS must be preserved (DevHelp requirement)

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  dh_ProcRun
        SaveReg <fs,gs>
        call    ProcRun                 ; do not run with IOBoost
        RestoreReg <gs,fs>
        ret                             ; exit
EndProc   dh_ProcRun


;***    dh_ProcRun2 - DevHelp ProcRun2 (IO-boost)
;
;       This is the devhlp router for the ProcRun2 function.  The
;       sole purpose of this router is to preserve FS and GS.  See
;       ProcRun2 for more details.
;
;       ENTRY   (AX:BX) = event identifier
;       EXIT    (AX) = Count of those woken
;              'Z' set according to (ax)
;               CARRY CLEAR
;       USES    NONE
;
;       FS and GS must be preserved (DevHelp requirement)

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure  dh_ProcRun2
        SaveReg <fs,gs>
        call    ProcRun2                ; run with IOBoost
        RestoreReg <gs,fs>
        ret                             ; exit
EndProc   dh_ProcRun2


;**     dh_SendEvent - Indicate an event
;
;       This routine is called by a device driver to indicate
;       the occurrence of any of a given set of keyboard events:
;
;           event_SM_Mou        - Session Manager request from mouse
;           event_CtrlBrk       - Ctrl-Break pressed
;           event_CtrlC         - Ctrl-C pressed
;           event_CtrlScrLk     - Ctrl-ScrollLock/Ctrl-NumLock pressed
;           event_CtrlPrtSc     - Ctrl-PrtSc pressed
;           event_ShftPrtSc     - Shift-PrtSc pressed
;           event_SM_Kbd        - Ctrl+Esc pressed - Session Manager kbd key
;           event_SM_CAD        - Ctl-Alt-Del pressed
;           event_HOT_PLUG      - Keyboard Hot Plug/Reset   DCR1187
;           event_POWER_ID      - Power event for susspend  Feature #26087
;           event_POWEROFF      - Power off event for APM 1.1 d153441 
;
;       This function may be called in both kernel mode and interrupt
;       mode.
;
;       ENTRY   AH = event number
;               BX = event argument (see spec sect. 2.3.13.10.2)
;
;       EXIT    Carry set if error
;               Carry clear if no error
;
;       USES    AX, BX, CX, DX, BP, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       NOTE:   sends arguments to SysServQueueEvent via AX + DX
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

SendEventTab    dw      DOSCODE:dse3
                dw      DOSCODE:dse5
                dw      DOSCODE:dse6
                dw      DOSCODE:dse10
                dw      DOSCODE:dse12
                dw      DOSCODE:dse13
                dw      DOSCODE:dse2
                dw      DOSCODE:dse1    ;Ctl-Alt-Del
                dw      DOSCODE:dse3    ; Keyboard Hot Plug/Reset DCR1187
                dw      DOSCODE:dse1c   ; Power event - susspension
                dw      DOSCODE:dse1d   ; Power off event d153441 
                .errnz  (event_count - (($ - SendEventTab) SHR 1))

Procedure dh_SendEvent,near

        pusha                           ; Save registers
        SaveReg <ds,es,fs,gs>

        .386p
        NullContext <fs,gs>,cx          ; Zero FS and GS in case we block
        CpuMode reset

        cmp     ah,event_count          ; Event in range?
        jae     dseie                   ;  no, internal error
        mov     cx,bx                   ; Put argument in CX
        mov     bl,ah                   ; Put event number in BX
        xor     bh,bh
        shl     bx,1                    ; Scale to WORD index
        jmp     cs:SendEventTab[bx]     ; Jump to handler

dseie:  InternalError <dh_SendEvent: invalid function number>

;       reboot event.  Arm context hook to reboot machine.
dse1:   CpuMode 386
        DosContext <ds>
        cmp     ds:[nPanic], 0           ; If coming from panic loop    ;F78693
        jne     short dse1a              ; Reboot immediately           ;F78693
        cmp     ds:[CADCount], 1         ; Override hot key if count is 1  ;#$$$
        je      short dse1b              ; Countinue                       ;#$$$
        test    ds:[_InitCodeInvalid], 1 ; Check for init time             ;#55189
        jz      short dse1b              ; if past init time let pm handle ;#55189
        cmp     ds:[_ShutdownFlag], 1    ; Flag set to Shutdown         ;150101
        je      short dse1a

        mov     ds:[CADCount], 1         ; Set variable in the event the    ;#$$$
                                         ; shell hangs, so that a second    ;#$$$
                                         ; ctl-alt-del can be administered  ;#$$$
        ASSUME  DS:NOTHING,ES:NOTHING
        CpuMode  Reset
        mov    cx, CAD_EVENT     ; ctl-alt-del event  #54743
        inc    cx                ; indicate keyboard  #54743
        jmp    short dse4        ; continue           #54743

dse1b:  cmp     dword ptr ds:[CADHookHandle],0 ; hook been allocated yet?   ;#55189
        je      dse1a                          ; no, reboot right here      ;#55189
        FlatContext <ds,es>,ax                                              ;#55189
        PCALL32 KMARMCONTEXTHOOK,<dword ptr [CADHookHandle],KM_GLOBAL_HOOK> ;#55189
        ASSUME  DS:NOTHING,ES:NOTHING                                       ;#55189
        CpuMode Reset                                                       ;#55189
        jmp     dse17                   ; return                            ;#55189

dse1c:  mov    cx, POWER_EVENT   ; Power susspension  #26087
        jmp    short dse4        ; continue           #26087

dse1d:  mov    cx, POWEROFF_EVENT ; Power off event ;d153441 
        jmp    dse4  	          ; continue        ;d153441

;
; End up here if the context hook for cad hasn't been created yet.

dse1a:  CALLFAR SysServReboot           ; DOES NOT RETURN

;       Wake up the session manager daemon
;        compress data for hot key as follows:
;        bits 14 to 1 have event arg data - bit 0 = 1 is kby or = 0 is mouse

dse2:   shl     cx,1                    ; make least sig bit vacant  @@
        inc     cx                      ; indicate keyboard          @@
        and     cx,NOT CAD_EVENT        ; clear Ctl-Alt-Del bit
        jmp     short dse4              ; go to common code          @@

dse3:   shr     cx,1                    ; lose least sig bit of time @@
        shl     cx,1                    ; restore with bit 15 = 0    @@
dse4:   mov     si,offset DOSGROUP:SMHotKeyQ ; Queue offset in SI
        jmp     short dse11             ; Go queue the event

;       Send SIGBREAK

dse5:   mov     al,SIGBREAK             ; Sending SIGBREAK, maybe
        jmp     short dse7              ; Jump to shared code

;       Send SIGINTR

dse6:   mov     al,SIGINTR              ; Sending SIGINTR, maybe

;       Common signal code

dse7:   DosContext ds
;       We cannot send signals at interrupt time, so we'll
;       arm a Global Context Hook which will post the signal ASAP

        xor     bx,bx                   ; Put signal number in BX and scale
        mov     bl,al
        shl     bx,1
        mov     PendingSigArg[bx],cx    ; Save argument
        SigBit                          ; CX = signal bit flag
        or      PendingSignals,cx       ; Set flag (OR clears carry)
        .386p
        bts     [PendingSignals],SIGGCHARMED-1  ; Context hook armed
        jc      short   dse8            ; But it was already armed
        FlatContext <ds,es>,bx
        mov     ebx,[_pSigGCH]
        CCALL32 KMARMCONTEXTHOOK,<ebx, -1>,,pascal
        CpuMode reset
dse8:   clc                             ; Might have been set in bts
        jmp     short dse17

    ASSUME      DS:NOTHING

;       Wake the SysServ_CtrlScrLk daemon

dse10:  mov     si,offset DOSGROUP:CtrlNumLkQ ; Queue offset in SI
dse11:  mov     ax,cx                   ; Queue the argument
        mov     bx,SysServ_SM           ; Wake the daemon
        jmp     short dse15

;       Wake the SysServ_PrtSc daemon on Ctrl-PrtSc

dse12:  mov     ax,1                    ; Set ResponsePacket value
        jmp     short dse14             ; (Jump to common code)

;       Wake the SysServ_PrtSc daemon on Shift-PrtSc

dse13:  xor     ax,ax                   ; Set ResponsePacket value
dse14:  mov     bx,SysServ_PrtSc        ; PrtSc event
        mov     si,offset DOSGROUP:CtrlPrtScQ
dse15:  call    SysServQueueEvent       ; Queue the keyboard event
;       jmp     short dse17             ;  carry clear if wakeup succeeds

        jnc     short dse17             ; normal exit as always         ;152084
        DosContext <ds>                                                 ;152084
        cmp     ds:[CADCount],1                                         ;152084
        jz      dse1b                   ; go arm the CAD context hook   ;152084

;       Error

dse16:  stc

dse17:  RestoreReg <gs,fs,es,ds>        ; Restore registers
        popa                            ; restore all user registers
        ret
EndProc   dh_SendEvent

CODE    ENDS

        END
