;------------------------------ Module Header ------------------------------;
; Module Name: Timer interface procedures
;
; Created: ??-???-83
;
; Copyright (c) 1983, 1984, 1985, 1986, 1987  Microsoft Corporation
;
; History:
;  10-Jan-87 by ronm  Adusted StackBase to be even
;   9-Jan-87 by ronm  Patches to support HiTime.asm
;---------------------------------------------------------------------------;

	TITLE	Timer interface procedures

include system.inc
include wow.inc
include wowusr.inc
include vint.inc

; Interrupt vector to use

VECTOR	    equ 08h

assumes CS,CODE

sBegin DATA

		PUBLIC	timerTable
timerTable	LABEL	BYTE
tiblock     <-1,0,0>
tiblock     <-1,0,0>
tiblock     <-1,0,0>
tiblock     <-1,0,0>
tiblock     <-1,0,0>
tiblock     <-1,0,0>
tiblock     <-1,0,0>
tiblock     <-1,0,0>
	    DW	    -1
	    DW	    -1

ifdef WOW
cTimers         DW  0
endif

enabled 	DB  0		; 0 means int 8 hook not installed
				; 1 means int 8 hook installed
				; >1 means inside our int 8 hook


if 0
;
;   no longer used
;
	public	StackBase
		EVEN		; Put the stack at a word boundary!
StackBase	DB  64	DUP (-1)

	PUBLIC	prevInt8Proc,prevSSSP,enabled
	PUBLIC	cms, cmsRound

		DB  128 DUP (?)
int8stack	LABEL	BYTE	; Stack to use inside our int 8 hook

prevSSSP	DD  0		; Previous stack when inside our hook
endif

prevInt8Proc	DD  0		; Previous int 8 interrupt handler
cms		DD  0		; msec count.
cmsRound	DW  0		; for rounding off the msec count.

sEnd

sBegin	CODE	    ; Beginning of code segment
assumes CS,CODE

externW  MyCSDS     ; always in CS (even in ROM)

;--- timer hardware service -----------------------
;
noevent:
	assumes ds, DATA
	; push address
	push	word ptr prevInt8Proc[2]
	push	word ptr prevInt8Proc[0]

	; restore ds out of stack
	push	bp
	mov	bp, sp
	mov	ds, [bp+6]
	assumes ds,nothing
	pop	bp

	; jump to prev proc popping saved ds
	retf	2

;----------------------------- Private Function ----------------------------;
;
; Entry:	call	far ptr timer_int
;
; Returns:	nothing
;
; Registers Destroyed: none
;
; History:
;  09-Jan-87 by ronm  Added hooks for the high resolution timer fns
;		      in hitime.asm
;  ??-???-?? by ????  Wrote it
;---------------------------------------------------------------------------;

	assumes	ds,nothing
	assumes	es,nothing

cProc	timer_int,<FAR,PUBLIC>

cBegin nogen

; Don't trash any registers.

	push	ds
	mov	ds,MyCSDS
	assumes ds, DATA
	add	word ptr [cms][0],(res_low / 1000)
	adc	word ptr [cms][2],0
	add	[cmsRound],(res_low - ((res_low / 1000) * 1000))
	cmp	[cmsRound],1000
	jb	ti0
	sub	[cmsRound],1000
	inc	word ptr [cms][0]
	jnz	ti0
	inc	word ptr [cms][2]
ti0:
	cmp	[enabled],1
	jne	noevent
	inc	[enabled]

	pushf

	; enable IF flag in stack flags to prevInt8Proc if they were
	; on when this routine was entered -- this allows the 286 DOS
	; extender to enable ints after running real mode Int 8 handler.

	FLAGS1 = 3			;	 +0   +2   +4	+6   +8  +10
	FLAGS2 = 11			; BP -> [bp] [fl] [ds] [ip] [cs] [fl]

	push	bp
	mov	bp,sp
	test	byte ptr FLAGS2[bp],02h
	jz	@f
	or	byte ptr FLAGS1[bp],02h
@@:	pop	bp


	call	[prevInt8Proc]	; call previous Int 8 routine

	public	ti1
ti1:

