abrash-black-book/22-03.html
2013-12-30 12:21:49 +11:00

140 lines
8.2 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: Zenning and the Flexible Mind</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=22//-->
<!--PAGES=419-420//-->
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
<CENTER>
<TABLE BORDER>
<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><BR></P>
<P><B>LISTING 22.5 L22-5.ASM</B></P>
<!-- CODE //-->
<PRE>
ClearS proc near
push bp ;save caller&#146;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&#146;s BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
</PRE>
<!-- END CODE //-->
<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&#146;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&#146;s get that pointer into registers and keep it there; that way we can check to see whether it&#146;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>
<!-- CODE //-->
<PRE>
ClearS proc near
push bp ;save caller&#146;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&#146;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&#146;s BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
</PRE>
<!-- END CODE //-->
<P>Well. Now we&#146;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&#151;but in order to do that, we must stretch our minds and use the 8088&#146;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&#146;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&#146;t going to be able to improve the code if we don&#146;t bend the rules a bit.</P>
<P>Let&#146;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&#146;s easy&#151;we can use the <I>stack pointer</I> to address the stack rather than BP. While it&#146;s true that the stack pointer can&#146;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&#151;and <B>POP</B> is a one-byte instruction. Instructions don&#146;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&#151;the address of the calling code&#151;is on top of the stack, so the parameters we want can&#146;t be reached with <B>POP</B>. That&#146;s easily solved, however&#151;we&#146;ll just pop the return address into an unused register, then branch through that register when we&#146;re done, as we learned to do in Chapter 14. As we pop the parameters, we&#146;ll also be removing them from the stack, thereby neatly avoiding the need to discard them when it&#146;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>
<!-- CODE //-->
<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&#146;re done
cld ;make STOSW count up
rep stosw ;do the string store
Bye:
jmp dx ;return to the calling code
ClearS endp
</PRE>
<!-- END CODE //-->
<P>At long last, we&#146;re down to the bare metal. This version of <B>ClearS</B> is just 19 bytes long. That&#146;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&#151;<I>never say never!</I>&#151;but an awfully good one.</P><P><BR></P>
<CENTER>
<TABLE BORDER>
<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>
<div align="center">
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book &copy; 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 -->