Convert notes into blockquotes

This commit is contained in:
James Gregory 2013-12-31 11:06:31 +11:00
commit 8ed9dea394
105 changed files with 884 additions and 411 deletions

View file

@ -85,9 +85,10 @@ solid programming skills, preferably using an optimizing compiler or
assembly language. The optimization at the end is just the finishing
touch, however.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Without good design, good algorithms, and complete understanding of the program's operation, your carefully optimized code will amount to one of mankind's least fruitful creations—a fast slow program*.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Without good design, good algorithms, and complete understanding of the
> program's operation, your carefully optimized code will amount to one of
> mankind's least fruitful creations—a fast slow program.
"What's a fast slow program?" you ask. That's a good question, and a
brief (true) story is perhaps the best answer.

View file

@ -131,9 +131,9 @@ with optimization both on and off; all four times are pretty much the
same, however, and all are much too slow to be acceptable. Listing 1.1
requires over two and one-half minutes to checksum *one* file!
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Listings 1.2 and 1.3 form the C/assembly equivalent to Listing 1.1, and Listings 1.6 and 1.7 form the C/assembly equivalent to Listing 1.5.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Listings 1.2 and 1.3 form the C/assembly equivalent to Listing 1.1, and
> Listings 1.6 and 1.7 form the C/assembly equivalent to Listing 1.5.
These results make it clear that it's folly to rely on your compiler's
optimization to make your programs fast. Listing 1.1 is simply poorly

View file

@ -10,9 +10,10 @@ chapter: '01'
pages: 013-015
---
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Make sure you understand what really goes on when you insert a seemingly-innocuous function call into the time-critical portions of your code.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Make sure you understand what really goes on when you insert a
> seemingly-innocuous function call into the time-critical portions of
> your code.
In this case that means knowing how DOS and the C/C++ file-access
libraries do their work. In other words, *know the territory*!
@ -74,9 +75,13 @@ that handle the time-critical operations, but C is still used for
checking command-line parameters, operning files, printing, and the
like.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *If you were to implement any of the listings in this chapter entirely in hand-optimized assembly, I suppose you might get a performance improvement of a few percent—but I rather doubt you'd get even that much, and you'd sure as heck spend an awful lot of time for whatever meager improvement does result. Let C do what it does well, and use assembly only when it makes a perceptible difference.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> If you were to implement any of the listings in this chapter entirely in
> hand-optimized assembly, I suppose you might get a performance
> improvement of a few percent—but I rather doubt you'd get even that
> much, and you'd sure as heck spend an awful lot of time for whatever
> meager improvement does result. Let C do what it does well, and use
> assembly only when it makes a perceptible difference.
Besides, we don't want to optimize until the design is refined to our
satisfaction, and that won't be the case until we've thought about other

View file

@ -21,9 +21,12 @@ Table 1.1 shows that Listing 1.5 is 2.5 to 4 times faster than Listing
1.4 (and as much as 49 times faster than Listing 1.1!), even though it
uses no assembly at all.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Clearly, you can do well by using special-purpose C code in place of a C library function—if you have a thorough understanding of how the C library function operates and exactly what your application needs done. Otherwise, you'll end up rewriting C library functions in C, which makes no sense at all.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Clearly, you can do well by using special-purpose C code in place of a C
> library function—if you have a thorough understanding of how the C
> library function operates and exactly what your application needs done.
> Otherwise, you'll end up rewriting C library functions in C, which makes
> no sense at all.
**LISTING 1.5 L1-5.C**

View file

@ -81,9 +81,9 @@ instructions and/or unrolled loops can be used effectively, assembly
tends to be considerably faster relative to C than it is in this very
specific case.
------------------- -----------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Don't get hung up on optimizing compilers or assembly language—the best optimizer is between your ears.*
------------------- -----------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Don't get hung up on optimizing compilers or assembly language—the best
> optimizer is between your ears.
All this is basically a way of saying: Know where you're going, know the
territory, and know when it matters.

View file

@ -62,9 +62,9 @@ me—the code was rotating each bit into place separately, so that a
multibit rotation was being performed every time through the loop, for a
total of four separate time-consuming multibit rotations!
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *While the instructions themselves were individually optimized, the overall approach did not make the best possible use of the instructions.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> While the instructions themselves were individually optimized, the
> overall approach did not make the best possible use of the instructions.
I changed the code to the following:

View file

@ -105,6 +105,6 @@ knowledge about programming them effectively is by far the hardest
knowledge to gather. A good portion of this book is devoted to seeking
out such knowledge.
------------------- ------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Be forewarned, though: No matter how much you learn about programming the PC in assembly, there's always more to discover.*
------------------- ------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Be forewarned, though: No matter how much you learn about programming
> the PC in assembly, there's always more to discover.

View file

@ -43,9 +43,12 @@ reusability, source code control, choice of development environment, and
the like that they often forget rule \#1: From the user's perspective,
*performance is fundamental*.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Comment your code, design it carefully, and write non-time-critical portions in a high-level language, if you wish—but when you write the portions that interact with the user and/or affect response time, performance must be your paramount objective, and assembly is the path to that goal*.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Comment your code, design it carefully, and write non-time-critical
> portions in a high-level language, if you wish—but when you write the
> portions that interact with the user and/or affect response time,
> performance must be your paramount objective, and assembly is the path
> to that goal.
Knowledge of the sort described earlier is absolutely essential to
fulfilling either of the objectives of assembly programming. What that

View file

@ -57,9 +57,13 @@ performance of his code and found it to be unexpectedly slow, curiosity
might well have led him to experiment further and thereby add to his
store of reliable information about the CPU.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *There you have an important tenet of assembly language optimization: After crafting the best code possible, check it in action to see if it's really doing what you think it is. If it's not behaving as expected, that's all to the good, since solving mysteries is the path to knowledge. You'll learn more in this way, I assure you, than from any manual or book on assembly language.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> There you have an important tenet of assembly language optimization:
> After crafting the best code possible, check it in action to see if it's
> really doing what you think it is. If it's not behaving as expected,
> that's all to the good, since solving mysteries is the path to
> knowledge. You'll learn more in this way, I assure you, than from any
> manual or book on assembly language.
*Assume nothing*. I cannot emphasize this strongly enough—when you care
about performance, do your best to improve the code and then *measure*

View file

@ -38,9 +38,12 @@ depending on the code mix preceding that instruction.* Similarly, the
state in which a given instruction leaves the prefetch queue affects the
overall execution time of the following instructions.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In other words, while the execution time for a given instruction is constant, the fetch time for that instruction depends heavily on the context in which the instruction is executing—the amount of prefetching the preceding instructions allowed—and can vary from a full 4 cycles per instruction byte to no time at all.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In other words, while the execution time for a given instruction is
> constant, the fetch time for that instruction depends heavily on the
> context in which the instruction is executing—the amount of prefetching
> the preceding instructions allowed—and can vary from a full 4 cycles per
> instruction byte to no time at all.
As we'll see later, other cycle-eaters, such as DRAM refresh and display
memory wait states, can cause prefetching variations even during

View file

@ -83,9 +83,9 @@ in the Intel manuals, but may take as much as 4 cycles per byte longer,
depending on the state of the prefetch queue when the preceding
instruction ends.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The only true execution time for an instruction is a time measured in a certain context, and that time is meaningful only in that context.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The only true execution time for an instruction is a time measured in a
> certain context, and that time is meaningful only in that context.
What we *really* want is to know how long useful working code takes to
run, not how long a single instruction takes, and the Zen timer gives us

View file

@ -10,9 +10,14 @@ chapter: '04'
pages: 106-109
---
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *A line-drawing subroutine, which executes perhaps a dozen instructions for each display memory access, generally loses less performance to the display adapter cycle-eater than does a block-copy or scrolling subroutine that uses **REP MOVS** instructions. Scaled and three-dimensional graphics, which spend a great deal of time performing calculations (often using very slow floating-point arithmetic), tend to suffer less.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> A line-drawing subroutine, which executes perhaps a dozen instructions
> for each display memory access, generally loses less performance to the
> display adapter cycle-eater than does a block-copy or scrolling
> subroutine that uses **REP MOVS** instructions. Scaled and
> three-dimensional graphics, which spend a great deal of time performing
> calculations (often using very slow floating-point arithmetic), tend to
> suffer less.
In addition, code that accesses display memory infrequently tends to
suffer only about half of the maximum display memory wait states,
@ -48,9 +53,10 @@ and alter the rest, and write the byte back to display memory. Half as
many display memory accesses means half as many display memory wait
states.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Moreover, 486s and Pentiums, as well as recent Super VGAs, employ write-caching schemes that make display memory writes considerably faster than display memory reads.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Moreover, 486s and Pentiums, as well as recent Super VGAs, employ
> write-caching schemes that make display memory writes considerably
> faster than display memory reads.
Along the same line, the display adapter cycle-eater makes the popular
exclusive-OR animation technique, which requires paired reads and writes

View file

@ -58,9 +58,10 @@ the overhead of driving back and forth made for miserable performance.
Renting a truck (the restartable block approach) would have required
more effort and forethought, but would have paid off handsomely.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The easy, familiar approach often has nothing in its favor except that it requires less thinking; not a great virtue when writing high-performance code—or when moving.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The easy, familiar approach often has nothing in its favor except that
> it requires less thinking; not a great virtue when writing
> high-performance code—or when moving.
And with that, let's look at a fairly complex application of restartable
blocks.

View file

@ -18,9 +18,11 @@ exactly how long our search buffer is. Even if a given **strstr()**
implementation is well-written, its performance will suffer, at least
for our application, from unnecessary overhead.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *This illustrates why you shouldn't think of C/C++ library functions as black boxes; understand what they do and try to figure out how they do it, and relate that to their performance in the context you're interested in.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> This illustrates why you shouldn't think of C/C++ library functions as
> black boxes; understand what they do and try to figure out how they do
> it, and relate that to their performance in the context you're
> interested in.
### Brute-Force Techniques {#Heading5}

View file

@ -22,9 +22,11 @@ And so we find, as we so often will, that optimization is definitely not
a cut-and-dried matter, and that there is no such thing as a single
"best" approach.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *You must know what your application will typically do, and you must know whether you're more concerned with average or worst-case performance before you can decide how best to speed up your program—and, indeed, whether speeding it up is worth doing at all.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> You must know what your application will typically do, and you must know
> whether you're more concerned with average or worst-case performance
> before you can decide how best to speed up your program—and, indeed,
> whether speeding it up is worth doing at all.
By the way, don't think that just because very large block sizes don't
much improve performance, it wasn't worth using restartable blocks in
@ -56,9 +58,11 @@ to the **read()** function, but a code profiler could be used to do the
same thing much more easily), I found that the best code in the world
wouldn't make much difference.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *When you try to speed up code, take a moment to identify the hot spots in your program so that you know where optimization is needed and whether it will make a significant difference before you invest your time.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> When you try to speed up code, take a moment to identify the hot spots
> in your program so that you know where optimization is needed and
> whether it will make a significant difference before you invest your
> time.
As for restartable blocks: Here we tackled a considerably more complex
application of restartable blocks than we did in Chapter 1—which turned

View file

@ -173,9 +173,11 @@ Multiplying a 32-bit value by a non-power-of-two multiplier in just 2
cycles is a pretty neat trick, even though it works only on a 386 or
486.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The full list of values that **LEA** can multiply a register by on a 386 or 486 is: 2, 3, 4, 5, 8, and 9. That list doesn't include every multiplier you might want, but it covers some commonly used ones, and the performance is hard to beat.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The full list of values that **LEA** can multiply a register by on a 386
> or 486 is: 2, 3, 4, 5, 8, and 9. That list doesn't include every
> multiplier you might want, but it covers some commonly used ones, and
> the performance is hard to beat.
I'd like to extend my thanks to Duane Strong of Metagraphics for his
help in brainstorming uses for the 386 version of **LEA** and for

View file

