Normal quotes
This commit is contained in:
parent
5e10794a24
commit
18011c595a
352 changed files with 6040 additions and 6040 deletions
40
07-05.md
40
07-05.md
|
|
@ -5,7 +5,7 @@
|
|||
#### Rotating and Shifting with Tables {#Heading8}
|
||||
|
||||
As another example of local optimization, consider the matter of
|
||||
rotating or shifting a mask into position. First, let’s look at the
|
||||
rotating or shifting a mask into position. First, let's look at the
|
||||
simple task of setting bit N of AX to 1.
|
||||
|
||||
The obvious way to do this is to place N in CL, rotate the bit into
|
||||
|
|
@ -18,7 +18,7 @@ position, and OR it with AX, as follows:
|
|||
This solution is obvious because it takes good advantage of the special
|
||||
ability of the x86 family to shift or rotate by the variable number of
|
||||
bits specified by CL. However, it takes an average of about 45 cycles on
|
||||
an 8088. It’s actually far faster to precalculate the results, pass the
|
||||
an 8088. It's actually far faster to precalculate the results, pass the
|
||||
bit number in BX, and look the shifted bit up, as shown in Listing 7.3.
|
||||
|
||||
**LISTING 7.3 L7-3.ASM**
|
||||
|
|
@ -40,7 +40,7 @@ instructions, but by selecting the fastest *sequence* of instructions.
|
|||
|
||||
In the particular example above, we once again run into the difficulty
|
||||
of optimizing across the x86 family. The table lookup is faster on the
|
||||
8088 and 286, but it’s slightly slower on the 386 and no faster on the
|
||||
8088 and 286, but it's slightly slower on the 386 and no faster on the
|
||||
486. However, 386/486-specific code could use enhanced addressing to
|
||||
accomplish the whole job in just one instruction, along the lines of the
|
||||
code snippet in Listing 7.4.
|
||||
|
|
@ -63,34 +63,34 @@ code snippet in Listing 7.4.
|
|||
#### NOT Flips Bits—Not Flags {#Heading9}
|
||||
|
||||
The **NOT** instruction flips all the bits in the operand, from 0 to 1
|
||||
or from 1 to 0. That’s as simple as could be, but **NOT** nonetheless
|
||||
has a minor but interesting talent: It doesn’t affect the flags. That
|
||||
or from 1 to 0. That's as simple as could be, but **NOT** nonetheless
|
||||
has a minor but interesting talent: It doesn't affect the flags. That
|
||||
can be irritating; I once spent a good hour tracking down a bug caused
|
||||
by my unconscious assumption that **NOT** does set the flags. After all,
|
||||
every other arithmetic and logical instruction sets the flags; why not
|
||||
**NOT**? Probably because **NOT** isn’t considered to be an arithmetic
|
||||
or logical instruction at all; rather, it’s a data manipulation
|
||||
**NOT**? Probably because **NOT** isn't considered to be an arithmetic
|
||||
or logical instruction at all; rather, it's a data manipulation
|
||||
instruction, like **MOV** and the various rotates. (These are **RCR,
|
||||
RCL, ROR,** and **ROL,** which affect only the Carry and Overflow
|
||||
flags.) NOT is often used for tasks, such as flipping masks, where
|
||||
there’s no reason to test the state of the result, and in that context
|
||||
there's no reason to test the state of the result, and in that context
|
||||
it can be handy to keep the flags unmodified for later testing.
|
||||
|
||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
 *Besides, if you want to **NOT** an operand and set the flags in the process, you can just **XOR** it with -1. Put another way, the only functional difference between **NOT AX** and **XOR AX,0FFFFH** is that **XOR** modifies the flags and **NOT** doesn’t.*
|
||||
 *Besides, if you want to **NOT** an operand and set the flags in the process, you can just **XOR** it with -1. Put another way, the only functional difference between **NOT AX** and **XOR AX,0FFFFH** is that **XOR** modifies the flags and **NOT** doesn't.*
