SCCSID equ  "@(#)tmr.asm	6.2 91/02/18"

;
;	IBM/Microsoft Confidential
;
;	Copyright (c) IBM Corporation 1987, 1989
;	Copyright (c) Microsoft Corporation 1987, 1989
;
;	All Rights Reserved

	PAGE	78, 132
	TITLE	'Tmr--PIT Ctr 0'

;***	TMR.ASM
;
;	Programmable Interval Timer (PIT -- Intel 8254) Ctr 0 device
;	driver (Tmr).  This module contains the code for handling the
;	non-hardware specific interface functions to the Tmr device driver.
;
;	NOTES	If TMR is NOT defined, then all that remains of this module
;		stubs for the APIs.
;
;		In order to be callable from C code, procedures must obey the
;		following interface rules:
;		1) All procedure names must start with '_'
;		2) Upon entry all procedures have DS = ES = FLAT
;		3) All procedures must preserve EBX,ESI,EDI,SS,ESP,EBP,DS,ES
;		4) All procedures must receive parameters from stack (4 bytes
;		   for each parameter)
;		5) All procedures must return their return code in EAX.

INCL_DOSERRORS equ 0		;for bseerr.inc error codes

.XLIST
	INCLUDE dosseg.inc
	INCLUDE basemaca.inc
	INCLUDE fnodev.inc
	INCLUDE osmaca.inc
	INCLUDE osconst.inc
	INCLUDE kmode.inc	;thunksef_s
	INCLUDE mi.inc
	INCLUDE devhlp.inc	;Devhlp function equates
	INCLUDE int.inc
	INCLUDE pvwxport.inc	;qw_s
	INCLUDE perfvw.inc	;Macros: PvwCnt, etc.
	INCLUDE doshlp.inc
	INCLUDE bseerr.inc
.LIST

	ModName , FNO_TMR_ASM
	CPUMode 386


;	EXTERNALS

ReferGlobal	DHF,DWORD
ReferFlat	_f32pfnPTTimer0,fword	; in profile.c

EXTRN		_TKSuDWord:NEAR
EXTRN		_TKSuBuff:NEAR


;	DGROUP data (USE32)

_DATA	SEGMENT

GenPublic	ulTmrFreq
ulTmrFreq	dd	0	;Tmr (Timer 0) frequency (in hz).

;	Tmr status flags -- valid values:	= 0
;						= TMR_INSTALLED (perfvw.h)

GenPublic	_fsTmrStatus
_fsTmrStatus	dw	0

;	Tmr semaphore.  Since the Tmr device is shared by Profiler, this
;	sem. provides a way to monitor the Profiler's use of the Tmr.  The
;	sem. is initially = 0.  The Profiler will set this sem. = +1 when
;	it is using the Tmr, and set it = -1 when it is done using the Tmr.
;	The Tmr APIs will return appropriate error codes, depending on the
;	current state of Tmr.  If the Tmr APIs find this sem. = -1, they
;	will reset this sem. = 0.  Since the Profiler modifies this sem.
;	at the User level, and the Tmr APIs operate at the Kernel level,
;	there is no mutual exclusion problem -- the Kernel has higher
;	priority.
;
;		= +1 -- In use by profiler.
;		= -1 -- Was in use by profiler, but now is free.
;		=  0 -- Not in use by profiler.

GenPublic	_usTmrSem
_usTmrSem	dw	0

;	These provide 32-bit 'C' code with access to a 16:16 address

GenPublic	_pfnTMR16AddTime
_pfnTMR16AddTime LABEL DWORD
	DDFAR TMR16AddTime

GenPublic	_pfnTMR16SubTime
_pfnTMR16SubTime LABEL DWORD
	DDFAR TMR16SubTime

_DATA	ENDS


	FARCODE INIT		;DevHlp router segment (see DHROUTER.ASM).
INITCODE segment

