;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
;       SCCSID = src/dev/dasd/addcalls/addserva.asm, dasdlib, ddk_subset, b_bdd.032 93/03/21

        page    ,132

;**************************************************************************
;*
;* SOURCE FILE NAME = ADDSERVA.ASM
;*
;* DESCRIPTIVE NAME = ADDCALLS common service routines
;*
;*
;*
;* VERSION = V2.0
;*
;* DATE
;*
;* DESCRIPTION :
;*
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vnnnnn     XXXXX  XXXXXXX
;*
;****************************************************************************/

        title   ADDSERVA - OS/2 2.0 ADD Common Service Routine
        name    ADDSERVA


        .xlist
        include basemaca.inc
        include ABINT13.inc             ; 
        include devhlp.inc              ; device helper definitions
        include infoseg.inc             ; information segment definitions
        include addserv.inc             ; ADD common internal include file
        include addmacs.inc             ; ADD common internal macro file
        .list


LIBDATA segment dword public 'DATA'

        extrn   _Device_Help:dword

        TimerPool_ptr    dd  0          ; save area of The Timer Pool address
        Number_of_Timers dw  0          ; save area of # of timer elements
                         dw  0          ; pad
;  It is defined for debug purpose

        public  XferBuffData, ConvRBAtoCHS, DMASetup
        public  InitTimer, StartTimerMS, CancelTimer, TimerHandler

        XferBuffData    db 0
        ConvRBAtoCHS    db 0
        DMASetup        db 0
        InitTimer       db 0
        StartTimerMS    db 0
        CancelTimer     db 0
        TimerHandler    db 0

LIBDATA ends


LIBCODE segment dword public 'CODE'
        assume  CS:LIBCODE,DS:LIBDATA

        CPUMODE 386
;******************************************************************************
;*
;*  SUBROUTINE NAME:   f_ADD_XferBuffData
;*
;*  DESCRIPTIVE NAME: Transfer buffer data ADD service routine
;*
;*  FUNCTION:     This function moves data to/from a SGList to/from a buffer.
;*
;*  ENTRY POINT:  f_ADD_XferBuffData
;*
;*  LINKAGE:      Far
;*
;*  INPUT:        Near Pointer to parameter block.
;*                The following is the structure of parameter block.
;*
;*                typedef struct _ADD_XFER_DATA
;*                {
;*                  USHORT        Mode;            Direction of xferdata
;*                  USHORT        cSGList;         Count of S/G list elements
;*                  PSCATGATENTRY pSGList;         Far pointer to S/G List
;*                  PBYTE         pBuffer;         Far pointer to buffer
;*                  ULONG         numTotalBytes    total bytes to v to copy
;*                  USHORT        iSGList;         Current index of S/G List
;*                  ULONG         SGOffset;        Current offset
;*                } ADD_XFER_DATA, FAR *PADD_XFER_DATA;
;*
;*                Mode:
;*                #define  SGLIST_TO_BUFFER     From SGList to buffer
;*                #define  BUFFER_TO_SGLIST     From buffer to SGList
;*
;*                NumSectors:
;*                The number of sectors to be copied by this routine.  If
;*                this function ends normally, the caller will get zero in
;*                this field. If it is not zero, it means that whole of data
;*                requested by the caller can't transferred.
;*
;*                iSGList:
;*                It is both of input and output parameters.  The caller sets
;*                the starting index of SGList. And after this routine
;*                executes, it will set the current index of SGList as
;*                output for the next call.
;*
;*                SGOffset:
;*                It is also both of input and output parameters. The caller
;*                sets the offset where starts to xfer in the current S/G
;*                buffer. After execution this routine will return the
;*                current position.
;*
;*  OUTPUT:       AX=0      Normal exit
;*                          NumSectors should be zero.
;*                          iSGList is the current index in SGList.
;*                          SGOffset is the offset in SG buffer.
;*
;*                AX!=0     Error exit
;*                          NumSectors is the same value as input.
;*                          iSGList is the current index in SGList.
;*                          SGOffset is the offset in SG buffer.
;*
;*  PSEUDOCODE:
;*
;*
;******************************************************************************

        ADDDef_F XferBuffData
        ADDArgs  PADD_XFER_DATA,  pXferData

             inc     XferBuffData

             push    Stk.pXferData.sel
             push    Stk.pXferData.off
             call    ADD_XferBuffData

             dec     XferBuffData

        ADDRETF1