@ -68,9 +68,11 @@ algorithm selection and good design are fundamental to performance. The
extra horsepower a superb assembly language implementation gives a
program is worth bothering with only in the context of a good design.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Assembly language optimization is a small but crucial corner of the PC programming world. Use it sparingly and only within the framework of a good design—but ignore it and you may find various portions of your anatomy out in the cold.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Assembly language optimization is a small but crucial corner of the PC
> programming world. Use it sparingly and only within the framework of a
> good design—but ignore it and you may find various portions of your
> anatomy out in the cold.
So, drawing fortitude from the knowledge that our quest is a pure and
worthy one, let's resume our exploration of assembly language
@ -96,9 +98,11 @@ instinctively stuffs the loop count in CX and reaches for **LOOP** when
setting up a loop. That's fine—**LOOP** does, of course, work as
advertised—but there is one problem:
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *On half of the processors in the x86 family, **LOOP** is slower than **DEC CX** followed by **JNZ**. (Granted, **DEC CX/JNZ** isn't precisely equivalent to **LOOP,** because **DEC** alters the flags and LOOP doesn't, but in most situations they're comparable.)*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> On half of the processors in the x86 family, **LOOP** is slower than
> **DEC CX** followed by **JNZ**. (Granted, **DEC CX/JNZ** isn't precisely
> equivalent to **LOOP,** because **DEC** alters the flags and LOOP
> doesn't, but in most situations they're comparable.)
How can this be? Don't ask me, ask Intel. On the 8088 and 80286,
**LOOP** is indeed faster than **DEC CX/JNZ** by a cycle, and **LOOP**
@ -116,6 +120,8 @@ Pentium as well. (By the way, all this is not just theory; I've timed
the relative performances of **LOOP** and **DEC CX/JNZ** on a cached
386, and LOOP really is slower.)
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Things are stranger still for **LOOP**'s relative **JCXZ,** which branches if and only if CX is zero. **JCXZ** is faster than **AND CX,CX/JZ** on the 8088 and 80286, and equivalent on the 80386—but is about twice as slow on the 486!*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Things are stranger still for **LOOP**'s relative **JCXZ,** which
> branches if and only if CX is zero. **JCXZ** is faster than **AND
> CX,CX/JZ** on the 8088 and 80286, and equivalent on the 80386—but is
> about twice as slow on the 486!

View file

@ -86,9 +86,12 @@ checking whether it's zero. There may be ways to perform those tasks a
little faster by selecting different instructions, but they can get only
so fast, and branching can't even get all that fast.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The trick, then, is not to find the fastest way to decrement a count and branch conditionally, but rather to figure out how to accomplish the same result without decrementing or branching as often. Remember the Kobiyashi Maru problem in* Star Trek*?The same principle applies here: Redefine the problem to one that offers better solutions.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The trick, then, is not to find the fastest way to decrement a count and
> branch conditionally, but rather to figure out how to accomplish the
> same result without decrementing or branching as often. Remember the
> Kobiyashi Maru problem in *Star Trek*? The same principle applies here:
> Redefine the problem to one that offers better solutions.
Consider Listing 7.1, which searches a buffer until either the specified
byte is found, a zero byte is found, or the specified number of

View file

@ -151,6 +151,11 @@ that algorithmic improvements can produce, but it can get you a critical
50 percent or 100 percent improvement when you've exhausted all other
avenues.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The point is simply this: You can gain far more by stepping back a bit and thinking of the fastest overall way for the CPU to perform a task than you can by saving a cycle here or there using different instructions. Try to think at the level of sequences of instructions rather than individual instructions, and learn to treat x86 instructions as building blocks with unique characteristics rather than as instructions dedicated to specific tasks.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The point is simply this: You can gain far more by stepping back a bit
> and thinking of the fastest overall way for the CPU to perform a task
> than you can by saving a cycle here or there using different
> instructions. Try to think at the level of sequences of instructions
> rather than individual instructions, and learn to treat x86 instructions
> as building blocks with unique characteristics rather than as
> instructions dedicated to specific tasks.

View file

@ -64,9 +64,13 @@ code snippet in Listing 7.4.
BIT_PATTERN=BIT_PATTERN SHL 1
ENDM
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Besides illustrating the advantages of local optimization, this example also shows that it generally pays to precalculate results; this is often done at or before assembly time, but precalculated tables can also be built at run time. This is merely one aspect of a fundamental optimization rule: Move as much work as possible out of your critical code by whatever means necessary.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Besides illustrating the advantages of local optimization, this example
> also shows that it generally pays to precalculate results; this is often
> done at or before assembly time, but precalculated tables can also be
> built at run time. This is merely one aspect of a fundamental
> optimization rule: Move as much work as possible out of your critical
> code by whatever means necessary.
#### NOT Flips Bits—Not Flags {#Heading9}
@ -84,9 +88,11 @@ 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
it can be handy to keep the flags unmodified for later testing.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *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.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> 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
@ -152,9 +158,11 @@ altering the arithmetic flags is a common characteristic of program
control instructions (as opposed to arithmetic and logical instructions
like **SUB** and **AND,** which do alter the flags).
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The rule is not that the arithmetic flags change whenever the CPU performs a calculation; rather, the flags change whenever you execute an arithmetic, logical, or flag control (such as **CLC** to clear the Carry flag) instruction.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The rule is not that the arithmetic flags change whenever the CPU
> performs a calculation; rather, the flags change whenever you execute an
> arithmetic, logical, or flag control (such as **CLC** to clear the Carry
> flag) instruction.
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.

View file

@ -50,9 +50,12 @@ C/C++ and Watcom C are by now pretty good at fine-tuning C code, and
you're not likely to do much better by taking the compiler's assembly
language output and tweaking it.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *To make the process of translating C code to assembly language worth the trouble, you must ignore what the compiler does and design your assembly language code from a pure assembly language perspective. With a merely adequate translation, you risk laboring mightily for little or no reward.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> To make the process of translating C code to assembly language worth the
> trouble, you must ignore what the compiler does and design your assembly
> language code from a pure assembly language perspective. With a merely
> adequate translation, you risk laboring mightily for little or no
> reward.
Apropos of which, when was the last time you heard of Terry Jacks?
@ -88,9 +91,13 @@ Also make it a point to concentrate on refining your program design and
algorithmic approach at the conceptual and/or C levels before doing any
assembly language optimization.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Assembly language optimization is the final and far from the only step in the optimization chain, and as such should be performed last; converting to assembly too soon can lock in your code before the design is optimal. At the very least, conversion to assembly tends to make future changes and debugging more difficult, slowing you down and limiting your options.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Assembly language optimization is the final and far from the only step
> in the optimization chain, and as such should be performed last;
> converting to assembly too soon can lock in your code before the design
> is optimal. At the very least, conversion to assembly tends to make
> future changes and debugging more difficult, slowing you down and
> limiting your options.
### Don't Call Your Functions on Me, Baby {#Heading4}

View file

@ -37,9 +37,11 @@ for the duration of the loop. But two far pointers used in the same loop
confuse every compiler I've seen, causing the full segment:offset
address to be reloaded each time either pointer is used.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *This particularly affects performance in 286 protected mode (under OS/2 1.X or the Rational DOS Extender, for example) because segment loads in protected mode take a minimum of 17 cycles, versus a mere 2 cycles in real mode.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> This particularly affects performance in 286 protected mode (under OS/2
> 1.X or the Rational DOS Extender, for example) because segment loads in
> protected mode take a minimum of 17 cycles, versus a mere 2 cycles in
> real mode.
In assembly language you have full control over segments. Use it, and,
if necessary, reorganize your code to minimize segment loading.
@ -61,9 +63,11 @@ Am I saying that C compilers produce better code than you do? No, I'm
saying that they *can,* unless you use assembly language properly.
Writing code in assembly language rather than C guarantees nothing.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *You can write good assembly, bad assembly, or assembly that is virtually indistinguishable from compiled code; you are more likely than not to write the latter if you think that optimization consists of tweaking compiled C code.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> You can write good assembly, bad assembly, or assembly that is virtually
> indistinguishable from compiled code; you are more likely than not to
> write the latter if you think that optimization consists of tweaking
> compiled C code.
Sure, you can probably use the registers more efficiently and take
advantage of an instruction or two that the compiler missed, but the
@ -109,9 +113,12 @@ enough, there's no reason why you can't reorganize the data. This might
mean removing the array elements from the structures and storing them in
their own array so that **REP SCASW** *could* be used.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Organizing a program's data so that the performance of the critical sections can be optimized is a key part of design, and one that's easily shortchanged unless, during the design stage, you thoroughly understand and work to bring together your data needs, the critical sections of your program, and potential assembly language optimizations.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Organizing a program's data so that the performance of the critical
> sections can be optimized is a key part of design, and one that's easily
> shortchanged unless, during the design stage, you thoroughly understand
> and work to bring together your data needs, the critical sections of
> your program, and potential assembly language optimizations.
More on this shortly.

View file

@ -68,9 +68,13 @@ now on) by arranging your operands so that the multiplier (always the
rightmost operand following **MUL** or **IMUL**) is no larger than the
other operand.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Why? Because the 386 processes one multiplier bit per cycle and immediately ends a multiplication when all significant bits of the multiplier have been processed, so fewer cycles are required to multiply a large multiplicand times a small multiplier than a small multiplicand times a large multiplier, by a factor of about 1 cycle for each significant multiplier bit eliminated.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Why? Because the 386 processes one multiplier bit per cycle and
> immediately ends a multiplication when all significant bits of the
> multiplier have been processed, so fewer cycles are required to multiply
> a large multiplicand times a small multiplier than a small multiplicand
> times a large multiplier, by a factor of about 1 cycle for each
> significant multiplier bit eliminated.
(There's a minimum execution time on this trick; below 3 significant
multiplier bits, no additional cycles are saved.) For example,
@ -89,9 +93,11 @@ This highlights another interesting point: **MUL** and **IMUL** on the
386 are so fast that alternative multiplication approaches, while
generally still faster, are worthwhile only in truly time-critical code.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *On 386SXs and uncached 386s, where code size can significantly affect performance due to instruction prefetching, the compact **MUL** and **IMUL** instructions can approach and in some cases even outperform the "optimized" alternatives.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> On 386SXs and uncached 386s, where code size can significantly affect
> performance due to instruction prefetching, the compact **MUL** and
> **IMUL** instructions can approach and in some cases even outperform the
> "optimized" alternatives.
All in all, **MUL** and **IMUL** are reasonable performers on the 386,
no longer to be avoided in most cases—and you can help that along by
@ -127,9 +133,10 @@ deal is handling **REPNZ SCASB** matches, which require checking the
remainder of the string with **REPZ CMPS** and restarting **REPNZ
SCASB** if no match is found.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Rob points out that the number of **REPNZ SCASB** matches can easily be reduced simply by scanning for the character in the searched-for string that appears least often in the buffer being searched.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Rob points out that the number of **REPNZ SCASB** matches can easily be
> reduced simply by scanning for the character in the searched-for string
> that appears least often in the buffer being searched.
Imagine, if you will, that you're searching for the string "EQUAL." By
my approach, you'd use **REPNZ SCASB** to scan for each occurrence of

View file

@ -31,9 +31,10 @@ counting. Listings 9.1 and 9.2 use pretty much the same instructions,
and even use the same approach of scanning with **REPNZ SCASB** and
using **REPZ CMPS** to check scanning matches.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The difference between Listings 9.1 and 9.2 (which gives you more than a doubling of performance) is due entirely to understanding the nature of the data being handled, and biasing the code to reflect that knowledge.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The difference between Listings 9.1 and 9.2 (which gives you more than a
> doubling of performance) is due entirely to understanding the nature of
> the data being handled, and biasing the code to reflect that knowledge.
![**Figure 9.2**  *Faster searching method for locating a text string.*](images/09-02.jpg)

View file

@ -127,9 +127,9 @@ slows performance because instructions must be fetched through the
half-pint 16-bit bus; on a 386, the effect depends on the instruction
mix and whether there's a cache.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *On balance, though, it's as important to keep your most-used variables in the stack's sweet spot in 386 native mode as it was on the 8088.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> On balance, though, it's as important to keep your most-used variables
> in the stack's sweet spot in 386 native mode as it was on the 8088.
In assembly, it's easy to control the organization of your stack frame.
In C, however, you'll have to figure out the allocation scheme your

View file

@ -67,9 +67,12 @@ always hungered to get results from my work as soon as possible; I
gravitated toward graphics for its instant and very visible
gratification. Over time, however, I've learned patience.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *I've come to spend an increasingly large portion of my time choosing algorithms, designing, and simply giving my mind quiet time in which to work on problems and come up with non-obvious approaches before coding; and I've found that the extra time up front more than pays for itself in both decreased coding time and superior programs.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> I've come to spend an increasingly large portion of my time choosing
> algorithms, designing, and simply giving my mind quiet time in which to
> work on problems and come up with non-obvious approaches before coding;
> and I've found that the extra time up front more than pays for itself in
> both decreased coding time and superior programs.
In this chapter, I'm going to walk you through a simple but illustrative
case history that nicely points up the wisdom of delaying gratification

View file

