117 lines
7.5 KiB
HTML
117 lines
7.5 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: Aiming the 486</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=13//-->
|
|
<!--PAGES=252-254//-->
|
|
<!--UNASSIGNED1//-->
|
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
|
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="13-01.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="13-03.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
<P><BR></P>
|
|
<P>“When I looked closely as this, I realized that the two cycles for the final <B>ADD</B> is just the sum of 1 cycle to load the data from memory, and 1 cycle to add it to DX, so the code could just as well have been written as shown in Listing 13.3. The final breakthrough came when I realized that by initializing AX to zero outside the loop, I could rearrange it as shown in Listing 13.4 and do the final <B>ADD DX,AX</B> after the loop. This way there are two single-cycle instructions between the first and the fourth line, avoiding all pipeline stalls, for a total throughput of two cycles/char.”</P>
|
|
<P><B>LISTING 13.3 L13-3.ASM</B></P>
|
|
<!-- CODE SNIP //-->
|
|
<PRE>
|
|
mov bl,[di] ;get the state value for the pair
|
|
mov di,[bp+OFFS] ;get the next pair of characters
|
|
mov ax,[bx+8000h] ;increment word and line count
|
|
add dx,ax ; appropriately for the pair
|
|
</PRE>
|
|
<!-- END CODE SNIP //-->
|
|
<P><B>LISTING 13.4 L13-4.ASM</B></P>
|
|
<!-- CODE SNIP //-->
|
|
<PRE>
|
|
mov bl,[di] ;get the state value for the pair
|
|
mov di,[bp+OFFS] ;get the next pair of characters
|
|
add dx,ax ;increment word and line count
|
|
; appropriately for the pair
|
|
mov ax,[bx+8000h] ;get increments for next time
|
|
</PRE>
|
|
<!-- END CODE SNIP //-->
|
|
<P>I’d like to point out two fairly remarkable things. First, the single cycle that Terje saved in Listing 13.4 sped up his entire word-counting engine by 25 percent or more; Listing 13.4 is fully twice as fast as Listing 13.1—all the result of nothing more than shifting an instruction and splitting another into two operations. Second, Terje’s word-counting engine can process more than 16 million characters <I>per second</I> on a 486/33.</P>
|
|
<P>Clever 486 optimization can pay off big. QED.</P>
|
|
<H3><A NAME="Heading4"></A><FONT COLOR="#000077">BSWAP: More Useful Than You Might Think</FONT></H3>
|
|
<P>There are only 3 non-system instructions unique to the 486. None is earthshaking, but they have their uses. Consider <B>BSWAP. BSWAP</B> does just what its name implies, swapping the bytes (not bits) of a 32-bit register from one end of the register to the other, as shown in Figure 13.2. (<B>BSWAP</B> can only work with 32-bit registers; memory locations and 16-bit registers are not valid operands.) The obvious use of <B>BSWAP</B> is to convert data from Intel format (least significant byte first in memory, also called <I>little endian</I>) to Motorola format (most significant byte first in memory, or <I>big endian</I>), like so:</P>
|
|
<!-- CODE SNIP //-->
|
|
<PRE>
|
|
lodsd
|
|
bswap
|
|
stosd
|
|
</PRE>
|
|
<!-- END CODE SNIP //-->
|
|
<P><B>BSWAP</B> can also be useful for reversing the order of pixel bits from a bitmap so that they can be rotated 32 bits at a time with an instruction such as <B>ROR EAX,1</B>. Intel’s byte ordering for multiword values (least-significant byte first) loads pixels in the wrong order, so far as word rotation is concerned, but <B>BSWAP</B> can take care of that.</P>
|
|
<P><A NAME="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/13-02.jpg',409,245 )"> --><IMG SRC="images/13-02.jpg"><BR><!-- </A>
|
|
<BR><A HREF="javascript:displayWindow('images/13-02.jpg',409,245)"> --><FONT COLOR="#000077"><B>Figure 13.2</B></FONT></A> <I>BSWAP in operation.</I>
|
|
</P>
|
|
<P>As it turns out, though, <B>BSWAP</B> is also useful in an unexpected way, having to do with making efficient use of the upper half of 32-bit registers. As any assembly language programmer knows, the x86 register set is too small; or, to phrase that another way, it sure would be nice if the register set were bigger. As any 386/486 assembly language programmer knows, there are many cases in which 16 bits is plenty. For example, a 16-bit scan-line counter generally does the trick nicely in a video driver, because there are <I>very</I> few video devices with more than 65,535 addressable scan lines. Combining these two observations yields the obvious conclusion that it would be great if there were some way to use the upper and lower 16 bits of selected 386 registers as separate 16-bit registers, effectively increasing the available register space.</P>
|
|
<P>Unfortunately, the x86 instruction set doesn’t provide any way to work directly with only the upper half of a 32-bit register. The next best solution is to rotate the register to give you access in the lower 16 bits to the half you need at any particular time, with code along the lines of that in Listing 13.5. Having to rotate the 16-bit fields into position certainly isn’t as good as having direct access to the upper half, but surely it’s better than having to get the values out of memory, isn’t it?</P>
|
|
<P><B>LISTING 13.5 L13-5.ASM</B></P>
|
|
<!-- CODE //-->
|
|
<PRE>
|
|
mov cx,[initialskip]
|
|
shl ecx,16 ;put skip value in upper half of ECX
|
|
mov cx,100 ;put loop count in CX
|
|
looptop:
|
|
:
|
|
ror ecx,16 ;make skip value word accessible in CX
|
|
add bx,cx ;skip BX ahead
|
|
inc cx ;set next skip value
|
|
ror ecx,16 ;put loop count in CX
|
|
dec cx ;count down loop
|
|
jnz looptop
|
|
</PRE>
|
|
<!-- END CODE //-->
|
|
<P>Not necessarily. Shifts and rotates are among the worst performing instructions of the 486, taking 2 to 3 cycles to execute. Thus, it takes 2 cycles to rotate the skip value into CX in Listing 13.5, and 2 more cycles to rotate it back to the upper half of ECX. I’d say four cycles is a pretty steep price to pay, especially considering that a <B>MOV</B> to or from memory takes only one cycle. Basically, using <B>ROR</B> to access a 16-bit value in the upper half of a 16-bit register is a pretty marginal technique, unless for some reason you can’t access memory at all (for example, if you’re using BP as a working register, temporarily making the stack frame inaccessible).</P><P><BR></P>
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="13-01.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="13-03.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 -->
|
|
|
|
|