;******************************************************************************
;*
;*  SUBROUTINE NAME:   ADD_XferBuffData
;*
;*  DESCRIPTIVE NAME: Transfer buffer data ADD service routine
;*
;*  FUNCTION:     This function moves data to/from a SGList to/from a buffer.
;*
;*  ENTRY POINT:  ADD_XferBuffData
;*
;*  LINKAGE:      Near
;*
;******************************************************************************


pDataBlk     equ    ds:[si]
pSGList      equ    gs:[bx]

        ADDDef   XferBuffData
        ADDArgs  PADD_XFER_DATA,  pXferData

             LocalVar   ulByteCount, DWORD
             LocalVar   ulSGOffset,  DWORD
             LocalVar   uSGIndex,    WORD
             LocalVar   uSGCount,    WORD
             LocalVar   uMode,       WORD
             LocalVar   ucAgain,     BYTE
             LocalVar   uReserved,   BYTE

             inc     XferBuffData

             sub     sp,16

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

             push    ds                          ; save DS for Devhelp call
             pop     fs                          ; 

             lds     si,Stk.pXferData            ; data block offset

             mov     ecx,pDataBlk.X_numTotalBytes
             mov     ulByteCount,ecx             ; save byte count

             mov     ax,pDataBlk.X_Mode
             mov     uMode,ax                    ; save mode for later
             les     di,pDataBlk.X_pBuffer       ; save buffer ptr for later

             mov     eax,pDataBlk.X_SGOffset     ; get current offset
             mov     ulSGOffset,eax              ; set current offset

             mov     ax,pDataBlk.X_iSGList       ; get current index of SG list
             mov     uSGIndex,ax                 ; set current index

             mov     ax,pDataBlk.X_pSGList.sel   ; get SGList selector
             mov     gs,ax                       ; 

X_got_more_entries_to_process:
             mov     ax,uSGIndex                 ; get current index of SGlist
             cmp     ax,pDataBlk.X_cSGList       ; ompare index to # of lists
             jae     X_error_exit                ; current index >= # of lists
             imul    ax,X_SG_LENGTH              ; 
             mov     bx,pDataBlk.X_pSGList.off   ; get SGlist offset
             add     bx,ax                       ; SI = current SG entry offset

             mov     edx,ulSGOffset              ; get current offset
             mov     eax,pSGList.X_ppXferBuf     ; get physical address of buff
             mov     ecx,pSGList.X_XferBufLen    ; get length of buff

             add     eax,edx                     ; calc phys address of start
             sub     ecx,edx                     ; calc length of xfer
             jo      X_error_exit                ; offset > buffer length

             mov     ucAgain,ADD_ERROR           ; assume only one loop
             cmp     ecx,ulByteCount             ; compare SG size to req. size
             je      X_dead_on                   ; SG size = req. size
             jb      X_another_try               ; SG size < req. size

             xchg    ecx,ulByteCount             ; SG size > req. size
             add     ulSGOffset,ecx              ; set offset for next
             mov     ulByteCount,0               ; 
             jmp     short X_ready_to_xfer       ; 

X_another_try:                                   ; 
             mov     ucAgain,ADD_SUCCESS         ; 

X_dead_on:
             sub     ulByteCount,ecx             ; set xfer bytes remaining
             mov     ulSGOffset,0                ; set to beginning od next
             inc     uSGIndex                    ; set next SG index

X_ready_to_xfer:
             SaveReg <ds,es,gs,si,di,cx,bx>
             mov     bx,ax                       ; set ax,bx pysical address
             shr     eax,16

             cmp     uMode,X_SGLIST_TO_BUFFER    ; xfer from SGlist to buff ?
             je      X_from_sglist_to_buffer     ; 

             mov     dh,1                        ; output => ES:DI

             push    ds                          ; buffer => DS:SI
             push    es                          ; SGlist => ES:DI
             pop     ds
             pop     es
             xchg    si,di

             cli
             SaveReg <ds,gs,fs,si>
             push    fs
             pop     ds                          ; DS <= data segment
             DEVHLP  DevHlp_PhysToVirt           ; get selector:offset
             RestoreReg <si,fs,gs,ds>
             jmp     short X_start_xfer

X_from_sglist_to_buffer:                         ; 
             mov     dh,0                        ; output => DS:SI

             cli
             SaveReg <gs,fs,di>
             push    fs
             pop     ds                          ; DS <= data segment
             DEVHLP  DevHlp_PhysToVirt           ; get selector:offset
             RestoreReg <di,fs,gs>

