alan@oetl1.scf.lmsc.lockheed.com (Alan Strassberg) (05/27/91)
In article <1991May23.152030.7710@ugle.unit.no> gunnarkn@itk.unit.no (Gunnar Knutson) writes: > >-- > I need to time the interval between two interrupts with >accuracy in the milli-seconds range. The maximum resolution on the AT >real time clock is one second, so I can't use that. Does anyone out >there have a better idea ? Here's the ASM source to do it. From Dr. Dobbs. alan ################################################################################ ;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 -- Alan Strassberg alan@oetl1.scf.lmsc.lockheed.com (408) 425-6139 alan@oetl.UUCP