84 lines
10 KiB
HTML
84 lines
10 KiB
HTML
<HTML>
|
|
<HEAD>
|
|
<META name=vsisbn content="1576101746">
|
|
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
|
<META name=vsauthor content="Michael Abrash">
|
|
<META name=vspublisher content="The Coriolis Group">
|
|
<META name=vspubdate content="07/01/97">
|
|
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Assume Nothing</TITLE>
|
|
|
|
<!-- HEADER -->
|
|
<!-- Empty Reference Subhead -->
|
|
|
|
<!--ISBN=1576101746//-->
|
|
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
|
<!--AUTHOR=Michael Abrash//-->
|
|
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
|
<!--CHAPTER=03//-->
|
|
<!--PAGES=045-048//-->
|
|
<!--UNASSIGNED1//-->
|
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
|
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="03-03.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="03-05.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
<P><BR></P>
|
|
<P>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.
|
|
</P>
|
|
<P>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 <B>ZTimerOn</B> 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.</P>
|
|
<P>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 <B>ZTimerOff</B> 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.</P>
|
|
<P>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.</P>
|
|
<P>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).</P>
|
|
<P>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.</P>
|
|
<H3><A NAME="Heading8"></A><FONT COLOR="#000077">Stopping the Zen Timer</FONT></H3>
|
|
<P>At some point after <B>ZTimerOn</B> is called, <B>ZTimerOff</B> must always be called to mark the end of the timing interval. <B>ZTimerOff</B> 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 <B>ZTimerOn</B> was called, and stores the result. Immediately after latching the timer 0 count—and before enabling interrupts—<B>ZTimerOff</B> 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.</P>
|
|
<P>After that, <B>ZTimerOff</B> executes just the overhead code of <B>ZTimerOn</B> and <B>ZTimerOff</B> 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.</P>
|
|
<P>Finally, <B>ZTimerOff</B> restores the context of the calling program, including the state of the interrupt flag that was in effect when <B>ZTimerOn</B> was called to start timing, and returns.</P>
|
|
<P>One interesting aspect of <B>ZTimerOff</B> 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.</P>
|
|
<H3><A NAME="Heading9"></A><FONT COLOR="#000077">Reporting Timing Results</FONT></H3>
|
|
<P><B>ZTimerReport</B> may be called to display timing results at any time after both <B>ZTimerOn</B> and <B>ZTimerOff</B> have been called. <B>ZTimerReport</B> first checks to see whether the timer overflowed (counted down to 0 and turned over) before <B>ZTimerOff</B> was called; if overflow did occur, <B>ZTimerOff</B> prints a message to that effect and returns. Otherwise, <B>ZTimerReport</B> subtracts the reference count (representing the overhead of the Zen timer) from the count measured between the calls to <B>ZTimerOn</B> and <B>ZTimerOff</B>, converts the result from timer counts to microseconds, and prints the resulting time in microseconds to the standard output.</P>
|
|
<P>Note that <B>ZTimerReport</B> need not be called immediately after <B>ZTimerOff</B>. In fact, after a given call to <B>ZTimerOff, ZTimerReport</B> can be called at any time right up until the next call to <B>ZTimerOn</B>.</P>
|
|
<P>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 <B>ZTimerReport</B> 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 <B>ZTimerReport</B>, instead running the program under a debugger that supports screen flipping (such as Turbo Debugger or CodeView), placing a breakpoint at the start of <B>ZTimerReport</B>, and directly observing the count in microseconds as <B>ZTimerReport</B> calculates it.</P>
|
|
<P>A second approach is modification of <B>ZTimerReport</B> to place the result at some safe location in memory, such as an unused portion of the BIOS data area.</P>
|
|
<P>A third approach is alteration of <B>ZTimerReport</B> 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.</P>
|
|
<P>Yet another approach is modification of <B>ZTimerReport</B> to send the result to the printer via either DOS function 5 or BIOS interrupt 17H.</P>
|
|
<P>A final approach is to modify <B>ZTimerReport</B> to print the result to the auxiliary output via DOS function 4, and to then write and load a special device driver named <B>AUX</B>, 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.)</P>
|
|
<P>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.</P><P><BR></P>
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="03-03.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="03-05.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
|
|
<hr width="90%" size="1" noshade>
|
|
<div align="center">
|
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
|
</div>
|
|
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
|
<!-- reference_subfoot = footer -->
|
|
<!-- reference_footer = subfoot -->
|
|
|
|
<!-- BEGIN SUB FOOTER -->
|
|
</BODY>
|
|
</HTML>
|
|
|
|
<!-- END FOOTER -->
|
|
|
|
|