: what's your favorite way of doing highly accurate timings nowadays?
: I was thinking about using the timestamp counter, but then I have to
: go and calculate the cpu speed. Is there a favorite way of doing
: that?
:
: since the timestamp gets updated on a per cpu clock, are there
: issues with clock multipliers and all the other fancy tricks that
: modern CPUs are doing vs timestamp and cpu speed?
:
: -jeff!
For my purposes, I've never needed the precision available using the CPU timestamp counter. I just use the BIOS clock interrupt generator, which has a resolution of approximately 1 microsecond. It requires I/O to get the data, so it is less accurate than getting data from a CPU register. But, it's always been good enough for my purposes up to this point in time. Following is some sample code that I use in some of my programs.
;------------------------------------------------------------------------------
;DELAY FOR A GIVEN NUMBER OF MICROSECONDS
;Inputs: AX = number of microseconds to wait (0-65,535)
;Outputs:
;Changes:
; NOTE: Delays will be slightly longer than what's asked for, never shorter.
; Accuracy decreases with small delays or slow computers!
;------------------------------------------------------------------------------
DelayUSAX:
PUSH AX,BX ;Save used registers
OR AX,AX ;Is there anything to do?
JZ >D90 ;If not, just quit
MOV BX,AX ;Put it in BX
MOV AX,(0FFFFh/3)+1 ;Maximum time for each sub-delay
D10: ;Loop to here for each sub-delay
CMP BX,AX ;Is it more than the maximum?
JA >D40 ;If so, just do the sub-delay
MOV AX,BX ;If not, just do the remainder that's left
D40: ;Do the delay
CALL DoDelayUS ;Do the US Delay
SUB BX,AX ;Subtract out how long we just waited
JNZ D10 ;If not 0 yet, do the sub-delay again
D90: ;We're done
POP BX,AX ;Restore used registers
RET
DoDelayUS:
PUSH AX,BX,CX,DX ;Save used registers
SHL AX,1 ;Multiply by 2
MOV BX,11932 ;Compensate for the fact
MUL BX ; that the clock frequency
MOV BX,10000 ; is actually
DIV BX ; 1.19318 MHz
MOV DX,AX ;Save it
CLI ;Disable interrupts
CALL GetTimer ;Get the starting timer tick counter
MOV CX,AX ;Save it
D20: ;Keep looping to here until we've waited long enough
MOV BX,CX ;Get the starting timer tick counter
CALL GetTimer ;Get the current timer tick counter
SUB BX,AX ;Calculate the elapsed time
CMP BX,DX ;Has it been long enough?
JB D20 ;If not, keep waiting
STI ;Enable interrupts
D90: ;We're done
POP DX,CX,BX,AX ;Restore used registers
RET
;------------------------------------------------------------------------------
;GET CURRENT TIMER COUNTER FROM PORT 40h (THE CLOCK INTERRUPT)
;Inputs:
;Outputs: AX = Current timer counter word
;Changes:
;------------------------------------------------------------------------------
GetTimer:
MOV AL,06h ;Bits 7:6 = 00 = Timer 0
;Bits 5:4 = 00 = Latch Counter
;Bits 3:1 = 011 = Mode 3 (Square Wave)
;Bit 0 = 0 = Binary Counter (16 bits)
OUT 43h,AL ;Tell the PIT what we're going to do
IN AL,40h ;Get LSB of timer counter
MOV AH,AL ;Save it
IN AL,40h ;Get MSB of timer counter
XCHG AH,AL ;Put things in the right order
RET