abrash-black-book/11-06.md
2013-12-30 20:26:41 +11:00

114 lines
6.4 KiB
Markdown

Finally, it's possible in real mode to use the 386's new addressing
modes, in which *any* 32-bit general-purpose register or pair of
registers can be used to address memory. What's more, multiplication of
memory-addressing registers by 2, 4, or 8 for look-ups in word,
doubleword, or quadword tables can be built right into the memory
addressing mode. (The 32-bit addressing modes are discussed further in
later chapters.) In protected mode, these new addressing modes allow you
to address a full 4 gigabytes per segment, but in real mode you're still
limited to 64K, even with 32-bit registers and the new addressing modes,
unless you play some unorthodox tricks with the segment registers.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Note well: Those tricks don't necessarily work with system software such as Windows, so I'd recommend against using them. If you want 4-gigabyte segments, use a 32-bit environment such as Win32.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#### Optimization Rules: The More Things Change... {#Heading15 align="center"}
Let's see what we've learned about 286/386 optimization. Mostly what
we've learned is that our familiar PC cycle-eaters still apply, although
in somewhat different forms, and that the major optimization rules for
the PC hold true on ATs and 386-based computers. You won't go wrong on
any of these computers if you keep your instructions short, use the
registers heavily and avoid memory, don't branch, and avoid accessing
display memory like the plague.
Although we haven't touched on them, repeated string instructions are
still desirable on the 286 and 386 since they provide a great deal of
functionality per instruction byte and eliminate both the prefetch queue
cycle-eater and branching. However, string instructions are not quite so
spectacularly superior on the 286 and 386 as they are on the 8088 since
non-string memory-accessing instructions have been speeded up
considerably on the newer processors.
There's one cycle-eater with new implications on the 286 and 386, and
that's the data alignment cycle-eater. From the data alignment
cycle-eater we get a new rule: Word-align your word-sized variables, and
start your subroutines at even addresses.
#### Detailed Optimization {#Heading16 align="center"}
While the major 8088 optimization rules hold true on computers built
around the 286 and 386, many of the instruction-specific optimizations
no longer hold, for the execution times of most instructions are quite
different on the 286 and 386 than on the 8088. We have already seen one
such example of the sometimes vast difference between 8088 and 286/386
instruction execution times: **MOV [WordVar],0**, which has an Execution
Unit execution time of 20 cycles on the 8088, has an EU execution time
of just 3 cycles on the 286 and 2 cycles on the 386.
In fact, the performance of virtually all memory-accessing instructions
has been improved enormously on the 286 and 386. The key to this
improvement is the near elimination of effective address (EA)
calculation time. Where an 8088 takes from 5 to 12 cycles to calculate
an EA, a 286 or 386 usually takes no time whatsoever to perform the
calculation. If a base+index+displacement addressing mode, such as **MOV
AX,[WordArray+bx+si]**, is used on a 286 or 386, 1 cycle is taken to
perform the EA calculation, but that's both the worst case and the only
case in which there's any EA overhead at all.
The elimination of EA calculation time means that the EU execution time
of memory-addressing instructions is much closer to the EU execution
time of register-only instructions. For instance, on the 8088 **ADD
[WordVar],100H** is a 31-cycle instruction, while **ADD DX,100H** is a
4-cycle instruction—a ratio of nearly 8 to 1. By contrast, on the 286
**ADD [WordVar],100H** is a 7-cycle instruction, while **ADD DX,100H**
is a 3-cycle instruction—a ratio of just 2.3 to 1.
It would seem, then, that it's less necessary to use the registers on
the 286 than it was on the 8088, but that's simply not the case, for
reasons we've already seen. The key is this: The 286 can execute
memory-addressing instructions so fast that there's no spare instruction
prefetching time during those instructions, so the prefetch queue runs
dry, especially on the AT, with its one-wait-state memory. On the AT,
the 6-byte instruction **ADD [WordVar],100H** is effectively at least a
15-cycle instruction, because 3 cycles are needed to fetch each of the
three instruction words and 6 more cycles are needed to read **WordVar**
and write the result back to memory.
Granted, the register-only instruction **ADD DX,100H** also slows
down—to 6 cycles—because of instruction prefetching, leaving a ratio of
2.5 to 1. Now, however, let's look at the performance of the same code
on an 8088. The register-only code would run in 16 cycles (4 instruction
bytes at 4 cycles per byte), while the memory-accessing code would run
in 40 cycles (6 instruction bytes at 4 cycles per byte, plus 2
word-sized memory accesses at 8 cycles per word). That's a ratio of 2.5
to 1, *exactly the same as on the 286*.
This is all theoretical. We put our trust not in theory but in actual
performance, so let's run this code through the Zen timer. On a PC,
Listing 11.4, which performs register-only addition, runs in 3.62 ms,
while Listing 11.5, which performs addition to a memory variable, runs
in 10.05 ms. On a 10 MHz AT clone, Listing 11.4 runs in 0.64 ms, while
Listing 11.5 runs in 1.80 ms. Obviously, the AT is much faster...but the
ratio of Listing 11.5 to Listing 11.4 is virtually identical on both
computers, at 2.78 for the PC and 2.81 for the AT. If anything, the
register-only form of **ADD** has a slightly *larger* advantage on the
AT than it does on the PC in this case.
Theory confirmed.
**LISTING 11.4 L11-4.ASM**
;
; *** Listing 11.4 ***
;
; Measures the performance of adding an immediate value
; to a register, for comparison with Listing 11.5, which
; adds an immediate value to a memory variable.
;
call ZTimerOn
rept 1000
add dx,100h
endm
call ZTimerOff