abrash-black-book/03-08.md
2013-12-30 20:26:41 +11:00

127 lines
No EOL
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Moreover, because it uses an undocumented feature, the timer-stop
approach could conceivably cause erratic 8253 operation, which could in
turn seriously affect your computer's operation until the next reboot.
In non-8253-compatible systems, I've observed not only wildly incorrect
timing results, but also failure of a diskette drive to operate properly
after the long-period Zen timer with **PS2** set to 0 has run, so be
alert for signs of trouble if you do set **PS2** to 0.
Rebooting should clear up any timer-related problems of the sort
described above. (This gives us another reason to reboot at the end of
each code-timing session.) You should *immediately* reboot and set the
**PS2** equate to 1 if you get erratic or obviously incorrect results
with the long-period Zen timer when **PS2** is set to 0. If you want to
set **PS2** to 0, it would be a good idea to time a few of the listings
in this book with **PS2** set first to 1 and then to 0, to make sure
that the results match. If they're consistently different, you should
set **PS2** to 1.
While the the non-PS/2 version is more dangerous than the PS/2 version,
it also produces more accurate results when it does work. If you have a
non-PS/2 PC-compatible computer, the choice between the two timing
approaches is yours.
If you do leave the **PS2** equate at 1 in Listing 3.5, you should
repeat each code-timing run several times before relying on the results
to be accurate to more than 54 ms, since variations may result from the
possible lack of synchronization between the timer 0 count and the BIOS
time-of-day count. In fact, it's a good idea to time code more than once
no matter which version of the long-period Zen timer you're using, since
interrupts, which must be enabled in order for the long-period timer to
work properly, may occur at any time and can alter execution time
substantially.
Finally, please note that the *precision* Zen timer works perfectly well
on both PS/2 and non-PS/2 computers. The PS/2 and 8253 considerations
we've just discussed apply *only* to the longZen timer.
### Example Use of the Long-Period Zen Timer {#Heading14}
The long-period Zen timer has exactly the same calling interface as the
precision Zen timer, and can be used in place of the precision Zen timer
simply by linking it to the code to be timed in place of linking the
precision timer code. Whenever the precision Zen timer informs you that
the code being timed takes too long for the precision timer to handle,
all you have to do is link in the long-period timer instead.
Listing 3.6 shows a test-bed program for the long-period Zen timer.
While this program is similar to Listing 3.2, it's worth noting that
Listing 3.6 waits for a few seconds before calling **ZTimerOn**, thereby
allowing any pending keyboard interrupts to be processed. Since
interrupts must be left on in order to time periods longer than 54 ms,
the interrupts generated by keystrokes (including the upstroke of the
Enter key press that starts the program)—or any other interrupts, for
that matter—could incorrectly inflate the time recorded by the
long-period Zen timer. In light of this, resist the temptation to type
ahead, move the mouse, or the like while the long-period Zen timer is
timing.
**LISTING 3.6 LZTEST.ASM**
; Program to measure performance of code that takes longer than
; 54 ms to execute. (LZTEST.ASM)
;
; Link with LZTIMER.ASM (Listing 3.5). LZTIME.BAT (Listing 3.7)
; can be used to assemble and link both files. Code to be
; measured must be in the file TESTCODE; Listing 3.8 shows
; a sample file (LST3-8.ASM) which should be named TESTCODE.
;
; By Michael Abrash
;
mystack segment para stack STACK'
db 512 dup(?)
mystack ends
;
Code segment para public CODE'
assume cs:Code, ds:Code
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
Startproc near
push cs
pop ds ;point DS to the code segment,
; so data as well as code can easily
; be included in TESTCODE
;
; Delay for 6-7 seconds, to let the Enter keystroke that started the
; program come back up.
;
mov ah,2ch
int 21h ;get the current time
mov bh,dh ;set the current time aside
DelayLoop:
mov ah,2ch
push bx ;preserve start time
int 21h ;get time
pop bx ;retrieve start time
cmp dh,bh ;is the new seconds count less than
; the start seconds count?
jnb CheckDelayTime ;no
add dh,60 ;yes, a minute must have turned over,
; so add one minute
CheckDelayTime:
sub dh,bh ;get time that's passed
cmp dh,7 ;has it been more than 6 seconds yet?
jb DelayLoop ;not yet
;
include TESTCODE ;code to be measured, including calls
; to ZTimerOn and ZTimerOff
;
; Display the results.
;
call ZTimerReport
;
; Terminate the program.
;
mov ah,4ch
int 21h
Start endp
Code ends
end Start
As with the precision Zen timer, the program in Listing 3.6 is used by
naming the file containing the code to be timed TESTCODE, then
assembling both Listing 3.6 and Listing 3.5 with MASM or TASM and
linking the two files together by way of the Microsoft or Borland
linker. Listing 3.7 shows a batch file, named LZTIME.BAT, which does all
of the above, generating and running the executable file LZTEST.EXE.
LZTIME.BAT assumes that the file LZTIMER.ASM contains Listing 3.5 and
the file LZTEST.ASM contains Listing 3.6.