@ -93,9 +93,13 @@ As you can see from Table 10.1, Euclid's algorithm is superior,
especially for large numbers (and imagine if we were working with large
*longs!*).
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Had I been implementing GCD determination without Sedgewick's help, I would surely not have settled for Listing 10.1—but I might well have ended up with Listing 10.2 in my enthusiasm over the "brilliant" discovery of subtracting the lesser Using Euclid's algorithm to find a GCD number from the greater. In a commercial product, my lack of patience and discipline could have been costly indeed.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Had I been implementing GCD determination without Sedgewick's help, I
> would surely not have settled for Listing 10.1—but I might well have
> ended up with Listing 10.2 in my enthusiasm over the "brilliant"
> discovery of subtracting the lesser Using Euclid's algorithm to find a
> GCD number from the greater. In a commercial product, my lack of
> patience and discipline could have been costly indeed.
![**Figure 10.3**  *Using Euclid's algorithm to find a GCD.*](images/10-03.jpg)

View file

@ -106,9 +106,11 @@ lousy at; compilers could out-optimize you at this level with one pass
tied behind their back *if* they knew as much about the code you're
writing as you do, which they don't.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Design optimization—conceptual breakthroughs in understanding the relationships between the needs of an application, the nature of the data the application works with, and what the computer can do—is global pattern matching.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Design optimization—conceptual breakthroughs in understanding the
> relationships between the needs of an application, the nature of the
> data the application works with, and what the computer can do—is global
> pattern matching.
Computers are *much* worse at that sort of pattern matching than humans;
computers have no way to integrate vast amounts of disparate

View file

@ -73,9 +73,11 @@ exact number of wait states inserted at any given time depends on the
interaction between the code being executed and the memory system it's
running on.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The performance of most 386 memory systems can vary greatly from one memory access to another, depending on factors such as what data happens to be in the cache and which interleaved bank and/or RAM column was accessed last.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The performance of most 386 memory systems can vary greatly from one
> memory access to another, depending on factors such as what data happens
> to be in the cache and which interleaved bank and/or RAM column was
> accessed last.
The many memory systems in use make it impossible for us to optimize for
286/386 computers with the precision that's possible on the 8088.

View file

@ -69,9 +69,9 @@ The penalty for performing a word-sized access starting at an odd
address is easy to calculate: Two accesses take twice as long as one
access.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In other words, the effective capacity of the 286's external data bus is* *halved* *when a word-sized access to an odd address is performed.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In other words, the effective capacity of the 286's external data bus
> is *halved* when a word-sized access to an odd address is performed.
That, in a nutshell, is the data alignment cycle-eater, the one new
cycle-eater of the 286 and 386. (The data alignment cycle-eater is a

View file

@ -53,9 +53,9 @@ instruction if necessary, and the time required to execute a **NOP** can
sometimes cancel the performance advantage of having a word-aligned
branch destination.
------------------- -----------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Consequently, it's best to word-align only those branch destinations that can be reached solely by branching.*
------------------- -----------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Consequently, it's best to word-align only those branch destinations
> that can be reached solely by branching.
I recommend that you only go out of your way to word-align the start
offsets of your subroutines, as in:
@ -112,9 +112,8 @@ interrupt occurring between the two instructions will be serviced more
slowly than it normally would. The same goes for decrementing twice; use
**SUB SP,2** instead.
------------------- ------------------------------------------------
![](images/i.jpg) *Keep the stack pointer aligned at all times.*
------------------- ------------------------------------------------
> ![](images/i.jpg)
> Keep the stack pointer aligned at all times.
#### The DRAM Refresh Cycle-Eater: Still an Act of God {#Heading11 align="center"}

View file

@ -61,9 +61,12 @@ adapter cycle-eater, as measured in cycles lost to wait states, is
indeed much worse on AT-class computers than it is on the PC, and it's
worse still on more powerful computers.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *How bad is the display adapter cycle-eater on an AT? It's this bad: Based on my (not inconsiderable) experience in timing display adapter access, I've found that the display adapter cycle-eater can slow an AT—or even a 386 computer—to near-PC speeds when display memory is accessed.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> How bad is the display adapter cycle-eater on an AT? It's this bad:
> Based on my (not inconsiderable) experience in timing display adapter
> access, I've found that the display adapter cycle-eater can slow an
> AT—or even a 386 computer—to near-PC speeds when display memory is
> accessed.
I know that's hard to believe, but the display adapter cycle-eater gives
out just so many display memory accesses in a given time, and no more,

View file

@ -21,9 +21,10 @@ 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.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](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"}

View file

@ -84,9 +84,14 @@ for earlier x86 processors. Generally, I'll be discussing optimization
for real mode (it being the most widely used mode at the moment),
although many of the rules should apply to protected mode as well.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *486 optimization is generally more precise and less frustrating than optimization for other x86 processors because every 486 has an identical internal cache. Whenever both the instructions being executed and the data the instructions access are in the cache, those instructions will run in a consistent and calculatable number of cycles on all 486s, with little chance of interference from the prefetch queue and without regard to the speed of external memory.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> 486 optimization is generally more precise and less frustrating than
> optimization for other x86 processors because every 486 has an identical
> internal cache. Whenever both the instructions being executed and the
> data the instructions access are in the cache, those instructions will
> run in a consistent and calculatable number of cycles on all 486s, with
> little chance of interference from the prefetch queue and without regard
> to the speed of external memory.
In other words, for cached code (which time-critical code almost always
is), performance is predictable and can be calculated with good
@ -115,9 +120,11 @@ matter what register is used.) **MOV AX, [BX+DI]** and **MOV CL,
[BP+SI+10]** perform indexed addressing; **MOV AX,[BX]** and **MOV DL,
[SI+1]** do not.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Therefore, in real mode, the rule is to avoid using two registers to point to memory whenever possible. Often, this simply means adding the two registers together outside a loop before memory is actually addressed.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Therefore, in real mode, the rule is to avoid using two registers to
> point to memory whenever possible. Often, this simply means adding the
> two registers together outside a loop before memory is actually
> addressed.
As an example, you might adhere to this rule by replacing the code

View file

@ -88,9 +88,10 @@ documentation understates the extent of the penalty for interrupting the
address calculation pipeline by loading a memory pointer just before
it's used.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The truth of the matter appears to be that if a register is the destination of one instruction and is then used by the next instruction to address memory in real mode, not one but two cycles are lost!*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The truth of the matter appears to be that if a register is the
> destination of one instruction and is then used by the next instruction
> to address memory in real mode, not one but two cycles are lost!
In 32-bit protected mode, however, the penalty is, in fact, the 1 cycle
that Intel .

View file

@ -146,9 +146,10 @@ mentioned in this chapter apply to **XLAT**, apparently because **XLAT**
is so slow—4 cycles—that it gives the 486 time to perform addressing
calculations during the course of the instruction.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *While it's nice that **XLAT** doesn't suffer from the various 486 addressing penalties, the reason for that is basically that **XLAT** is slow, so there's still no compelling reason to use **XLAT** on the 486.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> While it's nice that **XLAT** doesn't suffer from the various 486
> addressing penalties, the reason for that is basically that **XLAT** is
> slow, so there's still no compelling reason to use **XLAT** on the 486.
In general, penalties for interrupting the 486's pipeline apply
primarily to the fast core instructions of the 486, most notably

View file

@ -75,9 +75,11 @@ presumably, **MOV CL,AL** interrupts the pipeline again because the
pipeline is now on its first cycle: the one that loading a byte register
can affect.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *I know—it seems awfully complicated. It isn't, really. Generally, try not to use byte destinations exactly two cycles before using a register to address memory, and try not to load a register either one or two cycles before using it to address memory, and you'll be fine.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> I know—it seems awfully complicated. It isn't, really. Generally, try
> not to use byte destinations exactly two cycles before using a register
> to address memory, and try not to load a register either one or two
> cycles before using it to address memory, and you'll be fine.
#### Timing Your Own 486 Code {#Heading11}
@ -143,9 +145,9 @@ more than doubles to 224 µs, or 3.7 cycles per repetition; the extra
seven-tenths of a cycle comes from fetching non-cached instruction
bytes.
------------------- -----------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Whenever you see non-integral timing results of this sort, it's a good bet that the test code or data isn't cached.*
------------------- -----------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Whenever you see non-integral timing results of this sort, it's a good
> bet that the test code or data isn't cached.
### The Story Continues {#Heading12}

View file

@ -106,9 +106,10 @@ the intervening instruction takes two cycles, there's no penalty at all.
![**Figure 13.1**  *Cycle-eaters in the original WC.*](images/13-01.jpg)
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Remember, pipeline penalties diminish with increasing number of cycles, not instructions, between the pipeline disrupter and the potentially affected instruction.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Remember, pipeline penalties diminish with increasing number of cycles,
> not instructions, between the pipeline disrupter and the potentially
> affected instruction.
**LISTING 13.2 L13-2.ASM**

View file

@ -68,9 +68,13 @@ operations include all the string instructions except **REP MOVS,** as
well as **XLAT, LOOP,** and, of course, **PUSH *mem*** and **POP
*mem.***
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Whenever possible, try to use the 486's 1-cycle instructions, including **MOV, ADD, SUB, CMP, ADC, SBB, XOR, AND, OR, TEST, LEA**, and **PUSH reg** and **POP reg**. These instructions have an added benefit in that it's often possible to rearrange them for maximum pipeline efficiency, as is the case with Terje's optimization described earlier in this chapter.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Whenever possible, try to use the 486's 1-cycle instructions, including
> **MOV, ADD, SUB, CMP, ADC, SBB, XOR, AND, OR, TEST, LEA**, and **PUSH
> reg** and **POP reg**. These instructions have an added benefit in that
> it's often possible to rearrange them for maximum pipeline efficiency,
> as is the case with Terje's optimization described earlier in this
> chapter.
### Optimal 1-Bit Shifts and Rotates {#Heading6}

View file

@ -63,9 +63,12 @@ example, **MOV AL, [EBX]** is a 2-byte instruction; **MOV AL,
[EBX+10H]** is a 3-byte instruction; and **MOV AL, [EBX+10000H]** is a
6-byte instruction.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Note that 1 and 4-byte displacements, but not 2-byte displacements, are supported for 32-bit addressing. Code size can be greatly improved by keeping stack frame variables within 128 bytes of EBP, and variables in pointed-to structures within 127 bytes of the start of the structure, so that displacements can be 1 rather than 4 bytes.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Note that 1 and 4-byte displacements, but not 2-byte displacements, are
> supported for 32-bit addressing. Code size can be greatly improved by
> keeping stack frame variables within 128 bytes of EBP, and variables in
> pointed-to structures within 127 bytes of the start of the structure, so
> that displacements can be 1 rather than 4 bytes.
However, because 32-bit addressing supports many more addressing
combinations than 16-bit addressing, the Mod-R/M byte can't describe all

View file

@ -100,9 +100,10 @@ It's important to understand that we're assuming that the buffer is
typical text. That's what I meant at the outset, when I said that the
information you need may be under your nose.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Formally, you don't know a blessed thing about the search buffer, but experience, common sense, and your knowledge of the application give you a great deal of useful, if somewhat imprecise, information.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Formally, you don't know a blessed thing about the search buffer, but
> experience, common sense, and your knowledge of the application give you
> a great deal of useful, if somewhat imprecise, information.
If the buffer contains the letter A' repeated 1,000 times, followed by
the letter B,' then the **REPNZ SCASB/REPZ CMPS** approach will be much

View file

@ -54,9 +54,10 @@ of bytes (a portion of the buffer); it doesn't matter in the least in
what order we compare them, so long as all the bytes in one set are
compared to the corresponding bytes in the other set.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Why on earth would we want to start with the rightmost character? Because a mismatch on the rightmost character tells us a great deal more than a mismatch on the leftmost character.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Why on earth would we want to start with the rightmost character?
> Because a mismatch on the rightmost character tells us a great deal more
> than a mismatch on the leftmost character.
We learn nothing new from a mismatch on the leftmost character, except
that the pattern can't match starting at that location. A mismatch on

View file

@ -96,9 +96,11 @@ have learned it by spending five minutes with Sedgewick's book.
![**Figure 15.2**  *Using a dummy head and tail node with a linked list.*](images/15-02.jpg)
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The next-node pointer of the head node, which points to the first real node, is the only part of the head node that's actually used. This way the same code works on the head node as on the rest of the list, so there are no special cases.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The next-node pointer of the head node, which points to the first real
> node, is the only part of the head node that's actually used. This way
> the same code works on the head node as on the rest of the list, so
> there are no special cases.
Likewise, there should be a separate node for the tail of the list, so
that every node that contains real data is guaranteed to have a node on

View file

@ -142,9 +142,8 @@ One possible optimization is unrolling the loop, although that is truly
a last resort because it tends to make further changes extremely
difficult.
------------------- -----------------------------------------------------------
![](images/i.jpg) *Exhaust all other optimizations before unrolling loops.*
------------------- -----------------------------------------------------------
> ![](images/i.jpg)
> Exhaust all other optimizations before unrolling loops.
### Challenges and Hazards {#Heading5}

View file

@ -52,9 +52,10 @@ base+index addressing (**[EBX+EAX]**), which saves a cycle on the 386.
Second: Changing the instruction to **CMP [EAX],DH** saved 2 cycles—just
enough, by good fortune, to speed up the whole program by 5 percent.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) ***CMP reg,[mem]** takes 6 cycles on the 386, but **CMP [ mem ],reg** takes only 5 cycles; you should always perform**CMP** with the memory operand on the left on the 386.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> **CMP reg,[mem]** takes 6 cycles on the 386, but **CMP [ mem ],reg**
> takes only 5 cycles; you should always perform**CMP** with the memory
> operand on the left on the 386.
(Granted, **CMP [*mem*],*reg*** is 1 cycle slower than **CMP
*reg*,[*mem*]** on the 286, and they're both the same on the 8088; in
@ -99,9 +100,10 @@ faster. Consider the incongruity of Terje's willingness to consider a 5
percent speedup significant in light of his later near-doubling of
performance.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Don't get stuck in the rut of instruction-by-instruction optimization. It's useful in key loops, but very often, a change in approach will work far greater wonders than any amount of cycle counting can.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Don't get stuck in the rut of instruction-by-instruction optimization.
> It's useful in key loops, but very often, a change in approach will work
> far greater wonders than any amount of cycle counting can.
By the way, Terje's WC50 program is a full-fledged counting program; it
counts characters, words, and lines, can handle multiple files, and lets

View file

@ -22,9 +22,11 @@ don't even guarantee that David Stafford's entry works perfectly,
although, knowing him, it probably does; the more complex and cryptic
the code, the greater the chance for obscure bugs.)
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Remember, optimize only when needed, and stop when further optimization will not be noticed. Optimization that's not perceptible to the user is like buying Telly Savalas a comb; it's not going to do any harm, but it's nonetheless a waste of time.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Remember, optimize only when needed, and stop when further optimization
> will not be noticed. Optimization that's not perceptible to the user is
> like buying Telly Savalas a comb; it's not going to do any harm, but
> it's nonetheless a waste of time.
#### Optimization Level 1: Good Code {#Heading10}