|
||||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
The x86 instruction set offers many ways to accomplish almost any task.
|
||||
Understanding the subtle distinctions between the instructions—whether
|
||||
and which flags are set, for example—can be critical when you’re trying
|
||||
to optimize a code sequence and you’re running out of registers, or when
|
||||
you’re trying to minimize branching.
|
||||
and which flags are set, for example—can be critical when you're trying
|
||||
to optimize a code sequence and you're running out of registers, or when
|
||||
you're trying to minimize branching.
|
||||
|
||||
#### Incrementing with and without Carry {#Heading10}
|
||||
|
||||
Another case in which there are two slightly different ways to perform a
|
||||
task involves adding 1 to an operand. You can do this with **INC,** as
|
||||
in **INC AX,** or you can do it with **ADD,** as in **ADD AX,1.** What’s
|
||||
in **INC AX,** or you can do it with **ADD,** as in **ADD AX,1.** What's
|
||||
the difference? The obvious difference is that **INC** is usually a byte
|
||||
or two shorter (the exception being **ADD AL,1,** which at two bytes is
|
||||
the same length as **INC AL**), and is faster on some processors. Less
|
||||
|
|
@ -130,15 +130,15 @@ additions, with code along the lines shown in Listing 7.6.
|
|||
SAHF ;restore the carry flag
|
||||
LOOP LOOP_TOP
|
||||
|
||||
It’s not that the Listing 7.6 approach is necessarily better or worse;
|
||||
It's not that the Listing 7.6 approach is necessarily better or worse;
|
||||
that depends on the processor and the situation. The Listing 7.6
|
||||
approach is *different,* and if you understand the differences, you’ll
|
||||
approach is *different,* and if you understand the differences, you'll
|
||||
be able to choose the best approach for whatever code you happen to
|
||||
write. (**DEC** has the same property of preserving the Carry flag, by
|
||||
the way.)
|
||||
|
||||
There are a couple of interesting aspects to the last example. First,
|
||||
note that **LOOP** doesn’t affect any flags at all; this allows the
|
||||
note that **LOOP** doesn't affect any flags at all; this allows the
|
||||
Carry flag to remain unchanged from one addition to the next. Not
|
||||
altering the arithmetic flags is a common characteristic of program
|
||||
control instructions (as opposed to arithmetic and logical instructions
|
||||
|
|
@ -149,12 +149,12 @@ like **SUB** and **AND,** which do alter the flags).
|
|||
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Not only do **LOOP** and **JCXZ** not alter the flags, but **REP MOVS**,
|
||||
which counts down CX to 0, doesn’t affect the flags either.
|
||||
which counts down CX to 0, doesn't affect the flags either.
|
||||
|
||||
The other interesting point about the last example is the use of
|
||||
**LAHF** and **SAHF,** which transfer the low byte of the FLAGS register
|
||||
to and from AH, respectively. These instructions were created to help
|
||||
provide compatibility with the 8080’s (that’s *8080*, not *8088*)
|
||||
provide compatibility with the 8080's (that's *8080*, not *8088*)
|
||||
**PUSH** **PSW** and **POP PSW** instructions, but turn out to be
|
||||
compact (one byte) instructions for saving and restoring the arithmetic
|
||||
flags. A word of caution, however: **SAHF** restores the Carry, Zero,
|
||||
|
|
@ -163,13 +163,13 @@ which resides in the high byte of the FLAGS register. Also, be aware
|
|||
that **LAHF** and **SAHF** provide a fast way to preserve the flags on
|
||||
an 8088 but are relatively slow instructions on the 486 and Pentium.
|
||||
|
||||
There are times when it’s a clear liability that **INC** doesn’t set the
|
||||
There are times when it's a clear liability that **INC** doesn't set the
|
||||
Carry flag. For instance
|
||||
|
||||
INC AX
|
||||
ADC DX,0
|
||||
|
||||
does *not* increment the 32-bit value in DX:AX. To do that, you’d need
|
||||
does *not* increment the 32-bit value in DX:AX. To do that, you'd need
|
||||
the following:
|
||||
|
||||
ADD AX,1
|
||||
|
|
|
|||
Loading…
Reference in a new issue