abrash-black-book/22-03.html

142 lines
7.8 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<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: Zenning and the Flexible Mind</title>
<meta name="chapter" content="22" />
<meta name="pages" content="419-420" />
</head>
<body>
<center>
<table border="1">
<tr>
<td>
<a href="22-02.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="23-01.html">Next</a>
</td>
</tr>
</table>
</center>
<p><b>LISTING 22.5 L22-5.ASM</b></p>
<pre>
ClearS proc near
push bp ;save caller&rsquo;s BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
mov al,byte ptr [bp].Filler ;load AL with fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer segment:offset
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller&rsquo;s BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
</pre>
<p>(We could get rid of yet another instruction by having the calling code pack both the attribute and the fill value into the same word, but that&rsquo;s not part of the specification for this particular routine.)</p>
<p>Another nifty instruction-rearrangement trick saves 6 more bytes. <b>ClearS</b> checks to see whether the far pointer is null (zero) at the start of the routine...then loads and uses that same far pointer later on. Let&rsquo;s get that pointer into registers and keep it there; that way we can check to see whether it&rsquo;s null with a single comparison, and can use it later without having to reload it from memory. This technique is shown in Listing 22.6.</p>
<p><b>LISTING 22.6 L22-6.ASM</b></p>
<pre>
ClearS proc near
push bp ;save caller&rsquo;s BP
mov bp,sp ;point to stack frame
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer;segment:offset
mov ax,es ;put segment where we can test it
or ax,di ;is it a null pointer?
je Bye ;yes, so we&rsquo;re done
Start: cld ;make STOSW count up
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
mov al,byte ptr [bp].Filler ;load AL with fill char
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller&rsquo;s BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
</pre>
<p>Well. Now we&rsquo;re down to 28 bytes, having reduced the size of this subroutine by nearly 50 percent. Only 13 instructions remain. Realistically, how much smaller can we make this code?</p>
<p>About one-third smaller yet, as it turns out&mdash;but in order to do that, we must stretch our minds and use the 8088&rsquo;s instructions in unusual ways. Let me ask you this: What do most of the instructions in the current version of <b>ClearS</b> do?</p>
<p>They either load parameters from the stack frame or set up the registers so that the parameters can be accessed. Mind you, there&rsquo;s nothing wrong with the stack-frame-oriented instructions used in <b>ClearS</b>; those instructions access the stack frame in a highly efficient way, exactly as the designers of the 8088 intended, and just as the code generated by a high-level language would. That means that we aren&rsquo;t going to be able to improve the code if we don&rsquo;t bend the rules a bit.</p>
<p>Let&rsquo;s think...the parameters are sitting on the stack, and most of our instruction bytes are being used to read bytes off the stack with BP-based addressing...we need a more efficient way to address the stack...<i>the stack</i>...THE STACK!</p>
<p>Ye gods! That&rsquo;s easy&mdash;we can use the <i>stack pointer</i> to address the stack rather than BP. While it&rsquo;s true that the stack pointer can&rsquo;t be used for <i>mod-reg-rm</i> addressing, as BP can, it <i>can</i> be used to pop data off the stack&mdash;and <b>POP</b> is a one-byte instruction. Instructions don&rsquo;t get any shorter than that.</p>
<p>There is one detail to be taken care of before we can put our plan into action: The return address&mdash;the address of the calling code&mdash;is on top of the stack, so the parameters we want can&rsquo;t be reached with <b>POP</b>. That&rsquo;s easily solved, however&mdash;we&rsquo;ll just pop the return address into an unused register, then branch through that register when we&rsquo;re done, as we learned to do in Chapter 14. As we pop the parameters, we&rsquo;ll also be removing them from the stack, thereby neatly avoiding the need to discard them when it&rsquo;s time to return.</p>
<p>With that problem dealt with, Listing 22.7 shows the Zenned version of <b>ClearS</b>.</p>
<p><b>LISTING 22.7 L22-7.ASM</b></p>
<pre>
ClearS procnear
pop dx ;get the return address
pop ax ;put fill char into AL
pop bx ;get the attribute
mov ah,bh ;put attribute into AH
pop cx ;get the buffer size
pop di ;get the offset of the buffer origin
pop es ;get the segment of the buffer origin
mov bx,es ;put the segment where we can test it
or bx,di ;null pointer?
je Bye ;yes, so we&rsquo;re done
cld ;make STOSW count up
rep stosw ;do the string store
Bye:
jmp dx ;return to the calling code
ClearS endp
</pre>
<p>At long last, we&rsquo;re down to the bare metal. This version of <b>ClearS</b> is just 19 bytes long. That&rsquo;s just 37 percent as long as the original version, <i>without any change whatsoever in the functionality that <b>ClearS</b> makes available to the calling code</i>. The code is bound to run a bit faster too, given that there are far fewer instruction bytes and fewer memory accesses.</p>
<p>All in all, the Zenned version of <b>ClearS</b> is a vast improvement over the original. Probably not the best possible implementation&mdash;<i>never say never!</i>&mdash;but an awfully good one.</p>
<center>
<table border="1">
<tr>
<td>
<a href="22-02.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="23-01.html">Next</a>
</td>
</tr>
</table>
</center>
<hr width="90%" size="1" noshade="noshade" />
<div align="center">
Graphics Programming Black Book &copy; 2001 Michael Abrash
</div>
</body>
</html>