View file

@ -58,9 +58,13 @@ state of our Game of Life implementation, the only areas worth looking
at for possible optimizations are **cell\_state()** and
**next\_generation().**
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *It's worth noting, though, that one reason **draw\_pixel()** doesn't much affect performance is that in Listing 17.1, we're smart enough to redraw pixels only when their states change, rather than during every generation. Detecting and eliminating redundant operations is part of knowing the nature of your data, and is a potent optimization technique that will be extremely useful a little later in this chapter.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> It's worth noting, though, that one reason **draw\_pixel()** doesn't
> much affect performance is that in Listing 17.1, we're smart enough to
> redraw pixels only when their states change, rather than during every
> generation. Detecting and eliminating redundant operations is part of
> knowing the nature of your data, and is a potent optimization technique
> that will be extremely useful a little later in this chapter.
### The Hazards and Advantages of Abstraction {#Heading6}
@ -81,9 +85,11 @@ know the cell storage format! This is a wonderful thing, in general; it
saves programming time and bugs, and frees you to work on the
application's needs, rather than implementation details.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *However, if you never look beneath the surface of the abstract model at the implementation details, you have no idea of what the true performance cost of various operations* *is, and, without that, you have largely surrendered control over performance.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> However, if you never look beneath the surface of the abstract model at
> the implementation details, you have no idea of what the true performance
> cost of various operations is, and, without that, you have largely
> surrendered control over performance.
Having said that, let me hasten to add that algorithmic improvements can
make a big difference even when working at a purely abstract level. For

View file

@ -159,6 +159,7 @@ It's undoubtedly possible to improve the performance of Listing 17.4
further by fine-tuning the code, but no tremendous improvement is
possible that way.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Once you've reached the point of fine-tuning pointer usage and register variables and the like in C or C++, you've become compiler-dependent; you therefore might as well go to assembly and get the real McCoy.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Once you've reached the point of fine-tuning pointer usage and register
> variables and the like in C or C++, you've become compiler-dependent;
> you therefore might as well go to assembly and get the real McCoy.

View file

@ -65,9 +65,12 @@ possible. Large lookup tables, oddly encoded cellmaps, and lots of
bit-twiddling assembly code spring to mind as possible approaches. Can't
you just feel your adrenaline start to pump?
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Relax. Step back. Try to divine the true nature of the problem. The object is not to count neighbors and check cell states as quickly as possible; that's just one possible implementation. The object is to determine when a cell's state must be changed and to change it appropriately, and that's what we need to do as quickly as possible.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Relax. Step back. Try to divine the true nature of the problem. The
> object is not to count neighbors and check cell states as quickly as
> possible; that's just one possible implementation. The object is to
> determine when a cell's state must be changed and to change it
> appropriately, and that's what we need to do as quickly as possible.
What difference does that new perspective make? Let's approach it this
way. What does a typical cellmap look like? As it happens, after a few

View file

@ -41,9 +41,14 @@ well-optimized, and, as I noted, the program must be compiled with the
large model for large cellmaps. Also, of course, the entire program is
still in C++; note well that there's not a whit of assembly here.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *We've gotten more than a 30-times speedup simply by removing a little of the abstraction that C++ encourages, and by storing and processing the data in a manner appropriate for the typical nature of the data itself. In other words, we've done some linear, left-brained optimization (using pointers and reducing calls) and some non-linear, right-brained optimization (understanding the real problem and listening for the creative whisper of non-obvious solutions).*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> We've gotten more than a 30-times speedup simply by removing a little of
> the abstraction that C++ encourages, and by storing and processing the
> data in a manner appropriate for the typical nature of the data itself.
> In other words, we've done some linear, left-brained optimization (using
> pointers and reducing calls) and some non-linear, right-brained
> optimization (understanding the real problem and listening for the
> creative whisper of non-obvious solutions).
No doubt we could get another two to five times improvement with good
assembly code—but that's dwarfed by a 30-times improvement, so

View file

@ -38,9 +38,18 @@ circumscribed arena.
Why am I telling you this? First, because it is a useful lesson for
programming.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *All programming is performed within limitations, some of which can be bent or changed, but many of which cannot. You cannot change the maximum memory bandwidth of a VGA, or the maximum instruction execution rate of a 486. That is why the stunning 3D demos you see at SIGGRAPH have only passing relevance to everyday life on the desktop. A rule that Intel's chip designers cannot break is 8086 compatibility, much as I'm sure they'd like to, but of course the flip side is that although RISC chips are technically superior, they command but a small fraction of the market; raw performance is not the arena of competition. Similarly, you will often be unable to change the specifications for the software you implement.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> All programming is performed within limitations, some of which can be
> bent or changed, but many of which cannot. You cannot change the maximum
> memory bandwidth of a VGA, or the maximum instruction execution rate of
> a 486. That is why the stunning 3D demos you see at SIGGRAPH have only
> passing relevance to everyday life on the desktop. A rule that Intel's
> chip designers cannot break is 8086 compatibility, much as I'm sure
> they'd like to, but of course the flip side is that although RISC chips
> are technically superior, they command but a small fraction of the
> market; raw performance is not the arena of competition. Similarly, you
> will often be unable to change the specifications for the software you
> implement.
### Breaking the Rules {#Heading3}

View file

@ -10,9 +10,11 @@ chapter: '19'
pages: 373-375
---
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The Pentium, on the other hand, has two separate 8K caches, one for code and one for data, so code prefetches can never collide with data fetches; the prefetch queue can stall only when the code being fetched isn't in the internal code cache.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The Pentium, on the other hand, has two separate 8K caches, one for code
> and one for data, so code prefetches can never collide with data
> fetches; the prefetch queue can stall only when the code being fetched
> isn't in the internal code cache.
(And yes, self-modifying code still works; as with all Pentium changes,
the dual caches introduce no incompatibilities with 386/486 code.) Also,
@ -40,9 +42,12 @@ bits, and runs twice as fast, at 66 MHz, so the Pentium can fetch both
instruction and data bytes from the external cache four times as fast as
the 486.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Even when the Pentium is running flat-out with both pipes in use, it can generally consume only about twice as many bytes as the 486; so the ratio of external memory bandwidth to processing power is much improved, although real-world performance is heavily dependent on the size and speed of the external cache.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Even when the Pentium is running flat-out with both pipes in use, it can
> generally consume only about twice as many bytes as the 486; so the
> ratio of external memory bandwidth to processing power is much improved,
> although real-world performance is heavily dependent on the size and
> speed of the external cache.
The upshot of all this is that at the same clock speed, with code and
data that are mostly in the internal caches, the Pentium maxes out

View file

@ -49,9 +49,13 @@ recommends for 486 jump targets. The 32-byte alignment might make for
slightly more efficient Pentium cache usage, but would make code much
bigger overall.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In fact, given that most jump targets aren't in performance-critical code, it's hard to make a compelling argument for aligning branch targets even on the 486. I'd say that no alignment (except possibly where you know a branch target lies in a key loop), or at most dword alignment (for the 386) is plenty, and can shrink code size considerably.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In fact, given that most jump targets aren't in performance-critical
> code, it's hard to make a compelling argument for aligning branch
> targets even on the 486. I'd say that no alignment (except possibly
> where you know a branch target lies in a key loop), or at most dword
> alignment (for the 386) is plenty, and can shrink code size
> considerably.
Instruction prefixes are awfully expensive; avoid them if you can.
(These include size and addressing prefixes, segment overrides,
@ -85,9 +89,15 @@ primarily an operating system instruction—but there's a hidden gotcha
here because the **XCHG** instruction always locks the bus when used
with a memory operand.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) ***XCHG** is a tempting instruction that's often used in assembly language; for example, exchanging with video memory is a popular way to read and write VGA memory in a single instruction—but it's now a bad idea. As it happens, on the 486 and Pentium, using **MOV**s to read and write memory is faster, anyway; and even on the 486, my measurements indicate a five-cycle tax for **LOCK** in general, and a nine-cycle execution time for **XCHG** with memory. Avoid **XCHG** with memory if you possibly can.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> **XCHG** is a tempting instruction that's often used in assembly
> language; for example, exchanging with video memory is a popular way to
> read and write VGA memory in a single instruction—but it's now a bad
> idea. As it happens, on the 486 and Pentium, using **MOV**s to read and
> write memory is faster, anyway; and even on the 486, my measurements
> indicate a five-cycle tax for **LOCK** in general, and a nine-cycle
> execution time for **XCHG** with memory. Avoid **XCHG** with memory if
> you possibly can.
As with the 486, don't use **ENTER** or **LEAVE**, which are slower than
the equivalent discrete instructions. Also, start using **TEST

View file

@ -22,9 +22,13 @@ the branch or fall-through takes 4 or 5 cycles (if it executes in the U-
or V-pipe, respectively)—1 or 2 cycles more than a branch and 3 or 4
cycles more than a fall-through on the 486.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Branch prediction is unprecedented in the x86, and fundamentally alters the nature of pedal-to-the-metal optimization, for the simple reason that it renders unrolled loops largely obsolete. Rare indeed is the loop that can't afford to spare even 1 or 0 (yes, zero!) cycles per iteration for loop counting, and that's how low the cost can go for maintaining a loop on the Pentium.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Branch prediction is unprecedented in the x86, and fundamentally alters
> the nature of pedal-to-the-metal optimization, for the simple reason
> that it renders unrolled loops largely obsolete. Rare indeed is the loop
> that can't afford to spare even 1 or 0 (yes, zero!) cycles per iteration
> for loop counting, and that's how low the cost can go for maintaining a
> loop on the Pentium.
Also, unrolled loops are bigger than normal loops, so there are extra
(and expensive) cache misses the first time through the loop if the
@ -58,9 +62,13 @@ count on a branch to take 1 cycle when it falls through, but on the
Pentium you can't be sure whether it will take 1 or either 4 or 5 cycles
on any given iteration.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *As things currently stand, branch prediction is an annoyance for assembly language optimization because it's impossible to be certain exactly how code will perform until you measure it, and even then it's difficult to be sure exactly where the cycles went. All I can say is try to fall through branches if possible, and try to be consistent in your branching if not.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> As things currently stand, branch prediction is an annoyance for
> assembly language optimization because it's impossible to be certain
> exactly how code will perform until you measure it, and even then it's
> difficult to be sure exactly where the cycles went. All I can say is try
> to fall through branches if possible, and try to be consistent in your
> branching if not.
### Miscellaneous Pentium Topics {#Heading9}

View file

@ -120,9 +120,13 @@ because those instructions that the V-pipe can handle are able to pair
only with certain U-pipe instructions. For example, **MOVSD** uses both
pipes, so no instruction can be executed in parallel with **MOVSD**.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The use of both pipes does make **MOVSD** nearly twice as fast on the Pentium as on the 486, but it's nonetheless slower than using equivalent simpler instructions that allow for superscalar execution. Stick to the Pentium's RISC-like instructions—the pairable instructions I'll discuss next—when you're seeking maximum performance, with just a few exceptions such as **REP MOVS** and **REP STOS**.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The use of both pipes does make **MOVSD** nearly twice as fast on the
> Pentium as on the 486, but it's nonetheless slower than using equivalent
> simpler instructions that allow for superscalar execution. Stick to the
> Pentium's RISC-like instructions—the pairable instructions I'll discuss
> next—when you're seeking maximum performance, with just a few exceptions
> such as **REP MOVS** and **REP STOS**.
Trickier yet, register contention can shut down the V-pipe on any given
cycle, and Address Generation Interlocks (AGIs) can stall either pipe at

View file

@ -140,9 +140,14 @@ in Figure 20.3—a full cycle *faster* than **PUSH [*mem*]**, which takes
V-pipe-executable instructions to execute simultaneously (pair) in the
V-pipe.**
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *A fundamental rule of Pentium optimization is that it pays to break complex instructions into equivalent simple instructions, then shuffle the simple instructions for maximum use of the V-pipe. This is true partly because most of the pairable instructions are simple instructions, and partly because breaking instructions into pieces allows more freedom to rearrange code to avoid the AGIs and register contention I'll discuss in the next chapter.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> A fundamental rule of Pentium optimization is that it pays to break
> complex instructions into equivalent simple instructions, then shuffle
> the simple instructions for maximum use of the V-pipe. This is true
> partly because most of the pairable instructions are simple
> instructions, and partly because breaking instructions into pieces
> allows more freedom to rearrange code to avoid the AGIs and register
> contention I'll discuss in the next chapter.
![**Figure 20.2**  *Instruction flow through the two pipes.*](images/20-02.jpg)
@ -175,6 +180,8 @@ proper sequencing, interleaving the simple instructions with other
instructions that don't use EDX or **Mem Var**, the three-instruction
sequence can be reduced to 1.5 cycles, but it is *14* bytes long.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *It's not unusual for Pentium optimization to approximately double both performance and code size at the same time. In an important loop, go for performance and ignore the size, but on a program-wide basis, the size bears watching.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> It's not unusual for Pentium optimization to approximately double both
> performance and code size at the same time. In an important loop, go for
> performance and ignore the size, but on a program-wide basis, the size
> bears watching.

