abrash-black-book/11-08.html

132 lines
6.5 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: Pushing the 286 and 386</title>
<meta name="chapter" content="11" />
<meta name="pages" content="226-231" />
</head>
<body>
<center>
<table border="1">
<tr>
<td>
<a href="11-07.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="12-01.html">Next</a>
</td>
</tr>
</table>
</center>
<p>Well, there&rsquo;s only one instruction other than <b>POPF</b> that loads the FLAGS register directly from the stack, and that&rsquo;s <b>IRET</b>, which loads the FLAGS register from the stack as it branches, as shown in Figure 11.5. iret has no known bugs of the sort that plague <b>POPF</b>, so it&rsquo;s certainly a candidate to replace popf in non-interruptible applications. Unfortunately, <b>IRET</b> loads the FLAGS register with the <i>third</i> word down on the stack, not the word on top of the stack, as is the case with <b>POPF</b>; the far return address that <b>IRET</b> pops into CS:IP lies between the top of the stack and the word popped into the FLAGS register.</p>
<p>Obviously, the segment:offset that <b>IRET</b> expects to find on the stack above the pushed flags isn&rsquo;t present when the stack is set up for <b>POPF</b>, so we&rsquo;ll have to adjust the stack a bit before we can substitute <b>IRET</b> for <b>POPF</b>. What we&rsquo;ll have to do is push the segment:offset of the instruction after our workaround code onto the stack right above the pushed flags. <b>IRET</b> will then branch to that address and pop the flags, ending up at the instruction after the workaround code with the flags popped. That&rsquo;s just the result that would have occurred had we executed <b>POPF</b>&mdash;WITH the bonus that no interrupts can accidentally occur when the Interrupt flag is 0 both before and after the pop.</p>
<p><a id="Fig4"><img src="images/11-04.jpg" /><br />
<b>Figure 11.4</b></a>&nbsp;&nbsp;<i>The operation of POPF.</i></p>
<p>How can we push the segment:offset of the next instruction? Well, finding the offset of the next instruction by performing a near call to that instruction is a tried-and-true trick. We can do something similar here, but in this case we need a far call, since <b>IRET</b> requires both a segment and an offset. We&rsquo;ll also branch backward so that the address pushed on the stack will point to the instruction we want to continue with. The code works out like this:</p>
<pre>
jmpshort popfskip
popfiret:
iret; branches to the instruction after the
; call, popping the word below the address
; pushed by CALL into the FLAGS register
popfskip:
call far ptr popfiret
;pushes the segment:offset of the next
; instruction on the stack just above
; the flags word, setting things up so
; that IRET will branch to the next
; instruction and pop the flags
; When execution reaches the instruction following this comment,
; the word that was on top of the stack when JMP SHORT POPFSKIP
; was reached has been popped into the FLAGS register, just as
; if a POPF instruction had been executed.
</pre>
<p><a id="Fig5"><img src="images/11-05.jpg" /><br />
<b>Figure 11.5</b></a>&nbsp;&nbsp;<i>The operation of IRET.</i></p>
<p>The operation of this code is illustrated in Figure 11.6.</p>
<p>The <b>POPF</b> workaround can best be implemented as a macro; we can also emulate a far call by pushing CS and performing a near call, thereby shrinking the workaround code by 1 byte:</p>
<pre>
EMULATE_POPF macro
local popfskip, popfiret
jmp short popfskip
popfiret:
iret
popfskip:
push cs
call popfiret
endm
</pre>
<p>By the way, the flags can be popped much more quickly if you&rsquo;re willing to alter a register in the process. For example, the following macro emulates <b>POPF</b> with just one branch, but wipes out AX:</p>
<pre>
EMULATE_POPF_TRASH_AX macro
push cs
mov ax,offset $+5
push ax
iret
endm
</pre>
<p>It&rsquo;s not a perfect substitute for <b>POPF</b>, since <b>POPF</b> doesn&rsquo;t alter any registers, but it&rsquo;s faster and shorter than <b>EMULATE_POPF</b> when you can spare the register. If you&rsquo;re using 286-specific instructions, you can use which is shorter still, alters no registers, and branches just once. (Of course, this version of <b>EMULATE_POPF</b> won&rsquo;t work on an 8088.)</p>
<pre>
.286
:
EMULATE_POPFmacro
pushcs
pushoffset $+4
iret
endm
</pre>
<p><a id="Fig6"><img src="images/11-06.jpg" /><br />
<b>Figure 11.6</b></a>&nbsp;&nbsp;<i>Workaround code for the POPF bug.</i></p>
<p>The standard version of <b>EMULATE_POPF</b> is 6 bytes longer than <b>POPF</b> and much slower, as you&rsquo;d expect given that it involves three branches. Anyone in his/her right mind would prefer <b>POPF</b> to a larger, slower, three-branch macro&mdash;given a choice. In noncode, however, there&rsquo;s no choice here; the safer&mdash;if slower&mdash;approach is the best. (Having people associate your programs with crashed computers is <i>not</i> a desirable situation, no matter how unfair the circumstances under which it occurs.)</p>
<p>And now you know the nature of and the workaround for the <b>POPF</b> bug. Whether you ever need the workaround or not, it&rsquo;s a neatly packaged example of the tremendous flexibility of the x86 instruction set.</p>
<center>
<table border="1">
<tr>
<td>
<a href="11-07.html">Previous</a>
</td>
<td>
<a href="index.html">Table of Contents</a>
</td>
<td>
<a href="12-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>