;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
; ts.asm - timer service routines
;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²

ideal
ifdef PASCAL
model   large,pascal
else
model   large,c
endif
p386

;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; Data segment
;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
dataseg

oldtimer        dd      ?               ; BIOS INT 08h routine
irqcallback     dd      ?               ; user defined callback routine
irqactive       dw      ?               ; callback routine active flag
irqenabled      dw      0               ; timer service enabled flag
timerspeed      dw      ?               ; timer speed in clock ticks
timercount      dw      ?               ; timer counter used to call the
                                        ; old BIOS timer interrupt service

;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; Code segment
;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
codeseg

global  TSInit:proc                     ; initialize the routines
global  TSDone:proc                     ; deinitialize the routines
global  TSSetRate:proc                  ; set the callback rate in hertz
global  TSSetRoutine:proc               ; set the callback routine address
global  TSRestoreTime:proc              ; restore the time/date

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; TSInit - initialize the timer service routines
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    TSInit
        cmp     [irqenabled],0          ; services already enabled? exit
        jne     tsinitd0
        lea     ax,[dummy]              ; set the default callback routine
        mov     [word low irqcallback],ax
        mov     [word high irqcallback],cs
        mov     ax,3508h                ; save the BIOS INT 08h vector
        int     21h
        mov     [word low oldtimer],bx
        mov     [word high oldtimer],es
        mov     [irqactive],0
        push    ds                      ; set our timer interrupt vector
        mov     ax,cs
        mov     ds,ax
        lea     dx,[timerhandler]
        mov     ax,2508h
        int     21h
        pop     ds
        cli                             ; reprogram the PIT timer0 rate
        mov     al,36h                  ; to 18.2 Hertz (default)
        out     43h,al
        xor     al,al
        out     40h,al
        out     40h,al
        sti
        mov     [timerspeed],0FFFFh
        inc     [irqenabled]
tsinitd0:
        ret
endp    TSInit

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; TSDone - deinitialize the timer services
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    TSDone
        cmp     [irqenabled],0          ; services not enabled? exit
        je      tsdoned0
        cli                             ; restore the PIT timer0 rate
        mov     al,36h                  ; to 18.2 Hertz (normal)
        out     43h,al
        xor     al,al
        out     40h,al
        out     40h,al
        sti
        push    ds                      ; restore the BIOS INT 08h vector
        mov     dx,[word low oldtimer]
        mov     ds,[word high oldtimer]
        mov     ax,2508h
        int     21h
        pop     ds
        dec     [irqenabled]
tsdoned0:
        ret
endp    TSDone

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; TSRate - set the number of calls per seconds of the callback routine
; In:
;  Speed = speed in hertz
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    TSSetRate Speed:word
        cmp     [irqenabled],0
        je      tssetrated0
        mov     dx,0012h                ; get the clock ticks using the
        mov     ax,34DEh                ; formula: tc = 1193182/speed
        div     [Speed]
        mov     [timerspeed],ax
        mov     dx,ax
        cli                             ; program the PIT timer 0 rate
        mov     al,36h
        out     43h,al
        mov     al,dl
        out     40h,al
        mov     al,dh
        out     40h,al
        sti
tssetrated0:
        ret
endp    TSSetRate

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; TSSetRoutine - set the callback routine address
; In:
;  Rout = address of the callback routine
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    TSSetRoutine Rout:dword
        mov     eax,[Rout]              ; change the callback routine
        mov     [irqcallback],eax       ; address to the new one
        ret
endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; TSRestoreTime - restore the time/date using the CMOS
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    TSRestoreTime
        mov     al,0                    ; get seconds
        out     70h,al
        in      al,71h
        mov     ah,al
        and     al,0Fh
        shr     ah,4
        aad
        mov     dh,al

        mov     al,2                    ; get minutes
        out     70h,al
        in      al,71h
        mov     ah,al
        and     al,0Fh
        shr     ah,4
        aad
        mov     cl,al

        mov     al,4                    ; get hours
        out     70h,al
        in      al,71h
        mov     ah,al
        and     al,0Fh
        shr     ah,4
        aad
        mov     ch,al

        xor     dl,dl                   ; set hour:minute:secs
        mov     ah,2Dh
        int     21h

        mov     al,7                    ; get day
        out     70h,al
        in      al,71h
        mov     ah,al
        and     al,0Fh
        shr     ah,4
        aad
        mov     dl,al

        mov     al,8                    ; get month
        out     70h,al
        in      al,71h
        mov     ah,al
        and     al,0Fh
        shr     ah,4
        aad
        mov     dh,al

        mov     al,4                    ; get year
        out     70h,al
        in      al,71h
        mov     ah,al
        and     al,0Fh
        shr     ah,4
        aad
        mov     cl,al

        xor     ch,ch                   ; set the month/day/year
        add     cx,1900
        mov     ah,2Bh
        int     21h
        ret
endp    TSRestoreTime

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; timerhandler - hardware timer interrupt routine
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    timerhandler
        pushad
        push    ds
        push    es
        push    fs
        push    gs
        mov     ax,@data                ; get the DS data segment
        mov     ds,ax
        cmp     [irqactive],0           ; already active?
        jne     timerhandlerd0          ; exit, don't allow recursive calls
        inc     [irqactive]
        call    [irqcallback]           ; call the user callback routine
        dec     [irqactive]
timerhandlerd0:
        mov     ax,[timerspeed]
        add     [timercount],ax         ; call the old BIOS timer interrupt
        jnc     timerhandlerd1          ; service 18.2 times per second
        pushf
        call    [oldtimer]
        jmp     timerhandlerd2
timerhandlerd1:
        mov     al,20h                  ; send acknowledge to the PIC
        out     20h,al
timerhandlerd2:
        pop     gs
        pop     fs
        pop     es
        pop     ds
        popad
        iret
endp    timerhandler

proc    dummy
        ret
endp    dummy

end
