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

141 lines
No EOL
7.3 KiB
Markdown

Chapter 11\
Pushing the 286 and 386 {#Heading1}
------------------------
### New Registers, New Instructions, New Timings, New Complications {#Heading2}
This chapter, adapted from my earlier book *Zen of Assembly Language*
(1989; now out of print), provides an overview of the 286 and 386, often
contrasting those processors with the 8088. At the time I originally
wrote this, the 8088 was the king of processors, and the 286 and 386
were the new kids on the block. Today, of course, all three processors
are past their primes, but many millions of each are still in use, and
the 386 in particular is still well worth considering when optimizing
software.
This chapter provides an interesting look at the evolution of the x86
architecture, to a greater degree than you might expect, for the x86
family came into full maturity with the 386; the 486 and the Pentium are
really nothing more than faster 386s, with very little in the way of new
functionality. In contrast, the 286 added a number of instructions,
respectable performance, and protected mode to the 8088's capabilities,
and the 386 added more instructions and a whole new set of addressing
modes, and brought the x86 family into the 32-bit world that represents
the future (and, increasingly, the present) of personal computing. This
chapter also provides insight into the effects on optimization of the
variations in processors and memory architectures that are common in the
PC world. So, although the 286 and 386 no longer represent the
mainstream of computing, this chapter is a useful mix of history lesson,
x86 overview, and details on two workhorse processors that are still in
wide use.
#### Family Matters {#Heading3}
While the x86 family is a large one, only a few members of the
family—which includes the 8088, 8086, 80188, 80186, 286, 386SX, 386DX,
numerous permutations of the 486, and now the Pentium—really matter.
The 8088 is now all but extinct in the PC arena. The 8086 was used
fairly widely for a while, but has now all but disappeared. The 80186
and 80188 never really caught on for use in PC and don't require further
discussion.
That leaves us with the high-end chips: the 286, the 386SX, the 386, the
486, and the Pentium. At this writing, the 386SX is fast going the way
of the 8088; people are realizing that its relatively small cost
advantage over the 386 isn't enough to offset its relatively large
performance disadvantage. After all, the 386SX suffers from the same
debilitating problem that looms over the 8088—a too-small bus.
Internally, the 386SX is a 32-bit processor, but externally, it's a
16-bit processor, a non-optimal architecture, especially for 32-bit
code.
I'm not going to discuss the 386SX in detail. If you do find yourself
programming for the 386SX, follow the same general rules you should
follow for the 8088: use short instructions, use the registers as
heavily as possible, and don't branch. In other words, avoid memory,
since the 386SX is by definition better at processing data internally
than it is at accessing memory.
The 486 is a world unto itself for the purposes of optimization, and the
Pentium is a *universe* unto itself. We'll treat them separately in
later chapters.
This leaves us with just two processors: the 286 and the 386. Each was
*the* PC standard in its day. The 286 is no longer used in new systems,
but there are millions of 286-based systems still in daily use. The 386
is still being used in new systems, although it's on the downhill leg of
its lifespan, and it is in even wider use than the 286. The future
clearly belongs to the 486 and Pentium, but the 286 and 386 are still
very much a part of the present-day landscape.
#### Crossing the Gulf to the 286 and the 386 {#Heading4}
Apart from vastly improved performance, the biggest difference between
the 8088 and the 286 and 386 (as well as the later Intel CPUs) is that
the 286 introduced protected mode, and the 386 greatly expanded the
capabilities of protected mode. We're only going to talk about real-mode
operation of the 286 and 386 in this book, however. Protected mode
offers a whole new memory management scheme, one that isn't supported by
the 8088. Only code specifically written for protected mode can run in
that mode; it's an alien and hostile environment for MS-DOS programs.
In particular, segments are different creatures in protected mode.
They're *selectors*—indexes into a table of segment descriptors—rather
than plain old registers, and can't be set to arbitrary values. That
means that segments can't be used for temporary storage or as part of a
fast indivisible 32-bit load from memory, as in
les ax,dword ptr [LongVar]
mov dx,es
which loads **LongVar** into DX:AX faster than this:
mov ax,word ptr [LongVar]
mov dx,word ptr [LongVar+2]
Protected mode uses those altered segment registers to offer access to a
great deal more memory than real mode: The 286 supports 16 megabytes of
memory, while the 386 supports 4 gigabytes (4K megabytes) of physical
memory and 64 *terabytes* (64K gigabytes!) of virtual memory.
In protected mode, your programs generally run under an operating system
(OS/2, Unix, Windows NT or the like) that exerts much more control over
the computer than does MS-DOS. Protected mode operating systems can
generally run multiple programs simultaneously, and the performance of
any one program may depend far less on code quality than on how
efficiently the program uses operating system services and how often and
under what circumstances the operating system preempts the program.
Protected mode programs are often mostly collections of operating system
calls, and the performance of whatever code *isn't* operating-system
oriented may depend primarily on how large a time slice the operating
system gives that code to run in.
In short, taken as a whole, protected mode programming is a different
kettle of fish altogether from what I've been describing in this book.
There's certainly a knack to optimizing specifically for protected mode
under a given operating system...but it's not what we've been learning,
and now is not the time to pursue it further. In general, though, the
optimization strategies discussed in this book still hold true in
protected mode; it's just issues specific to protected mode or a
particular operating system that we won't discuss.
#### In the Lair of the Cycle-Eaters, Part II {#Heading5}
Under the programming interface, the 286 and 386 differ considerably
from the 8088. Nonetheless, with one exception and one addition, the
cycle-eaters remain much the same on computers built around the 286 and
386. Next, we'll review each of the familiar cycle-eaters I covered in
Chapter 4 as they apply to the 286 and 386, and we'll look at the new
member of the gang, the data alignment cycle-eater.
The one cycle-eater that vanishes on the 286 and 386 is the 8-bit bus
cycle-eater. The 286 is a 16-bit processor both internally and
externally, and the 386 is a 32-bit processor both internally and
externally, so the Execution Unit/Bus Interface Unit size mismatch that
plagues the 8088 is eliminated. Consequently, there's no longer any need
to use byte-sized memory variables in preference to word-sized
variables, at least so long as word-sized variables start at even
addresses, as we'll see shortly. On the other hand, access to byte-sized
variables still isn't any *slower* than access to word-sized variables,
so you can use whichever size suits a given task best.