comment ~
        FCLI
	mov	word ptr [prevSSSP][2],ss
	mov	word ptr [prevSSSP][0],sp
	push	ds
	pop	ss
	mov	sp,codeOffset int8stack
        FSTI                     ; Allow interrupts

end comment ~

	push	ax

	mov	al,00001011b	; ask for 8259 status
	out	20h,al
	jmp	$+2
	in	al,20h		; get the status
	or	al,al
	jnz	TheEnd		; if other pending EOIs, just exit

	push	bp
	push	es
	push	bx
	push	cx
	push	dx
	push	si
	push	di

	xor	bp,bp		    ; No valid BP chain
	mov	si,doffset TimerTable
nextent:
	cld
	lodsw			    ; Get timer rate
	.errnz	tirate
	inc	ax		    ; -1 means unused entry
	jnz	checkent	    ; no, check used entry
	lodsw			    ; yes, get timer count
	.errnz	2-ticount
	inc	ax		    ; another -1 means end of table
	jz	lastent 	    ; yes, all done
	add	si,4		    ; o.w. skip to next entry
	jmp	nextent
checkent:
	dec	ax		    ; 0 means call at maximum rate
	jz	callent
	dec	word ptr DS:[si]    ; o.w. decrement rate counter
	.errnz	2-ticount
	jz	callent 	    ; zero means timer has gone off
	add	si,6		    ; o.w. skip to next entry
	jmp	nextent
callent:
	mov	DS:[si],ax
	inc	si
	inc	si
	lea	ax,[si-4]	    ; Pass timer handle in AX
	.errnz	4-tiproc
	call	dword ptr DS:[si]
	add	si,4
	jmp	nextent
lastent:
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	es
	pop	bp
TheEnd:
	pop	ax
	dec	[enabled]

comment ~
        FCLI
	mov	ss,word ptr [prevSSSP][2]
	mov	sp,word ptr [prevSSSP][0]
        FSTI

end comment ~

	pop	ds


	iret

cEnd nogen


;============================================================================
; DWORD GetSystemMsecCount(void) - returns msec count.
;

	assumes ds,nothing
	assumes	es,nothing

    DUserThunk  GETSYSTEMMSECCOUNT,0

;LabelFP <PUBLIC, GetSystemMsecCount>
;
;	 push	 ds
;	 mov	 ds, MyCSDS
;	 assumes ds, DATA
;
;	 mov	 ax,word ptr [cms][0]
;	 mov	 dx,word ptr [cms][2]
;	 pop	 ds
;	 retf

;----------------------------- Private Function ----------------------------;
;
; EnableSystemTimers() - enable hardware timer interrupts
;
; Entry:    cCall   far ptr EnableSystemTimers
;
; Returns:  nothing
;
; Registers Destroyed:	??
;
; History:
;  09-Jan-87 by ronm  Patched to support hitime.asm
;  ??-???-?? by ????  Wrote it
;---------------------------------------------------------------------------;


	assumes	ds,nothing
	assumes	es,nothing

cProc	EnableSystemTimers,<FAR,PUBLIC>
cBegin	nogen

; All done if just already enabled

	push	ds
	mov	ds,MyCSDS
	assumes ds, DATA

ifdef WOW
        ; see if we're being called by Create to really enable tics
        cmp     cTimers, 1
        je      est_doit
endif

	cmp	enabled,0
	jne	edone

est_doit:
	mov	[enabled],1

ifdef WOW
        ; don't install the tic handler if no systemtimers registered
        cmp     cTimers, 0
        je      edone
endif

; Save away current timer interrupt vector value

	mov	ax,3500h or VECTOR
	int	21h
	mov	word ptr [PrevInt8Proc][0],bx
	mov	word ptr [PrevInt8Proc][2],es

; Setup timer interrupt vector to point to our interrupt routine

	mov	ax,2500h or VECTOR
	push	cs
	pop	ds
	mov	dx,codeOFFSET timer_int
	int	21h
edone:
	pop	ds
	ret
cEnd	nogen


;-----------------------------------------------------------------------;
; DisableSystemTimers
;
; DisableSystemTimers() - disable system timer interrupts, restoring
; the previous timer interrupt handler.
;
; Entry:
;
; Returns:
;
; Registers Destroyed:
;
; History:
;  Mon 21-Nov-1988 18:44:44  -by-  David N. Weise  [davidw]
; Added this nifty comment block.
;-----------------------------------------------------------------------;

	assumes ds,nothing
	assumes es,nothing