X_start_xfer:                                    ; 
             jc      X_error_exit_1              ; check r/c

             push    cx                          ; 
             and     cx,03h                      ; 
             cld                                 ; 
             rep     movsb                       ; xfer the odd non-DWORD
             pop     cx                          ; restore original count

             shr     cx,2                        ; adjust for DWORD xfer
             rep     movsd                       ; xfer the rest of data

             push    fs
             pop     ds                          ; DS <= data segment
             DEVHLP  DevHlp_UnPhysToVirt         ; relese sel:off from Devhelp

             sti
             RestoreReg <bx,cx,di,si,gs,es,ds>

             cmp     ucAgain,ADD_SUCCESS         ; do we have more iteration ?
             jne     X_its_all_over              ; no, then quit
             add     di,cx                       ; update buffer offset

             xor     cx,cx
             add     ulByteCount,ecx             ; update request count
             jmp     X_got_more_entries_to_process

X_its_all_over:
             mov     ax,uSGIndex
             mov     pDataBlk.X_iSGList,ax       ; restore current SGlist index
             mov     eax,ulSGOffset
             mov     pDataBlk.X_SGOffset,eax     ; restore current offset
             mov     eax,ulByteCount
             mov     pDataBlk.X_numTotalBytes,eax  ; set total bytes remaining
             mov     ax,ADD_SUCCESS
             jmp     short X_xfer_data_exit

X_error_exit_1:
             sti
             RestoreReg <bx,cx,di,si,gs,es,ds>

X_error_exit:
             mov     ax,uSGIndex
             mov     pDataBlk.X_iSGList,ax       ; restore current SGlist index
             mov     eax,ulSGOffset
             mov     pDataBlk.X_SGOffset,eax     ; restore current offset
             mov     eax,ulByteCount
             mov     pDataBlk.X_numTotalBytes,eax  ; set total bytes remaining
             mov     ax,ADD_ERROR

X_xfer_data_exit:
             RestoreReg <edx,ecx,ebx,di,si,fs,gs,es,ds>

             mov     sp,bp

             dec     XferBuffData

             ADDRET1


;******************************************************************************
;*
;*  SUBROUTINE NAME:   f_ADD_ConvRBAtoCHS
;*
;*  DESCRIPTIVE NAME: Conversion from RBA to CHS
;*
;*  FUNCTION:    Converts a Relative Block Address to a Cylinder / Head /
;*               Sector.
;*
;*  EXTRY POINT: f_ADD_ConvRBAtoCHS
;*
;*  LINKAGE:     Call far
;*
;*  INPUT:       ULONG      RBA
;*               NPGEOMETRY Near pointer to Geometry control block
;*               NPCHS_ADDR Near pointer to CHS output.
;*
;*               GEOMETRY and CHS_ADDR structures are defined in IORB.H.
;*
;*  OUTPUT:      Exit normal:    AX = 0
;*                               CHS_ADDR will be filled up.
;*
;*               Exit error:     AX != 0
;*                                - invalid parameter
;*
;*  PSEUDOCODE:  ( RBA ) / ( SectorsPerTrack )
;*                     =>  Remainder + 1  = Sector#
;*
;*               ( Quatient of the above function ) / ( NumberOfHeads )
;*                     =>  Quarient  = Cylinder#
;*                     =>  Remainder = Head#
;*
;******************************************************************************



        ADDDef_F ConvRBAtoCHS
        ADDArgs  ULONG,      RBA
        ADDArgs  PGEOMETRY,  pGeometry
        ADDArgs  PCHS_ADDR,  pCHS

             inc     ConvRBAtoCHS

             push    Stk.RBA.D_high
             push    Stk.RBA.D_low
             push    Stk.pGeometry.sel
             push    Stk.pGeometry.off
             push    Stk.pCHS.sel
             push    Stk.pCHS.off

             call    ADD_ConvRBAtoCHS

             dec     ConvRBAtoCHS

        ADDRETF1

;******************************************************************************
;*
;*  SUBROUTINE NAME:   ADD_ConvRBAtoCHS
;*
;*  DESCRIPTIVE NAME: Conversion from RBA to CHS
;*
;*  FUNCTION:    Converts a Relative Block Address to a Cylinder / Head /
;*               Sector.
;*
;*  EXTRY POINT: ADD_ConvRBAtoCHS
;*
;*  LINKAGE:     Call near
;******************************************************************************

