Category : Files from Magazines
Archive   : PJ-VGA.ZIP
Filename : TIMER.ASM

 
Output of file : TIMER.ASM contained in archive : PJ-VGA.ZIP
;
; *** Listing 1 ***
;
; *******************************************************
; * From The Zen of Assembler. Appears by permission of *
; * Scott, Foresman & Company. *
; *******************************************************
;
; Uses the 8253 timer to time the performance of code that takes
; less than about 54 milliseconds to execute, with a resolution
; of better than 10 microseconds.
;
; By Michael Abrash 4/14/89
;
; Externally callable routines:
;
; TimerOn: Starts the timer, with interrupts disabled.
;
; TimerOff: Stops the timer, saves the timer count,
; times the overhead code, and restores interrupts to the
; state they were in when TimerOn was called.
;
; TimerReport: Prints the net time that passed between starting
; and stopping the timer.
;
; Note: If more than about 54 ms passes between TimerOn and
; TimerOff calls, the timer turns over and the count is
; inaccurate. When this happens, an error message is displayed
; instead of a count.
;
; Note: Interrupts *MUST* be left off between calls to TimerOn
; and TimerOff for accurate timing and for detection of
; timer overflow.
;
; Note: These routines can introduce slight inaccuracies into the
; system clock count for each code section timed even if
; timer 0 doesn't overflow. If timer 0 does overflow, the
; system clock can become slow by virtually any amount of
; time, since the system clock can't advance while the
; timer is timing. Consequently, it's a good idea to reboot
; at the end of a timing session. (The battery-backed clock,
; if any, is not affected.)
;
; All registers, and all flags except the interrupt flag, are
; preserved by all routines. Interrupts are enabled and then disabled
; by TimerOn, and are restored by TimerOff to the state they were
; in when TimerOn was called.
;

Code segment word public 'CODE'
assume cs:Code, ds:nothing
public TimerOn, TimerOff, TimerReport

;
; Base address of the 8253 timer chip.
;
BASE_8253 equ 40h
;
; The address of the timer 0 count registers in the 8253.
;
TIMER_0_8253 equ BASE_8253 + 0
;
; The address of the mode register in the 8253.
;
MODE_8253 equ BASE_8253 + 3
;
; The address of Operation Command Word 3 in the 8259 Programmable
; Interrupt Controller (PIC) (write only, and writable only when
; bit 4 of the byte written to this address is 0 and bit 3 is 1).
;
OCW3 equ 20h
;
; The address of the Interrupt Request register in the 8259 PIC
; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
; of OCW3 = 0).
;
IRR equ 20h
;
; Macro to emulate a POPF instruction while fixing the bug in some
; 80286 chips which allows interrupts to occur during a POPF even when
; interrupts remain disabled.
;
MPOPF macro
local p1, p2
jmp short p2
p1: iret ;jump to pushed address & pop flags
p2: push cs ;construct far return address to
call p1 ; the next instruction
endm

;
; Macro to delay briefly to ensure that enough time has elapsed
; between successive I/O accesses so that the device being accessed
; can respond to both. Jumping flushes the prefetch queue, forcing
; a memory access.
;
DELAY macro
jmp $+2
jmp $+2
jmp $+2
endm

OriginalFlags dw ? ;processor flags in effect
; when TimerOn called
TimedCount dw ? ;timer 0 count when the timer
; is stopped
ReferenceCount dw ? ;number of counts required to
; execute timer overhead code
OverflowFlag db ? ;used to indicate whether the
; timer overflowed during the
; timing interval
;
; String printed to report results.
;
OutputStr label byte
db 0dh, 0ah, 'Timed count: ', 5 dup (?)
ASCIICountEnd label byte
db ' microseconds', 0dh, 0ah
db '$'
;
; String printed to report timer overflow.
;
OverflowStr label byte
db 0dh, 0ah
db '****************************************************'
db 0dh, 0ah
db '* The timer overflowed; the interval timed was too *'
db 0dh, 0ah
db '* long for the timer to measure. *'
db 0dh, 0ah
db '****************************************************'
db 0dh, 0ah
db '$'

;********************************************************************
;* Routine called to start timing. *
;********************************************************************

TimerOn proc near

;
; Save the context of the program being timed.
;
push ax
pushf
pop ax ;get flags so we can force
; interrupts off when leaving
; this routine
mov cs:[OriginalFlags],ax
and ax,0fdffh ;set pushed interrupt flag
; to 0
push ax
;
; Turn on interrupts, so the timer interrupt can occur if it's
; pending.
;
sti
;
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
; linear counting rather than count-by-two counting. Also
; leaves the 8253 waiting for the initial timer 0 count to
; be loaded.
;
mov al,00110100b ;mode 2
out MODE_8253,al
;
; Set the timer count to 0, so we know we won't get another
; timer interrupt right away.
; Note: this introduces an inaccuracy of up to 54 ms in the system
; clock count each time it is executed.
;
DELAY
sub al,al
out TIMER_0_8253,al ;lsb
DELAY
out TIMER_0_8253,al ;msb
;
; Wait before clearing interrupts to allow the interrupt generated
; when switching from mode 3 to mode 2 to be recognized. This delay
; must be at least 210 ns long to allow time for that interrupt to
; occur. Here, 10 jumps are used for the delay to ensure that the
; delay time will be more than long enough even on fast 80386
; computers.
;
rept 10
jmp $+2
endm
;
; Disable interrupts to get an accurate count.
;
cli
;
; Set the timer count to 0 again to start the timing interval.
;
mov al,00110100b ;set up to load initial
out MODE_8253,al ; timer count
DELAY
sub al,al
out TIMER_0_8253,al ;load count lsb
DELAY
out TIMER_0_8253,al ;load count msb
;
; Restore the context and return.
;
MPOPF ;keeps interrupts off
pop ax
ret

