mcain@whiskey.USWest.COM (05/03/89)
I need to time intervals in milliseconds on an AT clone. I'm sure the beast has some hardware counters that could be used for such a job, but am not sure where they might be and how to access them. The box seems to be a very close hardware copy of the AT, aside from running at a higher clock. Solutions that are known to work on a real AT will probably work here. I am interested in details at the level of hardware I/O ports or memory locations, and can write my own high-level language interface to an assembly language routine. Please respond by mail (if feasible), and I'll summarize any interesting solutions for the net. Thanks in advance, Michael Cain mcain@uswest.com
jeffd@hp-lsd.HP.COM (Jeff Downs) (05/06/89)
Listed below is a timer routine that I got from Dr. Dobbs Journal.
It works real well and is simple to call from a high level program.
Jeff Downs
Hewlett-Packard
Logic Systems Division
hp-lsd!jeffd
################################################################################
;TITLE: software performance analyzer
;DESCRIPTION: fully compensated, high resolution timer.
;Internal timing resolution = 838 ns.
;CALLING SEQUENCE: call TIMER_START (FAR call)
; code to be timed
; call TIMER_STOP (FAR call)
;OUTPUT: Display of elapsed time between START and STOP calls
;STACK REQUIREMENTS: 10 bytes
;SPECIAL NOTES: timing events > 54.925 ms requires interrupts
; to be enabled.
_DATA segment word 'data' public
timer_low equ ds:[006ch]
bios_dataseg equ 0040h
timer_mode equ 43h
timer0 equ 40h
count dw 0 ;# of interrupt ticks
;(54.925 ms)
count_micro dw 0
count_milli dw 0
timer_micro dw 0 ;from 8253 countdown
timer_milli dw 0 ;final value
timer_sec dw 0 ;final value
max_count dw 65535 ;65536 ticks in a full count
adjustm dw 16 ;compensation factor
timer_convert dw 8381 ;838.096 nsec per tick
count_convert dw 54925 ;54.925 msec per count
ten_thousand dw 10000
five_thousand dw 5000
thousand dw 1000
ten dw 10
message_sec db 'Seconds: ','$'
message_milli db 'Milli-seconds: ','$'
message_micro db 'Micro-seconds: ','$'
ASCII_string db 5 dup('d'),0dh,0ah,'$'
_DATA ends
;print macro
print_string macro ;DOS function to print strings
mov ah,9 ;pointed to by DS:DX
int 21h
endm
public _timer_start,_timer_stop,bin_asc
_TEXT segment byte 'code' public
assume cs:_text,ds:_data
_timer_start proc far
push bp
mov bp,sp
push ax
push dx
push ds
mov dx,_DATA
mov ds,dx
mov timer_micro,0
mov timer_milli,0
mov timer_sec,0
;initialize counter 0 of 8253 timer
mov al,00110100B ;ctr 0, LSB then MSB
;mode 2, binary
out timer_mode,al ;mode register of 8253
sub ax,ax
out timer0,al ;LSB first
out timer0,al ;MSB next
;read current bios time of day
mov dx,bios_dataseg ;point to bios data segment
mov ds,dx
mov ax,timer_low ;get count
mov dx,_DATA ;point to my data
mov ds,dx
mov count,ax ;save count
pop ds
pop dx
pop ax
pop bp
ret
_timer_start endp
_timer_stop proc far
push bp
mov bp,sp
push ax
push bx
push dx
push ds
mov ax,_DATA
mov ds,ax
;elapsed time since TIMER_START consists of:
; 1) timer count intervals - 840 ns
; 2) interrupt ticks - 54 ms
;read counter 0 of 8253 timer
mov al,0h ;latch counter for read
cli ;interrupts off until
;bios tod is read
out timer_mode,al ;8253 mode register
in al,timer0
mov dl,al
in al,timer0
mov dh,al ;dx has 16 bit timer count
;calculate the time due to 8253 counting
mov ax,max_count
sub ax,dx ;timer count value
mul timer_convert ;get in usable form
div ten_thousand ;gives time in usec
mov timer_micro,ax ;save usec, round nsec
cmp dx,five_thousand
jb cont ;round down
inc timer_micro ;round up
;get bios time due to interrupt ticks
cont: mov dx,bios_dataseg ;point to bios data segment
mov ds,dx
mov ax,timer_low
mov dx,_DATA
mov ds,dx
sti ;interrupts OK now
sub ax,count ;now have # of 54 ms ticks
mul count_convert ;get in usable form
div thousand
mov count_milli,ax ;save millisecond part
mov count_micro,dx ;save usec part
;check for jitter
cmp ax,0 ;check if elapsed time is small
jne jitter_ok ;if not, don't worry
mov ax,adjustm
cmp timer_micro,ax
jae jitter_ok ;no jitter so OK
mov timer_micro,ax ;else, fix up
;combine timer and count values, put result in timer variables.
jitter_ok: mov ax,dx ;get count_micro
add ax,timer_micro ;sum micro fields
cmp ax,adjustm ;check for underflow
jae compensate ;go ahead - safe
dec count_milli ;borrow
add ax,1000
compensate: sub ax,adjustm ;compensate for time delays
mov timer_micro,ax
cmp ax,1000 ;check for field overflow
jb fld_ok
sub dx,dx
div thousand
mov timer_milli,ax
mov timer_micro,dx
fld_ok: mov ax,count_milli
add timer_milli,ax
cmp timer_milli,1000
jb display
sub dx,dx
mov ax,timer_milli
div thousand
mov timer_sec,ax
mov timer_milli,dx
;display results
display: lea dx,message_sec
print_string
lea bx,ASCII_string
mov ax,timer_sec
call bin_asc
mov dx,bx
print_string
lea dx,message_milli
print_string
lea bx,ASCII_string
mov ax,timer_milli
call bin_asc
mov dx,bx
print_string
lea dx,message_micro
print_string
lea bx,ASCII_string
mov ax,timer_micro
call bin_asc
mov dx,bx
print_string
pop ds
pop dx
pop bx
pop ax
pop bp
ret
_timer_stop endp
;binary to ASCII conversion routine
; Entry bx = pointer to string buffer
; ax = unsigned binary number
; Exit bx = pointer to ASCII number
bin_asc proc near
push dx
push cx
push ax
mov cx,5
clear_buf: mov byte ptr [bx],30h
inc bx
loop clear_buf
convert: sub dx,dx
div ten
add dx,30h
dec bx
mov [bx],dl
or ax,ax
jnz convert
pop ax
pop cx
pop dx
ret
bin_asc endp
_TEXT ends
end