sCHS         equ    es:[di]
sGeometry    equ    ds:[si]

        ADDDef   ConvRBAtoCHS
        ADDArgs  ULONG,      RBA
        ADDArgs  PGEOMETRY,  pGeometry
        ADDArgs  PCHS_ADDR,  pCHS

             inc     ConvRBAtoCHS

             SaveReg <ds,es,di,si,ebx,dx>

             lds     si,Stk.pGeometry             ; Geometry offset
             les     di,Stk.pCHS                  ; CHS offset
                                                  ; 
             mov     bx,sGeometry.C_SectorsPerTrack ; get sectors per track
             or      bx,bx                        ;  if 0 => error
             jz      C_error_exit                 ; ( to avoid divided by zero)
                                                  ; 
             mov     eax,Stk.RBA                  ; get RBA
             xor     edx,edx                      ; 
             movzx   ebx,bx                       ; 
             div     ebx                          ; RBA / Sectors per track
                                                  ; 
             inc     dx                           ; Remainder + 1
             xor     dh,dh                        ; Sector field is byte field
             jnz     C_error_exit                 ;  if more than 1 byte => err
             mov     sCHS.C_Sector,dl             ; set Sector number
                                                  ; 
             mov     bx,sGeometry.C_NumHeads      ; get the number of heads
             or      bx,bx                        ; if 0 => error
             jz      C_error_exit                 ; ( to avoid divided by zero)
                                                  ; 
             xor     edx,edx                      ; 
             movzx   ebx,bx                       ; 
             div     ebx                          ; Quitient / Number of heads
                                                  ; 
             mov     bx,ax                        ; Cylinder filed is 2 bytes
             shr     eax,16                       ;  if more than 2 bytes
             or      ax,ax                        ;         => error
             jnz     C_error_exit                 ; 
                                                  ; 
             mov     sCHS.C_Cylinder,bx           ; set cylinder number
                                                  ; 
             or      dh,dh                        ; Head field is byte
             jnz     C_error_exit                 ;  if more than 1 byte => err
                                                  ; 
             mov     SCHS.C_Head,dl               ; set head number
                                                  ; 
             mov     ax,ADD_SUCCESS               ; set return code
             jmp     short C_conv_exit            ;     and goto exit
                                                  ; 
C_error_exit:                                     ; 
             mov     ax,ADD_ERROR                 ; set return code
                                                  ; 
C_conv_exit:                                      ; 
             RestoreReg <dx,ebx,si,di,es,ds>      ; 

             dec     ConvRBAtoCHS

             ADDRET1                              ; 
                                                  ; 


;******************************************************************************
;*
;*  SUBROUTINE NAME:  f_ADD_DMASetup
;*
;*  DESCRIPTIVE NAME: DMA setup ADD service routine
;*
;*  Function:    Set the DMA channel up to do the I/O
;*
;*  Entry Point: f_ADD_DMASetup
;*
;*  LINKAGE:     Far
;*
;*  INPUT:       USHORT   DMA mode
;*               USHORT   DMA channel number
;*               USHORT   The number of bytes
;*               ULONG    24 bits
;*
;*  OUTPUT:      Exit normal:    AX = 0
;*
;*               Exit error:     AX != 0
;*                                - Over 64K boundary
;*
;*  PSEUDOCODE:
;*
;******************************************************************************

        ADDDef_F DMASetup
        ADDArgs  USHORT,     DMA_mode
        ADDArgs  USHORT,     DMA_channel
        ADDArgs  USHORT,     numBytes
        ADDArgs  ULONG,      address

             inc     DMASetup

             push    Stk.DMA_mode
             push    Stk.DMA_channel
             push    Stk.numBytes
             push    Stk.address.D_high
             push    Stk.address.D_low

             call    ADD_DMASetup

             dec     DMASetup

        ADDRETF1


