;       SCCSID = @(#)timserv.asm	6.2 91/05/24
SCCSID  equ     "@(#)timserv.asm	6.2 91/05/24"
;
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
;

        PAGE    60,132
;** Modification History

; @WWM  Changed position of CLI's and STI's in w_TimerAsync for PTM 7131
;       (performance problems)
;
; @L    Fix to call SEMHANDLE to dec ref count if out of timers  PTM 7378
;
; @L    Fix to w_MakeEntry and dh_TickCount to bump tickleft by 1 so that
;       the first call to handler is at least the number of ticks requested.
;       PTM 7413
;
; @TARNG 2/12/88 Fix bugs for PTM 1901
;
; @SUDEEP 08-Feb-1990 Removes TIMER APIs worker routines and subroutines.
;

;.286c
.LIST
;*
;*           SOURCE MODULE    TIMSERV
;*
;******* START OF SOURCE FILE SPECIFICATIONS ********************
;*
;* SOURCE FILE NAME:  Timserv
;* DESCRIPTIVE NAME:  Various Timer Services (see below)
;*
;* COPYRIGHT:   (TOP MODULE ONLY)
;*
;*  STATUS:     RELEASE 1 LEVEL 0
;*
;* NOTES:
;*    DEPENDENCIES:
;*    RESTRICTIONS
;*    PATCH LABEL:      (TOP MODULE ONLY)
;*
;* ENTRY POINTS EXTERNAL TO COMPONENT TIMER SERVICES
;*
;*      w_GetDateTime
;*      dh_SetTimer
;*      dh_TickCount
;*      dh_ResetTimer
;*
;* ENTRY POINTS EXTERNAL TO MODULE TIMSERV
;*
;* OTHER SUBROUTINES CONTAINED IN THIS MODULE
;*
;*      w_EntryChk
;*      w_MakeEntry
;*
;* EXTERNAL TIMER SERVICES (COMPONENT) REFERENCES:
;*
;* EXTERNAL TIMSERV (MODULE) REFERENCES:
;*
;*      None
;*
;************ END OF SOURCE FILE SPECIFICATIONS *****************
.XLIST
.SALL
;
;  INCLUDED LIBRARIES
;
INCL_ERROR_H    equ     1
INCL_ERROR2_H   equ     1
include         fnotask.inc
include         dosseg.inc
include         basemaca.inc
include         osmaca.inc
include         mi.inc
include         timlib.inc
include         bseerr.inc
include         devhdr.inc
include         infoseg.inc
include         kmode.inc

        ModName timserv,FNO_TIMSERV_ASM

        EXTRNFAR        SemSetWait
        EXTRNFAR        SuBuff

        EXTRN32         KMALLOCCONTEXTHOOK
        EXTRN32         KMARMCONTEXTHOOK
        EXTRN32         KMFREEHOOK
.LIST
.XALL  ;
;
; PUBLIC VARIABLES
;

;
; EXTRN VARIABLES
;

ReferGlobal     handler_max,WORD
ReferGlobal     InDos,BYTE
ReferGlobal     InterruptLevel,BYTE
ReferGlobal     handler_list,byte
ReferGlobal     slot_present,BYTE
ReferGlobal     semaphore_handle,DWORD
ReferGlobal     SISData,BYTE
page
        FARCODE LOW
CODE    SEGMENT
        ASSUME  DS:nothing,ES:nothing,SS:nothing

;
;
;********************** START OF SPECIFICATIONS *****************
;*
;* SUBROUTINE NAME:     w_GetDateTime
;* DESCRIPTIVE NAME:    Get date and time from infoseg
;* FUNCTION:    fills DateTime structure with time and day.
;* NOTES:
;* ENTRY POINT: w_GetDateTime
;*    LINKAGE:  near
;*
;* INPUT: BX:DI Address of user buffer.
;*
;* EXIT-NORMAL:  AX = 0
;*
;* EXIT-ERROR:   none
;*
;* EFFECTS:     none
;* INTERNAL REFERENCES:
;* EXTERNAL REFERENCES:
;*********************** END OF SPECIFICATIONS *********************
page
Procedure   w_GetDateTime,HYBRID
;-------------------------------------------------------------------
;      Does bx:di point to something writable and in bounds?
;-------------------------------------------------------------------
        ALIGN   4               ; This align gorp is due to a 386 bug in
                                ;   VERW requiring the last bytes of VERW
                                ;     and the next jump to all be in a DWORD.
        verw    bx              ; Is the passed selector writable?
        jnz     w_GetDT_error   ; No? give bad parm return.
; This LSL isn't subject to errata because we *know* the selector is OK now.
        lsl     ax, bx          ; AX==offset of last writable byte in seg.
        sub     ax, di          ; Is offset it within the segment?
        jc      w_GetDT_error   ; No? give bad parm return.
        cmp     ax, 10          ; Does it "fit"? (10==time structure len-1)
        jb      w_GetDT_error   ; No? give bad parm return.
;------------------- End of pointer validation ------------------------


        mov     es, bx
        DosContext ds

        cli                             ; disable all interrupts
;----------------------------------------------------------------------
;    Copy from the global info seg to the caller's time structure
;----------------------------------------------------------------------
        cld                             ; Strings go ---->
        lea     si, SISData.SIS_HrsTime ; Top of time struct in GDT info seg
        movsw                           ; Block move for hours, minutes
        movsw                           ;    seconds, hundredths
        lodsw                           ; AX==TimeZone
        inc     si                      ; Skip over the 'clock interval' word
        inc     si                      ;
        movsw                           ; Block move for Day-of-month,
        movsw                           ;    month, and year
        stosw                           ; Now, TimeZone fits in.
        movsb                           ; Finish up with 'Day of week' day.
;------------------------------------------------
        sti                             ; enable all interrupts

        xor     ax,ax                   ; no error found
        ret

w_GetDT_error:
        mov     ax, ERROR_INVALID_PARAMETER
        stc
        ret

EndProc w_GetDateTime

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:     w_EntryChk                                  */
;/*                                                                  */
;/* DESCRIPTIVE NAME:    Worker routine to check handler input with  */
;/*                      handlers already registered.                */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*      This routine will check the list of handlers already        */
;/*      registered for a match, return a pointer to the slot        */
;/*      when a match is found, and set a flag if an empty slot      */
;/*      was found.                                                  */
;/*                                                                  */
;/* ENTRY POINT: w_EntryChk                                          */
;/*    LINKAGE:  near                                                */
;/*                                                                  */
;/* EFFECTS:                                                         */
;/*         CX,DX,SI,DI                                              */
;/*                                                                  */
;/*         FS and GS must be preserved (DevHelp requirement)        */
;/*                                                                  */
;/* INPUT:                                                           */
;/*      AX = offset of Tick Handler                                 */
;/*      ES = Seg/Sel of Tick Handler                                */
;/*      DS = Segment of Handler_list                                */
;/*                                                                  */
;/* EXIT-NORMAL:                                                     */
;/*              cf = 0  (handler was found in table)                */
;/*              cf = 1  (no handler match found)                    */
;/*              SI = pointer to slot (if handler registered)        */
;/*              DI = slot number (if handler is registered)         */
;/*              Slot_Present = 0 if no empty slots found            */
;/*              Slot_Present = 1 if an empty slot exists            */
;/*                                                                  */
;/*                                                                  */
;/* EXIT-ERROR:                                                      */
;/*      none                                                        */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*                        handler_list                              */
;/*                        slot_present                              */
;/*                        max_handlers                              */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*               none                                               */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page
;**********************START OF PSEUDO CODE ************************
;
; for slot_number = 1 to max_handlers
;  if handler[slot_number].status == empty
;     set slot_present = 1 to express there exists an empty slot
;  else
;     if handler[slot_number].offset == Tick Handler offset
;        if handler[slot_number].selector/segment == Dev selector/segment
;           clear carry flag
;           return
;        endif
;     endif
;  endelse
; endfor
;
; set carry flag
; return
;
;*********************** END OF PSEUDO CODE ************************

Procedure   w_EntryChk,NEAR             ;@@


        push    bx                      ;@@ save it for tickcount

        lea     si,handler_list         ;@@ point to beginning of table
        xor     di,di                   ;@@
        mov     cx,max_handlers         ;@@ check the entire table
        mov     slot_present,0          ;@@ No empty slot found


try_next_handler:                       ;@@

        inc     di
        cmp     ds:[si].status,0        ;@@ empty slot?
        jne     more                    ;@@ no, go check the offset
        or      slot_present,1          ;@@ we found an empty slot
        jmp     short    try_next_slot  ;@@ keep looking


more:

        cmp     ds:word ptr[si].virtprotect_off,ax ;@@ is the offset the same?
        je      offset_match                       ;@@


try_next_slot:                                     ;@@

        add     si,SIZE handler         ;@@ point to next entry
        loop    try_next_handler        ;@@

        stc                             ;@@ indicate entry was not found
        jmp     short   fini            ;@@


offset_match:                           ;@@

        smsw    bx                      ;@@ If in protect mode then
        test    bx,1                    ;@@
        jnz     try_protect             ;@@ compare protect mode data selector

        mov     bx,es:[0].SDevRealDS        ;@@
        cmp     bx,ds:word ptr[si].realds   ;@@ is the data segment thesame?
        jne     try_next_slot               ;@@
        jmp     short   its_a_match         ;@@


try_protect:                                  ;@@

        mov     bx,es:[0].SDevProtDS          ;@@
        cmp     bx,ds:word ptr[si].protectds  ;@@ is the data selector the same
        jne     try_next_slot                 ;@@


its_a_match:                                  ;@@

        clc                                   ;@@

fini:                                         ;@@

        pop     bx                            ;@@
        ret                                   ;@@

EndProc w_EntryChk                            ;@@

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:     w_MakeEntry                                 */
;/*                                                                  */
;/* DESCRIPTIVE NAME:    Worker routine to register handler in       */
;/*                      handler list.                               */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*      This function takes the information about the handler       */
;/*      from the device driver and makes the appropriate entry      */
;/*      in Handler List.                                            */
;/*                                                                  */
;/* ENTRY POINT: w_MakeEntry                                         */
;/*    LINKAGE:  near                                                */
;/*                                                                  */
;/* EFFECTS:                                                         */
;/*      none                                                        */
;/*                                                                  */
;/*      FS and GS must be preserved (DevHelp requirement)           */
;/*                                                                  */
;/* INPUT:                                                           */
;/*      DS = Data Segment for Handler_List                          */
;/*      ES = Segment for Device Driver                              */
;/*      AX = Offset of DD handler info                              */
;/*      BX = Number of Tick Counts requested                        */
;/*                                                                  */
;/* EXIT-NORMAL:                                                     */
;/*                                                                  */
;/*      cf = 0                                                      */
;/*      Handler_list updated for new handler                        */
;/*                                                                  */
;/* EXIT-ERROR:                                                      */
;/*                                                                  */
;/*      cf = 1     (no slots available)                             */
;/*                                                                  */
;/* INTERNAL REFERENCES:   handler_list                              */
;/*                        max_handlers                              */
;/*                                                                  */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*      none                                                        */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page
;**********************START OF PSEUDO CODE ************************
;
; for slot_number = 1 to max_handlers
;  if handler[slot_number].status == empty(0)
;     if handler_max < slot_number
;        handler_max = slot_number
;     copy offset to handler table
;     set CS and DS segment/selector in the handler table
;     set tick count, ntick
;     if tick count == 0
;        tick count++
;     ticks_left = tick count
;     set handler[slot_number].status = active(1)
;     clear carry flag
;     return
;   endif
; endfor
;
; set carry flag
; return
;
;*********************** END OF PSEUDO CODE ************************

Procedure   w_MakeEntry,NEAR                    ;@@


        lea     si,handler_list                 ;@@
        xor     di,di                           ;@@
        mov     cx,max_handlers                 ;@@


next_slot:                                     ;@@

        inc     di                             ;@@
        cmp     ds:[si].status,0               ;@@ if entry is not active
        je      found                          ;@@
        add     si,size handler                ;@@
        loop    next_slot                      ;@@

        stc                                    ;@@ no empty slots found!
        ret                                    ;@@


found:                                         ;@@
        cmp     di,handler_max                 ;@@
        jbe     osf1                           ;@@
        mov     handler_max,di                 ;@@

osf1:                                          ;@@
        mov     ds:[si].virtprotect_off,ax     ;@@ copy offset to table
        mov     ds:[si].virtreal_off,ax        ;@@

        mov     ax,es:[0].SDevProtCS           ;@@ get protect mode CS selector
        mov     ds:[si].virtprotect_s,ax       ;@@

        mov     ax,es:[0].SDevProtDS           ;@@ get protect mode DS selector
        mov     ds:[si].protectds,ax           ;@@

        mov     ax,es:[0].SDevRealCS           ;@@ get real mode CS selector
        mov     ds:[si].virtreal_s,ax          ;@@

        mov     ax,es:[0].SDevRealDS           ;@@ get real mode DS selector
        mov     ds:[si].realds,ax              ;@@

        mov     ds:[si].ntick,bx               ;@@ set tick count
        or      bx,bx                          ;@L Will ticksleft wrap around?
        jz      osf2                           ;@L  Yes - bypass
        inc     bx                             ;@L Bump ticksleft by 1 so first
                                               ;@L call is at least ntick.
osf2:   mov     ds:[si].ticksleft,bx           ;@@ start counting from top

        mov     ds:[si].status,1               ;@@ Set active

        clc                                    ;@@ Return ok
        ret                                    ;@@
EndProc w_MakeEntry                            ;@@
page
;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:     dh_SetTimer                                 */
;/*                                                                  */
;/* DESCRIPTIVE NAME:    DevHlp routine to register a device timer   */
;/*                      handler.                                    */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*      Inserts an entry in the list of timer tick handlers         */
;/*      that receives control every timer tick.                     */
;/*                                                                  */
;/* ENTRY POINT: dh_SetTimer                                         */
;/*    LINKAGE:  near                                                */
;/*                                                                  */
;/* EFFECTS:    All registers preserved except AX.                   */
;/*                                                                  */
;/*             FS and GS must be preserved (DevHelp requirement)    */
;/*                                                                  */
;/* INPUT:                                                           */
;/*      AX - offset of DD tick handler                              */
;/*      DS - data segment/selector of tick handler.                 */
;/*                                                                  */
;/* EXIT-NORMAL:                                                     */
;/*      Entry representing timer tick handler in Handler_List       */
;/*      Carry Flag Cleared.                                         */
;/*                                                                  */
;/* EXIT-ERROR:                                                      */
;/*      Carry Flag Set (CF=1) if tick handler already registered    */
;/*      or Maximum number of handlers has already been reached.     */
;/*                                                                  */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*               w_EntryChk                                         */
;/*               w_MakeEntry                                        */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page
;**********************START OF PSEUDO CODE ************************
;
; call w_EntryChk to see if entry already exists or slots available
; if entry already exists or any slot is not available
;    set carry flag on
; else call w_MakeEntry(Tick count = 1)
; return
;
;*********************** END OF PSEUDO CODE ************************

Procedure   dh_SetTimer,NEAR            ;@@

        push    bx                      ;@@
        push    cx                      ;@@
        push    dx                      ;@@
        push    si                      ;@@
        push    di                      ;@@
        push    ds                      ;@@
        push    es                      ;@@

        push    ds                      ;@@ Take Data segment/selector and
        pop     es                      ;@@  point ES to Tick Handler DD

        DosContext      ds              ;@@ DS for handler_list


        Call    w_EntryChk              ;@@ See if entry exists and slot status
        jnc     no_can_do               ;@@ Entry already exists

        cmp     slot_present,0          ;@@
        jz      no_can_do               ;@@ No slots left in table

        mov     bx,1                    ;@@ set handler for every timer tick
        call    w_MakeEntry             ;@@ make entry in handler table

        jnc     done                    ;@@ Everything is fine


no_can_do:                              ;@@

        stc                             ;@@ Handler Exists already or no slots


done:                                   ;@@


        pop     es                      ;@@
        pop     ds                      ;@@
        pop     di                      ;@@
        pop     si                      ;@@
        pop     dx                      ;@@
        pop     cx                      ;@@
        pop     bx                      ;@@

        ret                             ;@@

EndProc dh_SetTimer                     ;@@



;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:     dh_ResetTimer                               */
;/*                                                                  */
;/* DESCRIPTIVE NAME:    DevHlp routine to delete a device timer.    */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*      Takes a particular timer tick handler entry out             */
;/*      of the list of timer tick handlers (handler_list)           */
;/*                                                                  */
;/* ENTRY POINT: dh_ResetTimer                                       */
;/*    LINKAGE:  near                                                */
;/*                                                                  */
;/* EFFECTS:    All registers preserved except AX.                   */
;/*                                                                  */
;/*             FS and GS must be preserved (DevHelp requirement)    */
;/*                                                                  */
;/* INPUT:                                                           */
;/*      AX - offset to DD tick handler                              */
;/*      DS - data segment/selector of the tick handler              */
;/*                                                                  */
;/* EXIT-NORMAL:                                                     */
;/*                                                                  */
;/*      Entry representing timer tick handler removed from          */
;/*      Handler_list.                                               */
;/*                                                                  */
;/*      Carry Flag Clear                                            */
;/*                                                                  */
;/* EXIT-ERROR:                                                      */
;/*                                                                  */
;/*      Carry Flag Set = indicating "Invalid Handler"               */
;/*                                                                  */
;/* INTERNAL REFERENCES:   none                                      */
;/*                                                                  */
;/* EXTERNAL REFERENCES:   none                                      */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page
;**********************START OF PSEUDO CODE ************************
;
; call w_EntryChk to see if entry already exists
; if entry does not exist
;    set carry flag on
; else
;    set entry status inactive
;    if slot_number >= highest slot number(handler_max)
;       while not end of handler table
;         slot_number--
;         if status of the slot_number == active
;            set highest slot number(handler_max) = slot_number
;            break
;         endif
;       endwhile
;   endif
; endelse
; return
;
;*********************** END OF PSEUDO CODE ************************

Procedure   dh_ResetTimer,NEAR          ;@@

        push    ds                      ;@@
        push    es                      ;@@
        push    ax                      ;@@
        push    bx                      ;@@
        push    cx                      ;@@
        push    si                      ;@@
        push    di                      ;@@

        push    ds                      ;@@
        pop     es                      ;@@  point ES to Tick Handler DD

        DosContext      ds              ;@@ DS for handler_list


        call    w_EntryChk              ;@@ Verify that the handler is there
        jc      bad_ResetTimer          ;@@

        mov     ds:[si].status,0        ;@@ indicate entry is not active
        cmp     di,handler_max          ;@@ should we reset highest slot number
        jb      end_ResetTimer          ;@@ no,there's a higher registered slot
        mov     ax,OFFSET handler_list  ;@@


reduce_handler_max:                     ;@@

        cmp     si,ax                   ;@@
        jbe     set_handler_max         ;@@ We are at the start of the list
        dec     di                      ;@@
        sub     si,SIZE handler         ;@@
        cmp     ds:[si].status,0        ;@@ Is the previous slot empty?
        jz      reduce_handler_max      ;@@ Yes, reduce again.

set_handler_max:                        ;@@

        mov     handler_max,di          ;@@ Set new end of list

end_ResetTimer:                         ;@@

        clc

bad_ResetTimer:                         ;@@

        pop     di                      ;@@
        pop     si                      ;@@
        pop     cx                      ;@@
        pop     bx                      ;@@
        pop     ax                      ;@@
        pop     es                      ;@@
        pop     ds                      ;@@

        ret                             ;@@


EndProc dh_ResetTimer                   ;@@
;
;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* MODULE NAME: dh_TickCount                                        */
;/*                                                                  */
;/* DESCRIPTIVE NAME:  Modify a Timer Handler's nTick                */
;/*                                                                  */
;/* FUNCTION:    NEW FUNCTION in CP/DOS 1.1                          */
;/*                                                                  */
;/*   For a new timer handler: registers the timer handler to be     */
;/*   called on every N timer ticks instead of every timer tick.     */
;/*   (Not permitted in user or interrupt mode)                      */
;/*                                                                  */
;/*   For a previously-registered timer handler:  changes the number */
;/*   of ticks that must take place before the timer handler gets    */
;/*   control.  Modifies a timer handler registered via SetTimer.    */
;/*                                                                  */
;/*                                                                  */
;/* ENTRY POINT:  dh_TickCount                                       */
;/*    LINKAGE:  near                                                */
;/*                                                                  */
;/* EFFECTS:    All registers preserved except AX.                   */
;/*                                                                  */
;/*             FS and GS must be preserved (DevHelp requirement)    */
;/*                                                                  */
;/* INPUT:                                                           */
;/*      AX - offset of tick handler DD                              */
;/*      DS - data segment of the device driver                      */
;/*      BX - 0-FFFFh tick counts requested for this handler         */
;/*                                                                  */
;/*                                                                  */
;/* EXIT-NORMAL:                                                     */
;/*                                                                  */
;/*      Entry representing timer tick handler in Handler_List       */
;/*          if new entry                                            */
;/*      Tick count is updated if handler was already registered     */
;/*                                                                  */
;/*                                                                  */
;/* EXIT-ERROR:                                                      */
;/*                                                                  */
;/*      Carry Flag set (CF=1) if tick count is invalid (maximum     */
;/*      number of handlers reached or handler already registered.)  */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:                                                     */
;/*               w_EntryChk                                         */
;/*               w_MakeEntry                                        */
;/*                                                                  */
;/* EXTERNAL REFERENCES:  none                                       */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page
;**********************START OF PSEUDO CODE ************************
;
; call w_EntryChk to see if entry already exists or slots available
; if entry already exists
;    save original flags
;    disable interrupts
;    set tick count, ntick, and ticksleft
;    if ticksleft != 0
;       ticksleft++
;    restore original flags
;    clear carry flag
; endif
; else
;    if any slot is not available || in user mode || in interrupt mode
;       set carry flag on
;    else call w_MakeEntry(Tick count)
; endelse
; return
;
;*********************** END OF PSEUDO CODE ************************

Procedure   dh_TickCount,NEAR           ;@@

        push    ds                      ;@@
        push    es                      ;@@
        push    ax                      ;@@
        push    bx                      ;@@
        push    cx                      ;@@
        push    si                      ;@@
        push    di                      ;@@

        push    ds                      ;@@
        pop     es                      ;@@ point ES to Tick Handler DD

        DosContext      ds              ;@@ DS for handler_list


        call    w_EntryChk              ;@@ See if the handler is registered
                                        ;@@     and if any slots open
        jc      new_entry               ;@@ Not there, make a new entry

        pushf                           ;@@@ save current flags
        cli                             ;@@@ clear interrupts
        mov     ds:[si].ntick,bx        ;@@ Set tick count for handler
        or      bx,bx                   ;@L Will ticksleft wrap around?
        jz      dh_T1                   ;@L  Yes - bypass
        inc     bx                      ;@L Bump ticksleft by 1 so first
                                        ;@L call is at least ntick.
dh_T1:  mov     ds:[si].ticksleft,bx    ;@@ Start off ticks left at max
        popf                            ;@@@ restore previous interrupt state

        clc                             ;@@
        jmp     finish                  ;@@


new_entry:                              ;@@

        cmp     slot_present,0          ;@@
        je      nope                    ;@@ no slots available

        cmp     InDos,0                 ;@@
        je      nope                    ;@@ Can't create in user mode

        cmp     InterruptLevel,-1       ;@@ Check that no interrupts active
        jne     nope                    ;@@ Can't create when in interrupt mode


        call    w_MakeEntry             ;@@ make new entry in table
        jmp     finish                  ;@@ carry flag reflects success/failure

nope:   stc                             ;@@ Could not find or create entry


finish:                                 ;@@

        pop     di                      ;@@
        pop     si                      ;@@
        pop     cx                      ;@@
        pop     bx                      ;@@
        pop     ax                      ;@@
        pop     es                      ;@@
        pop     ds                      ;@@

        ret                             ;@@


EndProc dh_TickCount                    ;@@

CpuMode 386


; The following structure is used by the Context Hook DevHelps

dhctxhd struc
    dhd_handler     dd ?                ; 16:16 address of hook handler
    dhd_usr_data    dd ?                ; data to be passed to hook handler
    dhd_prot_ds     dw ?                ; DS of device driver
    dhd_padding     dw ?                ; padding to align the stucture
dhctxhd ends


;***    CallDHHookHandler - thunk to call 16:16 driver address from 32 bit code
;
;       This routine is a worker for _DHContextHook.  It calls the 16:16
;       function address in ECX and returns.
;
;       ENTRY   ECX = 16:16 function address
;
;       EXIT    none
;
;       USES    Flags
;
;       EFFECTS Switches context
;

Procedure   CallDHHookHandler,far16
    assume  ds:nothing, es:nothing

LocalVar    cdhh_Handler,DWORD              ; address of the handler

        EnterProc   <ecx>

        call    [cdhh_Handler]              ; call the 16:16 function
        LeaveProc
        RETP

EndProc     CallDHHookHandler


;***EP  dh_AllocateCtxHook - Allocate a Context Hook
;
;       dh_AllocateCtxHook allocates a global context hook for use by
;       by device drivers
;
;       ENTRY   EAX - offset of hook handler in device driver CS
;               EBX - 0FFFFFFFFh (Global Context)
;               DL  - DevHlp_AllocateCtxHook
;               DS  - protect mode DS for device driver
;
;       EXIT    Carry clear if success
;                   (EAX) = context hook handle
;               Carry set if error
;                   (EAX) = Error code
;       USES    EAX, EFlags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PSEUDOCODE
;
;       allocate a context hook
;       save the CS:IP of the handler routine in the context hook
;       save the DD DS in the context hook
;
Procedure   dh_AllocateCtxHook, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

LocalVar    alch_handler,DWORD          ; hook handler address
LocalVar    alch_ds_seg,WORD            ; Protect DS of the device driver
LocalVar    alch_hook,DWORD             ; handle to the context hook

        SaveReg <ebx, ecx, edx, esi, ds, es, fs, gs>

        EnterProc <eax>                 ; save the offset of the function

        cmp     ebx,KM_GLOBAL_CONTEXT   ; Is this a global context hook request?
        jne     alch90                  ; no, return error

        mov     ax,ds:[0].SDevProtCS    ; (ax) = prot CS of hook handler routine
        mov     [alch_handler].Segmt,ax ; save the segment

        mov     ax,ds:[0].SDevProtDS    ; (ax) = prot DS of the device driver
        mov     [alch_ds_seg],ax        ; save the segment


; We are now ready to try to allocate the context hook


        NullContext <fs, gs>, dx
        FlatContext <ds, es>, dx

        lea     eax,FLAT:alch_hook
        SSToDS  eax

        CCALL32 KMALLOCCONTEXTHOOK <offset FLAT:_DHContextHook, size dhctxhd, KM_GLOBAL_HOOK, eax>,,pascal

        or      eax, eax                ; was there an error?
        jnz     short alch90            ; yes, alloc failed, return error

        mov     eax,[alch_hook]         ; ds:esi -> hook

        KMQueryHookData esi,eax         ; (esi) = reference data pointer

        mov     eax,[alch_handler]
        mov     [esi].dhd_handler,eax   ; store handler address in hook
        mov     ax,[alch_ds_seg]
        mov     [esi].dhd_prot_ds,ax    ; store device driver DS in hook

        mov     eax,[alch_hook]         ; ds:esi -> hook

alchx:  LeaveProc
        RestoreReg <gs, fs, es, ds, esi, edx, ecx, ebx>
        ret

alch90: stc
        jmp     short alchx

EndProc dh_AllocateCtxHook


;***EP  dh_FreeCtxHook - Free a Context Hook
;
;       dh_FreeCtxHook frees a global context hook allocated by
;       dh_AllocateCtxHooks
;
;       ENTRY   EAX - context hook handle
;               DL  - DevHlp_FreeCtxHook
;
;       EXIT    Carry clear if success
;                   (EAX) = 0
;               Carry set if error
;                   (EAX) = Error code
;       USES    EAX, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PSEUDOCODE
;
;       free the context hook
;
Procedure   dh_FreeCtxHook, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

        SaveReg <ebx, ecx, edx, esi, ds, es, fs, gs>

        NullContext <fs, gs>, dx
        FlatContext <ds, es>, dx

        CCALL32 KMFREEHOOK <eax>,,pascal

        xor     eax, eax                ; never an error

        RestoreReg <gs, fs, es, ds, esi, edx, ecx, ebx>
        ret

EndProc dh_FreeCtxHook


;***EP  dh_ArmCtxHook - Arm a Context Hook
;
;       dh_ArmCtxHook arms a context hook allocated by the AllocCtxHook
;       DevHelp.
;
;       ENTRY   EAX - data to be passed on to the hook handler
;               EBX - hook handle returned by AllocCtxHook DevHlp
;               ECX - 0FFFFFFFFh (Global Context)
;               DL  - DevHlp_ArmCtxHook
;
;       EXIT    Carry clear if success
;                   hook is armed
;               Carry set if error
;                   (EAX) = Error code
;       USES    EAX, EFlags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PSEUDOCODE
;
;       save the caller data in the hook
;       call KMArmContextHook to arm the context hook
;
Procedure   dh_ArmCtxHook, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

        SaveReg <ebx, ecx, edx, ds, es, fs, gs>

        cmp     ecx,KM_GLOBAL_CONTEXT   ; Is this a global context hook request?
        jne     arch90                  ; no, return error

        NullContext <fs, gs>, dx
        FlatContext <ds, es>, dx

        KMQueryHookData edx,ebx         ; (edx) = reference data pointer
        mov     [edx].dhd_usr_data,eax  ; save away the user data

        CCALL32 KMARMCONTEXTHOOK <ebx,ecx>,,pascal  ; call hook in global cntext

;BUGBUG KMARMCONTEXTHOOK does not correctly return a value in EAX

;       or      eax,eax                 ; Bool - Did the call succeed?
;       jz      arch90                  ; no, (false) return error

        xor     eax,eax                 ; return no error to the caller

archx:  RestoreReg <gs, fs, es, ds, edx, ecx, ebx>
        ret

arch90: mov     eax,ERROR_INVALID_PARAMETER
        stc
        jmp     short archx

EndProc dh_ArmCtxHook

CpuMode Reset

CODE    ENDS


CpuMode 386

FARCODE HIGH32
HIGH32CODE segment

;***    _DHContextHook - device help context hook routine
;
;       This is a 32-bit routine called as a worker during the enumeration of
;       the VDMs at shutdown time.
;
;       From C: DHContextHook(pFrame,pRefData)
;
;       ENTRY:  SS:ESP          return address
;               SS:(ESP+4)      pRefData -  offset to hook specific data
;               SS:(ESP+8)      pFrame   -  global context (-1)
;
;       RETURNS: EAX = TRUE
;
;       USES: EAX,Flags
;
;       PSEUDOCODE:
;


Procedure _DHContextHook
        ASSUME  DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING,SS:TASKAREA

ArgVar  _dhch_pFrame,DWORD              ; frame pointer
ArgVar  _dhch_pRefData,DWORD            ; dword pointer to reference data

        EnterProc
        SaveReg <eax,ebx,ecx,esi,ds,es>

        mov     esi,[_dhch_pRefData]    ; (esi) -> base of hook data
        mov     eax,[esi].dhd_usr_data  ; (eax) = user data dword
        mov     ecx,[esi].dhd_handler   ; (ecx) = 16:16 function address
        mov     ds,[esi].dhd_prot_ds    ; (ds) = device driver prot DS
        xor     bx,bx
        mov     es,bx                   ; (es) = 0

        ASSUME  DS:NOTHING,ES:NOTHING

        mov     ebx,[_dhch_pFrame]      ; (ebx) = pointer to context (-1)
        CALL16  CallDHHookHandler       ; get routed to device driver

        RestoreReg <es,ds,esi,ecx,ebx,eax>
        LeaveProc
        RETC

EndProc _DHContextHook

HIGH32CODE ends


        END