View file

@ -39,9 +39,11 @@ fully overlap, as described below). The logical conclusion would seem to
be that we should strive to pair instructions of the same lengths, but
that is often not correct.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The actual rule is that we should strive to pair one-cycle instructions (or, at most, two-cycle instructions, but not three-cycle instructions), which in turn leads to the corollary that we should, in general, use mostly one-cycle instructions when optimizing.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The actual rule is that we should strive to pair one-cycle instructions
> (or, at most, two-cycle instructions, but not three-cycle instructions),
> which in turn leads to the corollary that we should, in general, use
> mostly one-cycle instructions when optimizing.
![**Figure 20.4**  *Lockstep execution and idle time in the V-pipe.*](images/20-04.jpg)

View file

@ -78,9 +78,13 @@ complex memory operation; in pre-superscalar days, we used to employ
those handy CISC memory instructions to do all that stuff without using
any extra registers.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *More problematic still is that for maximum pairing, you'll typically have two operations proceeding at once, one in each pipe, and trying to keep two operations in registers at once is difficult indeed. There's not much to be done about this, other than clever and Spartan register usage, but be aware that it's a major element of Pentium performance programming.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> More problematic still is that for maximum pairing, you'll typically
> have two operations proceeding at once, one in each pipe, and trying to
> keep two operations in registers at once is difficult indeed. There's
> not much to be done about this, other than clever and Spartan register
> usage, but be aware that it's a major element of Pentium performance
> programming.
Also be aware that prefixes of every sort, with the sole exception of
the 0FH prefix on non-short conditional jumps, always execute in the

View file

@ -98,29 +98,33 @@ fixed. Moreover, a great deal of graphics software now uses word
**OUT**s, so any computer or VGA that doesn't properly support word
**OUT**s could scarcely be considered a clone at all.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *A speed tip: The setting of each chip's Index register remains the same until it is reprogrammed. This means that in cases where you are setting the same internal register repeatedly, you can set the Index register to point to that internal register once, then write to the Data register multiple times. For example, the Bit Mask register (GC register 8) is often set repeatedly inside a loop when drawing lines. The standard code for this is:*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MOV DX,03CEH ;point to GC Index register
MOV AL,8 ;internal index of Bit Mask register
OUT DX,AX ;AH contains Bit Mask register setting
-- ------------------------------------------------------------------------------------------------------
*Alternatively, the GC Index register could initially be set to point to the Bit Mask register with*
-- ------------------------------------------------------------------------------------------------------
MOV DX,03CEH ;point to GC Index register
MOV AL,8 ;internal index of Bit Mask register
OUT DX,AL ;set GC Index register
INC DX ;point to GC Data register
-- -------------------------------------------------------------------------------------------------
*and then the Bit Mask register could be set repeatedly with the byte-size **OUT** instruction*
-- -------------------------------------------------------------------------------------------------
OUT DX,AL ;AL contains Bit Mask register setting
-- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*which is generally faster (and never slower) than a word-sized **OUT**, and which does not require AH to be set, freeing up a register. Of course, this method only works if the GC Index register remains unchanged throughout the loop.*
-- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> A speed tip: The setting of each chip's Index register remains the same
> until it is reprogrammed. This means that in cases where you are setting
> the same internal register repeatedly, you can set the Index register to
> point to that internal register once, then write to the Data register
> multiple times. For example, the Bit Mask register (GC register 8) is
> often set repeatedly inside a loop when drawing lines. The standard code
> for this is:
>
> MOV DX,03CEH ;point to GC Index register
> MOV AL,8 ;internal index of Bit Mask register
> OUT DX,AX ;AH contains Bit Mask register setting
>
> Alternatively, the GC Index register could initially be set to point to
> the Bit Mask register with
>
> MOV DX,03CEH ;point to GC Index register
> MOV AL,8 ;internal index of Bit Mask register
> OUT DX,AL ;set GC Index register
> INC DX ;point to GC Data register>
>
> and then the Bit Mask register could be set repeatedly with the
> byte-size **OUT** instruction
>
> OUT DX,AL ;AL contains Bit Mask register setting
>
> which is generally faster (and never slower) than a word-sized **OUT**,
> and which does not require AH to be set, freeing up a register. Of
> course, this method only works if the GC Index register remains
> unchanged throughout the loop.

View file

@ -107,6 +107,10 @@ just to be safe.) Immediately after setting palette RAM, however, 20h
Index register to restore normal video, and at all other times bit 5
should be set to 1.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *By the way, palette RAM can be set via the BIOS video interrupt (interrupt 10H), function 10H. Whenever an VGA function can be performed reasonably well through a BIOS function, as it can in the case of setting palette RAM, it should be, both because there is no point in reinventing the wheel and because the BIOS may well mask incompatibilities between the IBM VGA and VGA clones.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> By the way, palette RAM can be set via the BIOS video interrupt
> (interrupt 10H), function 10H. Whenever an VGA function can be performed
> reasonably well through a BIOS function, as it can in the case of
> setting palette RAM, it should be, both because there is no point in
> reinventing the wheel and because the BIOS may well mask
> incompatibilities between the IBM VGA and VGA clones.

View file

@ -24,9 +24,24 @@ sync to happen, sets the new pel panning state, and then continues
drawing. Don't worry about the details right now; page flipping will
come up again, at considerably greater length, in later chapters.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *As an interesting side note, be aware that if you run DOS software under a multitasking environment such as Windows NT, timeslicing delays can make mismatched start address bytes or mismatched start address and pel panning settings much more likely, for the graphics code can be interrupted at any time. This is also possible, although much less likely, under non-multitasking environments such as DOS, because strategically placed interrupts can cause the same sorts of problems there. For maximum safety, you should disable interrupts around the key portions of your page-flipping code, although here we run into the problem that if interrupts are disabled from the time we start looking for Display Enable until we set the Pel Panning register, they will be off for far too long, and keyboard, mouse, and network events will potentially be lost. Also, disabling interrupts won't help in true multitasking environments, which never let a program hog the entire CPU. This is one reason that pel panning, although indubitably flashy, isn't widely used and should be reserved for only those cases where it's absolutely necessary.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> As an interesting side note, be aware that if you run DOS software under
> a multitasking environment such as Windows NT, timeslicing delays can
> make mismatched start address bytes or mismatched start address and pel
> panning settings much more likely, for the graphics code can be
> interrupted at any time. This is also possible, although much less
> likely, under non-multitasking environments such as DOS, because
> strategically placed interrupts can cause the same sorts of problems
> there. For maximum safety, you should disable interrupts around the key
> portions of your page-flipping code, although here we run into the
> problem that if interrupts are disabled from the time we start looking
> for Display Enable until we set the Pel Panning register, they will be
> off for far too long, and keyboard, mouse, and network events will
> potentially be lost. Also, disabling interrupts won't help in true
> multitasking environments, which never let a program hog the entire CPU.
> This is one reason that pel panning, although indubitably flashy, isn't
> widely used and should be reserved for only those cases where it's
> absolutely necessary.
Waiting for the sync pulse has the side effect of causing program
execution to synchronize to the VGA's frame rate of 60 or 70 frames per

View file

@ -110,9 +110,18 @@ separate byte values, making matters easier for the programmer, while
the macro itself can combine the values into a single word-sized
constant.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *A minor optimization tip illustrated in the listing is the use of **INC AX** and **DEC AX** in the **DrawVerticalBox** subroutine when only AL actually needs to be modified. Word-sized register increment and decrement instructions (or dword-sized instructions in 32-bit protected mode) are only one byte long, while byte-size register increment and decrement instructions are two bytes long. Consequently, when size counts, it is worth using a whole 16-bit (or 32-bit) register instead of the low 8 bits of that register for **INC** and **DEC**—if you don't need the upper portion of the register for any other purpose, or if you can be sure that the **INC** or **DEC** won't affect the upper part of the register.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> A minor optimization tip illustrated in the listing is the use of **INC
> AX** and **DEC AX** in the **DrawVerticalBox** subroutine when only AL
> actually needs to be modified. Word-sized register increment and
> decrement instructions (or dword-sized instructions in 32-bit protected
> mode) are only one byte long, while byte-size register increment and
> decrement instructions are two bytes long. Consequently, when size
> counts, it is worth using a whole 16-bit (or 32-bit) register instead of
> the low 8 bits of that register for **INC** and **DEC**—if you don't
> need the upper portion of the register for any other purpose, or if you
> can be sure that the **INC** or **DEC** won't affect the upper part of
> the register.
The latches and ALUs are central to high-performance VGA code, since
they allow programs to process across all four memory planes without a

View file

@ -24,9 +24,16 @@ mask; unfortunately, we're already using it for stencilling in this
case), but it completely eliminates reading display memory and does
foreground-plus-background drawing in one blurry-fast pass.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *This last-described example is a good illustration of how I'd suggest you approach the VGA: As a rich collection of hardware resources that can profitably be combined in some non-obvious ways. Don't let yourself be limited by the obvious applications for the latches, bit mask, write modes, read modes, map mask, ALUs, and set/reset circuitry. Instead, try to imagine how they could work together to perform whatever task you happen to need done at any given time. I've made my code as much as four times faster by doing this, as the discussion of Mode X in Chapters 47-49 demonstrates.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> This last-described example is a good illustration of how I'd suggest
> you approach the VGA: As a rich collection of hardware resources that
> can profitably be combined in some non-obvious ways. Don't let yourself
> be limited by the obvious applications for the latches, bit mask, write
> modes, read modes, map mask, ALUs, and set/reset circuitry. Instead, try
> to imagine how they could work together to perform whatever task you
> happen to need done at any given time. I've made my code as much as four
> times faster by doing this, as the discussion of Mode X in Chapters
> 47-49 demonstrates.
The example code in Listing 25.1 is designed to illustrate the use of
the Data Rotate and Bit Mask registers, and is not as fast or as

View file

@ -31,9 +31,17 @@ Set/Reset register is inactive in write mode 3, but the Set/Reset
register provides the primary drawing color in write mode 3, as
discussed in the next chapter.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Be aware that because set/reset directly replaces CPU data, it does not necessarily have to force an entire display memory byte to 0 or 0FFH, even when set/reset is replacing CPU data for all planes. For example, if the Bit Mask register is set to 80H, the set/reset circuitry can only modify bit 7 of the destination byte in each plane, since the other seven bits will come from the latches for each plane. Similarly, the set/reset value for each plane can be modified by that plane's ALU. Once again, this illustrates that set/reset merely replaces the CPU data for selected planes; the set/reset value is then processed in exactly the same way that CPU data normally is.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Be aware that because set/reset directly replaces CPU data, it does not
> necessarily have to force an entire display memory byte to 0 or 0FFH,
> even when set/reset is replacing CPU data for all planes. For example,
> if the Bit Mask register is set to 80H, the set/reset circuitry can only
> modify bit 7 of the destination byte in each plane, since the other
> seven bits will come from the latches for each plane. Similarly, the
> set/reset value for each plane can be modified by that plane's ALU. Once
> again, this illustrates that set/reset merely replaces the CPU data for
> selected planes; the set/reset value is then processed in exactly the
> same way that CPU data normally is.
### A Brief Note on Word OUTs {#Heading9}

View file

@ -17,9 +17,16 @@ equal to 1 indicating where character pixels are to appear. The Data
Rotate register is set to rotate the CPU data to pixel-align it, since
without rotation characters could only be drawn on byte boundaries.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *As I pointed out in Chapter 25, the CPU is perfectly capable of rotating the data itself, and it's often the case that that's more efficient. The problem with using the Data Rotate register is that the **OUT** that sets that register is time-consuming, especially for proportional text, which requires a different rotation for each character. Also, if the code performs full-byte accesses to display memory—that is, if it combines pieces of two adjacent characters into one byte—whenever possible for efficiency, the CPU generally has to do extra work to prepare the data so the VGA's rotator can handle it.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> As I pointed out in Chapter 25, the CPU is perfectly capable of rotating
> the data itself, and it's often the case that that's more efficient. The
> problem with using the Data Rotate register is that the **OUT** that
> sets that register is time-consuming, especially for proportional text,
> which requires a different rotation for each character. Also, if the
> code performs full-byte accesses to display memory—that is, if it
> combines pieces of two adjacent characters into one byte—whenever
> possible for efficiency, the CPU generally has to do extra work to
> prepare the data so the VGA's rotator can handle it.
At the same time that the Data Rotate register is set, the Bit Mask
register is set to allow the CPU to modify only that portion of the

View file