;******************************************************************************
;*
;*  SUBROUTINE NAME:  ADD_DMASetup
;*
;*  DESCRIPTIVE NAME: DMA setup ADD service routine
;*
;*  Function:    Set the DMA channel up to do the I/O
;*
;*  Entry Point: ADD_DMASetup
;*
;*  LINKAGE:     Near
;*
;******************************************************************************

        ADDDef   DMASetup
        ADDArgs  USHORT,     DMA_mode
        ADDArgs  USHORT,     DMA_channel
        ADDArgs  USHORT,     numBytes
        ADDArgs  ULONG,      address

             inc     DMASetup

             SaveReg <es,di,si,bx,cx,dx>
             DISABLE                          ; Prevent net card from coming in
                                              ; 
             mov     bx,Stk.address.D_low     ; get low word of address
             mov     cx,Stk.numBytes          ; get the number of bytes
             mov     dx,bx
             add     dx,cx                    ; check 64K boundary
             jc      D_error_exit             ; if overlow => error
                                              ; 
             mov     ax,Stk.DMA_mode          ; get DMA mode
             mov     dx,Stk.DMA_channel       ; get DMA channel
             mov     ah,dl                    ; AL=>DMA mode AH=>DMA channel
             push    ax                       ; save channel and mode
                                              ; 
             xchg    al,ah                    ; 
             or      al,4                     ; 
             out     PDMA+10,al               ; set channel's mask bit
             IOWait                           ; 
             out     PDMA+12,al               ; clear byte pointer F/F
             IOWait                           ; 
                                              ; 
             pop     ax                       ; restore channel and mode
             push    ax                       ; save again
                                              ; 
             or      al,ah                    ; add channel number to command
             out     PDMA+11,al               ; set DMA mode
             IOWait                           ; 
                                              ; 
             mov     dx,PDMA                  ; 
             rol     ah,1                     ; 
             add     dl,ah                    ; 
             mov     ax,bx                    ; get low word of address
             out     dx,al                    ; output low byte address
             IOWait                           ; 
                                              ; 
             xchg    ah,al                    ; 
             out     dx,al                    ; output second byte address
             IOWait                           ; 
                                              ; 
             inc     dx                       ; address `word' count register
             mov     ax,cx                    ; get # of bytes
             dec     ax                       ; 
             out     dx,al                    ; 
             xchg    ah,al                    ; 
             IOWait                           ; 
                                              ; 
             out     dx,al                    ; tell DMA how many bytes
             pop     ax                       ; get back DMA mode and channel
             push    ax                       ; 
                                              ; 
             mov     dl,PDMAX                 ; 
             add     dl,ah                    ; 
             mov     ax,Stk.address.D_high    ; 
             IOWait                           ; 
             out     dx,al                    ; output highest byte of address
                                              ; 
             pop     ax                       ; 
             ENABLE                           ; 
                                              ; 
             IOWait                           ; 
             xchg    al,ah                    ; 
             out     PDMA+10,al               ; initialize DMA channel
                                              ; 
                                              ; 
             mov     ax,ADD_SUCCESS           ; 
             jmp     short D_setup_exit       ; 
                                              ; 
D_error_exit:                                 ; 
             ENABLE                           ; 
             mov     ax,ADD_ERROR             ; 
                                              ; 
D_setup_exit:                                 ; 
             RestoreReg <dx,cx,bx,si,di,es>

             dec     DMASetup

             ADDRET1


;******************************************************************************
;*
;*  SUBROUTINE NAME:   f_ADD_InitTimer
;*
;*  DESCRIPTIVE NAME:  Initialize Timer
;*
;*  FUNCTION:          Initializes the timer service
;*
;*  LINKAGE:           call far
;*
;*  PARAMETERS:        Far pointer to the timer data pool
;*                     The size of the timer pool in bytes
;*
;*  EXIT-NORMAL:       AX = 0   ( timer handle )
;*
;*  EXIT-ERROR:        AX != 0
;*
;*  PSEUDOCODE:
;*
;******************************************************************************

         ADDDef_F InitTimer                   ; 
         ADDArgs  PBYTE,     pTimerPool       ; 
         ADDArgs  USHORT,    lnTimerPool      ; 
                                              ; 
         inc      InitTimer

         push     Stk.pTimerPool.sel          ; push parameters
         push     Stk.pTimerPool.off          ; 
         push     Stk.lnTimerPool             ; 
                                              ; 
         call     ADD_InitTimer               ; call near routine

         dec      InitTimer
                                              ; 
         ADDRETF1                              ; 

;******************************************************************************
;*
;*  SUBROUTINE NAME:   ADD_InitTimer
;*
;*  DESCRIPTIVE NAME:  Initialize Timer
;*
;*  FUNCTION:          Initializes the timer service
;*
;*  LINKAGE:           call near
;*
;******************************************************************************

TimerPool    equ   es:[di]

         ADDDef   InitTimer
         ADDArgs  PBYTE,     pTimerPool
         ADDArgs  USHORT,    lnTimerPool

             inc      InitTimer

             SaveReg <es,di,si,bx,cx,dx>
                                              ; 
             mov     cx,Stk.lnTimerPool       ; get timer pool length
             xor     bx,bx                    ; 
             les     di,Stk.pTimerPool        ; set pointer to timer pool
                                              ; 
TI_loop1:                                     ; clear timer pool
             mov     byte ptr TimerPool[bx],0 ; 
             inc     bx                       ; 
             loop    TI_loop1                 ; 
                                              ; 
             mov     al,DHGETDOSV_SYSINFOSEG  ; get system global infoseg

             SaveReg <es,ds,si,cx,dx>
             DEVHLP  DevHlp_GetDOSVar         ; 
             RestoreReg <dx,cx,si,ds,es>

             jc      TI_error_exit            ; if carry on => error
                                              ; 
             push    es                       ; 
             mov     es,ax                    ; ax:bx => sysinfoseg
             mov     es,word ptr es:[bx]      ; 
             xor     bx,bx                    ; es:bx => addr/sysinfo
             mov     ax,es:[bx].SIS_ClkIntrvl ; get timer interval
                                              ; 
             xor     dx,dx                    ; convert MS/TICK
             mov     cx,10                    ; 
             div     cx                       ; 
                                              ; 
             pop     es                       ; 
             mov     TimerPool.MTick,ax       ; save MS/TICK
                                              ; 
             mov     ax,offset f_ADD_TimerHandler ; ax <= Timer Handler offset
                                              ; 
             SaveReg <es,gs,di,si,bx,cx>
             cli                              ; 
             DEVHLP  DevHlp_SetTimer          ; Set timer handler to be
             sti                              ;    called on every timer tick
             RestoreReg <cx,bx,si,di,gs,es>

             jc      TI_error_exit            ; if carry on => error
             mov     eax,Stk.pTimerPool       ; save ptr to Pool
             mov     dword ptr TimerPool_ptr,eax  ; save ptr to Pool
             xor     dx,dx                    ; 
             mov     ax,Stk.lnTimerPool       ; culc. how many timer elements
             mov     cx,lnTimerData           ; 
             div     cx                       ; 
             mov     Number_of_Timers,ax      ; save number of elements
             mov     ax,ADD_SUCCESS           ; set return code
             jmp     short TI_exit            ; 
                                              ; 
TI_error_exit:                                ; 
             mov     ax,ADD_ERROR             ; set return code and go to exit

TI_exit:
             RestoreReg <dx,cx,bx,si,di,es>

             dec     InitTimer

             ADDRET1                          ; 
                                              ; 

;******************************************************************************
;*
;*  SUBROUTINE NAME:   f_ADD_DeInstallTimer
;*
;*  DESCRIPTIVE NAME:  Deinstall Timer Support
;*
;*  FUNCTION:
;*
;*  LINKAGE:           call far
;*
;*  PARAMETERS:
;*
;*
;*  EXIT-NORMAL:
;*
;*  EXIT-ERROR:
;*
;*  PSEUDOCODE:
;*
;******************************************************************************

         ADDDef_F DeInstallTimer              ; 
                                              ; 
         call     ADD_InitTimer               ; call near routine

         dec      InitTimer
                                              ; 
         ADDRETF1                              ; 

;******************************************************************************
;*
;*  SUBROUTINE NAME:   ADD_DeInstallTimer
;*
;*  DESCRIPTIVE NAME:  Disable Timer Support
;*
;*  FUNCTION:
;*
;*  LINKAGE:           call near
;*
;******************************************************************************

         ADDDef   DeInstallTimer

             mov     ax,offset f_ADD_TimerHandler ; ax <= Timer Handler offset

             DEVHLP  DevHlp_ResetTimer

             mov     Number_of_Timers, 0

             ADDRET1                          ; 


;******************************************************************************
;*
;*  SUBROUTINE NAME:   f_ADD_StartTimerMS
;*
;*  DESCRIPTIVE NAME:  Start Timer
;*
;*  FUNCTION:          This function set the interval of the timer
;*
;*  LINKAGE:           call far
;*
;*  PARAMETERS:        Far pointer to timer handle
;*                     Timer interval
;*                     Notify address
;*                     parameters which ADD wants when the timer is expired
;*
;*  EXIT-NORMAL:       AX = 0
;*
;*  EXIT-ERROR:        AX != 0
;*
;*  PSEUDOCODE:
;*
;******************************************************************************

        ADDDef_F StartTimerMS                   ; 
        ADDArgs  PULONG,     phTimer            ; 
        ADDArgs  ULONG,      vTimer             ; 
        ADDArgs  PFN,        addrNotify         ; 
        ADDArgs  PVOID,      parm_1             ; 
        ADDArgs  PVOID,      parm_2             ; 
                                                ; 
             pushf
             DISABLE
             inc     StartTimerMS

             push    Stk.phTimer.sel            ; push parameters
             push    Stk.phTimer.off            ; 
             push    Stk.vTimer.D_high          ; 
             push    Stk.vTimer.D_low           ; 
             push    Stk.addrNotify.sel         ; 
             push    Stk.addrNotify.off         ; 
             push    Stk.parm_1.sel             ; 
             push    Stk.Parm_1.off             ; 
             push    Stk.parm_2.sel             ; 
             push    Stk.Parm_2.off             ; 
                                                ; 
             call    ADD_StartTimerMS           ; call near routine

             dec     StartTimerMS
             popf
                                                ; 
        ADDRETF1                                 ; 

;******************************************************************************
;*
;*  SUBROUTINE NAME:   ADD_StartTimerMS
;*
;*  DESCRIPTIVE NAME:  Start Timer
;*
;*  FUNCTION:          This function set the interval of the timer
;*
;*  LINKAGE:           call near
;*
;******************************************************************************

TimerData  equ   es:[di+bx]                     ; 
                                                ; 
        ADDDef   StartTimerMS                   ; 
        ADDArgs  PULONG,     phTimer            ; 
        ADDArgs  ULONG,      vTimer             ; 
        ADDArgs  PFN,        addrNotify         ; 
        ADDArgs  PVOID,      parm_1             ; 
        ADDArgs  PVOID,      parm_2             ; 
                                                ; 
             inc     StartTimerMS

             SaveReg <es,di,ebx,cx,edx>         ; 
                                                ; 
             les     di,TimerPool_ptr           ; set timer pool address
             add     di,2                       ; add "MTick" length
             xor     bx,bx                      ; set timer data address in pool
             mov     cx,Number_of_Timers        ; 
                                                ; 
TS_loop1:                                       ; 
             cmp     TimerData.NotifyEntry,0    ; look for available entry
             je      TS_entry_available         ; 
             add     bx,lnTimerData             ; 
             loop    TS_loop1                   ; 
             jmp     short TS_error_exit        ; 
                                                ; 
TS_entry_available:                             ; 
             mov     eax,Stk.vTimer             ; 
             mov     edx,Stk.addrNotify         ; 
             mov     TimerData.NotifyEntry,edx  ; save notify address
             mov     edx,Stk.parm_1             ; 
             mov     TimerData.Parm1,edx        ; save parameter ADD wants
             mov     edx,Stk.parm_2             ; 
             mov     TimerData.Parm2,edx        ; save parameter ADD wants
                                                ; 
             push    di                         ; save ptr to the start of data
             push    bx

             sub     di,2                       ; back to the strat of pool
             xor     edx,edx                    ; convert to tick
             movzx   ebx,TimerPool.MTick        ; 
             div     ebx                        ; 
             or      edx,edx                    ; if dx!=0 => raising value
             jnz     TS_increment_value         ; 
             or      eax,eax                    ; if value is 0 => one tick
             jnz     TS_not_increment           ; 
                                                ; 
TS_increment_value:                             ; 
             inc     ax                         ; 
                                                ; 
TS_not_increment:                               ; 
             inc     ax                         ; partial timer tick
             pop     bx                         ; 
             pop     di                         ; 
                                                ; 
             mov     TimerData.Interval,eax     ; save timer interval
             mov     TimerData.BackupInterval,eax ; save timer interval for backup

             xor     dx,dx                      ; culc. entry index
             movzx   eax,bx                     ;     => timer handle
             mov     cx,lnTimerData             ; 
             div     cx                         ; 
                                                ; 
             inc     ax                         ; 
             les     di,Stk.phTimer             ; 
             mov     dword ptr es:[di],eax      ; set timer handle
                                                ; 
             mov     ax,ADD_SUCCESS             ; set return code
             jmp     short TS_exit              ;       and go to exit
                                                ; 
TS_error_exit:                                  ; 
             mov     ax,ADD_ERROR               ; set return code
                                                ; 
TS_exit:                                        ; 
             RestoreReg <edx,cx,ebx,di,es>      ; 

             dec     StartTimerMS

        ADDRET1                                 ; 


;******************************************************************************
;*
;*  SUBROUTINE NAME:   f_ADD_Cacel Timer
;*
;*  DESCRIPTIVE NAME:  Cacel Timer
;*
;*  FUNCTION:          This function cancels the timer, so the
;*                     timer handle won't be effective any more.
;*
;*  LINKAGE:           call far
;*
;*  PARAMETERS:        Timer handle
;*
;*  EXIT-NORMAL:       AX = 0
;*
;*  EXIT-ERROR:        none
;*
;*  PSEUDOCODE:
;*
;******************************************************************************

        ADDDef_F CancelTimer                    ; 
        ADDArgs  ULONG,      hTimer             ; 
                                                ; 
            pushf
            DISABLE
            inc      CancelTimer

            push     Stk.hTimer.D_high          ; push parameters
            push     Stk.hTimer.D_low           ; 

            call     ADD_CancelTimer            ; call near routine

            dec      CancelTimer
            popf
                                                ; 
        ADDRETF1                                 ; 

;******************************************************************************
;*
;*  SUBROUTINE NAME:   ADD_Cacel Timer
;*
;*  DESCRIPTIVE NAME:  Cacel Timer
;*
;*  FUNCTION:          This function calces the timer, so the
;*                     timer handle won't be effective any more.
;*
;*  LINKAGE:           call near
;*
;******************************************************************************

        ADDDef   CancelTimer                    ; 
        ADDArgs  ULONG,      hTimer             ; 
                                                ; 
             inc     CancelTimer

             SaveReg <es,di,bx>                    ; 
                                                ; 
             mov     eax,Stk.hTimer             ; 
             cmp     ax,Number_of_Timers        ; valid check of timer handle
             ja      TC_error_exit              ; 
                                                ; 
             dec     ax                         ; 
             imul    ax,lnTimerData             ; culc. timerdata offset
             mov     bx,ax                      ; 
                                                ; 
             les     di,TimerPool_ptr           ; get timer pool address
             add     di,2                       ; 
                                                ; 
             mov     TimerData.NotifyEntry,0    ; clear notify address
             mov     TimerData.Interval,0       ; clear interval value
             mov     TimerData.BackupInterval,0 ; clear interval for backup
                                                ; 
             mov     ax,ADD_SUCCESS             ; set return code
             jmp     short TC_exit              ;         and goto exit
                                                ; 
TC_error_exit:                                  ; 
             mov     ax,ADD_ERROR               ; set return code
                                                ; 

TC_exit:                                        ; 
             RestoreReg <bx,di,es>                 ; 

             dec     CancelTimer

        ADDRET1                                 ; 
                                                ; 

;******************************************************************************
;*
;*  SUBROUTINE NAME:   TimerHandler
;*
;*  DESCRIPTIVE NAME:  TimerHandler
;*
;*  FUNCTION:          this routiner is called on every timer tick.
;*
;*  LINKAGE:           call far
;*
;*  PARAMETERS:        None
;*
;*  EXIT-NORMAL:       Always
;*
;*  EXIT-ERROR:        None
;*
;*  PSEUDOCODE:        call the routine of timer requester
;*
;******************************************************************************

                                                ; 
        ADDDef_F TimerHandler                   ; 
                                                ; 
             DISABLE

             inc     TimerHandler

             SaveReg <es,di,bx,cx,dx>           ; 
                                                ; 
             les     di,TimerPool_ptr           ; 
             add     di,2                       ; 
             xor     bx,bx                      ; 
             mov     cx,Number_of_Timers        ; 
                                                ; 
TH_loop1:                                       ; check each entry.....
             cmp     TimerData.NotifyEntry,0    ; 
             je      TH_skip_entry              ; 
             dec     TimerData.Interval         ; decrease every tick count.
             jnz     TH_skip_entry              ; if zero => WAKE UP

             mov     eax,TimerData.BackupInterval ; set interval for the next
             mov     TimerData.Interval,eax     ; 
             movzx   eax,bx                     ; 
             push    cx
             mov     cx,lnTimerData             ; culc. timer handle
             xor     dx,dx
             div     cx                         ; 
             pop     cx                         ; 

             SaveReg <es,di,bx,cx,dx>           ; 
             inc     eax                        ; 
             push    TimerData.Parm2.sel        ; 
             push    TimerData.Parm2.off        ; 
             push    TimerData.Parm1.sel        ; 
             push    TimerData.Parm1.off        ; 
             push    eax                        ; set paramters
                                                ; 
             call    TimerData.NotifyEntry      ; call notify address of ADD

             add     sp,12
             RestoreReg <dx,cx,bx,di,es>
                                                ; 

TH_skip_entry:                                  ; 
             add     bx,lnTimerData             ; set the next entry pointer
             loop    TH_loop1                   ; 
                                                ; 
             RestoreReg <dx,cx,bx,di,es>

             dec     TimerHandler

             ENABLE
        ADDRETF1


LIBCODE         ends

                end