;***EP	dh_RegisterTmrDD
;
;	Dev Help procedure.
;		Get the Tmr frequency.
;		Send the device driver pointers to the Tmr value and
;		Tmr rollover count in kernel address space.
;
;	ENTRY	This function is ONLY called at Tmr Device Driver
;		Initialization.
;		(cx:di) -> PTTimer0 entry point
;
;	EXIT	(DI:BX) -> qwTmrRollover (16:16) in kernel address space.
;		(DI:CX) -> qwTmr (16:16) in kernel address space.
;			=  NULL if DosHlp function not present.
;
;	USES	EAX, BX, CX, DI, EFlags
;
;   FS and GS must be preserved (DevHelp requirement)

Procedure dh_RegisterTmrDD,near
	ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

	SaveReg <ds>				;save ds
	FlatContext <ds>

	movzx	edi,di
	mov	DWORD PTR [_f32pfnPTTimer0],edi
	mov	WORD PTR [_f32pfnPTTimer0+4],cx;store the entry point

	xor	bx,bx
	xor	cx,cx
	xor	di,di

	DosContext ds
	call	ds:DHF.DosHlpRegisterTmrDD
	FlatContext ds

	or	di,di				;Valid Selector returned?
	jz	short dhrtx			;  No.

	mov	ulTmrFreq,eax			;ulTmrFreq = Tmr freq.

	or	_fsTmrStatus,TMR_INSTALLED	;Indicate Tmr present.

dhrtx:	RestoreReg <ds>
	ASSUME DS:NOTHING
	RETC

EndProc dh_RegisterTmrDD

INITCODE ends



	    FARCODE HIGH3
HIGH3CODE   SEGMENT

