Initial conversion to markdown
This commit is contained in:
parent
a96a114403
commit
5e10794a24
362 changed files with 67353 additions and 0 deletions
156
03-04.md
Normal file
156
03-04.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
------------------------ --------------------------------- --------------------
|
||||
[Previous](03-03.html) [Table of Contents](index.html) [Next](03-05.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
In fact, the Zen timer shown in Listing 3.1 can only time intervals of
|
||||
up to about 54 ms in length, since that is the period of time that can
|
||||
be measured by timer 0 before its count turns over and repeats.
|
||||
fifty-four ms may not seem like a very long time, but even a CPU as slow
|
||||
as the 8088 can perform more than 1,000 divides in 54 ms, and division
|
||||
is the single instruction that the 8088 performs most slowly. If a
|
||||
measured period turns out to be longer than 54 ms (that is, if timer 0
|
||||
has counted down and turned over), the Zen timer will display a message
|
||||
to that effect. A long-period Zen timer for use in such cases will be
|
||||
presented later in this chapter.
|
||||
|
||||
The Zen timer determines whether timer 0 has turned over by checking to
|
||||
see whether an IRQ0 interrupt is pending. (Remember, interrupts are off
|
||||
while the Zen timer runs, so the timer interrupt cannot be recognized
|
||||
until the Zen timer stops and enables interrupts.) If an IRQ0 interrupt
|
||||
is pending, then timer 0 has turned over and generated a timer
|
||||
interrupt. Recall that **ZTimerOn** initially sets timer 0 to 0, in
|
||||
order to allow for the longest possible period—about 54 ms—before timer
|
||||
0 reaches 0 and generates the timer interrupt.
|
||||
|
||||
Now we’re ready to look at the ways in which the Zen timer can introduce
|
||||
inaccuracy into the system clock. Since timer 0 is initially set to 0 by
|
||||
the Zen timer, and since the system clock ticks only when timer 0 counts
|
||||
off 54.925 ms and reaches 0 again, an average inaccuracy of one-half of
|
||||
54.925 ms, or about 27.5 ms, is incurred each time the Zen timer is
|
||||
started. In addition, a timer interrupt is generated when timer 0 is
|
||||
switched from mode 3 to mode 2, advancing the system clock by up to
|
||||
54.925 ms, although this only happens the first time the Zen timer is
|
||||
run after a warm or cold boot. Finally, up to 54.925 ms can again be
|
||||
lost when **ZTimerOff** is called, since that routine again sets the
|
||||
timer count to zero. Net result: The system clock will run up to 110 ms
|
||||
(about a ninth of a second) slow each time the Zen timer is used.
|
||||
|
||||
Potentially far greater inaccuracy can be incurred by timing code that
|
||||
takes longer than about 110 ms to execute. Recall that all interrupts,
|
||||
including the timer interrupt, are disabled while timing code with the
|
||||
Zen timer. The 8259 interrupt controller is capable of remembering at
|
||||
most one pending timer interrupt, so all timer interrupts after the
|
||||
first one during any given Zen timing interval are ignored.
|
||||
Consequently, if a timing interval exceeds 54.9 ms, the system clock
|
||||
effectively stops 54.9 ms after the timing interval starts and doesn’t
|
||||
restart until the timing interval ends, losing time all the while.
|
||||
|
||||
The effects on the system time of the Zen timer aren’t a matter for
|
||||
great concern, as they are temporary, lasting only until the next warm
|
||||
or cold boot. System that have batteryclocks, (AT-style machines; that
|
||||
is, virtually all machines in common use) automatically reset the
|
||||
correct time whenever the computer is booted, and systems without
|
||||
battery-clocks prompt for the correct date and time when booted.
|
||||
Also,repeated use of the Zen timer usually makes the system clock slow
|
||||
by at most a total of a few seconds, unless code that takes much longer
|
||||
than 54 ms to run is timed (in which case the Zen timer will notify you
|
||||
that the code is too long to time).
|
||||
|
||||
Nonetheless, it’s a good idea to reboot your computer at the end of each
|
||||
session with the Zen timer in order to make sure that the system clock
|
||||
is correct.
|
||||
|
||||
### Stopping the Zen Timer {#Heading8}
|
||||
|
||||
At some point after **ZTimerOn** is called, **ZTimerOff** must always be
|
||||
called to mark the end of the timing interval. **ZTimerOff** saves the
|
||||
context of the calling program, latches and reads the timer 0 count,
|
||||
converts that count from the countdown value that the timer maintains to
|
||||
the number of counts elapsed since **ZTimerOn** was called, and stores
|
||||
the result. Immediately after latching the timer 0 count—and before
|
||||
enabling interrupts—**ZTimerOff** checks the 8259 interrupt controller
|
||||
to see if there is a pending timer interrupt, setting a flag to mark
|
||||
that the timer overflowed if there is indeed a pending timer interrupt.
|
||||
|
||||
After that, **ZTimerOff** executes just the overhead code of
|
||||
**ZTimerOn** and **ZTimerOff** 16 times, and averages and saves the
|
||||
results in order to determine how many of the counts in the timing
|
||||
result just obtained were incurred by the overhead of the Zen timer
|
||||
rather than by the code being timed.
|
||||
|
||||
Finally, **ZTimerOff** restores the context of the calling program,
|
||||
including the state of the interrupt flag that was in effect when
|
||||
**ZTimerOn** was called to start timing, and returns.
|
||||
|
||||
One interesting aspect of **ZTimerOff** is the manner in which timer 0
|
||||
is stopped in order to read the timer count. We don’t actually have to
|
||||
stop timer 0 to read the count; the 8253 provides a special latched read
|
||||
feature for the specific purpose of reading the count while a time is
|
||||
running. (That’s a good thing, too; we’ve no documented way to stop
|
||||
timer 0 if we wanted to, since its gate input isn’t connected. Later in
|
||||
this chapter, though, we’ll see that timer 0 can be stopped after all.)
|
||||
We simply tell the 8253 to latch the current count, and the 8253 does so
|
||||
without breaking stride.
|
||||
|
||||
### Reporting Timing Results {#Heading9}
|
||||
|
||||
**ZTimerReport** may be called to display timing results at any time
|
||||
after both **ZTimerOn** and **ZTimerOff** have been called.
|
||||
**ZTimerReport** first checks to see whether the timer overflowed
|
||||
(counted down to 0 and turned over) before **ZTimerOff** was called; if
|
||||
overflow did occur, **ZTimerOff** prints a message to that effect and
|
||||
returns. Otherwise, **ZTimerReport** subtracts the reference count
|
||||
(representing the overhead of the Zen timer) from the count measured
|
||||
between the calls to **ZTimerOn** and **ZTimerOff**, converts the result
|
||||
from timer counts to microseconds, and prints the resulting time in
|
||||
microseconds to the standard output.
|
||||
|
||||
Note that **ZTimerReport** need not be called immediately after
|
||||
**ZTimerOff**. In fact, after a given call to **ZTimerOff,
|
||||
ZTimerReport** can be called at any time right up until the next call to
|
||||
**ZTimerOn**.
|
||||
|
||||
You may want to use the Zen timer to measure several portions of a
|
||||
program while it executes normally, in which case it may not be
|
||||
desirable to have the text printed by **ZTimerReport** interfere with
|
||||
the program’s normal display. There are many ways to deal with this. One
|
||||
approach is removal of the invocations of the DOS print string function
|
||||
(INT 21H with AH equal to 9) from **ZTimerReport**, instead running the
|
||||
program under a debugger that supports screen flipping (such as Turbo
|
||||
Debugger or CodeView), placing a breakpoint at the start of
|
||||
**ZTimerReport**, and directly observing the count in microseconds as
|
||||
**ZTimerReport** calculates it.
|
||||
|
||||
A second approach is modification of **ZTimerReport** to place the
|
||||
result at some safe location in memory, such as an unused portion of the
|
||||
BIOS data area.
|
||||
|
||||
A third approach is alteration of **ZTimerReport** to print the result
|
||||
over a serial port to a terminal or to another PC acting as a terminal.
|
||||
Similarly, many debuggers can be run from a remote terminal via a serial
|
||||
link.
|
||||
|
||||
Yet another approach is modification of **ZTimerReport** to send the
|
||||
result to the printer via either DOS function 5 or BIOS interrupt 17H.
|
||||
|
||||
A final approach is to modify **ZTimerReport** to print the result to
|
||||
the auxiliary output via DOS function 4, and to then write and load a
|
||||
special device driver named **AUX**, to which DOS function 4 output
|
||||
would automatically be directed. This device driver could send the
|
||||
result anywhere you might desire. The result might go to the secondary
|
||||
display adapter, over a serial port, or to the printer, or could simply
|
||||
be stored in a buffer within the driver, to be dumped at a later time.
|
||||
(Credit for this final approach goes to Michael Geary, and thanks go to
|
||||
David Miller for passing the idea on to me.)
|
||||
|
||||
You may well want to devise still other approaches better suited to your
|
||||
needs than those I’ve presented. Go to it! I’ve just thrown out a few
|
||||
possibilities to get you started.
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](03-03.html) [Table of Contents](index.html) [Next](03-05.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
* * * * *
|
||||
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
Loading…
Reference in a new issue