@ -94,9 +94,11 @@ register is set to 1.
![**Figure 27.1**  *VGA data flow in write mode 2.*](images/27-01.jpg)
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *It's worth noting two differences between write mode 2 and write mode 0, the standard write mode of the VGA. First, rotation of the CPU data byte does not take place in write mode 2. Second, the Set/Reset and Enable Set/Reset registers have no effect in write mode 2.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> It's worth noting two differences between write mode 2 and write mode 0,
> the standard write mode of the VGA. First, rotation of the CPU data byte
> does not take place in write mode 2. Second, the Set/Reset and Enable
> Set/Reset registers have no effect in write mode 2.
Now that we understand the mechanics of write mode 2, we can step back
and get a feel for what it might be useful for. View bits 3-0 of the CPU

View file

@ -15,9 +15,14 @@ pages: 508-515
into VGA bitmaps does have its uses, Listing 27.1 is primarily intended
to illustrate the mechanics of write mode 2.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *For performance, it's best to store 16-color bitmaps in pre-separated four-plane format in system memory, and copy one plane at a time to the screen. Ideally, such bitmaps should be copied one scan line at a time, with all four planes completed for one scan line before moving on to the next. I say this because when entire images are copied one plane at a time, nasty transient color effects can occur as one plane becomes visibly changed before other planes have been modified.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> For performance, it's best to store 16-color bitmaps in pre-separated
> four-plane format in system memory, and copy one plane at a time to the
> screen. Ideally, such bitmaps should be copied one scan line at a time,
> with all four planes completed for one scan line before moving on to the
> next. I say this because when entire images are copied one plane at a
> time, nasty transient color effects can occur as one plane becomes
> visibly changed before other planes have been modified.
#### Drawing Color-Patterned Lines Using Write Mode 2 {#Heading6}

View file

@ -18,9 +18,18 @@ far better VGA animation techniques.
As you'd expect, neither the read mode nor the setting of the Read Map
register affects CPU *writes* to VGA memory in any way.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *An important point regarding reading VGA memory involves the VGA's latches. (Remember that each of the four latches stores a byte for one plane; on CPU writes, the latches can provide some or all of the data written to display memory, allowing fast copying and efficient pixel masking.) Whenever the CPU reads a given address in VGA memory, each of the four latches is loaded with the contents of the byte at that address in its respective plane. Even though the CPU only receives data from one plane in read mode 0, all four planes are always read, and the values read are stored in the latches. This is true in read mode 1 as well. In short, whenever the CPU reads VGA memory in any read mode, all four planes are read and all four latches are always loaded.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> An important point regarding reading VGA memory involves the VGA's
> latches. (Remember that each of the four latches stores a byte for one
> plane; on CPU writes, the latches can provide some or all of the data
> written to display memory, allowing fast copying and efficient pixel
> masking.) Whenever the CPU reads a given address in VGA memory, each of
> the four latches is loaded with the contents of the byte at that address
> in its respective plane. Even though the CPU only receives data from one
> plane in read mode 0, all four planes are always read, and the values
> read are stored in the latches. This is true in read mode 1 as well. In
> short, whenever the CPU reads VGA memory in any read mode, all four
> planes are read and all four latches are always loaded.
### Read Mode 1 {#Heading4}

View file

@ -10,9 +10,16 @@ chapter: '29'
pages: 547-550
---
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *While these requirements are no problem if you're simply calling a subroutine in order to save an image from your program, they pose a considerable problem if you're designing a hot-key operated TSR that can capture a screen image at any time. With the EGA specifically, there's never any way to tell what state the registers are currently in, since the registers aren't readable. (More on this issue later in this chapter.) As a result, any TSR that sets the Bit Mask to 0FFH, the Data Rotate register to 0, and so on runs the risk of interfering with the drawing code of the program that's already running.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> While these requirements are no problem if you're simply calling a
> subroutine in order to save an image from your program, they pose a
> considerable problem if you're designing a hot-key operated TSR that can
> capture a screen image at any time. With the EGA specifically, there's
> never any way to tell what state the registers are currently in, since
> the registers aren't readable. (More on this issue later in this
> chapter.) As a result, any TSR that sets the Bit Mask to 0FFH, the Data
> Rotate register to 0, and so on runs the risk of interfering with the
> drawing code of the program that's already running.
What's the solution? Frankly, the solution is to get VGA-specific. A TSR
designed for the VGA can simply read out and save the state of the
@ -52,9 +59,15 @@ as long as the high-level language you're using can perform direct port
I/O to set up the adapter and can read and write display memory
directly.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *One tip if you're saving and restoring the screen from a high-level language on an EGA, though: After you've completed the save or restore operation, be sure to put any registers that you've changed back to their default settings. Some high-level languages (and the BIOS as well) assume that various registers are left in a certain state, so on the EGA it's safest to leave the registers in their most likely state. On the VGA, of course, you can just read the registers out before you change them, then put them back the way you found them when you're done.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> One tip if you're saving and restoring the screen from a high-level
> language on an EGA, though: After you've completed the save or restore
> operation, be sure to put any registers that you've changed back to
> their default settings. Some high-level languages (and the BIOS as well)
> assume that various registers are left in a certain state, so on the EGA
> it's safest to leave the registers in their most likely state. On the
> VGA, of course, you can just read the registers out before you change
> them, then put them back the way you found them when you're done.
### 16 Colors out of 64 {#Heading4}

View file

@ -20,9 +20,14 @@ programmed to any of the 64 possible colors by either setting Attribute
Controller register 11H directly or calling video function 10H,
subfunction 1.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *On ECD-compatible monitors, however, there's too little scan time to display a proper border when the EGA is in 350-scan-line mode, so overscan should always be 0 (black) unless you're in 200-scanmode. Note, though, that a VGA can easily display a border on a VGA-compatible monitor, and VGAs are in fact programmed at mode set for an 8-pixel-wide border in all modes; all you need do is set the overscan color on any VGA to see the border.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> On ECD-compatible monitors, however, there's too little scan time to
> display a proper border when the EGA is in 350-scan-line mode, so
> overscan should always be 0 (black) unless you're in 200-scanmode. Note,
> though, that a VGA can easily display a border on a VGA-compatible
> monitor, and VGAs are in fact programmed at mode set for an 8-pixel-wide
> border in all modes; all you need do is set the overscan color on any
> VGA to see the border.
### A Bonus Blanker {#Heading6}

View file

@ -57,9 +57,12 @@ screen start scan line is set—that seems to me to be an advantage—but
because the changed screen can appear *before* the new split screen
start scan line is set.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Remember, the split screen start scan line is spread out over two or three registers. What if the incompletely-changed value matches the current scan line after you've set one register but before you've set the rest? For one frame, you'll see the split screen in a wrong place—possibly a very wrong place—resulting in jumping and flicker.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Remember, the split screen start scan line is spread out over two or
> three registers. What if the incompletely-changed value matches the
> current scan line after you've set one register but before you've set
> the rest? For one frame, you'll see the split screen in a wrong
> place—possibly a very wrong place—resulting in jumping and flicker.
The solution is simple: Set the split screen start scan line at a time
when it can't possibly match the currently displayed scan line. The easy
@ -103,9 +106,12 @@ designing split-screen images that might be displayed on EGAs, and you
should in any case check how your split-screens look on both VGAs and
EGAs.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *I have an important caution here: Don't count on the EGA's split-screen bug; that is, don't rely on the first scan line being doubled when you design your split screens. IBM designed and made the original EGA, but a lot of companies cloned it, and there's no guarantee that all EGA clones copy the bug. It is a certainty, at least, that the VGA didn't copy it.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> I have an important caution here: Don't count on the EGA's split-screen
> bug; that is, don't rely on the first scan line being doubled when you
> design your split screens. IBM designed and made the original EGA, but a
> lot of companies cloned it, and there's no guarantee that all EGA clones
> copy the bug. It is a certainty, at least, that the VGA didn't copy it.
There's another respect in which the EGA is inferior to the VGA when it
comes to the split screen, and that's in the area of panning when the

View file

@ -36,9 +36,11 @@ current byte. As the top part of the screen moves smoothly about, the
split screen will move and jump, move and jump, over and over. Believe
me, it's not a pretty sight.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *What's to be done? On the EGA, nothing. Unless you're willing to have your users' eyes doing the jitterbug, don't use horizontal smooth scrolling while the split screen is up. Byte panning is fine—just don't change the Pel Panning register from its default setting.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> What's to be done? On the EGA, nothing. Unless you're willing to have
> your users' eyes doing the jitterbug, don't use horizontal smooth
> scrolling while the split screen is up. Byte panning is fine—just don't
> change the Pel Panning register from its default setting.
On the VGA, there is recourse. A VGA-only bit, bit 5 of the AC Mode
Control register (AC register 10H), turns off pel panning in the split

View file

@ -62,9 +62,14 @@ I'm sure Richard is right when it comes to the real McCoy IBM VGA and
EGA, but I'm less confident that every clone out there loads the start
address at the start of vertical sync.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *For that very reason, I generally advise people not to use horizontal smooth panning unless they can test their software on all the makes of display adapter it might run on. I've used Richard's approach in Listings 30.1 and 30.2, and so far as I've seen it works fine, but be aware that there are potential, albeit unproven, hazards to relying on the setting of the start address registers to occur at a specific time in the frame.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> For that very reason, I generally advise people not to use horizontal
> smooth panning unless they can test their software on all the makes of
> display adapter it might run on. I've used Richard's approach in
> Listings 30.1 and 30.2, and so far as I've seen it works fine, but be
> aware that there are potential, albeit unproven, hazards to relying on
> the setting of the start address registers to occur at a specific time
> in the frame.
The interaction of the start address registers and the Pel Panning
register is worthy of note. After waiting for the end of vertical sync

View file

@ -217,6 +217,11 @@ The first thing you'll notice when you run this code is that the speed
of 360x480 256-color mode is pretty good, especially considering that
most of the program is im-plemented in C.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Drawing in 360x480 256-color mode can sometimes actually be faster than in the 16-color modes, because the byte-per-pixel display memory organization of 256-color mode eliminates the need to read display memory before writing to it in order to isolate individual pixels coexisting within a single byte. In addition, 360x480 256-color mode is a variant of Mode X, which we'll encounter in detail in Chapter 47, and supports all the high-performance features of Mode X.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Drawing in 360x480 256-color mode can sometimes actually be faster than
> in the 16-color modes, because the byte-per-pixel display memory
> organization of 256-color mode eliminates the need to read display
> memory before writing to it in order to isolate individual pixels
> coexisting within a single byte. In addition, 360x480 256-color mode is
> a variant of Mode X, which we'll encounter in detail in Chapter 47, and
> supports all the high-performance features of Mode X.

View file

@ -50,9 +50,17 @@ load all 256 DAC locations without showing *some* sort of garbage on the
screen for at least one frame, but that's not the BIOS's fault; it's a
problem endemic to the VGA.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *These findings lead me inexorably to the conclusion that the BIOS should not be used to load the DAC dynamically. That is, if you're loading the DAC just once in preparation for a graphics session—sort of a DAC mode set—by all means load by way of the BIOS. No one will care that some garbage is displayed for a single frame; heck, I have boards that bounce and flicker and show garbage every time I do a mode set, and the amount of garbage produced by loading the DAC once is far less noticeable. If, however, you intend to load the DAC repeatedly for color cycling, avoid the BIOS DAC load functions like the plague. They will bring you only heartache.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> These findings lead me inexorably to the conclusion that the BIOS should
> not be used to load the DAC dynamically. That is, if you're loading the
> DAC just once in preparation for a graphics session—sort of a DAC mode
> set—by all means load by way of the BIOS. No one will care that some
> garbage is displayed for a single frame; heck, I have boards that bounce
> and flicker and show garbage every time I do a mode set, and the amount
> of garbage produced by loading the DAC once is far less noticeable. If,
> however, you intend to load the DAC repeatedly for color cycling, avoid
> the BIOS DAC load functions like the plague. They will bring you only
> heartache.
As but one example of the unsuitability of the BIOS DAC-loading
functions for color cycling, imagine that you want to cycle all 256

View file

@ -125,6 +125,15 @@ much the same for the jets. The only remaining task would be to animate
the spaceship across the screen, which is not a particularly difficult
task.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The key to getting all the color cycling to work in the above example, however, would be to assign each color cycling task a different part of the DAC, with each part cycled independently as needed. If, as is likely, the total number of DAC locations cycled proved to be too great to manage in one frame, you could simply cycle the colors of the stars after one frame, the colors of the meteors after the next, and the colors of the jets after yet another frame, then back around to cycling the colors of the stars. By splitting up the DAC in this manner and interleaving the cycling tasks, you can perform a great deal of seemingly complex color animation without loading very much of the DAC during any one frame.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The key to getting all the color cycling to work in the above example,
> however, would be to assign each color cycling task a different part of
> the DAC, with each part cycled independently as needed. If, as is
> likely, the total number of DAC locations cycled proved to be too great
> to manage in one frame, you could simply cycle the colors of the stars
> after one frame, the colors of the meteors after the next, and the
> colors of the jets after yet another frame, then back around to cycling
> the colors of the stars. By splitting up the DAC in this manner and
> interleaving the cycling tasks, you can perform a great deal of
> seemingly complex color animation without loading very much of the DAC
> during any one frame.