;***EP	TMR16AddTime
;
;	Add current Tmr value to input QWRD. Note that QWRD must be non-zero,
;	otherwise no addition is performed. (See NOTES).
;
;	ENTRY	(PQWRD) pTimer - Address of time storage location (QWRD) to be
;				 updated
;
;	EXIT	if (*pTimer != 0)
;			*pTimer += Current Tmr value;
;
;		(AX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			*pTimer = 7fff ffff ffff ffffh.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags
;
;	NOTES	If input QWRD is 0, then no addition takes place.  This is
;		to facilitate initialization of timing operations.
;		If the input QWRD is initialized to 0, this ensures that an
;		'End Time' operation (which adds the current time) does not
;		take place before a 'Start Time' operation (which subtracts
;		the current time).

Procedure TMR16AddTime,faronly
	ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

ArgVar	T16AT_pqw, DWORD

	EnterProc
	SaveReg <bx,ds,es>

	FlatContext es,bx
	test	es:_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short t16at_Err20		; no -- return error code.

	DosContext es,bx
	lds	bx,T16AT_pqw
	cmp	DWORD PTR [bx].qw_ulLo,0	;Lower half = 0?
	je	short t16atz			; yes -- do expensive jump.
t16at5:	call	es:DHF.DosHlpTmr16QueryTime	;(EDX:EAX) = current QWRD time
	add	DWORD PTR [bx].qw_ulLo,eax
	adc	DWORD PTR [bx].qw_ulHi,edx
	jo	short t16at_Of
	xor	ax,ax				;Indicate no error

t16atx: RestoreReg <es,ds,bx>
	LeaveProc
	RETC

t16atz: cmp	DWORD PTR [bx].qw_ulHi,0	;Upper half also = 0?
	jne	short t16at5			; no -- do add.
	jmp	short t16atx			; yes -- exit.

t16at_Of:
	mov	DWORD PTR [bx].qw_ulLo,QW_MAX_LO
	mov	DWORD PTR [bx].qw_ulHi,QW_MAX_HI
	mov	ax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short t16atx

t16at_Err20:
	mov	ax,ERROR_TMR_NO_DEVICE
	jmp	short t16atx

EndProc TMR16AddTime


;***EP	TMR16SubTime
;
;	Subtract current Tmr value from input QWRD.
;
;	ENTRY	(PQWRD) pTimer - Address of time storage location (QWRD) to be
;				 updated
;
;	EXIT	*pTimer -= Current Tmr value;
;
;		(AX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			*pTimer = 8000 0000 0000 00000h.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags

Procedure TMR16SubTime,faronly
	ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

ArgVar	T16ST_pqw, DWORD

	EnterProc
	SaveReg <bx,ds,es>

	FlatContext es,bx
	test	es:_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short t16st_Err20		; no -- return error code.

IFDEF TESTONLY
	PvwCnt spr,Spare3,1,strict
ENDIF	;TESTONLY

	DosContext es,bx
	lds	bx,T16ST_pqw
	call	es:DHF.DosHlpTmr16QueryTime	;(EDX:EAX) = current QWRD time
	sub	DWORD PTR [bx].qw_ulLo,eax
	sbb	DWORD PTR [bx].qw_ulHi,edx
	jo	short t16st_Of
	xor	ax,ax				;Indicate no error

t16stx: RestoreReg <es,ds,bx>
	LeaveProc
	RETC

t16st_Of:
	mov	DWORD PTR [bx].qw_ulLo,QW_MIN_LO
	mov	DWORD PTR [bx].qw_ulHi,QW_MIN_HI
	mov	ax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short t16stx

t16st_Err20:
	mov	ax,ERROR_TMR_NO_DEVICE
	jmp	short t16stx

EndProc TMR16SubTime

HIGH3CODE ENDS



	    FARCODE HIGH32
_TEXT SEGMENT


;***EP	_TMR32QueryTime_DW
;
;	Provides a snapshot of IRQ0 high resolution timer (Intel 8254).
;
;	_TMR32QueryTime_DW is C callable.
;
;	ENTRY	(DS) = (ES) = Flat.
;
;	EXIT	(EAX) = low order 4 bytes of time
;
;	USES	EAX, EDX, EFlags
;
;	NOTES	This function does NOT return an error code; therefore, it
;		must be used with caution -- the Tmr may not be installed or
;		it may be in use by the Profiler.  (see the global variables
;		_fsTmrStatus and _usTmrSem).

Procedure _TMR32QueryTime_DW,near
	ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short tdwx			; no -- exit.

	jmp	DHF.DosHlpTmr32QueryTime	;(EDX:EAX) = QWRD time.

tdwx:	RETC

EndProc _TMR32QueryTime_DW



;***EP	_TMR32QueryTime_CQW
;
;	Provide snapshot of IRQ0 high resolution timer (Intel 8254).
;
;	_TMR32QueryTime_CQW is C callable.
;
;	ENTRY	Arg1 = TCQW_pqw		Ptr to QWRD structure which will
;					receive Timer time (in timer units).
;					The ptr is ASSUMED to be FLAT (0:32)
;		(DS) = (ES) = Flat.
;
;	EXIT	(eax) = return code
;			0 - success
;			nonzero - error
;		ERROR_PARAMETER_INVALID
;		ERROR_TMR_NO_DEVICE
;		ERROR_DEVICE_IN_USE
;		ERROR_TMR_INVALID_TIME
;
;	USES	EAX, ECX, EDX, EFlags

Procedure _TMR32QueryTime_CQW,near
	ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING

ArgVar	TCQW_pqw, DWORD

	EnterProc

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short tcqw_Err20		; no -- return error code.

	CCALL32	DHF.DosHlpTmr32QueryTime ;(EDX:EAX) = QWRD time.

	mov	ecx,TCQW_pqw		;(ECX) -> QWRD destination.
	mov	[ecx].qw_ulHi,edx	;Store high DWORD of time.
	mov	[ecx].qw_ulLo,eax	;Store low DWORD of time.

	cmp	_usTmrSem,0		;Tmr in use.
	jb	short tcqw_Err40	; yes -- it WAS in use => time invalid
	ja	short tcqw_Err60	; yes -- it IS in use
	xor	eax,eax			; no -- indicate NO ERROR.

tcqw_Exit:
;	BugBug 26-Sep-89,DaveBe,<ExitProc macro uses PASCAL calling convention>
;	ExitProc
	LeaveProc
	RETC

tcqw_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short tcqw_Exit
tcqw_Err40:
	mov	_usTmrSem,0
	mov	eax,ERROR_TMR_INVALID_TIME
	jmp	short tcqw_Exit
tcqw_Err60:
	mov	eax,ERROR_DEVICE_IN_USE
	jmp	short tcqw_Exit

EndProc _TMR32QueryTime_CQW


;***EP	_TMR32AddTime -- 'C' function
;
;	Add current Tmr value to input QWRD. Note that QWRD must be non-zero,
;	otherwise no addition is performed. (See NOTES).
;
;	ENTRY	(PQWRD) pTimer - Address of time storage location (QWRD) to be
;				 updated
;		(DS) = (ES) = Flat.
;
;	EXIT	if (*pTimer != 0)
;			*pTimer += Current Tmr value;
;
;		(EAX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			*pTimer -> 7fff ffff ffff ffffh.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, ECX, EDX, EFlags
;
;	NOTES	If input QWRD is 0, then no addition takes place.  This is
;		to facilitate initialization of the operations.
;		If the input QWRD is initialized to 0, this ensures that an
;		'End Time' operation (which adds the current time) does not
;		take place before a 'Start Time' operation (which subtracts
;		the current time).

Procedure _TMR32AddTime,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

ArgVar	T32AT_pqw, DWORD

	EnterProc

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short _T32AT_Err20		; no -- return error code.

	mov	ecx,T32AT_pqw			;(ECX) -> QWRD destination.
	cmp	DWORD PTR [ecx].qw_ulLo,0	;Lower half = 0?
	je	short _T32ATZ			; yes -- do expensive jump.
_T32AT5:
	CCALL32	DHF.DosHlpTmr32QueryTime	;(EDX:EAX) = current QWRD time
	add	DWORD PTR [ecx].qw_ulLo,eax
	adc	DWORD PTR [ecx].qw_ulHi,edx
	jo	short _T32AT_Of
	xor	eax,eax				;Indicate no error
_T32ATX:
	LeaveProc
	RETC

_T32ATZ:
	cmp	DWORD PTR [ecx].qw_ulHi,0	;Upper half also = 0?
	jne	short _T32AT5			; no -- do add.
	jmp	short _T32ATX			; yes -- exit.

_T32AT_Of:
	mov	DWORD PTR [ecx].qw_ulLo,QW_MAX_LO
	mov	DWORD PTR [ecx].qw_ulHi,QW_MAX_HI
	mov	eax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short _T32ATX

_T32AT_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short _T32ATX

EndProc _TMR32AddTime


;***EP	_TMR32SubTime -- 'C' function
;
;	Subtract current Tmr value from input QWRD.
;
;	ENTRY	(PQWRD) pTimer - Address of time storage location (QWRD) to be
;				 updated
;		(DS) = (ES) = Flat.
;
;	EXIT	*pTimer -= Current Tmr value;
;
;		(EAX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			*pTimer -> 8000 0000 0000 0000h.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags

Procedure _TMR32SubTime,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

ArgVar	T32ST_pqw, DWORD

	EnterProc

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short _T32ST_Err20		; no -- return error code.

	CCALL32	DHF.DosHlpTmr32QueryTime	;(EDX:EAX) = current QWRD time
	mov	ecx,T32ST_pqw			;(ECX) -> QWRD destination.
	sub	DWORD PTR [ecx].qw_ulLo,eax
	sbb	DWORD PTR [ecx].qw_ulHi,edx
	jo	short _T32ST_Of
	xor	eax,eax				;Indicate no error
_T32ST_Exit:
	LeaveProc
	RETC

_T32ST_Of:
	mov	DWORD PTR [ecx].qw_ulLo,QW_MIN_LO
	mov	DWORD PTR [ecx].qw_ulHi,QW_MIN_HI
	mov	eax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short _T32ST_Exit

_T32ST_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short _T32ST_Exit

EndProc _TMR32SubTime


;***EP	TMR32AddTime -- MASM function
;
;	Add Tmr QWRD value to input QWRD.  Note that QWRD must be non-zero,
;	otherwise no addition is performed.  (See NOTES).
;
;	ENTRY	(ECX) -> QWRD
;		(DS) = (ES) = Flat.
;
;	EXIT	if [(ECX)] != 0
;			[(ECX)] = [(ECX)] + Tmr QWRD
;
;		(EAX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			(ECX) -> 7fff ffff ffff ffffh.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags
;
;	NOTES	If input QWRD is 0, then no addition takes place.  This is
;		to facilitate initialization of the operations.
;		If the input QWRD is initialized to 0, this ensures that an
;		'End Time' operation (which adds the current time) does not
;		take place before a 'Start Time' operation (which subtracts
;		the current time).

Procedure TMR32AddTime,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short T32AT_Err20		; no -- return error code.

	cmp	DWORD PTR [ecx].qw_ulLo,0	;Lower half = 0?
	je	short T32ATZ			; yes -- do expensive jump.
T32AT5:	CCALL32	DHF.DosHlpTmr32QueryTime	;(EDX:EAX) = current QWRD time
	add	DWORD PTR [ecx].qw_ulLo,eax
	adc	DWORD PTR [ecx].qw_ulHi,edx
	jo	short T32AT_Of
	xor	eax,eax				;Indicate no error
T32ATX:	RETC

T32ATZ:	cmp	DWORD PTR [ecx].qw_ulHi,0	;Upper half also = 0?
	jne	short T32AT5			; no -- do add.
	jmp	short T32ATX			; yes -- exit.

T32AT_Of:
	mov	DWORD PTR [ecx].qw_ulLo,QW_MAX_LO
	mov	DWORD PTR [ecx].qw_ulHi,QW_MAX_HI
	mov	eax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short T32ATX

T32AT_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short T32ATX

EndProc TMR32AddTime


;***EP	TMR32SubTime -- MASM function
;
;	Subtract Tmr QWRD value from input QWRD.
;
;	ENTRY	(ECX) -> QWRD
;		(DS) = (ES) = Flat.
;
;	EXIT	[(ECX)] = [(ECX)] - Tmr QWRD
;
;		(EAX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			(ECX) -> 8000 0000 0000 0000h.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags

Procedure TMR32SubTime,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short T32ST_Err20		; no -- return error code.

	CCALL32	DHF.DosHlpTmr32QueryTime	;(EDX:EAX) = current QWRD time
	sub	DWORD PTR [ecx].qw_ulLo,eax
	sbb	DWORD PTR [ecx].qw_ulHi,edx
	jo	short T32ST_Of
	xor	eax,eax				;Indicate no error
T32ST_Exit:
	RETC

T32ST_Of:
	mov	DWORD PTR [ecx].qw_ulLo,QW_MIN_LO
	mov	DWORD PTR [ecx].qw_ulHi,QW_MIN_HI
	mov	eax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short T32ST_Exit

T32ST_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short T32ST_Exit

EndProc TMR32SubTime

;***EP	TMR32ClrTime -- MASM function
;
;	Clear Tmr QWRD value.
;
;	ENTRY	(ECX) -> QWRD
;		(DS) = (ES) = Flat.
;
;	EXIT	[(ECX)] = 0
;
;		(EAX) = return code
;			0 - success
;
;	USES	EAX, EFlags

Procedure TMR32ClrTime,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

	xor	eax,eax				;Indicate no error
	mov	DWORD PTR [ecx].qw_ulLo,eax
	mov	DWORD PTR [ecx].qw_ulHi,eax
	RETC

EndProc TMR32ClrTime


;***LP	tmr32NTimes
;
;	Multiply current time (Tmr QWRD) by N (positive integer).
;
;	ENTRY	(EDX) = N (positive integer)
;		(DS) = (ES) = Flat.
;
;	EXIT	(EDX:EAX) = N*(Tmr QWRD)
;
;		CF = 0 if no error.
;		CF = 1 if overflow occurred ((EDX:EAX) = 7fff ffff ffff ffffh)
;
;	USES	EAX, EDX, EFlags
;
;	NOTES	No test is done of the Tmr status; this must be done by the
;		Caller.

Procedure tmr32NTimes,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

LocalVar	TANT_ulN, DWORD			;Input multiplier
LocalVar	TANT_qwTime, <SIZE qw_s>	;Local buffer for current time
LocalVar	TANT_qwProduct, <SIZE qw_s>	;Local buffer for product.

	EnterProc
	mov	TANT_ulN,edx			;Save multiplier.

	CCALL32	DHF.DosHlpTmr32QueryTime	;(EDX:EAX) = current QWRD time
;	mov	TANT_qwTime.qw_ulLo,eax		;Store time in local buffer.
	mov	TANT_qwTime.qw_ulHi,edx

	mul	TANT_ulN			;(EDX:EAX) = time[ulLo] * N
;	mov	TANT_qwProduct.qw_ulLo,eax	;Store product in local buffer.
	mov	TANT_qwProduct.qw_ulHi,edx

	mov	edx,TANT_qwTime.qw_ulHi
	imul	edx,TANT_ulN			;(EDX) = time[ulHi] * N
	jo	short t32nt_Of

	add	edx,TANT_qwProduct.qw_ulHi	;(EDX:EAX) =
	jo	short t32nt_Of			;   N*(time[ulHi]:time[ulLo])

	clc					;Indicate no error.
t32ntx:
;	BugBug 26-Sep-89,DaveBe,<ExitProc macro uses PASCAL calling convention>
;	ExitProc
	LeaveProc
	RETC

t32nt_Of:
	mov	edx,QW_MAX_LO
	mov	eax,QW_MAX_HI
	stc					;Indicate overflow.
	jmp	short t32ntx

EndProc tmr32NTimes


;***EP	TMR32AddNTimes
;
;	Add N*(Tmr QWRD) value to input QWRD, where N is positive integer.
;	Note that input QWRD must be non-zero, otherwise no addition is
;	performed.  (See NOTES).
;
;	ENTRY	(ECX) -> QWRD
;		(EDX) = N (positive integer)
;		(DS) = (ES) = Flat.
;
;	EXIT	if [(ECX)] != 0
;			[(ECX)] = [(ECX)] + N*(Tmr QWRD)
;
;		(EAX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			(ECX) -> 7fff ffff ffff ffffh.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags
;
;	NOTES	If input QWRD is 0, then no addition takes place.  This is
;		to facilitate initialization of the operations.
;		If the input QWRD is initialized to 0, this ensures that an
;		'End Time' operation (which adds the current time) does not
;		take place before a 'Start Time' operation (which subtracts
;		the current time).


Procedure TMR32AddNTimes,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short t32ant_Err20		; no -- return error code.

	cmp	DWORD PTR [ecx].qw_ulLo,0	;Lower half = 0?
	je	short t32antz			; yes -- do expensive jump.
t32ant5:
	CCALL32	tmr32NTimes			;(EDX:EAX) =
	jc	short t32ant_Of			;   N*(time[ulHi]:time[ulLo])

	add	DWORD PTR [ecx].qw_ulLo,eax
	adc	DWORD PTR [ecx].qw_ulHi,edx
	jo	short t32ant_Of
	xor	eax,eax				;Indicate no error
t32antx:
	RETC

t32antz:
	cmp	DWORD PTR [ecx].qw_ulHi,0	;Upper half also = 0?
	jne	short t32ant5			; no -- do add.
	jmp	short t32antx			; yes -- exit.

t32ant_Of:
	mov	DWORD PTR [ecx].qw_ulLo,QW_MAX_LO
	mov	DWORD PTR [ecx].qw_ulHi,QW_MAX_HI
	mov	eax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short t32antx

t32ant_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short t32antx

EndProc TMR32AddNTimes


;***EP	TMR32SubNTimes
;
;	Subtract N*(Tmr QWRD) value from input QWRD, where N is positive integer.
;
;	ENTRY	(ECX) -> QWRD
;		(EDX) = N (positive integer)
;		(DS) = (ES) = Flat.
;
;	EXIT	[(ECX)] = [(ECX)] - N*(Tmr QWRD)
;
;		(EAX) = return code
;			0 - success
;			nonzero - error
;		ERROR_ARITHMETIC_OVERFLOW
;			(ECX) -> 8000 0000 0000 0000h.
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;
;	USES	EAX, EDX, EFlags

Procedure TMR32SubNTimes,near
	ASSUME DS:FLAT,ES:FLAT,SS:FLAT

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short t32snt_Err20		; no -- return error code.

	CCALL32	tmr32NTimes			;(EDX:EAX) = N * (current Tmr)
	jc	short t32snt_Of

	sub	DWORD PTR [ecx].qw_ulLo,eax
	sbb	DWORD PTR [ecx].qw_ulHi,edx
	jo	short t32snt_Of
	xor	eax,eax				;Indicate no error

t32sntx:
	RETC

t32snt_Of:
	mov	DWORD PTR [ecx].qw_ulLo,QW_MIN_LO
	mov	DWORD PTR [ecx].qw_ulHi,QW_MIN_HI
	mov	eax,ERROR_ARITHMETIC_OVERFLOW
	jmp	short t32sntx

t32snt_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short t32sntx

EndProc TMR32SubNTimes



;***EP	_VR32TmrQueryFreq
;
;	Provide frequency of IRQ0 high resolution timer (Intel 8254).
;
;	This is the worker for the Dos32TmrQueryFreq API.
;
;	ENTRY	Arg1 = TQF_pdw		Ptr to DWORD which will receive Tmr
;					frequency in hertz (timer units/sec)
;
;	EXIT	(eax) = return code
;			0 - success
;			nonzero - error
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;		ERROR_DEVICE_IN_USE
;
;	USES	EAX, EFlags

Procedure _VR32TmrQueryFreq,<near,cdecl>
	ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING

					;Input parameters
ArgVar	    TQF_thunksef, <SIZE thunksef_s>
ArgVar	    TQF_pdw, DWORD		;Dest. buffer in user space

	EnterProc

IFDEF TESTONLY
Entry TMRTest0,,,nocheck
	xor	eax,eax
	xor	edx,edx
	CCALL32	DHF.DosHlpTmr32QueryTime
Entry TMRTest1,,,nocheck
	mov	ecx,eax			 ;(ECX) = 1st DWORD time.
	CCALL32	DHF.DosHlpTmr32QueryTime ;(EAX) = 2nd DWORD time.
Entry TMRTest2,,,nocheck
	xor	eax,eax
	mov	edx,2
	CCALL32 tmr32NTimes
Entry TMRTest3,,,nocheck
	xor	eax,eax
	mov	edx,2345
	CCALL32 tmr32NTimes
Entry TMRTest4,,,nocheck
ENDIF	;TESTONLY

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short tqf_Err20			; no -- return error code.

	xor	eax,eax
	CCALL32	_TKSuDWord,<ulTmrFreq, TQF_pdw>
	or	eax,eax
	jnz	short tqf_Err10

	cmp	_usTmrSem,0		;Tmr in use.
	jb	short tqf_Err40		; yes -- it WAS in use
	ja	short tqf_Err60		; yes -- it IS in use
tqf_50:
	xor	eax,eax			; no -- indicate NO ERROR.

tqf_Exit:
IFDEF TESTONLY
	PvwCnt spr,Spare2,0,strict
ENDIF	;TESTONLY
;	BugBug 26-Sep-89,DaveBe,<ExitProc macro uses PASCAL calling convention>
;	ExitProc
	LeaveProc
	RETC

tqf_Err10:
	mov	eax,ERROR_INVALID_PARAMETER
	jmp	short tqf_Exit
tqf_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short tqf_Exit
tqf_Err40:
	mov	_usTmrSem,0
	jmp	short tqf_50
tqf_Err60:
	mov	eax,ERROR_DEVICE_IN_USE
	jmp	short tqf_Exit

EndProc _VR32TmrQueryFreq


;***EP	_VR32TmrQueryTime
;
;	Provide snapshot of time from IRQ0 high resolution timer (Intel 8254).
;
;	This is the worker for the Dos32TmrQueryTime API.
;
;	ENTRY	Arg1 = TQT_pqw		Ptr to QWRD structure which will
;					receive Tmr time (in timer units).
;
;	EXIT
;		(eax) = return code
;			0 - success
;			nonzero - error
;		ERROR_INVALID_PARAMETER
;		ERROR_TMR_NO_DEVICE
;		ERROR_DEVICE_IN_USE
;		ERROR_TMR_INVALID_TIME
;
;	USES	EAX, ECX, EDX, EFlags

Procedure _VR32TmrQueryTime,<near,cdecl>
	ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING

					;Input parameters
ArgVar	    TQT_thunksef, <SIZE thunksef_s>
ArgVar	    TQT_pqw, DWORD		;Dest. buffer in User space
LocalVar    TQT_qw, <SIZE qw_s>		;Local buffer

	EnterProc
IFDEF TESTONLY
	PvwCntStartTm spr,Spare1,0,strict
ENDIF	;TESTONLY

	test	_fsTmrStatus,TMR_INSTALLED	;Tmr installed?
	jz	short tqt_Err20			; no -- return error code.

	CCALL32	DHF.DosHlpTmr32QueryTime ;(EDX:EAX) = QWRD time.
	mov	TQT_qw.qw_ulLo,eax	;Store in local buffer.
	mov	TQT_qw.qw_ulHi,edx

	SSToDS	ecx,TQT_qw		;(ECX) = address of local buffer
	xor	eax,eax
	CCALL32	_TKSuBuff,<TQT_pqw, ecx, <SIZE qw_s>, 0>
	or	eax,eax
	jnz	short tqt_Err10

	cmp	_usTmrSem,0		;Tmr in use.
	jb	short tqt_Err40		; yes -- it WAS in use
	ja	short tqt_Err60		; yes -- it IS in use
	xor	eax,eax			; no -- indicate NO ERROR.

tqt_Exit:
IFDEF TESTONLY
	PvwEndTm spr,Spare1,0,strict
ENDIF	;TESTONLY
;	BugBug 26-Sep-89,DaveBe,<ExitProc macro uses PASCAL calling convention>
;	ExitProc
	LeaveProc
	RETC

tqt_Err10:
	mov	eax,ERROR_INVALID_PARAMETER
	jmp	short tqt_Exit
tqt_Err20:
	mov	eax,ERROR_TMR_NO_DEVICE
	jmp	short tqt_Exit
tqt_Err40:
	mov	_usTmrSem,0
	mov	eax,ERROR_TMR_INVALID_TIME
	jmp	short tqt_Exit
tqt_Err60:
	mov	eax,ERROR_DEVICE_IN_USE
	jmp	short tqt_Exit

EndProc _VR32TmrQueryTime


_TEXT ENDS


	END