TimerOn endp

;********************************************************************
;* Routine called to stop timing and get count. *
;********************************************************************

TimerOff proc near

;
; Save the context of the program being timed.
;
push ax
push cx
pushf
;
; Latch the count.
;
mov al,00000000b ;latch timer 0
out MODE_8253,al
;
; See if the timer has overflowed by checking the 8259 for a pending
; timer interrupt.
;
mov al,00001010b ;OCW3, set up to read
out OCW3,al ; Interrupt Request register
DELAY
in al,IRR ;read Interrupt Request
; register
and al,1 ;set AL to 1 if IRQ0 (the
; timer interrupt) is pending
mov cs:[OverflowFlag],al ;yes, it did indeed overflow
;
; Allow interrupts to happen again.
;
sti
;
; Read out the count we latched earlier.
;
in al,TIMER_0_8253 ;least significant byte
DELAY
mov ah,al
in al,TIMER_0_8253 ;most significant byte
xchg ah,al
neg ax ;convert from countdown
; remaining to elapsed
; count
mov cs:[TimedCount],ax
; Time a zero-length code fragment, to get a reference for how
; much overhead this routine has. Time it 16 times and average it,
; for accuracy, rounding the result.
; Note: REferenceTimerOn resets the timer to 0, which introduces
; an inaccuracy of up to 54 ms in the system clock count each time
; it is executed.
;
mov cs:[ReferenceCount],0
mov cx,16
cli ;interrupts off to allow a
; precise reference count
RefLoop:
call ReferenceTimerOn
call ReferenceTimerOff
loop RefLoop
sti
add cs:[ReferenceCount],8 ;total + (0.5 * 16)
mov cl,4
shr cs:[ReferenceCount],cl ;(total) / 16 + 0.5
;
; Restore original interrupt state.
;
pop ax ;retrieve flags when called
mov cx,cs:[OriginalFlags] ;get back the original flags
and ch,not 0fdh ;only care about original
; interrupt flag...
and ah,0fdh ;...keep all other flags in
; their current condition
or ah,ch ;make flags word with original
; interrupt flag
push ax ;prepare flags to be popped
;
; Restore the context of the program being timed and return to it.
;
MPOPF ;restore the flags with the
; original interrupt state
pop cx
pop ax
ret

TimerOff endp

;
; Called by TimerOff to start timer for overhead measurements.
;

ReferenceTimerOn proc near
;
; Save the context of the program being timed.
;
push ax
pushf ;interrupts are already off
;
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
; linear counting rather than count-by-two counting.
;
mov al,00110100b ;set up to load
out MODE_8253,al ; initial timer count
DELAY
;
; Set the timer count to 0.
;
sub al,al
out TIMER_0_8253,al ;load count lsb
DELAY
out TIMER_0_8253,al ;load count msb
;
; Restore the context of the program being timed and return to it.
;
MPOPF
pop ax
ret

ReferenceTimerOn endp

;
; Called by TimerOff to stop timer and add result to ReferenceCount
; for overhead measurements.
;

ReferenceTimerOff proc near
;
; Save the context of the program being timed.
;
push ax
push cx
pushf
;
; Latch the count and read it.
;
mov al,00000000b ;latch timer 0
out MODE_8253,al
DELAY
in al,TIMER_0_8253 ;lsb
DELAY
mov ah,al
in al,TIMER_0_8253 ;msb
xchg ah,al
neg ax ;convert from countdown
; remaining to amount
; counted down
add cs:[ReferenceCount],ax
;
; Restore the context of the program being timed and return to it.
;
MPOPF
pop cx
pop ax
ret

ReferenceTimerOff endp

;********************************************************************
;* Routine called to report timing results. *
;********************************************************************

TimerReport proc near

pushf
push ax
push bx
push cx
push dx
push si
push ds
;
push cs
pop ds
assume ds:Code
;
; Check for timer 0 overflow.
;
cmp [OverflowFlag],0
jz PrintGoodCount
mov dx,offset OverflowStr
mov ah,9
int 21h
jmp short EndTimerReport
;
; Convert net count to decimal ASCII in microseconds.
;
PrintGoodCount:
mov ax,[TimedCount]
sub ax,[ReferenceCount]
mov si,offset ASCIICountEnd - 1
;
; Convert count to microseconds by multiplying by .8381.
;
mov dx,8381
mul dx
mov bx,10000
div bx ;* .8381 = * 8381 / 10000
;
; Convert time in microseconds to 5 decimal ASCII digits.
;
mov bx,10
mov cx,5
CTSLoop:
sub dx,dx
div bx
add dl,'0'
mov [si],dl
dec si
loop CTSLoop
;
; Print the results.
;
mov ah,9
mov dx,offset OutputStr
int 21h
;
EndTimerReport:
pop ds
pop si
pop dx
pop cx
pop bx
pop ax
MPOPF
ret

TimerReport endp

Code ends
end


  3 Responses to “Category : Files from Magazines
Archive   : PJ-VGA.ZIP
Filename : TIMER.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/