View file

@ -123,9 +123,17 @@ orientations are distinguished by which coordinate forms the major axis
and by whether each of X and Y increases or decreases from the line
start to the line end.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *A moment of thought will show, however, that four of the line orientations are redundant. Each of the four orientations for which **DeltaY**, the Y component of the line, is less than 0 (that is, for which the line start Y coordinate is greater than the line end Y coordinate) can be transformed into one of the four orientations for which the line start Y coordinate is less than the line end Y coordinate simply by reversing the line start and end coordinates, so that the line is drawn in the other direction. **EVGALine** does this by swapping (X0,Y0) (the line start coordinates) with (X1,Y1) (the line end coordinates) whenever Y0 is greater than Y1.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> A moment of thought will show, however, that four of the line
> orientations are redundant. Each of the four orientations for which
> **DeltaY**, the Y component of the line, is less than 0 (that is, for
> which the line start Y coordinate is greater than the line end Y
> coordinate) can be transformed into one of the four orientations for
> which the line start Y coordinate is less than the line end Y coordinate
> simply by reversing the line start and end coordinates, so that the line
> is drawn in the other direction. **EVGALine** does this by swapping
> (X0,Y0) (the line start coordinates) with (X1,Y1) (the line end
> coordinates) whenever Y0 is greater than Y1.
This accomplished, **EVGALine** must still distinguish among the four
remaining line orientations. Those four orientations form two major

View file

@ -55,9 +55,13 @@ When I compared the C and assembly implementations drawing to normal
system (nondisplay) memory, I found that the assembly code was actually
four times as fast as the C code.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In fact, Listing 37.1 draws VGA lines at about 92 percent of the maximum possible rate in my system—that is, it draws very nearly as fast as the VGA hardware will allow. All the optimization in the world would get me less than 10 percent faster line drawing—and only if I eliminated all overhead, an unlikely proposition at best. The code isn't fully optimized, but so what?*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In fact, Listing 37.1 draws VGA lines at about 92 percent of the maximum
> possible rate in my system—that is, it draws very nearly as fast as the
> VGA hardware will allow. All the optimization in the world would get me
> less than 10 percent faster line drawing—and only if I eliminated all
> overhead, an unlikely proposition at best. The code isn't fully
> optimized, but so what?
Now it's true that faster line-drawing code would likely be more
beneficial on faster VGAs, especially local-bus VGAs, and in slower

View file

@ -44,9 +44,14 @@ range around a pixel, then that pixel will be drawn once and only
once—just what we need in order to be able to fit filled polygons
together seamlessly.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *This sort of non-overlapping polygon filling isn't ideal for all purposes. Polygons are skewed toward the top and left edges, which not only introduces drawing error relative to the ideal polygon but also means that a filled polygon won't match the same polygon drawn unfilled. Narrow wedges and one-pixel-wide polygons will show up spottily. All in all, the choice of polygon-filling approach depends entirely on the ways in which the filled polygons must be used.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> This sort of non-overlapping polygon filling isn't ideal for all
> purposes. Polygons are skewed toward the top and left edges, which not
> only introduces drawing error relative to the ideal polygon but also
> means that a filled polygon won't match the same polygon drawn unfilled.
> Narrow wedges and one-pixel-wide polygons will show up spottily. All in
> all, the choice of polygon-filling approach depends entirely on the ways
> in which the filled polygons must be used.
For our purposes, nonoverlapping polygons are the way to go, so let's
have at them.

View file

@ -125,6 +125,6 @@ the floating point results were sufficiently imprecise to creep from
just under an integer value to just over it, so that the **ceil**
function returned a coordinate that was one too large.
------------------- ---------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Floating point is very accurate—but it is not precise. Integer calculations, properly performed, are.*
------------------- ---------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Floating point is very accurate—but it is not precise. Integer
> calculations, properly performed, are.

View file

@ -17,10 +17,19 @@ states. With those wait states factored out, the assembly language
version of **DrawHorizontalLineList** becomes almost three times as fast
as the C code.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *There is a lesson here. An optimization has no fixed payoff; its value fluctuates according to the context in which it is used. There's relatively little benefit to further optimizing code that already spends half its time waiting for display memory; no matter how good your optimizations, you'll get only a two-times speedup at best, and generally much less than that. There is, on the other hand, potential for tremendous improvement when drawing to system memory, so if that's where most of your drawing will occur, optimizations such as Listing 39.3 are well worth the effort.*
*Know the environments in which your code will run, and know where the cycles go in those environments.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> There is a lesson here. An optimization has no fixed payoff; its value
> fluctuates according to the context in which it is used. There's
> relatively little benefit to further optimizing code that already spends
> half its time waiting for display memory; no matter how good your
> optimizations, you'll get only a two-times speedup at best, and
> generally much less than that. There is, on the other hand, potential
> for tremendous improvement when drawing to system memory, so if that's
> where most of your drawing will occur, optimizations such as Listing
> 39.3 are well worth the effort.
>
> Know the environments in which your code will run, and know where the
> cycles go in those environments.
**LISTING 39.3 L39-3.ASM**

View file

@ -23,9 +23,11 @@ back links and insertion sorting aren't great enough; profiling a
variety of polygons reveals that less than ten percent of total time is
spent sorting the AET.
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *The potential 1 to 5 percent speedup gained by optimizing AET sorting just isn't worth it in any but the most demanding application—a good example of the need to keep an overall perspective when comparing the theoretical characteristics of various approaches.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> The potential 1 to 5 percent speedup gained by optimizing AET sorting
> just isn't worth it in any but the most demanding application—a good
> example of the need to keep an overall perspective when comparing the
> theoretical characteristics of various approaches.
### Nonconvex Polygons {#Heading8}

View file

@ -47,9 +47,15 @@ called "nonconvex" is actually "simple," and I suppose what I called
"complex" should be referred to as "nonsimple," or maybe just "none of
the above."
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *This may seem like nit-picking, but actually, it isn't; what it's really about is the tremendous importance of having a shared language. In one of his books, Richard Feynman describes having developed his own mathematical framework, complete with his own notation and terminology, in high school. When he got to college and started working with other people who were at his level, he suddenly understood that people can't share ideas effectively unless they speak the same language; otherwise, they waste a great deal of time on misunderstandings and explanation.*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> This may seem like nit-picking, but actually, it isn't; what it's really
> about is the tremendous importance of having a shared language. In one
> of his books, Richard Feynman describes having developed his own
> mathematical framework, complete with his own notation and terminology,
> in high school. When he got to college and started working with other
> people who were at his level, he suddenly understood that people can't
> share ideas effectively unless they speak the same language; otherwise,
> they waste a great deal of time on misunderstandings and explanation.
Or, as Bill Huber put it, "You are free to adopt your own terminology
when it suits your purposes well. But you risk losing or confusing those

View file

@ -297,9 +297,15 @@ accurate polygon-antialiasing approach around Wu's basic weighting
technique. The results would not be quite so good as more sophisticated
antialiasing techniques, but they would be much faster.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In general, the results obtained by Wu antialiasing are only so-so, by theoretical measures. Wu antialiasing amounts to a simple box filter placed over a fixed-point step approximation of a line, and that process introduces a good deal of deviation from the ideal. On the other hand, Wu notes that even a 10 percent error in intensity doesn't lead to noticeable loss of image quality, and for Wu-antialiased lines up to 1K pixels in length, the error is under 10 percent. If it looks good, it is good—and it looks good.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In general, the results obtained by Wu antialiasing are only so-so, by
> theoretical measures. Wu antialiasing amounts to a simple box filter
> placed over a fixed-point step approximation of a line, and that process
> introduces a good deal of deviation from the ideal. On the other hand,
> Wu notes that even a 10 percent error in intensity doesn't lead to
> noticeable loss of image quality, and for Wu-antialiased lines up to 1K
> pixels in length, the error is under 10 percent. If it looks good, it is
> good—and it looks good.
With a 16-bit error accumulator, fixed-point inaccuracy becomes a
problem for Wu-antialiased lines longer than 1K. For such lines, you

View file

@ -107,9 +107,16 @@ set/reset color:
mov es,dx
mov byte ptr es:[di],0ffh
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *If you're familiar with VGA programming, you're no doubt aware that everything that can be done with write mode 3 can also be accomplished in write mode 0 or write mode 2 by using the Bit Mask register. However, setting the Bit Mask register requires at least one **OUT** per byte written, in addition to the read and write of display memory, and **OUT**s are often slower than display memory accesses, especially on 386s and 486s. One of the great virtues of write mode 3 is that it requires virtually no **OUT**s and is therefore substantially faster for masking than the other write modes.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> If you're familiar with VGA programming, you're no doubt aware that
> everything that can be done with write mode 3 can also be accomplished
> in write mode 0 or write mode 2 by using the Bit Mask register. However,
> setting the Bit Mask register requires at least one **OUT** per byte
> written, in addition to the read and write of display memory, and
> **OUT**s are often slower than display memory accesses, especially on
> 386s and 486s. One of the great virtues of write mode 3 is that it
> requires virtually no **OUT**s and is therefore substantially faster for
> masking than the other write modes.
In short, write mode 3 is a good choice for single-color drawing that
modifies individual pixels within display memory bytes. Not

View file

@ -20,9 +20,21 @@ using write mode 3 because write mode 3 requires a read/write of display
memory (or at least preloading the latches with the background color),
while the write mode 0 approach requires only a write to display memory.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Is write mode 0 always the best way to do text? Not at all. The write mode 0 approach described above draws both foreground and background pixels within the character box, forcing the background pixels to black at the same time that it forces the foreground pixels to white. If you want to draw transparent text (that is, draw only the character pixels, not the surrounding background box), write mode 3 is ideal. Also, matters get far more complicated if characters that aren't 8 pixels wide are drawn, or if characters are drawn starting at arbitrary pixel locations, without the multiple-of-8 column restriction, so that rotation and masking are required. Lastly, the Map Mask register can be used to draw text in colors other than white—but only if the background is black. Otherwise, the data remaining in the planes protected by the Map Mask will remain and can interfere with the colors of the text being drawn.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Is write mode 0 always the best way to do text? Not at all. The write
> mode 0 approach described above draws both foreground and background
> pixels within the character box, forcing the background pixels to black
> at the same time that it forces the foreground pixels to white. If you
> want to draw transparent text (that is, draw only the character pixels,
> not the surrounding background box), write mode 3 is ideal. Also,
> matters get far more complicated if characters that aren't 8 pixels wide
> are drawn, or if characters are drawn starting at arbitrary pixel
> locations, without the multiple-of-8 column restriction, so that
> rotation and masking are required. Lastly, the Map Mask register can be
> used to draw text in colors other than white—but only if the background
> is black. Otherwise, the data remaining in the planes protected by the
> Map Mask will remain and can interfere with the colors of the text being
> drawn.
I'm not going to delve any deeper into the considerable issues of
drawing VGA text; I just want to sensitize you to the existence of

View file

@ -36,11 +36,12 @@ memory tends not to be as bad as VGA I/O, but lord knows it isn't
Table: Table 45.1 Results of I/O performance tests run under the Phar
Lap386|DOS-Extender.
* * * * *
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) ***OUT**s, in general, are lousy on the 486 (and to think they only took three cycles on the 286!). **OUT**s to VGAs are particularly lousy. Display memory performance is pretty poor, especially for reads. The conclusions are obvious, I would hope. Structure your graphics code, and, in general, all 486 code, to avoid **OUT**s.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> **OUT**s, in general, are lousy on the 486 (and to think they only took
> three cycles on the 286!). **OUT**s to VGAs are particularly lousy.
> Display memory performance is pretty poor, especially for reads. The
> conclusions are obvious, I would hope. Structure your graphics code,
> and, in general, all 486 code, to avoid **OUT**s.
For graphics, this especially means using write mode 3 rather than the
bit-mask register. When you must use the bit mask, arrange drawing so

View file

@ -22,9 +22,13 @@ bitmap into the system-memory buffer, then copy the buffer to the
screen, and the effect will be of having panned the window to the new
location.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Another argument in favor of a small viewing window is that it restricts the amount of display memory actually drawn to. Restricting the display memory used for animation reduces the total number of display-memory accesses, which in turn boosts overall performance; it also improves the performance and appearance of panning, in which the whole window has to be redrawn or copied.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Another argument in favor of a small viewing window is that it restricts
> the amount of display memory actually drawn to. Restricting the display
> memory used for animation reduces the total number of display-memory
> accesses, which in turn boosts overall performance; it also improves the
> performance and appearance of panning, in which the whole window has to
> be redrawn or copied.
If you keep a close watch, you'll notice that many high-performance
animation games similarly restrict their full-featured animation area to

View file

