Initial conversion to markdown
This commit is contained in:
parent
a96a114403
commit
5e10794a24
362 changed files with 67353 additions and 0 deletions
138
03-03.md
Normal file
138
03-03.md
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
------------------------ --------------------------------- --------------------
|
||||
[Previous](03-02.html) [Table of Contents](index.html) [Next](03-04.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
#### The Zen Timer Is a Means, Not an End {#Heading5}
|
||||
|
||||
We’re going to spend the rest of this chapter seeing what the Zen timer
|
||||
can do, examining how it works, and learning how to use it. I’ll be
|
||||
using the Zen timer again and again over the course of this book, so
|
||||
it’s essential that you learn what the Zen timer can do and how to use
|
||||
it. On the other hand, it is by no means essential that you understand
|
||||
exactly how the Zen timer works. (Interesting, yes; essential, no.)
|
||||
|
||||
In other words, the Zen timer isn’t really part of the knowledge we
|
||||
seek; rather, it’s one tool with which we’ll acquire that knowledge.
|
||||
Consequently, you shouldn’t worry if you don’t fully grasp the inner
|
||||
workings of the Zen timer. Instead, focus on learning how to *use* it,
|
||||
and you’ll be on the right road.
|
||||
|
||||
#### Starting the Zen Timer {#Heading6}
|
||||
|
||||
**ZTimerOn** is called at the start of a segment of code to be timed.
|
||||
**ZTimerOn** saves the context of the calling code, disables interrupts,
|
||||
sets timer 0 of the 8253 to mode 2 (divide-by-N mode), sets the initial
|
||||
timer count to 0, restores the context of the calling code, and returns.
|
||||
(I’d like to note that while Intel’s documentation for the 8253 seems to
|
||||
indicate that a timer won’t reset to 0 until it finishes counting down,
|
||||
in actual practice, timers seem to reset to 0 as soon as they’re
|
||||
loaded.)
|
||||
|
||||
Two aspects of **ZTimerOn** are worth discussing further. One point of
|
||||
interest is that **ZTimerOn** disables interrupts. (**ZTimerOff** later
|
||||
restores interrupts to the state they were in when **ZTimerOn** was
|
||||
called.) Were interrupts not disabled by **ZTimerOn**, keyboard, mouse,
|
||||
timer, and other interrupts could occur during the timing interval, and
|
||||
the time required to service those interrupts would incorrectly and
|
||||
erratically appear to be part of the execution time of the code being
|
||||
measured. As a result, code timed with the Zen timer should not expect
|
||||
any hardware interrupts to occur during the interval between any call to
|
||||
**ZTimerOn** and the corresponding call to **ZTimerOff**, and should not
|
||||
enable interrupts during that time.
|
||||
|
||||
### Time and the PC {#Heading7}
|
||||
|
||||
A second interesting point about **ZTimerOn** is that it may introduce
|
||||
some small inaccuracy into the system clock time whenever it is called.
|
||||
To understand why this is so, we need to examine the way in which both
|
||||
the 8253 and the PC’s system clock (which keeps the current time) work.
|
||||
|
||||
The 8253 actually contains three timers, as shown in Figure 3.1. All
|
||||
three timers are driven by the system board’s 14.31818 MHz crystal,
|
||||
divided by 12 to yield a 1.19318 MHz clock to the timers, so the timers
|
||||
count once every 838.1 ns. Each of the three timers counts down in a
|
||||
programmable way, generating a signal on its output pin when it counts
|
||||
down to 0. Each timer is capable of being halted at any time via a 0
|
||||
level on its gate input; when a timer’s gate input is 1, that timer
|
||||
counts constantly. All in all, the 8253’s timers are inherently very
|
||||
flexible timing devices; unfortunately, much of that flexibility depends
|
||||
on how the timers are connected to external circuitry, and in the PC the
|
||||
timers are connected with specific purposes in mind.
|
||||
|
||||
Timer 2 drives the speaker, although it can be used for other timing
|
||||
purposes when the speaker is not in use. As shown in Figure 3.1, timer 2
|
||||
is the only timer with a programmable gate input in the PC; that is,
|
||||
timer 2 is the only timer that can be started and stopped under program
|
||||
control in the manner specified by Intel. On the other hand, the
|
||||
*output* of timer 2 is connected to nothing other than the speaker. In
|
||||
particular, timer 2 cannot generate an interrupt to get the 8088’s
|
||||
attention.
|
||||
|
||||
Timer 1 is dedicated to providing dynamic RAM refresh, and should not be
|
||||
tampered with lest system crashes result.
|
||||
|
||||
\
|
||||
**Figure 3.1** *The configuration of the 8253 timer chip in the PC.*
|
||||
|
||||
Finally, timer 0 is used to drive the system clock. As programmed by the
|
||||
BIOS at power-up, every 65,536 (64K) counts, or 54.925 milliseconds,
|
||||
timer 0 generates a rising edge on its output line. (A millisecond is
|
||||
one-thousandth of a second, and is abbreviated ms.) This line is
|
||||
connected to the hardware interrupt 0 (IRQ0) line on the system board,
|
||||
so every 54.925 ms, timer 0 causes hardware interrupt 0 to occur.
|
||||
|
||||
The interrupt vector for IRQ0 is set by the BIOS at power-up time to
|
||||
point to a BIOS routine, **TIMER\_INT,** that maintains a time-of-day
|
||||
count. **TIMER\_INT** keeps a 16-bit count of IRQ0 interrupts in the
|
||||
BIOS data area at address 0000:046C (all addresses in this book are
|
||||
given in segment:offset hexadecimal pairs); this count turns over once
|
||||
an hour (less a few microseconds), and when it does, **TIMER\_INT**
|
||||
updates a 16-bit hour count at address 0000:046E in the BIOS data area.
|
||||
This count is the basis for the current time and date that DOS supports
|
||||
via functions 2AH (2A hexadecimal) through 2DH and by way of the DATE
|
||||
and TIME commands.
|
||||
|
||||
Each timer channel of the 8253 can operate in any of six modes. Timer 0
|
||||
normally operates in mode 3: *square wave mode*. In square wave mode,
|
||||
the initial count is counted down two at a time; when the count reaches
|
||||
zero, the output state is changed. The initial count is again counted
|
||||
down two at a time, and the output state is toggled back when the count
|
||||
reaches zero. The result is a square wave that changes state more slowly
|
||||
than the input clock by a factor of the initial count. In its normal
|
||||
mode of operation, timer 0 generates an output pulse that is low for
|
||||
about 27.5 ms and high for about 27.5 ms; this pulse is sent to the 8259
|
||||
interrupt controller, and its rising edge generates a timer interrupt
|
||||
once every 54.925 ms.
|
||||
|
||||
Square wave mode is not very useful for precision timing because it
|
||||
counts down by two twice per timer interrupt, thereby rendering exact
|
||||
timings impossible. Fortunately, the 8253 offers another timer mode,
|
||||
mode 2 (divide-by-N mode), which is both a good substitute for square
|
||||
wave mode and a perfect mode for precision timing.
|
||||
|
||||
Divide-by-N mode counts down by one from the initial count. When the
|
||||
count reaches zero, the timer turns over and starts counting down again
|
||||
without stopping, and a pulse is generated for a single clock period.
|
||||
While the pulse is not held for nearly as long as in square wave mode,
|
||||
it doesn’t matter, since the 8259 interrupt controller is configured in
|
||||
the PC to be edgeand hence cares only about the existence of a pulse
|
||||
from timer 0, not the duration of the pulse. As a result, timer 0
|
||||
continues to generate timer interrupts in divide-by-N mode, and the
|
||||
system clock continues to maintain good time.
|
||||
|
||||
Why not use timer 2 instead of timer 0 for precision timing? After all,
|
||||
timer 2 has a programmable gate input and isn’t used for anything but
|
||||
sound generation. The problem with timer 2 is that its output can’t
|
||||
generate an interrupt; in fact, timer 2 can’t do anything but drive the
|
||||
speaker. We need the interrupt generated by the output of timer 0 to
|
||||
tell us when the count has overflowed, and we will see shortly that the
|
||||
timer interrupt also makes it possible to time much longer periods than
|
||||
the Zen timer shown in Listing 3.1 supports.
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](03-02.html) [Table of Contents](index.html) [Next](03-04.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
* * * * *
|
||||
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
Loading…
Reference in a new issue