Initial conversion to markdown
This commit is contained in:
parent
a96a114403
commit
5e10794a24
362 changed files with 67353 additions and 0 deletions
123
11-08.md
Normal file
123
11-08.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
------------------------ --------------------------------- --------------------
|
||||
[Previous](11-07.html) [Table of Contents](index.html) [Next](12-01.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
Well, there’s only one instruction other than **POPF** that loads the
|
||||
FLAGS register directly from the stack, and that’s **IRET**, 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 **POPF**, so it’s
|
||||
certainly a candidate to replace popf in non-interruptible applications.
|
||||
Unfortunately, **IRET** loads the FLAGS register with the *third* word
|
||||
down on the stack, not the word on top of the stack, as is the case with
|
||||
**POPF**; the far return address that **IRET** pops into CS:IP lies
|
||||
between the top of the stack and the word popped into the FLAGS
|
||||
register.
|
||||
|
||||
Obviously, the segment:offset that **IRET** expects to find on the stack
|
||||
above the pushed flags isn’t present when the stack is set up for
|
||||
**POPF**, so we’ll have to adjust the stack a bit before we can
|
||||
substitute **IRET** for **POPF**. What we’ll have to do is push the
|
||||
segment:offset of the instruction after our workaround code onto the
|
||||
stack right above the pushed flags. **IRET** will then branch to that
|
||||
address and pop the flags, ending up at the instruction after the
|
||||
workaround code with the flags popped. That’s just the result that would
|
||||
have occurred had we executed **POPF**—WITH the bonus that no interrupts
|
||||
can accidentally occur when the Interrupt flag is 0 both before and
|
||||
after the pop.
|
||||
|
||||
\
|
||||
**Figure 11.4** *The operation of POPF.*
|
||||
|
||||
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 **IRET** requires both
|
||||
a segment and an offset. We’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:
|
||||
|
||||
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.
|
||||
|
||||
\
|
||||
**Figure 11.5** *The operation of IRET.*
|
||||
|
||||
The operation of this code is illustrated in Figure 11.6.
|
||||
|
||||
The **POPF** 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:
|
||||
|
||||
EMULATE_POPF macro
|
||||
local popfskip, popfiret
|
||||
jmp short popfskip
|
||||
popfiret:
|
||||
iret
|
||||
popfskip:
|
||||
push cs
|
||||
call popfiret
|
||||
endm
|
||||
|
||||
By the way, the flags can be popped much more quickly if you’re willing
|
||||
to alter a register in the process. For example, the following macro
|
||||
emulates **POPF** with just one branch, but wipes out AX:
|
||||
|
||||
EMULATE_POPF_TRASH_AX macro
|
||||
push cs
|
||||
mov ax,offset $+5
|
||||
push ax
|
||||
iret
|
||||
endm
|
||||
|
||||
It’s not a perfect substitute for **POPF**, since **POPF** doesn’t alter
|
||||
any registers, but it’s faster and shorter than **EMULATE\_POPF** when
|
||||
you can spare the register. If you’re using 286-specific instructions,
|
||||
you can use which is shorter still, alters no registers, and branches
|
||||
just once. (Of course, this version of **EMULATE\_POPF** won’t work on
|
||||
an 8088.)
|
||||
|
||||
.286
|
||||
:
|
||||
EMULATE_POPFmacro
|
||||
pushcs
|
||||
pushoffset $+4
|
||||
iret
|
||||
endm
|
||||
|
||||
\
|
||||
**Figure 11.6** *Workaround code for the POPF bug.*
|
||||
|
||||
The standard version of **EMULATE\_POPF** is 6 bytes longer than
|
||||
**POPF** and much slower, as you’d expect given that it involves three
|
||||
branches. Anyone in his/her right mind would prefer **POPF** to a
|
||||
larger, slower, three-branch macro—given a choice. In noncode, however,
|
||||
there’s no choice here; the safer—if slower—approach is the best.
|
||||
(Having people associate your programs with crashed computers is *not* a
|
||||
desirable situation, no matter how unfair the circumstances under which
|
||||
it occurs.)
|
||||
|
||||
And now you know the nature of and the workaround for the **POPF** bug.
|
||||
Whether you ever need the workaround or not, it’s a neatly packaged
|
||||
example of the tremendous flexibility of the x86 instruction set.
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](11-07.html) [Table of Contents](index.html) [Next](12-01.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
* * * * *
|
||||
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
Loading…
Reference in a new issue