@ -10,9 +10,15 @@ chapter: '47'
pages: 887-889
---
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In general, performing plane-at-a-time operations can make almost any Mode X operation, at the worst, nearly as fast as the same operation in mode 13H (although this sort of Mode X programming is admittedly fairly complex). In this pursuit, it can help to organize data structures with Mode X in mind. For example, icons could be prearranged in system memory with the pixels organized into four plane-oriented sets (or, again, in four sets per scan line to avoid a fading-in effect) to facilitate copying to the screen a plane at a time with **REP MOVS***
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In general, performing plane-at-a-time operations can make almost any
> Mode X operation, at the worst, nearly as fast as the same operation in
> mode 13H (although this sort of Mode X programming is admittedly fairly
> complex). In this pursuit, it can help to organize data structures with
> Mode X in mind. For example, icons could be prearranged in system memory
> with the pixels organized into four plane-oriented sets (or, again, in
> four sets per scan line to avoid a fading-in effect) to facilitate
> copying to the screen a plane at a time with **REP MOVS**.
**LISTING 47.5 L47-5.ASM**

View file

@ -83,6 +83,10 @@ set the Read Map register to select a source plane and the Map Mask
register to select the corresponding destination plane. Then, copy all
pixels in that plane, repeating for all four planes.)
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Although copying through the latches is, in general, a speedy technique, especially on slower VGAs, it's not always a win. Reading video memory tends to be quite a bit slower than writing, and on a fast VLB or PCI adapter, it can be faster to copy from main memory to display memory than it is to copy from display memory to display memory via the latches.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Although copying through the latches is, in general, a speedy technique,
> especially on slower VGAs, it's not always a win. Reading video memory
> tends to be quite a bit slower than writing, and on a fast VLB or PCI
> adapter, it can be faster to copy from main memory to display memory
> than it is to copy from display memory to display memory via the
> latches.

View file

@ -129,6 +129,12 @@ in, and simply pass pointers to these structures to the low level,
rather than passing many separate parameters, as is now the case. I've
used separate parameters for simplicity and flexibility.
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Be aware that as nifty as Mode X hardware-assisted masked copying is, whether or not it's actually faster than software-only masked or transparent copying depends upon the processor and the video adapter. The advantage of Mode X masked copying is the 32-bit parallelism; the disadvantages are the need to read display memory and the need to perform an **OUT** for every four pixels. (**OUT** is a slow 486/Pentium instruction, and most VGAs respond to **OUT**s much more slowly than to display memory writes.)*
------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Be aware that as nifty as Mode X hardware-assisted masked copying is,
> whether or not it's actually faster than software-only masked or
> transparent copying depends upon the processor and the video adapter.
> The advantage of Mode X masked copying is the 32-bit parallelism; the
> disadvantages are the need to read display memory and the need to
> perform an **OUT** for every four pixels. (**OUT** is a slow 486/Pentium
> instruction, and most VGAs respond to **OUT**s much more slowly than to
> display memory writes.)

View file

@ -29,9 +29,11 @@ This involves perspective, shading, proper handling of hidden surfaces,
and rapid and smooth screen updates; the whole deal is considerably more
difficult to pull off on a PC than 2-D animation.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In some senses, however, 3-D animation is easier than 2-D. Because there's more going on in 3-D animation, the eye and brain tend to make more assumptions, and so are more apt to see what they expect to see, rather than what's actually there.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In some senses, however, 3-D animation is easier than 2-D. Because
> there's more going on in 3-D animation, the eye and brain tend to make
> more assumptions, and so are more apt to see what they expect to see,
> rather than what's actually there.
If you're piloting a (virtual) ship through a field of thousands of
asteroids at high speed, you're unlikely to notice if the more distant

View file

@ -25,9 +25,18 @@ need the complete X-Sharp library to see texture mapping in action, but
Listings 56.1 and 56.2 are the actual texture mapping code in its
entirety.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Here's a major tip: DDA texture mapping looks best on fast-moving surfaces, where the eye doesn't have time to pick nits with the shearing and aliasing that's an inevi table by-product of such a crude approach. Compile DEMO1 from the X-Sharp archive in this chapter's subdirectory of the listings disk, and run it. The initial display looks okay, but certainly not great, because the rotational speed is so slow. Now press the S key a few times to speed up the rotation and flip between different rotation axes. I think you'll be amazed at how much better DDA texture mapping looks at high speed. This technique would be great for mapping textures onto hurtling asteroids or jets, but would come up short for slow, finely detailed movements.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Here's a major tip: DDA texture mapping looks best on fast-moving
> surfaces, where the eye doesn't have time to pick nits with the shearing
> and aliasing that's an inevi table by-product of such a crude approach.
> Compile DEMO1 from the X-Sharp archive in this chapter's subdirectory of
> the listings disk, and run it. The initial display looks okay, but
> certainly not great, because the rotational speed is so slow. Now press
> the S key a few times to speed up the rotation and flip between
> different rotation axes. I think you'll be amazed at how much better DDA
> texture mapping looks at high speed. This technique would be great for
> mapping textures onto hurtling asteroids or jets, but would come up
> short for slow, finely detailed movements.
**LISTING 56.1 L56-1.C**

View file

@ -68,9 +68,13 @@ VGA, I found that the faster rotation was too fast! The ball spun so
rapidly that the eye couldn't blend successive images together into
continuous motion, much like watching a badly flickering movie.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *So the second lesson is that either too little or too much speed can destroy the illusion. Unless you're antialiasing, you need to tune the shifting of your images so that they're in the "sweet spot" of apparent motion, in which the eye is willing to ignore the jumping and aliasing, and blend the images together into continuous motion. Only experience can give you a feel for that sweet spot.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> So the second lesson is that either too little or too much speed can
> destroy the illusion. Unless you're antialiasing, you need to tune the
> shifting of your images so that they're in the "sweet spot" of apparent
> motion, in which the eye is willing to ignore the jumping and aliasing,
> and blend the images together into continuous motion. Only experience
> can give you a feel for that sweet spot.
#### Fixed-Point Arithmetic, Redux {#Heading4}

View file

@ -16,9 +16,19 @@ scanning it out left to right; it is only in conjunction with the
hardware organization of Mode X that the scanning direction matters in
the least.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *That's what Zen programming is all about, though; tying together two pieces of seemingly unrelated information to good effect—and that's what I had failed to do. Like Robert Heinlein—like all of us—I had viewed the world through a filter composed of my ingrained assumptions, and one of those assumptions, based on all my past experience, was that pixel processing proceeds left to right. Eventually, I might have come up with Chris's approach; but I would only have come up with it when and if I relaxed and stepped back a little, and allowed myself—almost dared myself—to think of it. When you're optimizing, be sure to leave quiet, nondirected time in which to conjure up those less obvious solutions, and periodically try to figure out what assumptions you're making—and then question them!*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> That's what Zen programming is all about, though; tying together two
> pieces of seemingly unrelated information to good effect—and that's what
> I had failed to do. Like Robert Heinlein—like all of us—I had viewed the
> world through a filter composed of my ingrained assumptions, and one of
> those assumptions, based on all my past experience, was that pixel
> processing proceeds left to right. Eventually, I might have come up with
> Chris's approach; but I would only have come up with it when and if I
> relaxed and stepped back a little, and allowed myself—almost dared
> myself—to think of it. When you're optimizing, be sure to leave quiet,
> nondirected time in which to conjure up those less obvious solutions,
> and periodically try to figure out what assumptions you're making—and
> then question them!
![**Figure 58.3**  *Texture mapping a single vertical column.*](images/58-03.jpg)

View file

@ -130,6 +130,10 @@ of the left and right vertices indicates which way the wall is facing.
}
}
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Be aware that BSP trees can often be made smaller and more efficient by detecting collinear surfaces (like aligned wall segments) and generating only one BSP node for each collinear set, with the collinear surfaces stored in, say, a linked list attached to that node. Collinear surfaces partition space identically and can't occlude one another, so it suffices to generate one splitting node for each collinear set.*
------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Be aware that BSP trees can often be made smaller and more efficient by
> detecting collinear surfaces (like aligned wall segments) and generating
> only one BSP node for each collinear set, with the collinear surfaces
> stored in, say, a linked list attached to that node. Collinear surfaces
> partition space identically and can't occlude one another, so it
> suffices to generate one splitting node for each collinear set.

View file

@ -32,10 +32,29 @@ they are unfamiliar, they abandon the perfectly good model they used
before and try to rederive it in the new context by the seat of their
pants.
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Here's a secret when you're faced with a situation like this: Step back and get a clear picture of what your code has to do. Omit no steps. You should build a model that is so consistent and solid that you can instantly answer any question about how the code should behave in any situation. For example, my interviewees often decide, by trial and error, that there are two distinct types of right children: Right children visited after popping back to visit a node after the left subtree has been visited, and right children visited after descending to a node that has no left child. This makes the traversal code a mass of special cases, each of which has to be detected by the programmer by trying out scenarios. Worse, you can never be sure with this approach that you've caught all the special cases.*
*The alternative is to develop and apply a unifying model. There aren't really two types of right children; the rule is that all right children are visited after their parents are visited, period. The presence or absence of a left child is irrelevant. The possibility that a right child may be reached via different code paths depending on the presence of a left child does not affect the overall model. While this distinction may seem trivial it is in fact crucial, because if you have the model down cold, you can always tell if the implementation is correct by comparing it with the model.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Here's a secret when you're faced with a situation like this: Step back
> and get a clear picture of what your code has to do. Omit no steps. You
> should build a model that is so consistent and solid that you can
> instantly answer any question about how the code should behave in any
> situation. For example, my interviewees often decide, by trial and
> error, that there are two distinct types of right children: Right
> children visited after popping back to visit a node after the left
> subtree has been visited, and right children visited after descending to
> a node that has no left child. This makes the traversal code a mass of
> special cases, each of which has to be detected by the programmer by
> trying out scenarios. Worse, you can never be sure with this approach
> that you've caught all the special cases.
>
> The alternative is to develop and apply a unifying model. There aren't
> really two types of right children; the rule is that all right children
> are visited after their parents are visited, period. The presence or
> absence of a left child is irrelevant. The possibility that a right
> child may be reached via different code paths depending on the presence
> of a left child does not affect the overall model. While this
> distinction may seem trivial it is in fact crucial, because if you have
> the model down cold, you can always tell if the implementation is
> correct by comparing it with the model.
#### Measure and Learn {#Heading10}

View file

@ -40,9 +40,13 @@ vectors that lie within the surface. This means that we can generate the
screenspace normals we need by taking the cross product of two adjacent
polygon edges, as shown in Figure 61.4.
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In fact, we can cull with only one-third the work needed to generate a full cross product; because we're interested only in the sign of the z component of the normal, we can skip entirely calculating the x and y components. The only caveat is to be careful that neither edge you choose is zero-length and that the edges aren't collinear, because the dot product can't produce a normal in those cases.*
------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In fact, we can cull with only one-third the work needed to generate a
> full cross product; because we're interested only in the sign of the z
> component of the normal, we can skip entirely calculating the x and y
> components. The only caveat is to be careful that neither edge you
> choose is zero-length and that the edges aren't collinear, because the
> dot product can't produce a normal in those cases.
![**Figure 61.4**  *How the cross product of polygon edge vectors
generates a polygon normal.*](images/61-04.jpg)

View file

@ -106,9 +106,16 @@ Figure 61.8. In 3-D, this involves three dot products per point, one to
project the point onto each axis. Translation can be done separately
from rotation by simple addition.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Rotation by projection is exactly the same as rotation via matrix multiplication; in fact, the rows of a rotation matrix are the orthogonal unit vectors pointing along the new axes. Rotation by projection buys us no technical advantages, so that's not what's important here; the key is that the concept of rotation by projection, together with a separate translation step, gives us a new way to look at transformation that I, for one, find easier to visualize and experiment with. A new frame of reference for how we think about 3-D frames of reference, if you will.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> Rotation by projection is exactly the same as rotation via matrix
> multiplication; in fact, the rows of a rotation matrix are the
> orthogonal unit vectors pointing along the new axes. Rotation by
> projection buys us no technical advantages, so that's not what's
> important here; the key is that the concept of rotation by projection,
> together with a separate translation step, gives us a new way to look at
> transformation that I, for one, find easier to visualize and experiment
> with. A new frame of reference for how we think about 3-D frames of
> reference, if you will.
Three things I've learned over the years are that it never hurts to
learn a new way of looking at things, that it helps to have a clearer,

View file

@ -61,9 +61,15 @@ work yet, so I didn't waste any time due to my faulty assumption that
should have done experiments until I was sure I knew what was going on
before drawing any conclusions and acting on them.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *In general, make it a point not to fall into a tightly focused rut; stay loose and think of alternative possibilities and new approaches, and always, always, always keep asking questions. It'll pay off big in the long run. If I hadn't indulged my curiosity by running the Pentium counter test on the copy to the screen, even though there was no specific reason to do so, I would never have discovered the **memcpy()** problem—and by so doing I doubled the performance of the entire program in five minutes, a rare accomplishment indeed.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> ![](images/i.jpg)
> In general, make it a point not to fall into a tightly focused rut; stay
> loose and think of alternative possibilities and new approaches, and
> always, always, always keep asking questions. It'll pay off big in the
> long run. If I hadn't indulged my curiosity by running the Pentium
> counter test on the copy to the screen, even though there was no
> specific reason to do so, I would never have discovered the **memcpy()**
> problem—and by so doing I doubled the performance of the entire program
> in five minutes, a rare accomplishment indeed.
By the way, I have found the Pentium's performance counters to be very
useful in of information on the performance counters and other aspects

Some files were not shown because too many files have changed in this diff Show more