Change bold code snippets to inline code blocks
This commit is contained in:
parent
f658bfbb40
commit
3c76d884ef
185 changed files with 1772 additions and 1772 deletions
44
09-07.md
44
09-07.md
|
|
@ -14,19 +14,19 @@ pages: 185-188
|
|||
|
||||
Next, we come to an item that cycle counters will love, especially since
|
||||
it involves apparently incorrect documentation on Intel's part.
|
||||
According to Intel's documents, all **RCR** and **RCL** instructions,
|
||||
According to Intel's documents, all `RCR` and `RCL` instructions,
|
||||
which perform rotations through the Carry flag, as shown in Figure 9.4,
|
||||
take 9 cycles on the 386 when working with a register operand. My
|
||||
measurements indicate that the 9-cycle execution time almost holds true
|
||||
for *multibit* rotate-through-carries, which I've timed at 8 cycles
|
||||
apiece; for example, **RCR AX,CL** takes 8 cycles on *my* 386, as does
|
||||
**RCL DX,2**. Contrast that with **ROR** and **ROL**, which can rotate
|
||||
apiece; for example, `RCR AX,CL` takes 8 cycles on *my* 386, as does
|
||||
`RCL DX,2`. Contrast that with `ROR` and `ROL`, which can rotate
|
||||
the contents of a register any number of bits in just 3 cycles.
|
||||
|
||||
However, rotating by one bit through the Carry flag does *not* take 9
|
||||
cycles, contrary to Intel's *80386 Programmer's Reference Manual*, or
|
||||
even 8 cycles. In fact, **RCR** *reg*,1 and **RCL** *reg*,1 take 3
|
||||
cycles, just like **ROR, ROL, SHR,** and **SHL**. At least, that's how
|
||||
even 8 cycles. In fact, `RCR` *reg*,1 and `RCL` *reg*,1 take 3
|
||||
cycles, just like `ROR`, `ROL`, `SHR`, and `SHL`. At least, that's how
|
||||
fast they run on my 386, and I very much doubt that you'll find
|
||||
different execution times on other 386s. (Please let me know if you do,
|
||||
though!)
|
||||
|
|
@ -34,12 +34,12 @@ though!)
|
|||

|
||||
|
||||
Interestingly, according to Intel's *i486 Microprocessor Programmer's
|
||||
Reference Manual*, the 486 can **RCR** or **RCL** a register by one bit
|
||||
Reference Manual*, the 486 can `RCR` or `RCL` a register by one bit
|
||||
in 3 cycles, but takes between 8 and 30 cycles to perform a multibit
|
||||
register **RCR** or **RCL**!
|
||||
register `RCR` or `RCL`!
|
||||
|
||||
No great lesson here, just a caution to be leery of multibit **RCR** and
|
||||
**RCL** when performance matters—and to take cycle-time documentation
|
||||
No great lesson here, just a caution to be leery of multibit `RCR` and
|
||||
`RCL` when performance matters—and to take cycle-time documentation
|
||||
with a grain of salt.
|
||||
|
||||
#### Hardwired Far Jumps {#Heading11}
|
||||
|
|
@ -47,8 +47,8 @@ with a grain of salt.
|
|||
Did you ever wonder how to code a far jump to an absolute address in
|
||||
assembly language? Probably not, but if you ever do, you're going to be
|
||||
glad for this next item, because the obvious solution doesn't work. You
|
||||
might think all it would take to jump to, say, 1000:5 would be **JMP FAR
|
||||
PTR 1000:5**, but you'd be wrong. That won't even assemble. You might
|
||||
might think all it would take to jump to, say, 1000:5 would be `JMP FAR
|
||||
PTR 1000:5`, but you'd be wrong. That won't even assemble. You might
|
||||
then think to construct in memory a far pointer containing 1000:5, as in
|
||||
the following:
|
||||
|
||||
|
|
@ -60,9 +60,9 @@ Ptr dd ?
|
|||
jmp [Ptr]
|
||||
```
|
||||
|
||||
That will work, but at a price in performance. On an 8088, **JMP DWORD
|
||||
PTR [*mem*]** (an indirect far jump) takes at least 37 cycles; **JMP
|
||||
DWORD PTR *label*** (a direct far jump) takes only 15 cycles (plus,
|
||||
That will work, but at a price in performance. On an 8088, `JMP DWORD
|
||||
PTR [*mem*]` (an indirect far jump) takes at least 37 cycles; `JMP
|
||||
DWORD PTR *label*` (a direct far jump) takes only 15 cycles (plus,
|
||||
almost certainly, some cycles for instruction fetching). On a 386, an
|
||||
indirect far jump is documented to take at least 43 cycles in real mode
|
||||
(31 in protected mode); a direct far jump is documented to take at least
|
||||
|
|
@ -81,7 +81,7 @@ preferable.
|
|||
Listing 9.7 shows a short program that performs a direct far call to
|
||||
1000:5. (Don't run it, unless you want to crash your system!) It does
|
||||
this by creating a dummy segment at 1000H, so that the label
|
||||
**FarLabel** can be created with the desired far attribute at the proper
|
||||
`FarLabel` can be created with the desired far attribute at the proper
|
||||
location. (Segments created with "AT" don't cause the generation of any
|
||||
actual bytes or the allocation of any memory; they're just templates.)
|
||||
It's a little kludgey, but at least it does work. There may be a better
|
||||
|
|
@ -132,8 +132,8 @@ mov eax,1
|
|||
```
|
||||
|
||||
takes only 2 cycles to execute, but is 5 bytes long (because native mode
|
||||
constants are dwords and the **MOV** instruction doesn't sign-extend).
|
||||
Both code fragments are ways to set **EAX** to 1 (although the first
|
||||
constants are dwords and the `MOV` instruction doesn't sign-extend).
|
||||
Both code fragments are ways to set `EAX` to 1 (although the first
|
||||
affects the flags and the second doesn't); this is a classic trade-off
|
||||
of speed for space. Second,
|
||||
|
||||
|
|
@ -148,15 +148,15 @@ move bx,-1
|
|||
```
|
||||
|
||||
takes 2 cycles to execute and is 5 bytes long. Both instructions set
|
||||
**EBX** to -1; this is a classic trade-off of—gee, it's not a trade-off
|
||||
at all, is it? **OR** is a better way to set a 32-bit register to all
|
||||
1-bits, just as **SUB** or **XOR** is a better way to set a register to
|
||||
`EBX` to -1; this is a classic trade-off of—gee, it's not a trade-off
|
||||
at all, is it? `OR` is a better way to set a 32-bit register to all
|
||||
1-bits, just as `SUB` or `XOR` is a better way to set a register to
|
||||
all 0-bits. Who woulda thunk it? Just goes to show how the 32-bit
|
||||
displacements and constants of 386 native mode change the familiar
|
||||
landscape of 80x86 optimization.
|
||||
|
||||
Be warned, though, that I've found **OR, AND, ADD**, and the like to be
|
||||
a cycle slower than **MOV** when working with immediate operands on the
|
||||
Be warned, though, that I've found `OR`, `AND`, `ADD`, and the like to be
|
||||
a cycle slower than `MOV` when working with immediate operands on the
|
||||
386 under some circumstances, for reasons that thus far escape me. This
|
||||
just reinforces the first rule of optimization: Measure your code in
|
||||
action, and place not your trust in documented cycle times.
|
||||
Loading…
Reference in a new issue