cProc	DisableSystemTimers,<FAR,PUBLIC>
cBegin	nogen

	push	ds
	mov	ds,MyCSDS
	assumes ds, DATA

; Do nothing if not enabled

	cmp	[enabled],0
	je	ddone
	mov	[enabled],0

; Restore the timer interrupt vector to point to previous value

	mov	ax,2500h or VECTOR
	lds	dx,prevInt8Proc
	int	21h
ddone:
	pop	ds
	ret
cEnd	nogen

;-----------------------------------------------------------------------;
; CreateSystemTimer
;
;
; Entry:
;
; Returns:
;
; Registers Destroyed:
;
; History:
;  Mon 21-Nov-1988 18:44:44  -by-  David N. Weise  [davidw]
; Added this nifty comment block.
;-----------------------------------------------------------------------;

	assumes ds,nothing
	assumes es,nothing

cProc	CreateSystemTimer,<PUBLIC,FAR>
	ParmW	rate
	ParmD	lpproc
cBegin
	mov	ds,MyCSDS
	assumes ds, DATA
	mov	bx,doffset timerTable
	mov	ax,rate
	or	ax,ax
	jz	ctfirst
	mov	cx,1000 		; change msecs into ticks.
	mul	cx
	mov	cx,res_low
	div	cx
ctfirst:
        FCLI                             ; beginning of critical section
ctloop:
	cmp	ds:[bx].tirate,-1
	jne	ctnext
	cmp	ds:[bx].ticount,-1
	je	ctfail
	mov	cx,OFF_lpproc
	mov	dx,SEG_lpproc
	mov	word ptr ds:[bx].tiproc[0],cx
	mov	word ptr ds:[bx].tiproc[2],dx
	mov	ds:[bx].ticount,ax
	mov	ds:[bx].tirate,ax	   ; Set this last

ifdef WOW
        ; turn on tics if the count is going from 0 -> 1 and they're
        ; supposed to be enabled

        inc     cTimers
        cmp     cTimers, 1
        jne     @f

        cmp     enabled, 0      ; need to turn on tics?
        je      @f              ; -> nope

        push    bx
        call    EnableSystemTimers
        pop     bx
@@:
endif

	jmp	short ctexit

ctnext: add	bx,SIZE tiblock
	jmp	ctloop

ctfail: xor	bx,bx

ctexit: FSTI                                ; end of critical section
	mov	ax,bx
	mov	cx,bx
cEnd

;-----------------------------------------------------------------------;
; KillSystemTimer
;
;
; Entry:
;
; Returns:
;
; Registers Destroyed:
;
; History:
;  Mon 21-Nov-1988 18:44:44  -by-  David N. Weise  [davidw]
; Added this nifty comment block.
;-----------------------------------------------------------------------;

	assumes ds,nothing
	assumes es,nothing

cProc	KillSystemTimer,<PUBLIC,FAR>,<di>
	parmW	htimer
cBegin
	mov	es,MyCSDS
	assumes es,nothing
	mov	di,doffset TimerTable
	mov	ax,htimer

ktloop: cmp	es:[di].tirate,-1
	jne	ktmatch
	cmp	es:[di].ticount,-1
	jne	ktnext
	jmp	short ktexit
ktmatch:
	cmp	di,ax
	jne	ktnext
	cld
	mov	ax,-1
	stosw
	not	ax
	stosw
	stosw
	stosw
ifdef WOW
        dec     es:[cTimers]     ; was this the last one?
        jnz     @f                ;  -> nope

        cmp     es:[enabled], 0  ; are tics on?
        je      @f                ;  -> nope

; Restore the timer interrupt vector to point to previous value

        push    ax

	mov	ax,2500h or VECTOR
        lds     dx,es:[prevInt8Proc]
	int	21h

        pop     ax
@@:
endif

	jmp	short ktexit

ktnext: add	di,SIZE tiblock
	jmp	ktloop

ktexit: mov	cx,ax
cEnd

sEnd	CODE		; End of code segment

END
