Strip comments and inline ids onto h*

This commit is contained in:
James Gregory 2013-12-30 15:11:04 +11:00
commit 0f5f617adb
362 changed files with 2519 additions and 6694 deletions

View file

@ -10,16 +10,9 @@
<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: In the Lair of the Cycle-Eaters</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.//-->
<title>Michael Abrash's Graphics Programming Black Book Special Edition: In the Lair of the Cycle-Eaters</title>
<meta name="chapter" content="04" />
<meta name="pages" content="084-087" />
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//-->
</head>
<body>
@ -37,22 +30,22 @@
<p><br /></p>
<p>Back to the 8-bit bus cycle-eater. As I&rsquo;ve said, in 8088 work you should strive to use byte-sized memory variables whenever possible. That does <i>not</i> mean that you should use 2 byte-sized memory accesses to manipulate a word-sized memory variable in preference to 1 word-sized memory access, as, for instance,</p><!-- CODE SNIP //-->
<p>Back to the 8-bit bus cycle-eater. As I&rsquo;ve said, in 8088 work you should strive to use byte-sized memory variables whenever possible. That does <i>not</i> mean that you should use 2 byte-sized memory accesses to manipulate a word-sized memory variable in preference to 1 word-sized memory access, as, for instance,</p>
<pre>
mov dl,byte ptr [MemVar]
mov dh,byte ptr [MemVar+1]
</pre><!-- END CODE SNIP //-->
</pre>
<p>versus:</p><!-- CODE SNIP //-->
<p>versus:</p>
<pre>
mov dx,word ptr [MemVar]
</pre><!-- END CODE SNIP //-->
</pre>
<p>Recall that every access to a memory byte takes at least 4 cycles; that limitation is built right into the 8088. The 8088 is also built so that the second byte-sized memory access to a 16-bit memory variable takes just those 4 cycles and no more. There&rsquo;s no way you can manipulate the second byte of a word-sized memory variable faster with a second separate byte-sized instruction in less than 4 cycles. As a matter of fact, you&rsquo;re bound to access that second byte much more slowly with a separate instruction, thanks to the overhead of instruction fetching and execution, address calculation, and the like.</p>
<p>For example, consider Listing 4.3, which performs 1,000 word-sized reads from memory. This code runs in 3.77 &micro;s per word read on a 4.77 MHz 8088. That&rsquo;s 45 percent faster than the 5.49 &micro;s per word read of Listing 4.4, which reads the same 1,000 words as Listing 4.3 but does so with 2,000 byte-sized reads. Both listings perform exactly the same number of memory accesses&mdash;2,000 accesses, each byte-sized, as all 8088 memory accesses must be. (Remember that the Bus Interface Unit must perform two byte-sized memory accesses in order to handle a word-sized memory operand.) However, Listing 4.3 is considerably faster because it expends only 4 additional cycles to read the second byte of each word, while Listing 4.4 performs a second <b>LODSB,</b> requiring 13 cycles, to read the second byte of each word.</p>
<p><b>LISTING 4.3 LST4-3.ASM</b></p><!-- CODE SNIP //-->
<p><b>LISTING 4.3 LST4-3.ASM</b></p>
<pre>
; Measures the performance of reading 1,000 words
; from memory with 1,000 word-sized accesses.
@ -62,9 +55,9 @@ mov dx,word ptr [MemVar]
call ZTimerOn
rep lodsw
call ZTimerOff
</pre><!-- END CODE SNIP //-->
</pre>
<p><b>LISTING 4.4 LST4-4.ASM</b></p><!-- CODE SNIP //-->
<p><b>LISTING 4.4 LST4-4.ASM</b></p>
<pre>
; Measures the performance of reading 1000 words
; from memory with 2,000 byte-sized accesses.
@ -74,7 +67,7 @@ mov dx,word ptr [MemVar]
call ZTimerOn
rep lodsb
call ZTimerOff
</pre><!-- END CODE SNIP //-->
</pre>
<p>In short, if you must perform a 16-bit memory access, let the 8088 break the access into two byte-sized accesses for you. The 8088 is more efficient at that task than your code can possibly be.</p>
@ -86,7 +79,7 @@ mov dx,word ptr [MemVar]
<p>Yes and no. It&rsquo;s true that in general we know approximately how much longer a given instruction will take to execute with a word-sized memory operand than with a byte-sized operand, although the dynamic RAM refresh and wait state cycle-eaters (which I&rsquo;ll cover a little later) can raise the cost of the 8-bit bus cycle-eater considerably. However, <i>all</i> word-sized memory accesses lose 4 cycles to the 8-bit bus cycle-eater, and there&rsquo;s one sort of word-sized memory access we haven&rsquo;t discussed yet: instruction fetching. The ugliest manifestation of the 8-bit bus cycle-eater is in fact the prefetch queue cycle-eater.</p>
<h3><a id="Heading9"></a>The Prefetch Queue Cycle-Eater</h3>
<h3 id="Heading9">The Prefetch Queue Cycle-Eater</h3>
<p>In an 8088 context, here&rsquo;s the prefetch queue cycle-eater in a nutshell: The 8088&rsquo;s 8-bit external data bus keeps the Bus Interface Unit from fetching instruction bytes as fast as the 16-bit Execution Unit can execute them, so the Execution Unit often lies idle while waiting for the next instruction byte to be fetched.</p>
@ -96,19 +89,19 @@ mov dx,word ptr [MemVar]
<p>Clearly, then, the prefetch queue cycle-eater is nothing more than one aspect of the 8-bit bus cycle-eater. 8088 code often runs at less than the Execution Unit&rsquo;s maximum speed because the 8-bit data bus can&rsquo;t keep up with the demand for instruction bytes. That&rsquo;s straightforward enough&mdash;so why all the fuss about the prefetch queue cycle-eater?</p>
<p>What makes the prefetch queue cycle-eater tricky is that it&rsquo;s undocumented and unpredictable. That is, with a word-sized memory access, such as</p><!-- CODE SNIP //-->
<p>What makes the prefetch queue cycle-eater tricky is that it&rsquo;s undocumented and unpredictable. That is, with a word-sized memory access, such as</p>
<pre>
mov [bx],ax
</pre><!-- END CODE SNIP //-->
</pre>
<p>it&rsquo;s well-documented that an extra 4 cycles will always be required to write the upper byte of AX to memory. Not so with the prefetch queue cycle-eater lurking nearby. For instance, the instructions</p><!-- CODE SNIP //-->
<p>it&rsquo;s well-documented that an extra 4 cycles will always be required to write the upper byte of AX to memory. Not so with the prefetch queue cycle-eater lurking nearby. For instance, the instructions</p>
<pre>
shr ax,1
shr ax,1
shr ax,1
shr ax,1
shr ax,1
</pre><!-- END CODE SNIP //-->
</pre>
<p>should execute in 10 cycles, since each <b>SHR</b> takes 2 cycles to execute, according to Intel&rsquo;s specifications. Those specifications contain Intel&rsquo;s official instruction execution times, but in this case&mdash;and in many others&mdash;the specifications are drastically wrong. Why? Because they describe execution time <i>once an instruction reaches the prefetch queue.</i> They say nothing about whether a given instruction will be in the prefetch queue when it&rsquo;s time for that instruction to run, or how long it will take that instruction to reach the prefetch queue if it&rsquo;s not there already. Thanks to the low performance of the 8088&rsquo;s external data bus, that&rsquo;s a glaring omission&mdash;but, alas, an unavoidable one. Let&rsquo;s look at why the official execution times are wrong, and why that can&rsquo;t be helped.</p>
@ -129,10 +122,6 @@ shr ax,1
<div align="center">
Graphics Programming Black Book &copy; 2001 Michael Abrash
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
<!-- reference_subfoot = footer -->
<!-- reference_footer = subfoot -->
<!-- BEGIN SUB FOOTER -->
<!-- END FOOTER -->
</div>
</body>
</html>