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

118 lines
5.5 KiB
Markdown

Chapter 6\
Looking Past Face Value {#Heading1}
------------------------
### How Machine Instructions May Do More Than You Think {#Heading2}
I first met Jeff Duntemann at an authors' dinner hosted by *PC Tech
Journal* at Fall Comdex, back in 1985. Jeff was already reasonably
well-known as a computer editor and writer, although not as famous as
*Complete Turbo Pascal*, editions 1 through 672 (or thereabouts), *TURBO
TECHNIX*, and *PC TECHNIQUES* would soon make him. I was fortunate
enough to be seated next to Jeff at the dinner table, and, not
surprisingly, our often animated conversation revolved around computers,
computer writing, and more computers (not necessarily in that order).
Although I was making a living at computer work and enjoying it at the
time, I nonetheless harbored vague ambitions of being a science-fiction
writer when I grew up. (I have since realized that this hardly puts me
in elite company, especially in the computer world, where it seems that
every other person has told me they plan to write science fiction
"someday." Given that probably fewer than 500—I'm guessing here—original
science fiction and fantasy short stories, and perhaps a few more novels
than that, are published each year in this country, I see a few mid-life
crises coming.)
At any rate, I had accumulated a small collection of rejection slips,
and fancied myself something of an old hand in the field. At the end of
the dinner, as the other writers complained half-seriously about how
little they were paid for writing for *Tech Journal*, I leaned over to
Jeff and whispered, "You know, the pay isn't so bad here. You should see
what they pay for science fiction—even to the guys who win awards!"
To which Jeff replied, "I know. I've been nominated for two Hugos."
Oh.
Had I known I was seated next to a real, live science-fiction writer—an
*award-nominated* writer, by God!—I would have pumped him for all I was
worth, but the possibility had never occurred to me. I was at a dinner
put on by a computer magazine, seated next to an editor who had just
finished a book about Turbo Pascal, and, gosh, it was *obvious* that the
appropriate topic was computers.
For once, the moral is *not* "don't judge a book by its cover." Jeff is
in fact what he appeared to be at face value: a computer writer and
editor. However, he is more, too; face value wasn't full value. You'll
similarly find that face value isn't always full value in computer
programming, and especially so when working in assembly language, where
many instructions have talents above and beyond their obvious abilities.
On the other hand, there are also a number of instructions, such as
**LOOP**, that are designed to perform specific functions but aren't
always the best instructions for those functions. So don't judge a book
by its cover, either.
Assembly language for the x86 family isn't like any other language (for
which we should, without hesitation, offer our profuse thanks). Assembly
language reflects the design of the processor rather than the way we
think, so it's full of multiple instructions that perform similar
functions, instructions with odd and often confusing side effects, and
endless ways to string together different instructions to do much the
same things, often with seemingly minuscule differences that can turn
out to be surprisingly important.
To produce the best code, you must decide precisely what you need to
accomplish, then put together the sequence of instructions that
accomplishes that end most efficiently, regardless of what the
instructions are usually used for. That's why optimization for the PC is
an art, and it's why the best assembly language for the x86 family will
almost always handily outperform compiled code. With that in mind, let's
look past face value—and while we're at it, I'll toss in a few examples
of not judging a book by its cover.
The point to all this: You must come to regard the x86 family
instructions for what they do, not what you're used to thinking they do.
Yes, **SHL** shifts a pattern left—but a look-up table can do the same
thing, and can often do it faster. **ADD** can indeed add two operands,
but it can't put the result in a third register; **LEA** can. The
instruction set is your raw material for writing high-performance code.
By limiting yourself to thinking only in certain well-established ways
about the various instructions, you're putting yourself at a substantial
disadvantage every time you sit down to program.
In short, the x86 family can do much more than you think—if you'll use
everything it has to offer. Give it a shot!
#### Memory Addressing and Arithmetic {#Heading3}
Years ago, I saw a clip on the David Letterman show in which Letterman
walked into a store by the name of "Just Lamps" and asked, "So what do
you sell here?"
"Lamps," he was told. "Just lamps. Can't you read?"
"Lamps," he said. "I see. And what else?"
From that bit of sublime idiocy we can learn much about divining the
full value of an instruction. To wit:
Quick, what do the x86's memory addressing modes do?
"Calculate memory addresses," you no doubt replied. And you're right, of
course. But what *else* do they do?
They perform arithmetic, that's what they do, and that's a distinctly
different and often useful perspective on memory address calculations.
For example, suppose you have an array base address in BX and an index
into the array in SI. You could add the two registers together to
address memory, like this:
add bx,si
mov al,[bx]
Or you could let the processor do the arithmetic for you in a single
instruction:
mov al,[bx+si]