Remove heading ids, let pandoc generate them
This commit is contained in:
parent
498fdab210
commit
8b63f1f1a5
75 changed files with 631 additions and 631 deletions
2
about.md
2
about.md
|
|
@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel
|
|||
Graphics and Multimedia Development'
|
||||
---
|
||||
|
||||
# Foreword {#Heading1}
|
||||
# Foreword
|
||||
|
||||
I got my start programming on Apple II computers at school, and almost
|
||||
all of my early work was on the Apple platform. After graduating, it
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel
|
|||
Graphics and Multimedia Development'
|
||||
---
|
||||
|
||||
# Acknowledgments {#Heading1}
|
||||
# Acknowledgments
|
||||
|
||||
There are many people to thank—because this book was written over many
|
||||
years, in many different settings, an unusually large number of people
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel
|
|||
Graphics and Multimedia Development'
|
||||
---
|
||||
|
||||
# Afterword {#Heading1}
|
||||
# Afterword
|
||||
|
||||
If you've followed me this far, you might agree that we've come through
|
||||
some rough country. Still, I'm of the opinion that hard-won knowledge is
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel
|
|||
Graphics and Multimedia Development'
|
||||
---
|
||||
|
||||
# Index {#Heading1}
|
||||
# Index
|
||||
|
||||
`Numbers`
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ pages: 004-019
|
|||
# Part I
|
||||
|
||||
## Chapter 1\
|
||||
The Best Optimizer Is between Your Ears {#Heading1}
|
||||
The Best Optimizer Is between Your Ears
|
||||
|
||||
### The Human Element of Code Optimization {#Heading2}
|
||||
### The Human Element of Code Optimization
|
||||
|
||||
This book is devoted to a topic near and dear to my heart: writing
|
||||
software that pushes PCs to the limit. Given run-of-the-mill software,
|
||||
|
|
@ -51,7 +51,7 @@ this book, we're going to work some of those wonders, starting...
|
|||
|
||||
...now.
|
||||
|
||||
### Understanding High Performance {#Heading3}
|
||||
### Understanding High Performance
|
||||
|
||||
Before we can create high-performance code, we must understand what high
|
||||
performance is. The objective (not always attained) in creating
|
||||
|
|
@ -93,7 +93,7 @@ touch, however.
|
|||
"What's a fast slow program?" you ask. That's a good question, and a
|
||||
brief (true) story is perhaps the best answer.
|
||||
|
||||
#### When Fast Isn't Fast {#Heading4}
|
||||
#### When Fast Isn't Fast
|
||||
|
||||
In the early 1970s, as the first hand-held calculators were hitting the
|
||||
market, I knew a fellow named Irwin. He was a good student, and was
|
||||
|
|
@ -123,7 +123,7 @@ grand scheme of things—and they scarcely matter at all unless they're
|
|||
used in the context of a good design and a thorough understanding of
|
||||
both the task at hand and the PC.
|
||||
|
||||
### Rules for Building High-Performance Code {#Heading5}
|
||||
### Rules for Building High-Performance Code
|
||||
|
||||
We've got the following rules for creating high-performance software:
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ the real world. For my money, examining some actual working code is
|
|||
always a good way to get a handle on programming concepts, so let's look
|
||||
at some of the performance rules in action.
|
||||
|
||||
#### Know Where You're Going {#Heading6}
|
||||
#### Know Where You're Going
|
||||
|
||||
If we're going to create high-performance code, first we have to know
|
||||
what that code is going to do. As an example, let's write a program that
|
||||
|
|
@ -161,7 +161,7 @@ with the checksum value other than print it out, however; right now
|
|||
we're only interested in generating that checksum value as rapidly as
|
||||
possible.
|
||||
|
||||
#### Make a Big Map {#Heading7}
|
||||
#### Make a Big Map
|
||||
|
||||
How are we going to generate a checksum value for a specified file? The
|
||||
logical approach is to get the file name, open the file, read the bytes
|
||||
|
|
@ -169,7 +169,7 @@ out of the file, add them together, and print the result. Most of those
|
|||
actions are straightforward; the only tricky part lies in reading the
|
||||
bytes and adding them together.
|
||||
|
||||
#### Make Lots of Little Maps {#Heading8}
|
||||
#### Make Lots of Little Maps
|
||||
|
||||
Actually, we're only going to make one little map, because we only have
|
||||
one program section that requires much thought—the section that reads
|
||||
|
|
@ -390,7 +390,7 @@ design, optimization just creates fast slow code.
|
|||
Well, then, how are we going to improve our design? Before we can do
|
||||
that, we have to understand what's wrong with the current design.
|
||||
|
||||
#### Know the Territory {#Heading9}
|
||||
#### Know the Territory
|
||||
|
||||
Just why is Listing 1.1 so slow? In a word: overhead. The C library
|
||||
implements the `read()` function by calling DOS to read the desired
|
||||
|
|
@ -481,7 +481,7 @@ main(int argc, char *argv[]) {
|
|||
}
|
||||
```
|
||||
|
||||
#### Know When It Matters {#Heading10}
|
||||
#### Know When It Matters
|
||||
|
||||
The last section contained a particularly interesting phrase: *the
|
||||
time-critical portions of your code*. Time-critical portions of your
|
||||
|
|
@ -514,7 +514,7 @@ 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
|
||||
approaches.
|
||||
|
||||
#### Always Consider the Alternatives {#Heading11}
|
||||
#### Always Consider the Alternatives
|
||||
|
||||
Listing 1.4 is good, but let's see if there are other—perhaps less
|
||||
obvious—ways to get the same results faster. Let's start by considering
|
||||
|
|
@ -658,7 +658,7 @@ At any rate, Listing 1.5 isn't much more complicated than Listing
|
|||
1.4—and it's a *lot* faster. Always consider the alternatives; a bit of
|
||||
clever thinking and program redesign can go a long way.
|
||||
|
||||
#### Know How to Turn On the Juice {#Heading12}
|
||||
#### Know How to Turn On the Juice
|
||||
|
||||
I have said time and again that optimization is pointless until the
|
||||
design is settled. When that time comes, however, optimization can
|
||||
|
|
@ -808,7 +808,7 @@ specific case.
|
|||
All this is basically a way of saying: Know where you're going, know the
|
||||
territory, and know when it matters.
|
||||
|
||||
### Where We've Been, What We've Seen {#Heading13}
|
||||
### Where We've Been, What We've Seen
|
||||
|
||||
What have we learned? Don't let other people's code—even DOS—do the work
|
||||
for you when speed matters, at least not without knowing what that code
|
||||
|
|
@ -825,7 +825,7 @@ light of Table 1.1, does it? Your organic optimizer matters much more
|
|||
than your compiler's optimizer, and there's always assembly for those
|
||||
usually small sections of code where performance really matters.
|
||||
|
||||
#### Where We're Going {#Heading14}
|
||||
#### Where We're Going
|
||||
|
||||
This chapter has presented a quick step-by-step overview of the design
|
||||
process. I'm not claiming that this is the only way to create
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 021-030
|
|||
---
|
||||
|
||||
## Chapter 2\
|
||||
A World Apart {#Heading1}
|
||||
A World Apart
|
||||
|
||||
### The Unique Nature of Assembly Language Optimization {#Heading2}
|
||||
### The Unique Nature of Assembly Language Optimization
|
||||
|
||||
As I showed in the previous chapter, optimization is by no means always
|
||||
a matter of "dropping into assembly." In fact, in performance tuning
|
||||
|
|
@ -33,7 +33,7 @@ assembly specific dynamics.
|
|||
|
||||
As usual, the best way to wade in is to present a real-world example.
|
||||
|
||||
### Instructions: The Individual versus the Collective {#Heading3}
|
||||
### Instructions: The Individual versus the Collective
|
||||
|
||||
Some time ago, I was asked to work over a critical assembly subroutine
|
||||
in order to make it run as fast as possible. The task of the subroutine
|
||||
|
|
@ -97,7 +97,7 @@ fastest...and more. You must also learn to look at your programming
|
|||
problems from a variety of perspectives so that you can put those fast
|
||||
instructions to work in the most effective ways.
|
||||
|
||||
### Assembly Is Fundamentally Different {#Heading4}
|
||||
### Assembly Is Fundamentally Different
|
||||
|
||||
Is it really so hard as all that to write good assembly code for the PC?
|
||||
Yes! Thanks to the decidedly quirky nature of the x86 family CPUs,
|
||||
|
|
@ -115,7 +115,7 @@ implements a routine to search a list of 100,000 sorted items with a
|
|||
linear rather than binary search will end up with a disappointingly slow
|
||||
program.
|
||||
|
||||
#### Transformation Inefficiencies {#Heading5}
|
||||
#### Transformation Inefficiencies
|
||||
|
||||
No matter how well an implementation is derived from the corresponding
|
||||
design, however, high-level languages like C/C++ and Pascal inevitably
|
||||
|
|
@ -166,7 +166,7 @@ specification to machine language entirely on his or her own. (The
|
|||
assembler merely handles the *direct* translation from assembly to
|
||||
machine language.)
|
||||
|
||||
#### Self-Reliance {#Heading6}
|
||||
#### Self-Reliance
|
||||
|
||||
The first part of assembly language optimization, then, is self. An
|
||||
assembler is nothing more than a tool to let you design machine-language
|
||||
|
|
@ -181,7 +181,7 @@ High-level languages handle most of this transparently to the
|
|||
programmer, but in assembly everything is fair—and necessary—game, which
|
||||
brings us to another aspect of assembly optimization: knowledge.
|
||||
|
||||
#### Knowledge {#Heading7}
|
||||
#### Knowledge
|
||||
|
||||
In the PC world, you can never have enough knowledge, and every item you
|
||||
add to your store will make your programs better. Thorough familiarity
|
||||
|
|
@ -214,7 +214,7 @@ out such knowledge.
|
|||
> Be forewarned, though: No matter how much you learn about programming
|
||||
> the PC in assembly, there's always more to discover.
|
||||
|
||||
### The Flexible Mind {#Heading8}
|
||||
### The Flexible Mind
|
||||
|
||||
Is the never-ending collection of information all there is to the
|
||||
assembly optimization, then? Hardly. Knowledge is simply a necessary
|
||||
|
|
@ -316,7 +316,7 @@ the context of a solid overall framework unique to each program, and the
|
|||
flexible mind is the key to creating that framework and holding it
|
||||
together.
|
||||
|
||||
#### Where to Begin? {#Heading9}
|
||||
#### Where to Begin?
|
||||
|
||||
To summarize, the skill of assembly language optimization is a
|
||||
combination of knowledge, perspective, and a way of thought that makes
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 031-073
|
|||
---
|
||||
|
||||
## Chapter 3\
|
||||
Assume Nothing {#Heading1}
|
||||
Assume Nothing
|
||||
|
||||
### Understanding and Using the Zen Timer {#Heading2}
|
||||
### Understanding and Using the Zen Timer
|
||||
|
||||
When you're pushing the envelope in writing optimized PC code, you're
|
||||
likely to become more than a little compulsive about finding approaches
|
||||
|
|
@ -40,7 +40,7 @@ with the optimized version of the routine....
|
|||
|
||||
It ran slower than the original version!
|
||||
|
||||
### The Costs of Ignorance {#Heading3}
|
||||
### The Costs of Ignorance
|
||||
|
||||
As diligent as the author had been, he had nonetheless committed a
|
||||
cardinal sin of x86 assembly language programming: He had assumed that
|
||||
|
|
@ -94,7 +94,7 @@ video wait states as well, so the code they discussed was actually
|
|||
have been to run the code to see if snow resulted, since the only true
|
||||
measure of code performance is observing it in action.
|
||||
|
||||
### The Zen Timer {#Heading4}
|
||||
### The Zen Timer
|
||||
|
||||
Clearly, one key to mastering Zen-class optimization is a tool with
|
||||
which to measure code performance. The most accurate way to measure
|
||||
|
|
@ -558,7 +558,7 @@ Code ends
|
|||
end
|
||||
```
|
||||
|
||||
#### The Zen Timer Is a Means, Not an End {#Heading5}
|
||||
#### The Zen Timer Is a Means, Not an End
|
||||
|
||||
We're going to spend the rest of this chapter seeing what the Zen timer
|
||||
can do, examining how it works, and learning how to use it. I'll be
|
||||
|
|
@ -573,7 +573,7 @@ Consequently, you shouldn't worry if you don't fully grasp the inner
|
|||
workings of the Zen timer. Instead, focus on learning how to *use* it,
|
||||
and you'll be on the right road.
|
||||
|
||||
#### Starting the Zen Timer {#Heading6}
|
||||
#### Starting the Zen Timer
|
||||
|
||||
`ZTimerOn` is called at the start of a segment of code to be timed.
|
||||
`ZTimerOn` saves the context of the calling code, disables interrupts,
|
||||
|
|
@ -596,7 +596,7 @@ any hardware interrupts to occur during the interval between any call to
|
|||
`ZTimerOn` and the corresponding call to `ZTimerOff`, and should not
|
||||
enable interrupts during that time.
|
||||
|
||||
### Time and the PC {#Heading7}
|
||||
### Time and the PC
|
||||
|
||||
A second interesting point about `ZTimerOn` is that it may introduce
|
||||
some small inaccuracy into the system clock time whenever it is called.
|
||||
|
|
@ -742,7 +742,7 @@ Nonetheless, it's a good idea to reboot your computer at the end of each
|
|||
session with the Zen timer in order to make sure that the system clock
|
||||
is correct.
|
||||
|
||||
### Stopping the Zen Timer {#Heading8}
|
||||
### Stopping the Zen Timer
|
||||
|
||||
At some point after `ZTimerOn` is called, `ZTimerOff` must always be
|
||||
called to mark the end of the timing interval. `ZTimerOff` saves the
|
||||
|
|
@ -774,7 +774,7 @@ this chapter, though, we'll see that timer 0 can be stopped after all.)
|
|||
We simply tell the 8253 to latch the current count, and the 8253 does so
|
||||
without breaking stride.
|
||||
|
||||
### Reporting Timing Results {#Heading9}
|
||||
### Reporting Timing Results
|
||||
|
||||
`ZTimerReport` may be called to display timing results at any time
|
||||
after both `ZTimerOn` and `ZTimerOff` have been called.
|
||||
|
|
@ -829,7 +829,7 @@ You may well want to devise still other approaches better suited to your
|
|||
needs than those I've presented. Go to it! I've just thrown out a few
|
||||
possibilities to get you started.
|
||||
|
||||
### Notes on the Zen Timer {#Heading10}
|
||||
### Notes on the Zen Timer
|
||||
|
||||
The Zen timer subroutines are designed to be near-called from assembly
|
||||
language code running in the public segment `Code`. The Zen timer
|
||||
|
|
@ -890,7 +890,7 @@ useful—quite the contrary. The Zen timer is an excellent tool for
|
|||
evaluating code performance over the entire spectrum of PC-compatible
|
||||
computers.
|
||||
|
||||
### A Sample Use of the Zen Timer {#Heading11}
|
||||
### A Sample Use of the Zen Timer
|
||||
|
||||
Listing 3.2 shows a test-bed program for measuring code performance with
|
||||
the Zen timer. This program sets DS equal to CS (for reasons we'll
|
||||
|
|
@ -1114,7 +1114,7 @@ the test-bed program of Listing 3.2, simply insert calls to `ZTimerOn,
|
|||
ZTimerOff`, and `ZTimerReport` in the appropriate places and link
|
||||
PZTIMER to your program.
|
||||
|
||||
### The Long-Period Zen Timer {#Heading12}
|
||||
### The Long-Period Zen Timer
|
||||
|
||||
With a few exceptions, the Zen timer presented above will serve us well
|
||||
for the remainder of this book since we'll be focusing on relatively
|
||||
|
|
@ -1171,7 +1171,7 @@ major inaccuracy into the system clock time during a single timing run
|
|||
since it leaves interrupts enabled and therefore allows the system clock
|
||||
to update normally.
|
||||
|
||||
#### Stopping the Clock {#Heading13}
|
||||
#### Stopping the Clock
|
||||
|
||||
There's a potential problem with the long-period Zen timer. The problem
|
||||
is this: In order to measure times longer than 54 ms, we must maintain
|
||||
|
|
@ -1897,7 +1897,7 @@ Finally, please note that the *precision* Zen timer works perfectly well
|
|||
on both PS/2 and non-PS/2 computers. The PS/2 and 8253 considerations
|
||||
we've just discussed apply *only* to the longZen timer.
|
||||
|
||||
### Example Use of the Long-Period Zen Timer {#Heading14}
|
||||
### Example Use of the Long-Period Zen Timer
|
||||
|
||||
The long-period Zen timer has exactly the same calling interface as the
|
||||
precision Zen timer, and can be used in place of the precision Zen timer
|
||||
|
|
@ -2113,7 +2113,7 @@ PC if you are using MASM, with most of that time spent assembling
|
|||
Listing 3.8. Why? Because MASM is notoriously slow at assembling
|
||||
`REPT` blocks, and the block in Listing 3.8 is repeated 20,000 times.
|
||||
|
||||
### Using the Zen Timer from C {#Heading15}
|
||||
### Using the Zen Timer from C
|
||||
|
||||
The Zen timer can be used to measure code performance when programming
|
||||
in C—but not right out of the box. As presented earlier, the timer is
|
||||
|
|
@ -2169,7 +2169,7 @@ precision timer, but the long-period timer is very similar.
|
|||
The full listings for the C-callable Zen timers are presented in Chapter
|
||||
K on the companion CD-ROM.
|
||||
|
||||
#### Watch Out for Optimizing Assemblers! {#Heading16}
|
||||
#### Watch Out for Optimizing Assemblers!
|
||||
|
||||
One important safety tip when modifying the Zen timer for use with large
|
||||
code model C code: Watch out for optimizing assemblers! TASM actually
|
||||
|
|
@ -2221,7 +2221,7 @@ I've tested the changes shown in Figures 3.2 and 3.3 with TASM and
|
|||
Borland C++ 4.0, and also with the latest MASM and Microsoft C/C++
|
||||
compiler.
|
||||
|
||||
#### Further Reading {#Heading17}
|
||||
#### Further Reading
|
||||
|
||||
For those of you who wish to pursue the mechanics of code measurement
|
||||
further, one good article about measuring code performance with the 8253
|
||||
|
|
@ -2242,7 +2242,7 @@ how the Zen timer works. All you really need to know is what the Zen
|
|||
timer can do and how to use it, and we've accomplished that in this
|
||||
chapter.
|
||||
|
||||
#### Armed with the Zen Timer, Onward and Upward {#Heading18}
|
||||
#### Armed with the Zen Timer, Onward and Upward
|
||||
|
||||
The Zen timer is not perfect. For one thing, the finest resolution to
|
||||
which it can measure an interval is at best about 1µs, a period of time
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 075-109
|
|||
---
|
||||
|
||||
## Chapter 4\
|
||||
In the Lair of the Cycle-Eaters {#Heading1}
|
||||
In the Lair of the Cycle-Eaters
|
||||
|
||||
### How the PC Hardware Devours Code Performance {#Heading2}
|
||||
### How the PC Hardware Devours Code Performance
|
||||
|
||||
This chapter, adapted from my earlier book, *Zen of Assembly Language*
|
||||
located on the companion CD-ROM, goes right to the heart of my
|
||||
|
|
@ -47,7 +47,7 @@ changed over time, but do take the time to at least skim through this
|
|||
chapter to give yourself a good start on the material in the rest of
|
||||
this book.
|
||||
|
||||
### Cycle-Eaters {#Heading3}
|
||||
### Cycle-Eaters
|
||||
|
||||
Programming has many levels, ranging from the familiar (high-level
|
||||
languages, DOS calls, and the like) down to the esoteric things that lie
|
||||
|
|
@ -81,7 +81,7 @@ properly improve the performance of our code.
|
|||
|
||||
Which brings us to cycle-eaters.
|
||||
|
||||
### The Nature of Cycle-Eaters {#Heading4}
|
||||
### The Nature of Cycle-Eaters
|
||||
|
||||
Cycle-eaters are gremlins that live on the bus or in peripherals (and
|
||||
sometimes within the CPU itself), slowing the performance of PC code so
|
||||
|
|
@ -101,7 +101,7 @@ understand the simplest among them, those that haunted the original
|
|||
the newer generation of cycle-eaters in terms of those ancestral
|
||||
cycle-eaters—but we have to get the groundwork down first.
|
||||
|
||||
#### The 8088's Ancestral Cycle-Eaters {#Heading5}
|
||||
#### The 8088's Ancestral Cycle-Eaters
|
||||
|
||||
Internally, the 8088 is a 16-bit processor, capable of running at full
|
||||
speed at all times—unless external data is required. External data must
|
||||
|
|
@ -131,7 +131,7 @@ but do read it all even if the going gets a bit tough. Cycle-eaters play
|
|||
a key role in later chapters, so some familiarity with them is highly
|
||||
desirable.
|
||||
|
||||
### The 8-Bit Bus Cycle-Eater {#Heading6}
|
||||
### The 8-Bit Bus Cycle-Eater
|
||||
|
||||
*Look! Down on the motherboard! It's a 16-bit processor! It's an 8-bit
|
||||
processor! It's...*
|
||||
|
|
@ -194,7 +194,7 @@ a full 16-bit word at a time. The 386SX can process 32 bits (a
|
|||
doubleword) at a time, however, and loses a lot of time fetching that
|
||||
doubleword from memory in two halves.
|
||||
|
||||
#### The Impact of the 8-Bit Bus Cycle-Eater {#Heading7}
|
||||
#### The Impact of the 8-Bit Bus Cycle-Eater
|
||||
|
||||
One obvious effect of the 8-bit bus cycle-eater is that word-sized
|
||||
accesses to memory operands on the 8088 take 4 cycles longer than
|
||||
|
|
@ -255,7 +255,7 @@ operands. More ominously, as we will see shortly, the 8-bit bus
|
|||
cycle-eater can cause performance problems with other sorts of code as
|
||||
well.
|
||||
|
||||
#### What to Do about the 8-Bit Bus Cycle-Eater? {#Heading8}
|
||||
#### What to Do about the 8-Bit Bus Cycle-Eater?
|
||||
|
||||
The obvious implication of the 8-bit bus cycle-eater is that byte-sized
|
||||
memory variables should be used whenever possible. After all, the 8088
|
||||
|
|
@ -426,7 +426,7 @@ and there's one sort of word-sized memory access we haven't discussed
|
|||
yet: instruction fetching. The ugliest manifestation of the 8-bit bus
|
||||
cycle-eater is in fact the prefetch queue cycle-eater.
|
||||
|
||||
### The Prefetch Queue Cycle-Eater {#Heading9}
|
||||
### The Prefetch Queue Cycle-Eater
|
||||
|
||||
In an 8088 context, here's the prefetch queue cycle-eater in a nutshell:
|
||||
The 8088's 8-bit external data bus keeps the Bus Interface Unit from
|
||||
|
|
@ -492,7 +492,7 @@ external data bus, that's a glaring omission—but, alas, an unavoidable
|
|||
one. Let's look at why the official execution times are wrong, and why
|
||||
that can't be helped.
|
||||
|
||||
#### Official Execution Times Are Only Part of the Story {#Heading10}
|
||||
#### Official Execution Times Are Only Part of the Story
|
||||
|
||||
The sequence of 5 `SHR` instructions in the last example is 10 bytes
|
||||
long. That means that it can never execute in less than 24 cycles even
|
||||
|
|
@ -538,7 +538,7 @@ wrong, and why Intel can't provide better specifications. You also know
|
|||
now why it is that you must time your code if you want to know how fast
|
||||
it really is.
|
||||
|
||||
#### There Is No Such Beast as a True Instruction Execution Time {#Heading11}
|
||||
#### There Is No Such Beast as a True Instruction Execution Time
|
||||
|
||||
The effect of the code preceding an instruction on the execution time of
|
||||
that instruction makes the Zen timer trickier to use than you might
|
||||
|
|
@ -738,7 +738,7 @@ executes on the PC just by looking at it! Get used to the idea that
|
|||
execution times are only meaningful in context, learn the rules of thumb
|
||||
in this book, and use the Zen timer to measure your code.
|
||||
|
||||
#### Approximating Overall Execution Times {#Heading12}
|
||||
#### Approximating Overall Execution Times
|
||||
|
||||
Don't think that because overall instruction execution time is
|
||||
determined by both instruction fetch time and Execution Unit execution
|
||||
|
|
@ -767,7 +767,7 @@ instruction in a particular context to start when the first byte of the
|
|||
instruction is sent to the Execution Unit and end when the first byte of
|
||||
the next instruction is sent to the EU.
|
||||
|
||||
#### What to Do about the Prefetch Queue Cycle-Eater? {#Heading13}
|
||||
#### What to Do about the Prefetch Queue Cycle-Eater?
|
||||
|
||||
Reducing the impact of the prefetch queue cycle-eater is one of the
|
||||
overriding principles of high-performance assembly code. How can you do
|
||||
|
|
@ -809,7 +809,7 @@ then time your code to see how fast it really is. You should experiment
|
|||
freely, but always remember that actual, measured performance is the
|
||||
bottom line.
|
||||
|
||||
#### Holding Up the 8088 {#Heading14}
|
||||
#### Holding Up the 8088
|
||||
|
||||
In this chapter I've taken you further and further into the depths of
|
||||
the PC, telling you again and again that you must understand the
|
||||
|
|
@ -849,7 +849,7 @@ were programming in ignorance.
|
|||
Let's start with DRAM refresh, which affects the performance of every
|
||||
program that runs on the PC.
|
||||
|
||||
### Dynamic RAM Refresh: The Invisible Hand {#Heading15}
|
||||
### Dynamic RAM Refresh: The Invisible Hand
|
||||
|
||||
Dynamic RAM (DRAM) refresh is sort of an act of God. By that I mean that
|
||||
DRAM refresh invisibly and inexorably steals a certain fraction of all
|
||||
|
|
@ -881,7 +881,7 @@ within 4 µs of the last refresh. Since there's no guarantee that a given
|
|||
program will access each and every DRAM block once every 4 µs, the PC
|
||||
contains special circuitry and programming for providing DRAM refresh.
|
||||
|
||||
#### How DRAM Refresh Works in the PC {#Heading16}
|
||||
#### How DRAM Refresh Works in the PC
|
||||
|
||||
On the original 8088-based IBM PC, timer 1 of the 8253 timer chip is
|
||||
programmed at power-up to generate a signal once every 72 cycles, or
|
||||
|
|
@ -920,7 +920,7 @@ memory.
|
|||
|
||||

|
||||
|
||||
#### The Impact of DRAM Refresh {#Heading17}
|
||||
#### The Impact of DRAM Refresh
|
||||
|
||||
Let's look at examples from opposite ends of the spectrum in terms of
|
||||
the impact of DRAM refresh on code performance. First, consider the
|
||||
|
|
@ -1005,7 +1005,7 @@ ranging as high as 8.33 percent—is far more likely to occur. This is
|
|||
especially true for high-performance assembly code, which uses fast
|
||||
instructions that tend to cause non-stop instruction fetching.
|
||||
|
||||
#### What to Do About the DRAM Refresh Cycle-Eater? {#Heading18}
|
||||
#### What to Do About the DRAM Refresh Cycle-Eater?
|
||||
|
||||
*Hmmm.* When we discovered the prefetch queue cycle-eater, we learned to
|
||||
use short instructions. When we discovered the 8-bit bus cycle-eater, we
|
||||
|
|
@ -1059,7 +1059,7 @@ two timing results that differ less or more than they seemingly should,
|
|||
that's usually DRAM refresh too. Thanks to DRAM refresh, variations of
|
||||
up to 8.33 percent in PC code performance are par for the course.
|
||||
|
||||
### Wait States {#Heading19}
|
||||
### Wait States
|
||||
|
||||
Wait states are cycles during which a bus access by the CPU to a device
|
||||
on the PC's bus is temporarily halted by that device while the device
|
||||
|
|
@ -1121,7 +1121,7 @@ the PC. While any adapter *can* insert wait states, in the PC only
|
|||
display adapters do so to the extent that performance is seriously
|
||||
affected.
|
||||
|
||||
### The Display Adapter Cycle-Eater {#Heading20}
|
||||
### The Display Adapter Cycle-Eater
|
||||
|
||||
Display adapters must serve two masters, and that creates a fundamental
|
||||
performance problem. Master \#1 is the circuitry that drives the display
|
||||
|
|
@ -1212,7 +1212,7 @@ the foreseeable future, and since it is the hardest graphics adapter to
|
|||
wring performance from, we'll restrict our discussion to the VGA (and
|
||||
its close relative, the EGA) for the remainder of this chapter.
|
||||
|
||||
#### The Impact of the Display Adapter Cycle-Eater {#Heading21}
|
||||
#### The Impact of the Display Adapter Cycle-Eater
|
||||
|
||||
Even on the EGA and VGA, the effect of the display adapter cycle-eater
|
||||
depends on the display mode selected. In text mode, the display adapter
|
||||
|
|
@ -1366,7 +1366,7 @@ PC. Remember, the limited speed of access to a graphics adapter is an
|
|||
inherent characteristic of the adapter, so the fastest computer around
|
||||
can't access display memory one iota faster than the adapter will allow.
|
||||
|
||||
#### What to Do about the Display Adapter Cycle-Eater? {#Heading22}
|
||||
#### What to Do about the Display Adapter Cycle-Eater?
|
||||
|
||||
What can we do about the display adapter cycle-eater? Well, we can
|
||||
minimize display memory accesses whenever possible. In particular, we
|
||||
|
|
@ -1422,7 +1422,7 @@ significant. There is only one way to discover just how significant the
|
|||
impact of the display adapter cycle-eater is for any particular graphics
|
||||
code, and that is of course to measure the performance of that code.
|
||||
|
||||
#### Cycle-Eaters: A Summary {#Heading23}
|
||||
#### Cycle-Eaters: A Summary
|
||||
|
||||
We've covered a great deal of sophisticated material in this chapter, so
|
||||
don't feel bad if you haven't understood everything you've read; it will
|
||||
|
|
@ -1446,7 +1446,7 @@ This basic knowledge about cycle-eaters puts you in a good position to
|
|||
understand the results reported by the Zen timer, and that means that
|
||||
you're well on your way to writing high-performance assembler code.
|
||||
|
||||
#### What Does It All Mean? {#Heading24}
|
||||
#### What Does It All Mean?
|
||||
|
||||
There you have it: life under the programming interface. It's not a
|
||||
particularly pretty picture for the inhabitants of that strange realm
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 111-123
|
|||
---
|
||||
|
||||
## Chapter 5\
|
||||
Crossing the Border {#Heading1}
|
||||
Crossing the Border
|
||||
|
||||
### Searching Files with Restartable Blocks {#Heading2}
|
||||
### Searching Files with Restartable Blocks
|
||||
|
||||
*We just moved.* Those three little words should strike terror into the
|
||||
heart of anyone who owns more than a sleeping bag and a toothbrush. Our
|
||||
|
|
@ -66,7 +66,7 @@ more effort and forethought, but would have paid off handsomely.
|
|||
And with that, let's look at a fairly complex application of restartable
|
||||
blocks.
|
||||
|
||||
#### Searching for Text {#Heading3}
|
||||
#### Searching for Text
|
||||
|
||||
The application we're going to examine searches a file for a specified
|
||||
string. We'll develop a program that will search the file specified on
|
||||
|
|
@ -112,7 +112,7 @@ all-important inner loop of our searching program, where the program
|
|||
will spend virtually all of its time (aside from the unavoidable disk
|
||||
access overhead).
|
||||
|
||||
### Avoiding the String Trap {#Heading4}
|
||||
### Avoiding the String Trap
|
||||
|
||||
The easiest approach would be to use a C/C++ library function. The
|
||||
closest match to what we need is `strstr()`, which searches one string
|
||||
|
|
@ -136,7 +136,7 @@ for our application, from unnecessary overhead.
|
|||
> it, and relate that to their performance in the context you're
|
||||
> interested in.
|
||||
|
||||
### Brute-Force Techniques {#Heading5}
|
||||
### Brute-Force Techniques
|
||||
|
||||
Given that no C/C++ library function meets our needs precisely, an
|
||||
obvious alternative approach is the brute-force technique that uses
|
||||
|
|
@ -169,7 +169,7 @@ match for the first character, stopping to check for a match with the
|
|||
rest of the string *only* when the first character matches, as shown in
|
||||
Figure 5.2.
|
||||
|
||||
### Using memchr() {#Heading6}
|
||||
### Using memchr()
|
||||
|
||||
There's yet a better way to implement this approach, however. Use the
|
||||
`memchr()` function, which does nothing more or less than find the
|
||||
|
|
@ -201,7 +201,7 @@ Now that we've selected a searching approach, let's integrate it with
|
|||
file handling and searching through multiple blocks. In other words,
|
||||
let's make it restartable.
|
||||
|
||||
#### Making a Search Restartable {#Heading7}
|
||||
#### Making a Search Restartable
|
||||
|
||||
As it happens, there's no great trick to putting the pieces of this
|
||||
search program together. Basically, we'll read in a buffer of data
|
||||
|
|
@ -407,7 +407,7 @@ main(int argc, char *argv[]) {
|
|||
}
|
||||
```
|
||||
|
||||
### Interpreting Where the Cycles Go {#Heading8}
|
||||
### Interpreting Where the Cycles Go
|
||||
|
||||
To boost the overall performance of Listing 5.1, I would normally
|
||||
convert `SearchForString()` to assembly language at this point.
|
||||
|
|
@ -461,7 +461,7 @@ work.
|
|||
|
||||
Not likely.
|
||||
|
||||
#### Knowing When Assembly Is Pointless {#Heading9}
|
||||
#### Knowing When Assembly Is Pointless
|
||||
|
||||
So that's why we're not going to go to assembly language in this
|
||||
example—which is not to say it would never be worth converting the
|
||||
|
|
@ -516,7 +516,7 @@ search engine is by no means fully optimized, it's nonetheless as fast
|
|||
as there's any reason for it to be, given the balance of performance
|
||||
among the components of this program.
|
||||
|
||||
### Always Look Where Execution Is Going {#Heading10}
|
||||
### Always Look Where Execution Is Going
|
||||
|
||||
I've explained two important lessons: Know when it's worth optimizing
|
||||
further, and use restartable blocks to process large data sets as a
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 125-133
|
|||
---
|
||||
|
||||
## Chapter 6\
|
||||
Looking Past Face Value {#Heading1}
|
||||
Looking Past Face Value
|
||||
|
||||
### How Machine Instructions May Do More Than You Think {#Heading2}
|
||||
### How Machine Instructions May Do More Than You Think
|
||||
|
||||
I first met Jeff Duntemann at an authors' dinner hosted by *PC Tech
|
||||
Journal* at Fall Comdex, back in 1985. Jeff was already reasonably
|
||||
|
|
@ -95,7 +95,7 @@ disadvantage every time you sit down to program.
|
|||
In short, the x86 family can do much more than you think—if you'll use
|
||||
everything it has to offer. Give it a shot!
|
||||
|
||||
#### Memory Addressing and Arithmetic {#Heading3}
|
||||
#### Memory Addressing and Arithmetic
|
||||
|
||||
Years ago, I saw a clip on the David Letterman show in which Letterman
|
||||
walked into a store by the name of "Just Lamps" and asked, "So what do
|
||||
|
|
@ -169,7 +169,7 @@ calculations are free on the Pentium, however. I'll discuss 486
|
|||
performance issues in Chapters 12 and 13, and the Pentium in Chapters 19
|
||||
through 21.
|
||||
|
||||
### Math via Memory Addressing {#Heading4}
|
||||
### Math via Memory Addressing
|
||||
|
||||
You're probably not particularly wowed to hear that you can use
|
||||
addressing modes to perform memory addressing arithmetic that would
|
||||
|
|
@ -235,7 +235,7 @@ and Pentium, `LEA` can also be slowed down by addressing interlocks.
|
|||
|
||||

|
||||
|
||||
#### The Wonders of LEA on the 386 {#Heading5}
|
||||
#### The Wonders of LEA on the 386
|
||||
|
||||
`LEA` really comes into its own as a "super-ADD" instruction on the
|
||||
386, 486, and Pentium, where it can take advantage of the enhanced
|
||||
|
|
@ -282,7 +282,7 @@ add edi,offset TableBase
|
|||
|
||||
when pointing to an entry in a doubly indexed table.
|
||||
|
||||
### Multiplication with LEA Using Non-Powers of Two {#Heading6}
|
||||
### Multiplication with LEA Using Non-Powers of Two
|
||||
|
||||
Are you impressed yet with all that `LEA` can do on the 386? Believe
|
||||
it or not, one more feature still awaits us. `LEA` can actually
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 136-148
|
|||
---
|
||||
|
||||
## Chapter 7\
|
||||
Local Optimization {#Heading1}
|
||||
Local Optimization
|
||||
|
||||
### Optimizing Halfway between Algorithms and Cycle Counting {#Heading2}
|
||||
### Optimizing Halfway between Algorithms and Cycle Counting
|
||||
|
||||
You might not think it, but there's much to learn about performance
|
||||
programming from the Great Buffalo Sauna Fiasco. To wit:
|
||||
|
|
@ -88,7 +88,7 @@ possible given the capabilities of the x86 family instruction set.
|
|||
And yes, in case you're wondering, the above story is indeed true. Was I
|
||||
there? Let me put it this way: If I were, I'd never admit it!
|
||||
|
||||
#### When LOOP Is a Bad Idea {#Heading3}
|
||||
#### When LOOP Is a Bad Idea
|
||||
|
||||
Let's examine first an instruction that is less than it appears to be:
|
||||
`LOOP`. There's no mystery about what `LOOP` does; it decrements CX
|
||||
|
|
@ -144,7 +144,7 @@ jz SkipLoop ;If field is 0, don't bother
|
|||
will do just fine and is faster on all processors. Use `JCXZ` only
|
||||
when the Zero flag isn't already set to reflect the status of CX.
|
||||
|
||||
### The Lessons of LOOP and JCXZ {#Heading4}
|
||||
### The Lessons of LOOP and JCXZ
|
||||
|
||||
What can we learn from `LOOP` and `JCXZ`? First, that a single
|
||||
instruction that is intended to do a complex task is not necessarily
|
||||
|
|
@ -167,7 +167,7 @@ published cycle times are closer to actual execution times on the 386
|
|||
and 486 than on the 8088, and are reasonably reliable indicators of the
|
||||
relative performance levels of x86 instructions.
|
||||
|
||||
#### Avoiding LOOPS of Any Stripe {#Heading5}
|
||||
#### Avoiding LOOPS of Any Stripe
|
||||
|
||||
Cycle counting and directly substituting instructions (`DEC CX/JNZ`
|
||||
for `LOOP`, for example) are techniques that belong at the lowest
|
||||
|
|
@ -180,7 +180,7 @@ caught up in counting cycles because that's a small (albeit important)
|
|||
part of the optimization picture, and not the area in which your
|
||||
greatest advantage lies.
|
||||
|
||||
### Local Optimization {#Heading6}
|
||||
### Local Optimization
|
||||
|
||||
One level at which assembly language programming pays off handsomely is
|
||||
that of *local optimization;* that is, selecting the best *sequence* of
|
||||
|
|
@ -327,7 +327,7 @@ SearchMaxLengthendp
|
|||
end Start
|
||||
```
|
||||
|
||||
### Unrolling Loops {#Heading7}
|
||||
### Unrolling Loops
|
||||
|
||||
Listing 7.2 takes a different tack, unrolling the loop so that four
|
||||
bytes are checked for each `LOOP` performed. The same instructions are
|
||||
|
|
@ -490,7 +490,7 @@ avenues.
|
|||
> as building blocks with unique characteristics rather than as
|
||||
> instructions dedicated to specific tasks.
|
||||
|
||||
#### Rotating and Shifting with Tables {#Heading8}
|
||||
#### Rotating and Shifting with Tables
|
||||
|
||||
As another example of local optimization, consider the matter of
|
||||
rotating or shifting a mask into position. First, let's look at the
|
||||
|
|
@ -558,7 +558,7 @@ BIT_PATTERN=BIT_PATTERN SHL 1
|
|||
> optimization rule: Move as much work as possible out of your critical
|
||||
> code by whatever means necessary.
|
||||
|
||||
#### NOT Flips Bits—Not Flags {#Heading9}
|
||||
#### NOT Flips Bits—Not Flags
|
||||
|
||||
The `NOT` instruction flips all the bits in the operand, from 0 to 1
|
||||
or from 1 to 0. That's as simple as could be, but `NOT` nonetheless
|
||||
|
|
@ -586,7 +586,7 @@ and which flags are set, for example—can be critical when you're trying
|
|||
to optimize a code sequence and you're running out of registers, or when
|
||||
you're trying to minimize branching.
|
||||
|
||||
#### Incrementing with and without Carry {#Heading10}
|
||||
#### Incrementing with and without Carry
|
||||
|
||||
Another case in which there are two slightly different ways to perform a
|
||||
task involves adding 1 to an operand. You can do this with `INC`, as
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 149-166
|
|||
---
|
||||
|
||||
## Chapter 8\
|
||||
Speeding Up C with Assembly Language {#Heading1}
|
||||
Speeding Up C with Assembly Language
|
||||
|
||||
### Jumping Languages When You Know It'll Help {#Heading2}
|
||||
### Jumping Languages When You Know It'll Help
|
||||
|
||||
When I was a senior in high school, a pop song called "Seasons in the
|
||||
Sun," sung by one Terry Jacks, soared up the pop charts and spent, as
|
||||
|
|
@ -59,7 +59,7 @@ language output and tweaking it.
|
|||
|
||||
Apropos of which, when was the last time you heard of Terry Jacks?
|
||||
|
||||
#### Billy, Don't Be a Compiler {#Heading3}
|
||||
#### Billy, Don't Be a Compiler
|
||||
|
||||
The key to optimizing C programs with assembly language is, as always,
|
||||
writing good assembly language code, but with an added twist. Rule 1
|
||||
|
|
@ -99,7 +99,7 @@ assembly language optimization.
|
|||
> future changes and debugging more difficult, slowing you down and
|
||||
> limiting your options.
|
||||
|
||||
### Don't Call Your Functions on Me, Baby {#Heading4}
|
||||
### Don't Call Your Functions on Me, Baby
|
||||
|
||||
In order to think differently from a compiler, you must understand both
|
||||
what compilers and C programmers tend to do and how that differs from
|
||||
|
|
@ -130,7 +130,7 @@ extra cycles they take don't affect performance, then the code they're
|
|||
in probably isn't critical, and perhaps you've chosen to convert too
|
||||
much code to assembly, eh?
|
||||
|
||||
### Stack Frames Slow So Much {#Heading5}
|
||||
### Stack Frames Slow So Much
|
||||
|
||||
C compilers work within the stack frame model, whereby variables reside
|
||||
in a block of stack memory and are accessed via offsets from BP.
|
||||
|
|
@ -149,7 +149,7 @@ sometimes useful indeed.
|
|||
That doesn't mean you shouldn't use stack frames, which are useful and
|
||||
often necessary. Just don't fall victim to their undeniable charms.
|
||||
|
||||
### Torn Between Two Segments {#Heading6}
|
||||
### Torn Between Two Segments
|
||||
|
||||
C compilers are not terrific at handling segments. Some compilers can
|
||||
efficiently handle a single far pointer used in a loop by leaving ES set
|
||||
|
|
@ -166,7 +166,7 @@ address to be reloaded each time either pointer is used.
|
|||
In assembly language you have full control over segments. Use it, and,
|
||||
if necessary, reorganize your code to minimize segment loading.
|
||||
|
||||
#### Why Speeding Up Is Hard to Do {#Heading7}
|
||||
#### Why Speeding Up Is Hard to Do
|
||||
|
||||
You might think that the most obvious advantage assembly language has
|
||||
over C is that it allows the use of all forms of instructions and all
|
||||
|
|
@ -219,7 +219,7 @@ jz Match
|
|||
It's a simple example—but, I hope, a convincing one. Stretch your brain
|
||||
when you optimize.
|
||||
|
||||
### Taking It to the Limit {#Heading8}
|
||||
### Taking It to the Limit
|
||||
|
||||
The ultimate in assembly language optimization comes when you change the
|
||||
rules; that is, when you reorganize the entire program to allow the use
|
||||
|
|
@ -260,7 +260,7 @@ code into optimized assembly language:
|
|||
|
||||
That said, let me show some of these precepts in action.
|
||||
|
||||
#### A C-to-Assembly Case Study {#Heading9}
|
||||
#### A C-to-Assembly Case Study
|
||||
|
||||
Listing 8.1 is the sample C application I'm going to use to examine
|
||||
optimization in action. Listing 8.1 isn't really complete—it doesn't
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 167-188
|
|||
---
|
||||
|
||||
## Chapter 9\
|
||||
Hints My Readers Gave Me {#Heading1}
|
||||
Hints My Readers Gave Me
|
||||
|
||||
### Optimization Odds and Ends from the Field {#Heading2}
|
||||
### Optimization Odds and Ends from the Field
|
||||
|
||||
Back in high school, I took a pre-calculus class from Mr. Bourgeis,
|
||||
whose most notable characteristics were incessant pacing and truly
|
||||
|
|
@ -76,7 +76,7 @@ and many readers have sent me a slew of those over the years. So in this
|
|||
chapter, I think I'll return the favor by devoting a chapter to reader
|
||||
feedback.
|
||||
|
||||
#### Another Look at LEA {#Heading3}
|
||||
#### Another Look at LEA
|
||||
|
||||
Several people have pointed out that while `LEA` is great for
|
||||
performing certain additions (see Chapter 6), it isn't a perfect
|
||||
|
|
@ -136,7 +136,7 @@ on what you're trying to do.
|
|||
|
||||
But there sure are a lot of interesting options, aren't there?
|
||||
|
||||
#### The Kennedy Portfolio {#Heading4}
|
||||
#### The Kennedy Portfolio
|
||||
|
||||
Reader John Kennedy regularly passes along intriguing assembly
|
||||
programming tricks, many of which I've never seen mentioned anywhere
|
||||
|
|
@ -247,7 +247,7 @@ SHL AX,2 ;*64
|
|||
ADD AX,BX ;*80
|
||||
```
|
||||
|
||||
#### Speeding Up Multiplication {#Heading5}
|
||||
#### Speeding Up Multiplication
|
||||
|
||||
That brings us to multiplication, one of the slowest of x86 operations
|
||||
and one that allows for considerable optimization. One way to speed up
|
||||
|
|
@ -302,7 +302,7 @@ that's always in the range of, say, 2 to 10; because the scale value
|
|||
will always be small and the array elements may have any value, the
|
||||
scale value is the logical choice for the multiplier.
|
||||
|
||||
#### Optimizing Optimized Searching {#Heading6}
|
||||
#### Optimizing Optimized Searching
|
||||
|
||||
Rob Williams writes with a wonderful optimization to the `REPNZ
|
||||
SCASB`-based optimized searching routine I discussed in Chapter 5. As a
|
||||
|
|
@ -617,7 +617,7 @@ the difference lies not in elbow grease or cycle counting but in the
|
|||
organic integrating optimizer technology we all carry around in our
|
||||
heads.
|
||||
|
||||
#### Short Sorts {#Heading7}
|
||||
#### Short Sorts
|
||||
|
||||
David Stafford (recently of Borland and Borland Japan) who happens to be
|
||||
one of the best assembly language programmers I've ever met, has written
|
||||
|
|
@ -666,7 +666,7 @@ _sort: pop dx ;get return address (entry point)
|
|||
end
|
||||
```
|
||||
|
||||
#### Full 32-Bit Division {#Heading8}
|
||||
#### Full 32-Bit Division
|
||||
|
||||
One of the most annoying limitations of the x86 is that while the
|
||||
dividend operand to the `DIV` instruction can be 32 bits in size, both
|
||||
|
|
@ -802,7 +802,7 @@ main() {
|
|||
}
|
||||
```
|
||||
|
||||
#### Sweet Spot Revisited {#Heading9}
|
||||
#### Sweet Spot Revisited
|
||||
|
||||
Way back in Volume 1, Number 1 of *PC TECHNIQUES*, (April/May 1990) I
|
||||
wrote the very first of that magazine's HAX (\#1), which extolled the
|
||||
|
|
@ -844,7 +844,7 @@ a large one) by several K—not bad, when you consider that the "sweet
|
|||
spot" optimization is essentially free, with no code reorganization,
|
||||
change in logic, or heavy thinking involved.
|
||||
|
||||
#### Hard-Core Cycle Counting {#Heading10}
|
||||
#### Hard-Core Cycle Counting
|
||||
|
||||
Next, we come to an item that cycle counters will love, especially since
|
||||
it involves apparently incorrect documentation on Intel's part.
|
||||
|
|
@ -876,7 +876,7 @@ No great lesson here, just a caution to be leery of multibit `RCR` and
|
|||
`RCL` when performance matters—and to take cycle-time documentation
|
||||
with a grain of salt.
|
||||
|
||||
#### Hardwired Far Jumps {#Heading11}
|
||||
#### Hardwired Far Jumps
|
||||
|
||||
Did you ever wonder how to code a far jump to an absolute address in
|
||||
assembly language? Probably not, but if you ever do, you're going to be
|
||||
|
|
@ -949,7 +949,7 @@ If the obvious doesn't work (and it usually doesn't), just try
|
|||
everything you can think of, no matter how ridiculous, until you find
|
||||
something that does—a rule with plenty of history on its side.
|
||||
|
||||
#### Setting 32-Bit Registers: Time versus Space {#Heading12}
|
||||
#### Setting 32-Bit Registers: Time versus Space
|
||||
|
||||
To finish up this chapter, consider these two items. First, in 32-bit
|
||||
protected mode,
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 190-203
|
|||
---
|
||||
|
||||
## Chapter 10\
|
||||
Patient Coding, Faster Code {#Heading1}
|
||||
Patient Coding, Faster Code
|
||||
|
||||
### How Working Quickly Can Bring Execution to a Crawl {#Heading2}
|
||||
### How Working Quickly Can Bring Execution to a Crawl
|
||||
|
||||
My grandfather does *The New York Times* crossword puzzle every Sunday.
|
||||
In ink. With nary a blemish.
|
||||
|
|
@ -84,7 +84,7 @@ will be much better—and you'll never even have the chance to decide
|
|||
whether they're better or not if you take the first thing that comes
|
||||
into your head and run with it.
|
||||
|
||||
#### The Case for Delayed Gratification {#Heading3}
|
||||
#### The Case for Delayed Gratification
|
||||
|
||||
Once upon a time, I set out to read *Algorithms*, by Robert Sedgewick
|
||||
(Addison-Wesley), which turned out to be a wonderful, stimulating, and
|
||||
|
|
@ -110,7 +110,7 @@ The problem at hand, then, is simply this: Find the largest integer
|
|||
value that evenly divides two arbitrary positive integers. That's all
|
||||
there is to it. So warm up your pattern matchers...and go!
|
||||
|
||||
### The Brute-Force Syndrome {#Heading4}
|
||||
### The Brute-Force Syndrome
|
||||
|
||||
I have a funny feeling that you'd already figured out how to find the
|
||||
GCD before I even said "go." That's what I did when reading
|
||||
|
|
@ -217,7 +217,7 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
}
|
||||
```
|
||||
|
||||
#### Wasted Breakthroughs {#Heading5}
|
||||
#### Wasted Breakthroughs
|
||||
|
||||
Sedgewick's first solution to the GCD problem was pretty much the one I
|
||||
came up with. He then pointed out that the GCD of iL and iS is the same
|
||||
|
|
@ -364,7 +364,7 @@ your ears does its best work not at the implementation stage, but at the
|
|||
very beginning, when you try to imagine how what you want to do and what
|
||||
a computer is capable of doing can best be brought together.
|
||||
|
||||
### Recursion {#Heading6}
|
||||
### Recursion
|
||||
|
||||
Euclid's algorithm lends itself to recursion beautifully, so much so
|
||||
that an implementation like Listing 10.3 comes almost without thought.
|
||||
|
|
@ -417,7 +417,7 @@ unsigned int gcd(unsigned int int1, unsigned int int2) {
|
|||
}
|
||||
```
|
||||
|
||||
#### Patient Optimization {#Heading7}
|
||||
#### Patient Optimization
|
||||
|
||||
At long last, we're ready to optimize GCD determination in the classic
|
||||
sense. Table 10.1 shows the performance of Listing 10.4 with and without
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 205-231
|
|||
---
|
||||
|
||||
## Chapter 11\
|
||||
Pushing the 286 and 386 {#Heading1}
|
||||
Pushing the 286 and 386
|
||||
|
||||
### New Registers, New Instructions, New Timings, New Complications {#Heading2}
|
||||
### New Registers, New Instructions, New Timings, New Complications
|
||||
|
||||
This chapter, adapted from my earlier book *Zen of Assembly Language*
|
||||
(1989; now out of print), provides an overview of the 286 and 386, often
|
||||
|
|
@ -40,7 +40,7 @@ mainstream of computing, this chapter is a useful mix of history lesson,
|
|||
x86 overview, and details on two workhorse processors that are still in
|
||||
wide use.
|
||||
|
||||
#### Family Matters {#Heading3}
|
||||
#### Family Matters
|
||||
|
||||
While the x86 family is a large one, only a few members of the
|
||||
family—which includes the 8088, 8086, 80188, 80186, 286, 386SX, 386DX,
|
||||
|
|
@ -80,7 +80,7 @@ its lifespan, and it is in even wider use than the 286. The future
|
|||
clearly belongs to the 486 and Pentium, but the 286 and 386 are still
|
||||
very much a part of the present-day landscape.
|
||||
|
||||
#### Crossing the Gulf to the 286 and the 386 {#Heading4}
|
||||
#### Crossing the Gulf to the 286 and the 386
|
||||
|
||||
Apart from vastly improved performance, the biggest difference between
|
||||
the 8088 and the 286 and 386 (as well as the later Intel CPUs) is that
|
||||
|
|
@ -135,7 +135,7 @@ optimization strategies discussed in this book still hold true in
|
|||
protected mode; it's just issues specific to protected mode or a
|
||||
particular operating system that we won't discuss.
|
||||
|
||||
#### In the Lair of the Cycle-Eaters, Part II {#Heading5}
|
||||
#### In the Lair of the Cycle-Eaters, Part II
|
||||
|
||||
Under the programming interface, the 286 and 386 differ considerably
|
||||
from the 8088. Nonetheless, with one exception and one addition, the
|
||||
|
|
@ -447,7 +447,7 @@ can literally double the performance of certain code running on the 286.
|
|||
Even if it doesn't double performance, word alignment usually helps and
|
||||
never hurts.
|
||||
|
||||
#### Code Alignment {#Heading8}
|
||||
#### Code Alignment
|
||||
|
||||
Lack of word alignment can also interfere with instruction fetching on
|
||||
the 286, although not to the extent that it interferes with access to
|
||||
|
|
@ -698,7 +698,7 @@ What can we do about this new, more virulent form of the display adapter
|
|||
cycle-eater? The workaround is the same as it was on the PC: Access
|
||||
display memory as little as you possibly can.
|
||||
|
||||
#### New Instructions and Features: The 286 {#Heading13}
|
||||
#### New Instructions and Features: The 286
|
||||
|
||||
The 286 and 386 offer a number of new instructions. The 286 has a
|
||||
relatively small number of instructions that the 8088 lacks, while the
|
||||
|
|
@ -731,7 +731,7 @@ For another, the 286 allows all shifts and rotates to be performed for
|
|||
not just 1 bit or the number of bits specified by CL, but for *any*
|
||||
constant number of bits.
|
||||
|
||||
#### New Instructions and Features: The 386 {#Heading14}
|
||||
#### New Instructions and Features: The 386
|
||||
|
||||
The 386 is somewhat more complex than the 286 regarding new features.
|
||||
Once again, we won't discuss protected mode, which on the 386 comes with
|
||||
|
|
@ -941,7 +941,7 @@ the like.
|
|||
|
||||
The more things change, the more they remain the same....
|
||||
|
||||
#### POPF and the 286 {#Heading17}
|
||||
#### POPF and the 286
|
||||
|
||||
We've one final 286-related item to discuss: the hardware malfunction of
|
||||
`POPF` under certain circumstances on the 286.
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 233-246
|
|||
---
|
||||
|
||||
## Chapter 12\
|
||||
Pushing the 486 {#Heading1}
|
||||
Pushing the 486
|
||||
|
||||
### It's Not Just a Bigger 386 {#Heading2}
|
||||
### It's Not Just a Bigger 386
|
||||
|
||||
So this traveling salesman is walking down a road, and he sees a group
|
||||
of men digging a ditch with their bare hands. "Whoa, there!" he says.
|
||||
|
|
@ -45,7 +45,7 @@ register-to-register `MOV`s, Dorothy was heard to exclaim (before she
|
|||
sank out of sight in a swirl of hopelessly mixed metaphors), "I don't
|
||||
think we're in Kansas anymore, Toto."
|
||||
|
||||
#### Enter the 486 {#Heading3}
|
||||
#### Enter the 486
|
||||
|
||||
No chip that is a direct, fully compatible descendant of the 8088, 286,
|
||||
and 386 could ever be called a RISC chip, but the 486 certainly contains
|
||||
|
|
@ -72,7 +72,7 @@ Intel; "8086 Optimization: Aim Down the Middle and Pray," in the March,
|
|||
1991 *Dr. Dobb's Journal*; and "Peak Performance: On to the 486," in the
|
||||
November, 1990 *Programmer's Journal.*
|
||||
|
||||
### Rules to Optimize By {#Heading4}
|
||||
### Rules to Optimize By
|
||||
|
||||
In Appendix G of the *i486 Microprocessor Programmer*'*s* *Reference
|
||||
Manual*, Intel lists a number of optimization techniques for the 486.
|
||||
|
|
@ -102,7 +102,7 @@ the rules, documented and undocumented, that go into calculating actual
|
|||
execution times—and uncovering some of those rules is exactly what this
|
||||
chapter is about.
|
||||
|
||||
#### The Hazards of Indexed Addressing {#Heading5}
|
||||
#### The Hazards of Indexed Addressing
|
||||
|
||||
Rule \#1: Avoid indexed addressing (that is, try not to use either two
|
||||
registers or scaled addressing to point to memory).
|
||||
|
|
@ -176,7 +176,7 @@ makes the *whole loop* more than 14 percent faster.
|
|||
|
||||
In a key loop on the 486, 1 cycle can indeed matter.
|
||||
|
||||
#### Calculate Memory Pointers Ahead of Time {#Heading6}
|
||||
#### Calculate Memory Pointers Ahead of Time
|
||||
|
||||
Rule \#2: Don't use a register as a memory pointer during the next two
|
||||
cycles after loading it.
|
||||
|
|
@ -295,7 +295,7 @@ rearrangement of 486 code.
|
|||
|
||||

|
||||
|
||||
### Caveat Programmor {#Heading7}
|
||||
### Caveat Programmor
|
||||
|
||||
A caution: I'm quite certain that the 2-cycle-ahead addressing pipeline
|
||||
interruption penalty I've described exists in the two 486s I've tested.
|
||||
|
|
@ -319,7 +319,7 @@ undocumented optimizations, please write and let me know. And, of
|
|||
course, if anyone from Intel is reading this and wants to give us the
|
||||
gospel truth, please do!
|
||||
|
||||
#### Stack Addressing and Address Pipelining {#Heading8}
|
||||
#### Stack Addressing and Address Pipelining
|
||||
|
||||
Rule \#2A: Rule \#2 sometimes, but not always, applies to the stack
|
||||
pointer when it is implicitly used to point to memory.
|
||||
|
|
@ -396,7 +396,7 @@ from the stack pointer should ideally be done at least two cycles before
|
|||
`PUSH`, `POP`, `RET`, or any other instruction that uses the stack
|
||||
pointer to address memory.
|
||||
|
||||
#### Problems with Byte Registers {#Heading9}
|
||||
#### Problems with Byte Registers
|
||||
|
||||
There are two ways to lose cycles by using byte registers, and neither
|
||||
of them is documented by Intel, so far as I know. Let's start with the
|
||||
|
|
@ -479,7 +479,7 @@ you're a diehard ASMhead who does this stuff for fun. Just learn enough
|
|||
to be able to speed up the key portions of your programs, and spend the
|
||||
rest of your time on a fast design and overall implementation.
|
||||
|
||||
#### More Fun with Byte Registers {#Heading10}
|
||||
#### More Fun with Byte Registers
|
||||
|
||||
Rule \#4: Don't load *any* byte register exactly 2 cycles before using
|
||||
*any* register to address memory.
|
||||
|
|
@ -552,7 +552,7 @@ can affect.
|
|||
> 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}
|
||||
#### Timing Your Own 486 Code
|
||||
|
||||
In case you want to do some 486 performance analysis of your own, let me
|
||||
show you how I arrived at one of the above conclusions; at the same
|
||||
|
|
@ -624,7 +624,7 @@ bytes.
|
|||
> 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}
|
||||
### The Story Continues
|
||||
|
||||
There's certainly plenty more 486 lore to explore, including the 486's
|
||||
unique prefetch queue, more optimization rules, branching optimizations,
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 248-258
|
|||
---
|
||||
|
||||
## Chapter 13\
|
||||
Aiming the 486 {#Heading1}
|
||||
Aiming the 486
|
||||
|
||||
### Pipelines and Other Hazards of the High End {#Heading2}
|
||||
### Pipelines and Other Hazards of the High End
|
||||
|
||||
It's a sad but true fact that 84 percent of American schoolchildren are
|
||||
ignorant of 92 percent of American history. Not my daughter, though. We
|
||||
|
|
@ -58,7 +58,7 @@ For example, consider how Terje Mathisen doubled the speed of his
|
|||
word-counting program on a 486 simply by shuffling a couple of
|
||||
instructions.
|
||||
|
||||
#### 486 Pipeline Optimization {#Heading3}
|
||||
#### 486 Pipeline Optimization
|
||||
|
||||
I've mentioned Terje Mathisen in my writings before. Terje is an
|
||||
assembly language programmer extraordinaire, and author of the
|
||||
|
|
@ -167,7 +167,7 @@ engine can process more than 16 million characters *per second* on a
|
|||
|
||||
Clever 486 optimization can pay off big. QED.
|
||||
|
||||
### BSWAP: More Useful Than You Might Think {#Heading4}
|
||||
### BSWAP: More Useful Than You Might Think
|
||||
|
||||
There are only 3 non-system instructions unique to the 486. None is
|
||||
earthshaking, but they have their uses. Consider `BSWAP`. `BSWAP` does
|
||||
|
|
@ -268,7 +268,7 @@ looptop:
|
|||
jnz looptop
|
||||
```
|
||||
|
||||
### Pushing and Popping Memory {#Heading5}
|
||||
### Pushing and Popping Memory
|
||||
|
||||
Pushing or popping a memory location, as in `PUSH WORD PTR [BX]` or
|
||||
`POP [MemVar]`, is a compact, easy way to get a value onto or off of
|
||||
|
|
@ -315,7 +315,7 @@ well as `XLAT`, `LOOP`, and, of course, `PUSH *mem*` and `POP
|
|||
> pipeline efficiency, as is the case with Terje's optimization described
|
||||
> earlier in this chapter.
|
||||
|
||||
### Optimal 1-Bit Shifts and Rotates {#Heading6}
|
||||
### Optimal 1-Bit Shifts and Rotates
|
||||
|
||||
On a 486, the n-bit forms of the shift and rotate instructions—as in
|
||||
`ROR AX,2` and `SHL BX,9`—are 2-cycle instructions, but the 1-bit
|
||||
|
|
@ -357,7 +357,7 @@ critical cycles—and Lord knows that if you're optimizing for the
|
|||
unoptimized code on a 486—you almost certainly need all the speed you
|
||||
can get.
|
||||
|
||||
### 32-Bit Addressing Modes {#Heading7}
|
||||
### 32-Bit Addressing Modes
|
||||
|
||||
The 386 and 486 both support 32-bit addressing modes, in which any
|
||||
register may serve as the base memory addressing register, and almost
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 260-277
|
|||
---
|
||||
|
||||
## Chapter 14\
|
||||
Boyer-Moore String Searching {#Heading1}
|
||||
Boyer-Moore String Searching
|
||||
|
||||
### Optimizing a Pretty Optimum Search Algorithm {#Heading2}
|
||||
### Optimizing a Pretty Optimum Search Algorithm
|
||||
|
||||
When you seem to be stumped, stop for a minute and *think.* All the
|
||||
information you need may be right in front of your nose if you just look
|
||||
|
|
@ -61,7 +61,7 @@ said, "Hey! Did you guys put in a new floor?"
|
|||
As I said, sometimes everything you need to know is right in front of
|
||||
your nose. Which brings us to Boyer-Moore string searching.
|
||||
|
||||
### String Searching Refresher {#Heading3}
|
||||
### String Searching Refresher
|
||||
|
||||
I've discussed string searching earlier in this book, in Chapters 5 and
|
||||
9. You may want to refer back to these chapters for some background on
|
||||
|
|
@ -132,7 +132,7 @@ matches. Can we?
|
|||
|
||||
Actually, yes, we can.
|
||||
|
||||
### The Boyer-Moore Algorithm {#Heading4}
|
||||
### The Boyer-Moore Algorithm
|
||||
|
||||
All our *a priori* knowledge of string searching is stated above, but
|
||||
there's another sort of knowledge—knowledge that's generated
|
||||
|
|
@ -228,7 +228,7 @@ is to it!
|
|||
|
||||

|
||||
|
||||
### Boyer-Moore: The Good and the Bad {#Heading5}
|
||||
### Boyer-Moore: The Good and the Bad
|
||||
|
||||
The worst case for this version of Boyer-Moore is that the pattern
|
||||
mismatches on the leftmost character—the last character compared—every
|
||||
|
|
@ -627,7 +627,7 @@ thereby getting the best of both worlds.)
|
|||
Know your data and use your smarts. Don't stop thinking just because
|
||||
you're implementing a big-name algorithm; you know more than it does.
|
||||
|
||||
### Further Optimization of Boyer-Moore {#Heading6}
|
||||
### Further Optimization of Boyer-Moore
|
||||
|
||||
We can do substantially better yet than Listing 14.3 if we're willing to
|
||||
accept tighter limits on the data. Limiting the length of the
|
||||
|
|
@ -797,7 +797,7 @@ all but the first search when repeatedly searching for a particular
|
|||
pattern, by building the skip table externally and passing a pointer to
|
||||
it as a parameter.
|
||||
|
||||
### Know What You Know {#Heading7}
|
||||
### Know What You Know
|
||||
|
||||
Here we've turned up our nose at a repeated string instruction, we've
|
||||
gone against the grain by comparing backward, and yet we've speeded up
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 279-293
|
|||
---
|
||||
|
||||
## Chapter 15\
|
||||
Linked Lists and plain Unintended Challenges {#Heading1}
|
||||
Linked Lists and plain Unintended Challenges
|
||||
|
||||
### Unfamiliar Problems with Familiar Data Structures {#Heading2}
|
||||
### Unfamiliar Problems with Familiar Data Structures
|
||||
|
||||
After 21 years, this story still makes me wince. Oh, the humiliations I
|
||||
suffer for your enlightenment....
|
||||
|
|
@ -76,7 +76,7 @@ lifetime at this stuff and happens to be a genius?
|
|||
Maybe you can—but I sure can't. For example, consider the evolution of
|
||||
my understanding of linked lists.
|
||||
|
||||
### Linked Lists {#Heading3}
|
||||
### Linked Lists
|
||||
|
||||
Linked lists are data structures composed of discrete elements, or
|
||||
nodes, joined together with links. In C, the links are typically
|
||||
|
|
@ -230,7 +230,7 @@ nodes, and in fact in all link manipulation code. It's easy to end up
|
|||
working with either pointers to pointers or lots of special-case code,
|
||||
and while those approaches work, they're inelegant and inefficient.
|
||||
|
||||
### Dummies and Sentinels {#Heading4}
|
||||
### Dummies and Sentinels
|
||||
|
||||
A far better approach is to use a *dummy node* for the head of the list,
|
||||
as shown in Figure 15.2. I invented this one for myself the next time I
|
||||
|
|
@ -335,7 +335,7 @@ struct LinkNode *FindNodeBeforeValueNotLess(
|
|||
|
||||

|
||||
|
||||
### Circular Lists {#Heading5}
|
||||
### Circular Lists
|
||||
|
||||
One minor but elegant refinement yet remains: Use a single node as both
|
||||
the head *and* the tail of the list. We can do this by connecting the
|
||||
|
|
@ -597,7 +597,7 @@ void main()
|
|||
}
|
||||
```
|
||||
|
||||
### Hi/Lo in 24 Bytes {#Heading6}
|
||||
### Hi/Lo in 24 Bytes
|
||||
|
||||
In one of my *PC TECHNIQUES* "Pushing the Envelope" columns, I passed
|
||||
along one of David Stafford's fiendish programming puzzles: Write a
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 295-319
|
|||
---
|
||||
|
||||
## Chapter 16\
|
||||
There Ain't No Such Thing as the Fastest Code {#Heading1}
|
||||
There Ain't No Such Thing as the Fastest Code
|
||||
|
||||
### Lessons Learned in the Pursuit of the Ultimate Word Counter {#Heading2}
|
||||
### Lessons Learned in the Pursuit of the Ultimate Word Counter
|
||||
|
||||
I remember reading an overview of C++ development tools for Windows in a
|
||||
past issue of *PC Week*. In the lower left corner was the familiar box
|
||||
|
|
@ -54,7 +54,7 @@ particularly attractive keys. We are talking about people who are
|
|||
focusing on means, and have forgotten about ends. We are talking about
|
||||
people with no programming souls.
|
||||
|
||||
### Counting Words in a Hurry {#Heading3}
|
||||
### Counting Words in a Hurry
|
||||
|
||||
What are we to make of this? At the very least, we can safely guess that
|
||||
very few corporate buyers ever enter optimization contests. Most of my
|
||||
|
|
@ -331,7 +331,7 @@ _ScanBuffer endp
|
|||
end
|
||||
```
|
||||
|
||||
#### Which Way to Go from Here? {#Heading4}
|
||||
#### Which Way to Go from Here?
|
||||
|
||||
We could rearrange the tests in light of the nature of the data being
|
||||
scanned; for example, we could perform the tests more efficiently by
|
||||
|
|
@ -487,7 +487,7 @@ difficult.
|
|||
> 
|
||||
> Exhaust all other optimizations before unrolling loops.
|
||||
|
||||
### Challenges and Hazards {#Heading5}
|
||||
### Challenges and Hazards
|
||||
|
||||
The challenge I put to the readers of *PC TECHNIQUES* was to write a
|
||||
faster module to replace Listing 16.4. The author of the code that
|
||||
|
|
@ -520,7 +520,7 @@ code is the fastest possible is rollescating on a tightrope in a
|
|||
hurricane; you're due for a fall, if you catch my drift. Case in point:
|
||||
Terje Mathisen's word-counting program.
|
||||
|
||||
#### Blinding Yourself to a Better Approach {#Heading6}
|
||||
#### Blinding Yourself to a Better Approach
|
||||
|
||||
Not so long ago, Terje Mathisen, who I introduced earlier in this book,
|
||||
wrote a very fast word-counting program, and posted it on Bix. When I
|
||||
|
|
@ -561,7 +561,7 @@ enough, by good fortune, to speed up the whole program by 5 percent.
|
|||
this case, though, the code was specific to the 386. In case you're
|
||||
curious, both forms take 2 cycles on the 486; quite a lot faster, eh?)
|
||||
|
||||
#### Watch Out for Luggable Assumptions! {#Heading7}
|
||||
#### Watch Out for Luggable Assumptions!
|
||||
|
||||
The first lesson to be learned here is not to lug assumptions that may
|
||||
no longer be valid from the 8088/286 world into the wonderful new world
|
||||
|
|
@ -613,7 +613,7 @@ if you're interested in really fast assembly code. I wouldn't call it
|
|||
the *fastest* word-counting code, though, because I would of course
|
||||
never be so foolish as to call *anything* the fastest.
|
||||
|
||||
### The Astonishment of Right-Brain Optimization {#Heading8}
|
||||
### The Astonishment of Right-Brain Optimization
|
||||
|
||||
As it happened, the challenge I issued to my *PC TECHNIQUES* readers was
|
||||
a smashing success, with dozens of good entries. I certainly enjoyed it,
|
||||
|
|
@ -847,7 +847,7 @@ jumping.
|
|||
end
|
||||
```
|
||||
|
||||
### Levels of Optimization {#Heading9}
|
||||
### Levels of Optimization
|
||||
|
||||
Three levels of optimization were evident in the word-counting entries I
|
||||
received in response to my challenge. I'd briefly describe them as
|
||||
|
|
@ -865,7 +865,7 @@ the code, the greater the chance for obscure bugs.)
|
|||
> 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}
|
||||
#### Optimization Level 1: Good Code
|
||||
|
||||
The first level of optimization involves fine-tuning and clever use of
|
||||
the instruction set. The basic framework is still the same as my code
|
||||
|
|
@ -1038,7 +1038,7 @@ _ScanBuffer endp
|
|||
end
|
||||
```
|
||||
|
||||
### Level 2: A New Perspective {#Heading11}
|
||||
### Level 2: A New Perspective
|
||||
|
||||
The second level of optimization is one of breaking out of the mode of
|
||||
thinking established by my original code. Some entrants clearly did
|
||||
|
|
@ -1117,7 +1117,7 @@ John's approach makes it clear that word-counting is nothing more than a
|
|||
fairly simple state machine. The interesting part, of course, is
|
||||
building the fastest state machine.
|
||||
|
||||
#### Level 3: Breakthrough {#Heading12}
|
||||
#### Level 3: Breakthrough
|
||||
|
||||
The boundaries between the levels of optimization are not sharply
|
||||
defined. In a sense, level 3 optimization is just like levels 1 and 2,
|
||||
|
|
@ -1233,7 +1233,7 @@ cycles/byte.
|
|||
|
||||
Enough said, I trust.
|
||||
|
||||
#### Enough Word Counting Already! {#Heading13}
|
||||
#### Enough Word Counting Already!
|
||||
|
||||
Before I finish up this chapter, I'd like to mention that Terje
|
||||
Mathisen's WC word-counting program, which I've mentioned previously and
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 322-346
|
|||
---
|
||||
|
||||
## Chapter 17\
|
||||
The Game of Life {#Heading1}
|
||||
The Game of Life
|
||||
|
||||
### The Triumph of Algorithmic Optimization in a Cellular Automata Game {#Heading2}
|
||||
### The Triumph of Algorithmic Optimization in a Cellular Automata Game
|
||||
|
||||
I've spent a lot of my life discussing assembly language optimization,
|
||||
which I consider to be an important and underappreciated topic. However,
|
||||
|
|
@ -40,7 +40,7 @@ we will find is that it's possible to get a 50-times speed-up without
|
|||
using *one byte of assembly!* It's all a matter of perspective—how you
|
||||
look at your code and data.
|
||||
|
||||
### Conway's Game {#Heading3}
|
||||
### Conway's Game
|
||||
|
||||
The program that we're going to optimize is Conway's famous Game of
|
||||
Life, long-ago favorite of the hackers at MIT's AI Lab. If you've never
|
||||
|
|
@ -64,7 +64,7 @@ speed-up range. Then in the next chapter, I'll show you how several
|
|||
programmers *really* floored it in taking me up on my second
|
||||
Optimization Challenge, which involved the Game of Life.
|
||||
|
||||
#### The Rules of the Game {#Heading4}
|
||||
#### The Rules of the Game
|
||||
|
||||
The Game of Life is ridiculously simple. There is a cellmap, consisting
|
||||
of a rectangular matrix of cells, each of which may initially be either
|
||||
|
|
@ -359,7 +359,7 @@ void show_text(int x, int y, char *text)
|
|||
}
|
||||
```
|
||||
|
||||
### Where Does the Time Go? {#Heading5}
|
||||
### Where Does the Time Go?
|
||||
|
||||
How slow is Listing 17.1? Table 17.1 shows that even on a 486, Listing
|
||||
17.1 does fewer than three 96x96 generations per second. (The times in
|
||||
|
|
@ -415,7 +415,7 @@ at for possible optimizations are `cell_state()` and
|
|||
> 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}
|
||||
### The Hazards and Advantages of Abstraction
|
||||
|
||||
How can we speed up `cell_state()` and `next_generation()`? I'll
|
||||
tell you how *not* to do it: By writing those member functions in
|
||||
|
|
@ -685,7 +685,7 @@ assembly?
|
|||
|
||||
Not hardly.
|
||||
|
||||
### Heavy-Duty C++ Optimization {#Heading7}
|
||||
### Heavy-Duty C++ Optimization
|
||||
|
||||
Before we get to assembly, we still have to perform C++ optimization,
|
||||
then see if we can find an alternative approach that better fits the
|
||||
|
|
@ -826,7 +826,7 @@ How is this possible? Here are some hints:
|
|||
bit-per-pixel.
|
||||
* Cells change state relatively infrequently.
|
||||
|
||||
### Bringing In the Right Brain {#Heading8}
|
||||
### Bringing In the Right Brain
|
||||
|
||||
In the previous section, we saw how a C++ program could be sped up about
|
||||
eight times simply by rearranging the data and code in straightforward
|
||||
|
|
@ -843,7 +843,7 @@ encourage you to saturate your brain with everything you know about any
|
|||
particular optimization problem, then make space for your right brain to
|
||||
solve the problem.
|
||||
|
||||
#### Re-Examining the Task {#Heading9}
|
||||
#### Re-Examining the Task
|
||||
|
||||
Earlier in this chapter, we looked at a straightforward Game of Life
|
||||
implementation, then increased performance considerably by making the
|
||||
|
|
@ -907,7 +907,7 @@ only one-tenth that of the original approach!
|
|||
|
||||

|
||||
|
||||
#### Acting on What We Know {#Heading10}
|
||||
#### Acting on What We Know
|
||||
|
||||
Once we've changed the cellmap format to store neighbor counts as well
|
||||
as states, with a byte for each cell, we can get another performance
|
||||
|
|
@ -1254,7 +1254,7 @@ 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
|
||||
optimization at a conceptual level *must* come first.
|
||||
|
||||
#### The Challenge That Ate My Life {#Heading11}
|
||||
#### The Challenge That Ate My Life
|
||||
|
||||
The most recent optimization challenge I laid my community of readers
|
||||
was to write the fastest possible Game of Life generation engine. By
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 347-367
|
|||
---
|
||||
|
||||
## Chapter 18\
|
||||
It's a plain Wonderful Life {#Heading1}
|
||||
It's a plain Wonderful Life
|
||||
|
||||
### Optimization beyond the Pale {#Heading2}
|
||||
### Optimization beyond the Pale
|
||||
|
||||
When I was in high school, my gym teacher had us run a race around the
|
||||
soccer field, or rather, around a course marked with cones that roughly
|
||||
|
|
@ -51,7 +51,7 @@ programming.
|
|||
> will often be unable to change the specifications for the software you
|
||||
> implement.
|
||||
|
||||
### Breaking the Rules {#Heading3}
|
||||
### Breaking the Rules
|
||||
|
||||
The other reason for the anecdote has to do with the way my second
|
||||
Optimization Challenge worked itself out. If you'll recall from the last
|
||||
|
|
@ -119,7 +119,7 @@ specific "David Stafford" or "Peter Klerings.")
|
|||
|
||||
Onward to the code.
|
||||
|
||||
### Table-Driven Magic {#Heading4}
|
||||
### Table-Driven Magic
|
||||
|
||||
David Stafford won my first Optimization Challenge by means of a huge
|
||||
look-up table and an incredible state machine driven by that table. The
|
||||
|
|
@ -859,7 +859,7 @@ extern unsigned short far ChangeList1[];
|
|||
#define WRAPDOWN (UP * (HEIGHT - 1))
|
||||
```
|
||||
|
||||
### Keeping Track of Change with a Change List {#Heading5}
|
||||
### Keeping Track of Change with a Change List
|
||||
|
||||
In my earlier optimizations to the Game of Life, described in the last
|
||||
chapter, I noted that most cells in a Life cellmap are dead, and in most
|
||||
|
|
@ -966,7 +966,7 @@ Segment usage in David's assembly code is summarized in Listing 18.6.
|
|||
FS : Video segment
|
||||
GS : Unused
|
||||
|
||||
#### A Layperson's Overview of QLIFE {#Heading6}
|
||||
#### A Layperson's Overview of QLIFE
|
||||
|
||||
Most likely, you're scratching your head right now in bemusement. I
|
||||
don't blame you; I felt the same way myself at first. It's actually
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 369-379
|
|||
---
|
||||
|
||||
## Chapter 19\
|
||||
Pentium: Not the Same Old Song {#Heading1}
|
||||
Pentium: Not the Same Old Song
|
||||
|
||||
### Learning a Whole Different Set of Optimization Rules {#Heading2}
|
||||
### Learning a Whole Different Set of Optimization Rules
|
||||
|
||||
I can still remember the day I did my first 8088 programming. I had just
|
||||
moved over from the distantly related Z80, so the 8088 wasn't totally
|
||||
|
|
@ -45,7 +45,7 @@ that made cycle counting more meaningful than ever before, and careful
|
|||
code massaging sometimes yielded startling results. Nonetheless, the 486
|
||||
was still too simple to mark a return to the golden age of optimization.
|
||||
|
||||
### The Return of Optimization as Art {#Heading3}
|
||||
### The Return of Optimization as Art
|
||||
|
||||
Then the Pentium came around, and filled our code with optimization
|
||||
hazards, and life was good again. The Pentium has two execution
|
||||
|
|
@ -90,7 +90,7 @@ pitch Preparation H. I can hardly wait.
|
|||
|
||||
Gimme a "P"....
|
||||
|
||||
### The Pentium: An Overview {#Heading4}
|
||||
### The Pentium: An Overview
|
||||
|
||||
Architecturally, the Pentium is vastly different in many ways from the
|
||||
486, but most of those differences are transparent to programmers. After
|
||||
|
|
@ -139,7 +139,7 @@ to 128 bytes. In conjunction with the branch prediction feature
|
|||
branches, this larger prefetch queue means that the Pentium's two pipes
|
||||
should be better fed than those of any previous x86 processor.
|
||||
|
||||
#### Crossing Cache Lines {#Heading5}
|
||||
#### Crossing Cache Lines
|
||||
|
||||
There are three other characteristics of the Pentium that make for a
|
||||
healthy supply of instruction bytes. One is that the Pentium can
|
||||
|
|
@ -189,7 +189,7 @@ Architecture and Programming Manual* (ISBN 1-55512-195-0; Intel order
|
|||
number 241430-001), and the article "Optimizing Pentium Code" by Mike
|
||||
Schmidt, in *Dr. Dobb's Journal* for January 1994.
|
||||
|
||||
#### Cache Organization {#Heading6}
|
||||
#### Cache Organization
|
||||
|
||||
There are two other interesting changes in the Pentium's cache
|
||||
organization. First, the cache is two-way set-associative, whereas the
|
||||
|
|
@ -243,7 +243,7 @@ improvement. Clearly, avoiding AGIs becomes a much more challenging and
|
|||
rewarding game in a superscalar world, one to which I'll return in the
|
||||
next chapter.
|
||||
|
||||
### Faster Addressing and More {#Heading7}
|
||||
### Faster Addressing and More
|
||||
|
||||
I'll spend the rest of this chapter covering a variety of Pentium
|
||||
optimization tips. For starters, effective address calculations (that
|
||||
|
|
@ -346,7 +346,7 @@ Pentium may have to shut down the V-pipe for a cycle to avoid potential
|
|||
dependencies on the result of the `AND` or `OR`. `TEST` suffers
|
||||
from no such potential dependencies.
|
||||
|
||||
### Branch Prediction {#Heading8}
|
||||
### Branch Prediction
|
||||
|
||||
One brand-spanking-new feature of the Pentium is *branch prediction*,
|
||||
whereby the Pentium tries to guess, based on past history, which way
|
||||
|
|
@ -406,7 +406,7 @@ on any given iteration.
|
|||
> to fall through branches if possible, and try to be consistent in your
|
||||
> branching if not.
|
||||
|
||||
### Miscellaneous Pentium Topics {#Heading9}
|
||||
### Miscellaneous Pentium Topics
|
||||
|
||||
The Pentium has all the instructions of the 486, plus a few new ones.
|
||||
One much-needed instruction that has finally made it into the
|
||||
|
|
@ -418,7 +418,7 @@ to me to be a particularly useful instruction, but I'm sure Intel
|
|||
wouldn't have added it without a reason; if you know of a use for it,
|
||||
please pass it along to me.
|
||||
|
||||
#### 486 versus Pentium Optimization {#Heading10}
|
||||
#### 486 versus Pentium Optimization
|
||||
|
||||
Many Pentium optimizations help, or at least don't hurt, on the 486.
|
||||
Many, but not all—and many *do* hurt on the 386. As I discuss various
|
||||
|
|
@ -438,7 +438,7 @@ absolutely the best possible performance for your DOS and Windows apps
|
|||
on the fastest hardware, Pentium optimization can make your code
|
||||
*scream*.
|
||||
|
||||
#### Going Superscalar {#Heading11}
|
||||
#### Going Superscalar
|
||||
|
||||
In the next chapter, we'll look into the single biggest element of
|
||||
Pentium performance, cranking up the Pentium's second execution pipe.
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 381-396
|
|||
---
|
||||
|
||||
## Chapter 20\
|
||||
Pentium Rules {#Heading1}
|
||||
Pentium Rules
|
||||
|
||||
### How Your Carbon-Based Optimizer Can Put the "Super" in Superscalar {#Heading2}
|
||||
### How Your Carbon-Based Optimizer Can Put the "Super" in Superscalar
|
||||
|
||||
At the 1983 West Coast Computer Faire, my friend Dan Illowsky, Andy
|
||||
Greenberg (co-author of Wizardry, at that time the best-selling computer
|
||||
|
|
@ -71,7 +71,7 @@ that the `FXCH` instruction, which is largely free on the Pentium, is
|
|||
expensive on the 486.) So discard your x86 preconceptions as we delve
|
||||
into superscalar optimization for this one-of-a-kind processor.
|
||||
|
||||
### An Instruction in Every Pipe {#Heading3}
|
||||
### An Instruction in Every Pipe
|
||||
|
||||
In the last chapter, we took a quick tour of the Pentium's architecture,
|
||||
and started to look into the Pentium's optimization rules. Now we're
|
||||
|
|
@ -139,7 +139,7 @@ practice, this is not too difficult. The only hard part is keeping in
|
|||
mind the long list of rules governing instruction pairing. The place to
|
||||
begin is with the set of instructions that can go through the V-pipe.
|
||||
|
||||
### V-Pipe-Capable Instructions {#Heading4}
|
||||
### V-Pipe-Capable Instructions
|
||||
|
||||
Any instruction can go through the U-pipe, and, for practical purposes,
|
||||
the U-pipe is always executing instructions. (The exceptions are when
|
||||
|
|
@ -327,7 +327,7 @@ sequence can be reduced to 1.5 cycles, but it is *14* bytes long.
|
|||
> performance and ignore the size, but on a program-wide basis, the size
|
||||
> bears watching.
|
||||
|
||||
### Lockstep Execution {#Heading5}
|
||||
### Lockstep Execution
|
||||
|
||||
You may wonder why anyone would bother breaking `ADD [MemVar],EAX`
|
||||
into three instructions, given that this instruction can go through
|
||||
|
|
@ -470,7 +470,7 @@ one-cycle instructions, mixed together so that at least two operations
|
|||
are in progress at once. It's not the easiest code to read or write, but
|
||||
it's the only way to get both pipes running at capacity.
|
||||
|
||||
### Superscalar Notes {#Heading6}
|
||||
### Superscalar Notes
|
||||
|
||||
You may well ask why it's necessary to interleave operations, as is done
|
||||
in Figure 20.7. It seems simpler just to turn
|
||||
|
|
@ -505,7 +505,7 @@ hazard known as *register contention*. I'll return to the subject of
|
|||
register contention in the next chapter; in the remainder of this
|
||||
chapter I'd like to cover a few short items about superscalar execution.
|
||||
|
||||
#### Register Starvation {#Heading7}
|
||||
#### Register Starvation
|
||||
|
||||
The above examples should make it pretty clear that effective
|
||||
superscalar programming puts a lot of strain on the Pentium's relatively
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 397-411
|
|||
---
|
||||
|
||||
## Chapter 21\
|
||||
Unleashing the Pentium's V-Pipe {#Heading1}
|
||||
Unleashing the Pentium's V-Pipe
|
||||
|
||||
### Focusing on Keeping Both Pentium Pipes Full {#Heading2}
|
||||
### Focusing on Keeping Both Pentium Pipes Full
|
||||
|
||||
The other day, my daughter suggested that we each draw the prettiest
|
||||
picture we could, then see whose was prettier. I won't comment on who
|
||||
|
|
@ -52,7 +52,7 @@ written code from taking full advantage of the Pentium's two pipes, and
|
|||
can thereby keep your code from pushing the Pentium to maximum
|
||||
performance.
|
||||
|
||||
### Address Generation Interlocks {#Heading3}
|
||||
### Address Generation Interlocks
|
||||
|
||||
The Pentium is advertised as having a five-stage pipeline for each of
|
||||
its execution units. All this means is that at any given time, up to
|
||||
|
|
@ -186,7 +186,7 @@ want to insert an instruction between the two `MOV`s—and, of course,
|
|||
this is yet another reason why you should always measure your code's
|
||||
actual performance.
|
||||
|
||||
### Register Contention {#Heading4}
|
||||
### Register Contention
|
||||
|
||||
Finally, we come to the last major component of superscalar
|
||||
optimization: register contention. The basic premise here is simple: You
|
||||
|
|
@ -228,7 +228,7 @@ instructions comprising the above substitute for `MOVZX` should have
|
|||
at least one unrelated instruction between them when `SUB EAX,EAX`
|
||||
executes in the V-pipe.
|
||||
|
||||
#### Exceptions to Register Contention {#Heading5}
|
||||
#### Exceptions to Register Contention
|
||||
|
||||
Intel has special-cased some very useful exceptions to register
|
||||
contention. Happily, write-after-read operations do *not* cause
|
||||
|
|
@ -285,7 +285,7 @@ U-pipe and 5 cycles in the V-pipe, and mispredicted calls and
|
|||
unconditional jumps take 3 cycles in either pipe. Note that `RET`
|
||||
can't pair.
|
||||
|
||||
### Who's in First? {#Heading6}
|
||||
### Who's in First?
|
||||
|
||||
One of the trickiest things about superscalar optimization is that a
|
||||
given instruction stream can execute at a different speed depending on
|
||||
|
|
@ -321,7 +321,7 @@ usage, as discussed below. Shifts, rotates, `ADC, SBB`, and all other
|
|||
instructions not listed in Table 20.1 in the last chapter are likewise
|
||||
U-pipe markers.
|
||||
|
||||
### Pentium Optimization in Action {#Heading7}
|
||||
### Pentium Optimization in Action
|
||||
|
||||
Now, let's take a look at one of the simplest, tightest pieces of code
|
||||
imaginable, and see what our new Pentium perspective reveals. Listing
|
||||
|
|
@ -646,7 +646,7 @@ measurement to check the efficacy of your optimizations, so reserve it
|
|||
for when you really, really need it—but when you need it, you need it
|
||||
*bad*.
|
||||
|
||||
#### A Quick Note on the 386 and 486 {#Heading8}
|
||||
#### A Quick Note on the 386 and 486
|
||||
|
||||
I've mentioned that Pentium-optimized code does fine on the 486, but not
|
||||
always so well on the 386. On a 486, Listing 21.1 runs at 9 cycles per
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 413-420
|
|||
---
|
||||
|
||||
## Chapter 22\
|
||||
Zenning and the Flexible Mind {#Heading1}
|
||||
Zenning and the Flexible Mind
|
||||
|
||||
### Taking a Spin through What You've Learned {#Heading2}
|
||||
### Taking a Spin through What You've Learned
|
||||
|
||||
And so we come to the end of our journey; for now, at least. What
|
||||
follows is a modest bit of optimization, one which originally served to
|
||||
|
|
@ -36,7 +36,7 @@ plain fun.
|
|||
|
||||
Enjoy!
|
||||
|
||||
### Zenning {#Heading3}
|
||||
### Zenning
|
||||
|
||||
In Jeff Duntemann's excellent book *Borland Pascal From Square One*
|
||||
(Random House, 1993), there's a small assembly subroutine that's
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ pages: 423-448
|
|||
# Part II
|
||||
|
||||
## Chapter 23\
|
||||
Bones and Sinew {#Heading1}
|
||||
Bones and Sinew
|
||||
|
||||
### At the Very Heart of Standard PC Graphics {#Heading2}
|
||||
### At the Very Heart of Standard PC Graphics
|
||||
|
||||
The VGA is unparalleled in the history of computer graphics, for it is
|
||||
by far the most widely-used graphics standard ever, the closest we may
|
||||
|
|
@ -50,7 +50,7 @@ well underway.
|
|||
We'll start our exploration with a quick overview of the VGA, and then
|
||||
we'll dive right in and get a taste of what the VGA can do.
|
||||
|
||||
### The VGA {#Heading3}
|
||||
### The VGA
|
||||
|
||||
The VGA is the baseline adapter for modern IBM PC compatibles, present
|
||||
in virtually every PC sold today or in the last several years. (Note
|
||||
|
|
@ -103,7 +103,7 @@ rather to start you down the road to understanding the VGA.
|
|||
|
||||
Let's begin.
|
||||
|
||||
### An Introduction to VGA Programming {#Heading4}
|
||||
### An Introduction to VGA Programming
|
||||
|
||||
Most discussions of the VGA start out with a traditional "Here's a block
|
||||
diagram of the VGA" approach, with lists of registers and statistics.
|
||||
|
|
@ -128,7 +128,7 @@ conceptual level, letting the code itself demonstrate the implementation
|
|||
details. We'll return to many of these concepts in more depth later in
|
||||
this book.
|
||||
|
||||
### At the Core {#Heading5}
|
||||
### At the Core
|
||||
|
||||
A little background is necessary before we're ready to examine Listing
|
||||
23.1. The VGA is built around four functional blocks, named the CRT
|
||||
|
|
@ -247,7 +247,7 @@ fixed. Moreover, a great deal of graphics software now uses word
|
|||
> course, this method only works if the GC Index register remains
|
||||
> unchanged throughout the loop.
|
||||
|
||||
#### Linear Planes and True VGA Modes {#Heading6}
|
||||
#### Linear Planes and True VGA Modes
|
||||
|
||||
The VGA's memory is organized as four 64K planes. Each of these planes
|
||||
is a linear bitmap; that is, each byte from a given plane controls eight
|
||||
|
|
@ -867,7 +867,7 @@ cseg ends
|
|||
end start
|
||||
```
|
||||
|
||||
#### Smooth Panning {#Heading7}
|
||||
#### Smooth Panning
|
||||
|
||||
The first thing you'll notice upon running the sample program is the
|
||||
remarkable smoothness with which the display pans from side-to-side and
|
||||
|
|
@ -972,7 +972,7 @@ should be set to 1.
|
|||
> reinventing the wheel and because the BIOS may well mask
|
||||
> incompatibilities between the IBM VGA and VGA clones.
|
||||
|
||||
#### Color Plane Manipulation {#Heading8}
|
||||
#### Color Plane Manipulation
|
||||
|
||||
The VGA provides a considerable amount of hardware assistance for
|
||||
manipulating the four display memory planes. Two features illustrated by
|
||||
|
|
@ -1050,7 +1050,7 @@ first pass; the VGA is a complicated beast, and learning about it is an
|
|||
iterative process. We'll be going over these features again, in
|
||||
different contexts, over the course of the rest of this book.
|
||||
|
||||
#### Page Flipping {#Heading9}
|
||||
#### Page Flipping
|
||||
|
||||
When animated graphics are drawn directly on the screen, with no
|
||||
intermediate frame-composition stage, the image typically flickers
|
||||
|
|
@ -1156,7 +1156,7 @@ designing programs for the VGA.
|
|||
To see the program run in 640x200 16-color mode, comment out the `EQU`
|
||||
line for `MEDRES_VIDEO_MODE`.
|
||||
|
||||
### The Hazards of VGA Clones {#Heading10}
|
||||
### The Hazards of VGA Clones
|
||||
|
||||
Earlier, I said that any VGA that doesn't support the features and
|
||||
functionality covered in this book can't properly be called VGA
|
||||
|
|
@ -1186,7 +1186,7 @@ is that if you're going to use oversized virtual bitmaps and pan around
|
|||
them, you should take great care to test your software on a wide variety
|
||||
of VRAM- and DRAM-based VGAs.
|
||||
|
||||
### Just the Beginning {#Heading11}
|
||||
### Just the Beginning
|
||||
|
||||
That pretty well covers the important points of the sample VGA program
|
||||
in Listing 23.1. There are many VGA features we didn't even touch on,
|
||||
|
|
@ -1196,7 +1196,7 @@ VGA's resources, and in general to give you an initial sense of what VGA
|
|||
programming is like. Starting with the next chapter, we'll begin to
|
||||
explore the VGA systematically, on a more detailed basis.
|
||||
|
||||
### The Macro Assembler {#Heading12}
|
||||
### The Macro Assembler
|
||||
|
||||
The code in this book is written in both C and assembly. I think C is a
|
||||
good development environment, but I believe that often the best code
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 449-460
|
|||
---
|
||||
|
||||
## Chapter 24\
|
||||
Parallel Processing with the VGA {#Heading1}
|
||||
Parallel Processing with the VGA
|
||||
|
||||
### Taking on Graphics Memory Four Bytes at a Time {#Heading2}
|
||||
### Taking on Graphics Memory Four Bytes at a Time
|
||||
|
||||
This heading refers to the ability of the VGA chip to manipulate up to
|
||||
four bytes of display memory at once. In particular, the VGA provides
|
||||
|
|
@ -24,7 +24,7 @@ one part of the surprisingly complex data flow architecture of the VGA,
|
|||
but since they're involved in almost all memory access operations,
|
||||
they're a good place to begin.
|
||||
|
||||
### VGA Programming: ALUs and Latches {#Heading3}
|
||||
### VGA Programming: ALUs and Latches
|
||||
|
||||
I'm going to begin our detailed tour of the VGA at the heart of the flow
|
||||
of data through the VGA: the four ALUs built into the VGA's Graphics
|
||||
|
|
@ -395,7 +395,7 @@ encountered any particularly valuable applications for AND and OR, but
|
|||
they're the sort of building-block features that could come in handy in
|
||||
just the right context, so keep them in mind.
|
||||
|
||||
### Notes on the ALU/Latch Demo Program {#Heading4}
|
||||
### Notes on the ALU/Latch Demo Program
|
||||
|
||||
VGA settings such as the logical function select should be restored to
|
||||
their default condition before the BIOS is called to output text or draw
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 461-479
|
|||
---
|
||||
|
||||
## Chapter 25\
|
||||
VGA Data Machinery {#Heading1}
|
||||
VGA Data Machinery
|
||||
|
||||
### The Barrel Shifter, Bit Mask, and Set/Reset Mechanisms {#Heading2}
|
||||
### The Barrel Shifter, Bit Mask, and Set/Reset Mechanisms
|
||||
|
||||
In the last chapter, we examined a simplified model of data flow within
|
||||
the GC portion of the VGA, featuring the latches and ALUs. Now we're
|
||||
|
|
@ -21,7 +21,7 @@ ready to expand that model to include the barrel shifter, bit mask, and
|
|||
the set/reset capabilities, leaving only the write modes to be explored
|
||||
over the next few chapters.
|
||||
|
||||
### VGA Data Rotation {#Heading3}
|
||||
### VGA Data Rotation
|
||||
|
||||
Figure 25.1 shows an expanded model of GC data flow, featuring the
|
||||
barrel shifter and bit mask circuitry. Let's look at the barrel shifter
|
||||
|
|
@ -60,7 +60,7 @@ I'll demonstrate that application below. In general, though, don't knock
|
|||
yourself out trying to figure out how to work data rotation into your
|
||||
programs—it just isn't all that useful in most cases.
|
||||
|
||||
### The Bit Mask {#Heading4}
|
||||
### The Bit Mask
|
||||
|
||||
The VGA has bit mask circuitry for each of the four memory planes. The
|
||||
four bit masks operate in parallel and are all driven by the same mask
|
||||
|
|
@ -403,7 +403,7 @@ complex."
|
|||
|
||||
He's got a point there.
|
||||
|
||||
### The VGA's Set/Reset Circuitry {#Heading5}
|
||||
### The VGA's Set/Reset Circuitry
|
||||
|
||||
At last we come to the final aspect of data flow through the GC on write
|
||||
mode 0 writes: the set/reset circuitry. Figure 25.3 shows data flow on a
|
||||
|
|
@ -530,7 +530,7 @@ cseg ends
|
|||
end start
|
||||
```
|
||||
|
||||
#### Setting All Planes to a Single Color {#Heading6}
|
||||
#### Setting All Planes to a Single Color
|
||||
|
||||
The set/reset circuitry can be used to force some planes to 0-bits and
|
||||
others to 1-bits during a single write, while letting CPU data go to
|
||||
|
|
@ -680,7 +680,7 @@ cseg ends
|
|||
end start
|
||||
```
|
||||
|
||||
#### Manipulating Planes Individually {#Heading7}
|
||||
#### Manipulating Planes Individually
|
||||
|
||||
Listing 25.4 illustrates the use of set/reset to control only some,
|
||||
rather than all, planes. Here, the set/reset circuitry forces plane 2 to
|
||||
|
|
@ -826,7 +826,7 @@ you force all bits in each plane to either zero or one, or pass CPU data
|
|||
through unchanged, on each write to display memory. As tools go,
|
||||
set/reset is a handy one, and it'll pop up often in this book.
|
||||
|
||||
### Notes on Set/Reset {#Heading8}
|
||||
### Notes on Set/Reset
|
||||
|
||||
The set/reset circuitry is not active in write modes 1 or 2. The Enable
|
||||
Set/Reset register is inactive in write mode 3, but the Set/Reset
|
||||
|
|
@ -845,7 +845,7 @@ discussed in the next chapter.
|
|||
> 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}
|
||||
### A Brief Note on Word OUTs
|
||||
|
||||
In the early days of the EGA and VGA, there was considerable debate
|
||||
about whether it was safe to do word `OUT`s (`OUT DX,AX`) to set
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 481-497
|
|||
---
|
||||
|
||||
## Chapter 26\
|
||||
VGA Write Mode 3 {#Heading1}
|
||||
VGA Write Mode 3
|
||||
|
||||
### The Write Mode That Grows on You {#Heading2}
|
||||
### The Write Mode That Grows on You
|
||||
|
||||
Over the last three chapters, we've covered the VGA's write path from
|
||||
stem to stern—with one exception. Thus far, we've only looked at how
|
||||
|
|
@ -31,7 +31,7 @@ in the next chapter, but right now I want to focus on write mode 3,
|
|||
which can be confusing at first, but turns out to be quite a bit more
|
||||
powerful than one might initially think.
|
||||
|
||||
### A Mode Born in Strangeness {#Heading3}
|
||||
### A Mode Born in Strangeness
|
||||
|
||||
Write mode 3 is strange indeed, and its use is not immediately obvious.
|
||||
The first time I encountered write mode 3, I understood immediately how
|
||||
|
|
@ -806,7 +806,7 @@ of the X-Sharp library. Nonetheless, the performance benefit of this
|
|||
approach can be a speedup of as much as four times—all thanks to the
|
||||
decidedly quirky but surprisingly powerful and flexible write mode 3.
|
||||
|
||||
### A Note on Preserving Register Bits {#Heading4}
|
||||
### A Note on Preserving Register Bits
|
||||
|
||||
If you take a quick look, you'll see that the code in Listing 26.1 uses
|
||||
the readable register feature of the VGA to preserve reserved bits and
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 499-521
|
|||
---
|
||||
|
||||
## Chapter 27\
|
||||
Yet Another VGA Write Mode {#Heading1}
|
||||
Yet Another VGA Write Mode
|
||||
|
||||
### Write Mode 2, Chunky Bitmaps,and Text-Graphics Coexistence {#Heading2}
|
||||
### Write Mode 2, Chunky Bitmaps,and Text-Graphics Coexistence
|
||||
|
||||
In the last chapter, we learned about the markedly peculiar write mode 3
|
||||
of the VGA, after having spent three chapters learning the ins and outs
|
||||
|
|
@ -31,7 +31,7 @@ ever imagine.
|
|||
Let's start with the easy stuff, write mode 2, and save the read modes
|
||||
for the next chapter.
|
||||
|
||||
### Write Mode 2 and Set/Reset {#Heading3}
|
||||
### Write Mode 2 and Set/Reset
|
||||
|
||||
Remember how set/reset works? Good, because that's pretty much how write
|
||||
mode 2 works. (You *don't* remember? Well, I'll provide a brief
|
||||
|
|
@ -68,7 +68,7 @@ nonetheless, I suspect that some additional explanation of an admittedly
|
|||
non-obvious mode wouldn't hurt. Let's follow the CPU byte through the
|
||||
VGA in write mode 2, step by step.
|
||||
|
||||
#### A Byte's Progress in Write Mode 2 {#Heading4}
|
||||
#### A Byte's Progress in Write Mode 2
|
||||
|
||||
Figure 27.1 shows the write mode 2 data path. The CPU byte comes into
|
||||
the VGA and is split into four separate bits, one for each plane. Bits
|
||||
|
|
@ -130,7 +130,7 @@ on the VGA is to read the Graphics Mode register, mask off bits 1 and 0,
|
|||
OR in 00000010b (02H), and write the result back to the Graphics Mode
|
||||
register, thereby leaving the other bits in the register undisturbed.
|
||||
|
||||
#### Copying Chunky Bitmaps to VGA Memory Using Write Mode 2 {#Heading5}
|
||||
#### Copying Chunky Bitmaps to VGA Memory Using Write Mode 2
|
||||
|
||||
Let's take a look at two examples of write mode 2 in action. Listing
|
||||
27.1 presents a program that uses write mode 2 to copy a graphics image
|
||||
|
|
@ -406,7 +406,7 @@ to illustrate the mechanics of write mode 2.
|
|||
> 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}
|
||||
#### Drawing Color-Patterned Lines Using Write Mode 2
|
||||
|
||||
A more serviceable use of write mode 2 is shown in the program presented
|
||||
in Listing 27.2. The program draws multicolored horizontal, vertical,
|
||||
|
|
@ -764,7 +764,7 @@ Code ends
|
|||
end Start
|
||||
```
|
||||
|
||||
### When to Use Write Mode 2 and When to Use Set/Reset {#Heading7}
|
||||
### When to Use Write Mode 2 and When to Use Set/Reset
|
||||
|
||||
As indicated earlier, write mode 2 and set/reset are functionally
|
||||
interchangeable. Write mode 2 lends itself to more efficient
|
||||
|
|
@ -785,7 +785,7 @@ the value written to some planes to a fixed value while allowing the CPU
|
|||
byte to modify other planes. This is the mode of operation when
|
||||
set/reset is enabled for some but not all planes.
|
||||
|
||||
### Mode 13H—320x200 with 256 Colors {#Heading8}
|
||||
### Mode 13H—320x200 with 256 Colors
|
||||
|
||||
I'm going to take a minute—and I do mean a minute—to discuss the
|
||||
programming model for mode 13H, the VGA's 320x200 256-color mode.
|
||||
|
|
@ -803,7 +803,7 @@ program, especially given that some of the listings later in this book,
|
|||
such as the antialiasing code in Chapter F on the companion CD-ROM, use
|
||||
mode 13H.
|
||||
|
||||
### Flipping Pages from Text to Graphics and Back {#Heading9}
|
||||
### Flipping Pages from Text to Graphics and Back
|
||||
|
||||
A while back, I got an interesting letter from Phil Coleman, of La
|
||||
Jolla, who wrote:
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 523-537
|
|||
---
|
||||
|
||||
## Chapter 28\
|
||||
Reading VGA Memory {#Heading1}
|
||||
Reading VGA Memory
|
||||
|
||||
### Read Modes 0 and 1, and the Color Don't Care Register {#Heading2}
|
||||
### Read Modes 0 and 1, and the Color Don't Care Register
|
||||
|
||||
Well, it's taken five chapters, but we've finally covered the data write
|
||||
path and all four write modes of the VGA. Now it's time to tackle the
|
||||
|
|
@ -27,7 +27,7 @@ straightforward? Well...no. But then, clearing up the mysteries of VGA
|
|||
programming is what this part of the book is all about, so let's get
|
||||
started.
|
||||
|
||||
### Read Mode 0 {#Heading3}
|
||||
### Read Mode 0
|
||||
|
||||
Read mode 0 is actually relatively uncomplicated, given that you
|
||||
understand the four-plane nature of the VGA. (If you don't understand
|
||||
|
|
@ -335,7 +335,7 @@ register affects CPU *writes* to VGA memory in any way.
|
|||
> 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}
|
||||
### Read Mode 1
|
||||
|
||||
Read mode 0 is the workhorse read mode, but it's got an annoying
|
||||
limitation: Whenever you want to determine the color of a given pixel in
|
||||
|
|
@ -537,7 +537,7 @@ code ends
|
|||
end Start
|
||||
```
|
||||
|
||||
### When all Planes "Don't Care" {#Heading5}
|
||||
### When all Planes "Don't Care"
|
||||
|
||||
Still and all, there aren't all that many uses for basic color compare
|
||||
operations. There is, however, a genuinely odd application of read mode
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 539-559
|
|||
---
|
||||
|
||||
## Chapter 29\
|
||||
Saving Screens and Other VGA Mysteries {#Heading1}
|
||||
Saving Screens and Other VGA Mysteries
|
||||
|
||||
### Useful Nuggets from the VGA Zen File {#Heading2}
|
||||
### Useful Nuggets from the VGA Zen File
|
||||
|
||||
There are a number of VGA graphics topics that aren't quite involved
|
||||
enough to warrant their own chapters, yet still cause a fair amount of
|
||||
|
|
@ -25,7 +25,7 @@ writing VGA control registers.
|
|||
|
||||
That's a lot of ground to cover, so let's get started!
|
||||
|
||||
### Saving and Restoring EGA and VGA Screens {#Heading3}
|
||||
### Saving and Restoring EGA and VGA Screens
|
||||
|
||||
The memory architectures of EGAs and VGAs are similar enough to treat
|
||||
both together in this regard. The basic principle for saving EGA and VGA
|
||||
|
|
@ -400,7 +400,7 @@ directly.
|
|||
> 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}
|
||||
### 16 Colors out of 64
|
||||
|
||||
How does one produce the 64 colors from which the 16 colors displayed by
|
||||
the EGA can be chosen? The answer is simple enough: There's a BIOS
|
||||
|
|
@ -770,7 +770,7 @@ Code ends
|
|||
end Start
|
||||
```
|
||||
|
||||
### Overscan {#Heading5}
|
||||
### Overscan
|
||||
|
||||
While we're at it, I'm going to touch on overscan. Overscan is the color
|
||||
of the border of the display, the rectangular area around the edge of
|
||||
|
|
@ -789,7 +789,7 @@ subfunction 1.
|
|||
> border in all modes; all you need do is set the overscan color on any
|
||||
> VGA to see the border.
|
||||
|
||||
### A Bonus Blanker {#Heading6}
|
||||
### A Bonus Blanker
|
||||
|
||||
An interesting bonus: The Attribute Controller provides a very
|
||||
convenient way to blank the screen, in the form of the aforementioned
|
||||
|
|
@ -898,7 +898,7 @@ capabilities, which are supported by another set of BIOS functions, can
|
|||
be used to produce stunning color effects, as we'll see when we cover
|
||||
them starting in Chapter 33.
|
||||
|
||||
### Modifying VGA Registers {#Heading7}
|
||||
### Modifying VGA Registers
|
||||
|
||||
EGA registers are not readable. VGA registers are readable. This
|
||||
revelation will not come as news to most of you, but many programmers
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 561-585
|
|||
---
|
||||
|
||||
## Chapter 30\
|
||||
Video Est Omnis Divisa {#Heading1}
|
||||
Video Est Omnis Divisa
|
||||
|
||||
### The Joys and Galling Problems of Using Split Screens on the EGA and VGA {#Heading2}
|
||||
### The Joys and Galling Problems of Using Split Screens on the EGA and VGA
|
||||
|
||||
The ability to split the screen into two largely independent portions
|
||||
one—displayed above the other on the screen—is one of the more
|
||||
|
|
@ -33,7 +33,7 @@ we do have some ground to cover.
|
|||
|
||||
Let's start with the basic operation of the split screen.
|
||||
|
||||
### How the Split Screen Works {#Heading3}
|
||||
### How the Split Screen Works
|
||||
|
||||
The *operation* of the split screen is simplicity itself. A split screen
|
||||
start scan line value is programmed into two EGA registers or three VGA
|
||||
|
|
@ -97,7 +97,7 @@ line displayed; the safest such approach is to set all bits of the split
|
|||
screen start scan line to 1. (That is, in fact, the split screen start
|
||||
scan line value programmed by the BIOS during a mode set.)
|
||||
|
||||
#### The Split Screen in Action {#Heading4}
|
||||
#### The Split Screen in Action
|
||||
|
||||
All of these points are illustrated by Listing 30.1. Listing 30.1 fills
|
||||
display memory starting at offset zero (the split screen area of memory)
|
||||
|
|
@ -509,7 +509,7 @@ Code ends
|
|||
end Start
|
||||
```
|
||||
|
||||
#### VGA and EGA Split-Screen Operation Don't Mix {#Heading5}
|
||||
#### VGA and EGA Split-Screen Operation Don't Mix
|
||||
|
||||
You must set the `IS_VGA` equate at the start of Listing 30.1
|
||||
correctly for the adapter the code will run on in order for the program
|
||||
|
|
@ -537,7 +537,7 @@ highest-resolution mode the VGA and EGA share. That's not the only mode
|
|||
the split screen works in, however. In fact, it works in *all* modes, as
|
||||
we'll see later.
|
||||
|
||||
### Setting the Split-Screen-Related Registers {#Heading6}
|
||||
### Setting the Split-Screen-Related Registers
|
||||
|
||||
Setting the split-screen-related registers is not as simple a matter as
|
||||
merely outputting the right values to the right registers; timing is
|
||||
|
|
@ -585,7 +585,7 @@ aware that the VGA supports 70 Hz frame rates in all non-480-scan-line
|
|||
modes, while the VGA in 480-scan-line-modes and the EGA in all color
|
||||
modes support 60 Hz frame rates.
|
||||
|
||||
### The Problem with the EGA Split Screen {#Heading7}
|
||||
### The Problem with the EGA Split Screen
|
||||
|
||||
I mentioned earlier that the EGA's split screen is a little buggy. How?
|
||||
you may well ask, particularly given that Listing 30.1 illustrates that
|
||||
|
|
@ -618,7 +618,7 @@ split screen is on. This isn't a bug—it's just one of the many areas in
|
|||
which the VGA's designers learned from the shortcomings of the EGA and
|
||||
went the EGA one better.
|
||||
|
||||
### Split Screen and Panning {#Heading8}
|
||||
### Split Screen and Panning
|
||||
|
||||
Back in Chapter 23, I presented a program that performed smooth
|
||||
horizontal panning. Smooth horizontal panning consists of two parts:
|
||||
|
|
@ -661,7 +661,7 @@ can be used to produce an attractive "streaming tape" effect in the
|
|||
normal screen while the split screen is used to display non-moving
|
||||
information.
|
||||
|
||||
#### The Split Screen and Horizontal Panning: An Example {#Heading9}
|
||||
#### The Split Screen and Horizontal Panning: An Example
|
||||
|
||||
Listing 30.2 illustrates the interaction of horizontal smooth panning
|
||||
with the split screen, as well as the suppression of pel panning in the
|
||||
|
|
@ -1107,7 +1107,7 @@ Codeends
|
|||
endStart
|
||||
```
|
||||
|
||||
### Notes on Setting and Reading Registers {#Heading10}
|
||||
### Notes on Setting and Reading Registers
|
||||
|
||||
There are a few interesting points regarding setting and reading
|
||||
registers to be made about Listing 30.2. First, bit 5 of the AC Index
|
||||
|
|
@ -1190,7 +1190,7 @@ What if you wanted to pan faster? Well, you could of course just move
|
|||
two pixels at a time rather than one; I assure you no one will ever
|
||||
notice when you're panning at a rate of 10 or more times per second.
|
||||
|
||||
### Split Screens in Other Modes {#Heading11}
|
||||
### Split Screens in Other Modes
|
||||
|
||||
So far we've only discussed the split screen in mode 10H. What about
|
||||
other modes? Generally, the split screen works in any mode; the basic
|
||||
|
|
@ -1226,7 +1226,7 @@ noticeable on-screen effects depends on the text displayed by a
|
|||
particular application; for example, there should be no problem if the
|
||||
split screen has a border of blanks on the left side.
|
||||
|
||||
### How Safe? {#Heading12}
|
||||
### How Safe?
|
||||
|
||||
So, how safe *is* it to use the split screen? My opinion is that it's
|
||||
perfectly safe, although I'd welcome input from people with extensive
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 587-605
|
|||
---
|
||||
|
||||
## Chapter 31\
|
||||
Higher 256-Color Resolution on the VGA {#Heading1}
|
||||
Higher 256-Color Resolution on the VGA
|
||||
|
||||
### When Is 320x200 Really 320x400? {#Heading2}
|
||||
### When Is 320x200 Really 320x400?
|
||||
|
||||
One of the more appealing features of the VGA is its ability to display
|
||||
256 simultaneous colors. Unfortunately, one of the *less* appealing
|
||||
|
|
@ -43,7 +43,7 @@ games use.
|
|||
|
||||
So. Let's get started.
|
||||
|
||||
### Why 320x200? Only IBM Knows for Sure {#Heading3}
|
||||
### Why 320x200? Only IBM Knows for Sure
|
||||
|
||||
The first question, of course, is, "How can it be possible to get higher
|
||||
256-color resolutions out of the VGA?" After all, there were no unused
|
||||
|
|
@ -86,7 +86,7 @@ designing the chip. Whatever the reason, mode 13H is really a
|
|||
400-scan-line mode masquerading as a 200-scan-line mode, and we can
|
||||
readily end that masquerade.
|
||||
|
||||
### 320x400 256-Color Mode {#Heading4}
|
||||
### 320x400 256-Color Mode
|
||||
|
||||
Okay, what's so great about 320x400 256-color mode? Two things: easy,
|
||||
safe mode sets and page flipping.
|
||||
|
|
@ -122,7 +122,7 @@ That's why I like 320x400 256-color mode. The next step is to understand
|
|||
how display memory is organized in 320x400 mode, and that's not so
|
||||
simple.
|
||||
|
||||
#### Display Memory Organization in 320x400 Mode {#Heading5}
|
||||
#### Display Memory Organization in 320x400 Mode
|
||||
|
||||
First, let's look at why display memory must be organized differently in
|
||||
320x400 256-color mode than in mode 13H. The designers of the VGA
|
||||
|
|
@ -187,7 +187,7 @@ turned off, and selecting byte mode for video data display. All that's
|
|||
done in the `Set320x400Mode` subroutine in Listing 31.1, which we'll
|
||||
discuss next.
|
||||
|
||||
#### Reading and Writing Pixels {#Heading6}
|
||||
#### Reading and Writing Pixels
|
||||
|
||||
The basic graphics functions in any mode are functions to read and write
|
||||
single pixels. Any more complex function can be built on these
|
||||
|
|
@ -624,7 +624,7 @@ an adequately fast—and often *very* fast—version for 320x400 mode of
|
|||
whatever graphics function you need. If you're not all that concerned
|
||||
with speed, `WritePixel` and `ReadPixel` should meet your needs.
|
||||
|
||||
### Two 256-Color Pages {#Heading7}
|
||||
### Two 256-Color Pages
|
||||
|
||||
Listing 31.2 demonstrates the two pages of 320x400 256-color mode by
|
||||
drawing slanting color bars in page 0, then drawing color bars slanting
|
||||
|
|
@ -911,7 +911,7 @@ The displays produced by Listing 31.2 make it clear that 320x400
|
|||
256-color mode can produce effects that are simply not possible in any
|
||||
16-color mode.
|
||||
|
||||
### Something to Think About {#Heading8}
|
||||
### Something to Think About
|
||||
|
||||
You can, if you wish, use the display memory organization of 320x400
|
||||
mode in 320x200 mode by modifying `Set320x400Mode` to leave the
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 607-622
|
|||
---
|
||||
|
||||
## Chapter 32\
|
||||
Be It Resolved: 360x480 {#Heading1}
|
||||
Be It Resolved: 360x480
|
||||
|
||||
### Taking 256-Color Modes About as Far as the Standard VGA Can Take Them {#Heading2}
|
||||
### Taking 256-Color Modes About as Far as the Standard VGA Can Take Them
|
||||
|
||||
In the last chapter, we learned how to coax 320x400 256-color resolution
|
||||
out of a standard VGA. At the time, I noted that the VGA was actually
|
||||
|
|
@ -36,7 +36,7 @@ the line-drawing code that we'll develop in Chapter 35. Together, those
|
|||
routines will make a pretty nifty demo of the capabilities of 360x480
|
||||
256-color mode.
|
||||
|
||||
### Extended 256-Color Modes: What's Not to Like? {#Heading3}
|
||||
### Extended 256-Color Modes: What's Not to Like?
|
||||
|
||||
When last we left 256-color programming, we had found that the standard
|
||||
256-color mode, mode 13H, which officially offers 320x200 resolution,
|
||||
|
|
@ -101,7 +101,7 @@ undocumented, offers a better combination of resolution and color; even
|
|||
In other words, 360x480 256-color mode is worth considering—so let's
|
||||
have a look.
|
||||
|
||||
### 360x480 256-Color Mode {#Heading4}
|
||||
### 360x480 256-Color Mode
|
||||
|
||||
I'm going to start by showing you 360x480 256-color mode in action,
|
||||
after which we'll look at how it works. I suspect that once you see what
|
||||
|
|
@ -585,7 +585,7 @@ significant chunk of the market for your code.)
|
|||
Now that we've seen the wonders of which our new mode is capable, let's
|
||||
take the time to understand how it works.
|
||||
|
||||
### How 360x480 256-Color Mode Works {#Heading5}
|
||||
### How 360x480 256-Color Mode Works
|
||||
|
||||
In describing 360x480 256-color mode, I'm going to assume that you're
|
||||
familiar with the discussion of 320x400 256-color mode in the last
|
||||
|
|
@ -597,7 +597,7 @@ myself when the goods are just a few page flips (the paper kind) away.
|
|||
stretched in both dimensions. Let's look at the vertical stretching
|
||||
first, since that's the simpler of the two.
|
||||
|
||||
#### 480 Scan Lines per Screen: A Little Slower, But No Big Deal {#Heading6}
|
||||
#### 480 Scan Lines per Screen: A Little Slower, But No Big Deal
|
||||
|
||||
There's nothing unusual about 480 scan lines; standard modes 11H and 12H
|
||||
support that vertical resolution. The number of scan lines has nothing
|
||||
|
|
@ -620,7 +620,7 @@ isn't *bad*—that's the only refresh rate the EGA ever supported, and the
|
|||
EGA was the industry standard in its time—but it does tend to flicker a
|
||||
little more and so is a little harder on the eyes than 70 Hz.
|
||||
|
||||
#### 360 Pixels per Scan Line: No Mean Feat {#Heading7}
|
||||
#### 360 Pixels per Scan Line: No Mean Feat
|
||||
|
||||
Converting from 320 to 360 pixels per scan line is more difficult than
|
||||
converting from 400 to 480 scan lines per screen. None of the VGA's
|
||||
|
|
@ -682,7 +682,7 @@ resolution, as well.
|
|||
Once all that's done, the VGA is in 360x480 mode, awaiting our every
|
||||
high-resolution 256-color graphics whim.
|
||||
|
||||
#### Accessing Display Memory in 360x480 256-Color Mode {#Heading8}
|
||||
#### Accessing Display Memory in 360x480 256-Color Mode
|
||||
|
||||
Setting up for 360x480 256-color mode proved to be quite a task. Is
|
||||
drawing in this mode going to be as difficult?
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 623-636
|
|||
---
|
||||
|
||||
## Chapter 33\
|
||||
Yogi Bear and Eurythmics Confront VGA Colors {#Heading1}
|
||||
Yogi Bear and Eurythmics Confront VGA Colors
|
||||
|
||||
### The Basics of VGA Color Generation {#Heading2}
|
||||
### The Basics of VGA Color Generation
|
||||
|
||||
Kevin Mangis wants to know about the VGA's 4-bit to 8-bit to 18-bit
|
||||
color translation. Mansur Loloyan would like to find out how to generate
|
||||
|
|
@ -60,7 +60,7 @@ and the Digital Differential Analyzer," by Tim Paterson, is a good
|
|||
article about fast circle drawing, a topic we'll tackle soon. All in
|
||||
all, the dog days of 1990 were good times for graphics.
|
||||
|
||||
### VGA Color Basics {#Heading3}
|
||||
### VGA Color Basics
|
||||
|
||||
Briefly put, the VGA color translation circuitry takes in one 4- or
|
||||
8-bit pixel value at a time and translates it into three 6-bit values,
|
||||
|
|
@ -69,7 +69,7 @@ analog levels and sent to the monitor. Seems simple enough, doesn't it?
|
|||
Unfortunately, nothing is ever that simple on the VGA, and color
|
||||
translation is no exception.
|
||||
|
||||
#### The Palette RAM {#Heading4}
|
||||
#### The Palette RAM
|
||||
|
||||
The color path in the VGA involves two stages, as shown in Figure 33.1.
|
||||
The first stage fetches a 4-bit pixel from display memory and feeds it
|
||||
|
|
@ -91,7 +91,7 @@ attribute. In 256-color mode, which we'll get to eventually, the palette
|
|||
RAM is not a factor from the programmer's perspective and should be left
|
||||
alone.
|
||||
|
||||
#### The DAC {#Heading5}
|
||||
#### The DAC
|
||||
|
||||
Once the EGA-compatible palette RAM has fulfilled its karma and
|
||||
performed 4-bit to 6-bit translation on a pixel, the resulting value is
|
||||
|
|
@ -125,7 +125,7 @@ higher the number, the brighter the color, with 0 turning that color off
|
|||
in the pixel and 63 (3FH) making that color maximum brightness. Got all
|
||||
that?
|
||||
|
||||
#### Color Paging with the Color Select Register {#Heading6}
|
||||
#### Color Paging with the Color Select Register
|
||||
|
||||
"Wait a minute," you say bemusedly. "Aren't you missing some bits
|
||||
between the palette RAM and the DAC?" Indeed I am. The palette RAM puts
|
||||
|
|
@ -173,7 +173,7 @@ out of the circuit and simplifies life something wonderful. The palette
|
|||
RAM exists solely for EGA compatibility, and serves no useful purpose
|
||||
that I know of for VGA-only color programming.
|
||||
|
||||
#### 256-Color Mode {#Heading7}
|
||||
#### 256-Color Mode
|
||||
|
||||
So far I've spoken only of 16-color modes; what of 256-color modes?
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ VGA chip to the DAC come from display memory. Therefore, there is no
|
|||
color paging in 256-color mode. Of course, that makes sense given that
|
||||
all 256 DAC registers are simultaneously in use in 256-color mode.
|
||||
|
||||
#### Setting the Palette RAM {#Heading8}
|
||||
#### Setting the Palette RAM
|
||||
|
||||
The palette RAM can be programmed either directly or through BIOS
|
||||
interrupt 10H, function 10H. I strongly recommend using the BIOS
|
||||
|
|
@ -227,7 +227,7 @@ Having said that, let's leave the palette RAM behind (presumably in a
|
|||
pass-through state) and move on to the DAC, which is the right place to
|
||||
do color translation on the VGA.
|
||||
|
||||
#### Setting the DAC {#Heading9}
|
||||
#### Setting the DAC
|
||||
|
||||
Like the palette RAM, the DAC registers can be set either directly or
|
||||
through the BIOS. Again, the BIOS should be used whenever possible, but
|
||||
|
|
@ -264,7 +264,7 @@ consists of three bytes; the first byte is a 6-bit red component, the
|
|||
second byte is a 6-bit green component, and the third byte is a 6-bit
|
||||
blue component, as illustrated by Listing 33.1.
|
||||
|
||||
### If You Can't Call the BIOS, Who Ya Gonna Call? {#Heading10}
|
||||
### If You Can't Call the BIOS, Who Ya Gonna Call?
|
||||
|
||||
Although the palette RAM and DAC registers should be set through the
|
||||
BIOS whenever possible, there are times when the BIOS is not the best
|
||||
|
|
@ -340,7 +340,7 @@ In the meantime, if you can use the BIOS to set the DAC, do so; then you
|
|||
won't have to worry about the real and potential complications of
|
||||
setting the DAC directly.
|
||||
|
||||
### An Example of Setting the DAC {#Heading11}
|
||||
### An Example of Setting the DAC
|
||||
|
||||
This chapter has gotten about as big as a chapter really ought to be;
|
||||
the VGA color saga will continue in the next few. Quickly, then, Listing
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 637-652
|
|||
---
|
||||
|
||||
## Chapter 34\
|
||||
Changing Colors without Writing Pixels {#Heading1}
|
||||
Changing Colors without Writing Pixels
|
||||
|
||||
### Special Effects through Realtime Manipulation of DAC Colors {#Heading2}
|
||||
### Special Effects through Realtime Manipulation of DAC Colors
|
||||
|
||||
Sometimes, strange as it may seem, the harder you try, the less you
|
||||
accomplish. Brute force is fine when it suffices, but it does not always
|
||||
|
|
@ -31,7 +31,7 @@ by cleaning up some odds and ends about VGA color.
|
|||
There's a lot to be said about loading the DAC, so let's dive right in
|
||||
and see where the complications lie.
|
||||
|
||||
### Color Cycling {#Heading3}
|
||||
### Color Cycling
|
||||
|
||||
As we've learned in past chapters, the VGA's DAC contains 256 storage
|
||||
locations, each holding one 18-bit value representing an RGB color
|
||||
|
|
@ -78,7 +78,7 @@ In short, color cycling is really the method of choice for dynamic color
|
|||
effects only in 256-color mode—but, regrettably, color cycling is at its
|
||||
least reliable and capable in that mode, as we'll see next.
|
||||
|
||||
### The Heart of the Problem {#Heading4}
|
||||
### The Heart of the Problem
|
||||
|
||||
Here's the problem with loading the entire DAC repeatedly: The DAC
|
||||
contains 256 color storage locations, each loaded via either 3 or 4
|
||||
|
|
@ -122,7 +122,7 @@ let you explore for yourself the extent of the problem on computers in
|
|||
which you're interested. First, though, we must address *another* DAC
|
||||
loading problem: the BIOS.
|
||||
|
||||
#### Loading the DAC via the BIOS {#Heading5}
|
||||
#### Loading the DAC via the BIOS
|
||||
|
||||
The DAC can be loaded either directly or through subfunctions 10H (for a
|
||||
single DAC register) or 12H (for a block of DAC registers) of the BIOS
|
||||
|
|
@ -194,7 +194,7 @@ cycle by calling the BIOS.
|
|||
Which is not to say that loading the DAC directly is a picnic either, as
|
||||
we'll see next.
|
||||
|
||||
#### Loading the DAC Directly {#Heading6}
|
||||
#### Loading the DAC Directly
|
||||
|
||||
So we must load the DAC directly in order to perform color cycling. The
|
||||
DAC is loaded directly by sending (with an `OUT` instruction) the
|
||||
|
|
@ -233,7 +233,7 @@ blame me if you get a call from someone who's claims that your program
|
|||
sometimes turns their screen into something resembling month-old yogurt.
|
||||
It's not really your fault, of course—but try explaining that to *them!*
|
||||
|
||||
### A Test Program for Color Cycling {#Heading7}
|
||||
### A Test Program for Color Cycling
|
||||
|
||||
Anyway, the choice of how to load the DAC is yours. Given that I'm not
|
||||
providing you with any hard-and-fast rules (mainly because there don't
|
||||
|
|
@ -584,7 +584,7 @@ force isn't appropriate to the task of color cycling. That doesn't mean
|
|||
that color cycling can't be used, just that subtler approaches must be
|
||||
employed. Let's look at some of those alternatives.
|
||||
|
||||
### Color Cycling Approaches that Work {#Heading8}
|
||||
### Color Cycling Approaches that Work
|
||||
|
||||
First of all, I'd like to point out that when color cycling does work,
|
||||
it's a thing of beauty. Assemble Listing 34.1 so that it doesn't use the
|
||||
|
|
@ -680,7 +680,7 @@ That's what *I'd* do. Don't let yourself be held back by my limited
|
|||
imagination, though! Color cycling may be the most complicated of all
|
||||
the color control techniques, but it's also the most powerful.
|
||||
|
||||
### Odds and Ends {#Heading9}
|
||||
### Odds and Ends
|
||||
|
||||
In my experience, when relying on the autoincrementing feature while
|
||||
loading the DAC, the Write Index register wraps back from 255 to 0, and
|
||||
|
|
@ -694,7 +694,7 @@ understand exactly how your resources behave, and I never know when one
|
|||
of you might come up with a serviceable application for any particular
|
||||
quirk.
|
||||
|
||||
#### The DAC Mask {#Heading10}
|
||||
#### The DAC Mask
|
||||
|
||||
There's one register in the DAC that I haven't mentioned yet, the DAC
|
||||
Mask register at 03C6H. The operation of this register is simple but
|
||||
|
|
@ -710,7 +710,7 @@ DAC location 0 is looked up for every pixel, and the entire screen
|
|||
displays the color stored in DAC location 0. This makes setting the DAC
|
||||
Mask register to 0 a quick and easy way to blank the screen.
|
||||
|
||||
#### Reading the DAC {#Heading11}
|
||||
#### Reading the DAC
|
||||
|
||||
The DAC can be read directly, via the DAC Read Index register at 3C7H
|
||||
and the DAC Data register at 3C9H, in much the same way as it can be
|
||||
|
|
@ -742,7 +742,7 @@ conditionally assembling to either guard against interrupts or not and
|
|||
to use `REP INSB` or not. As you can see, reading the DAC settings is
|
||||
very much symmetric with setting the DAC.
|
||||
|
||||
#### Cycling Down {#Heading12}
|
||||
#### Cycling Down
|
||||
|
||||
And so, at long last, we come to the end of our discussion of color
|
||||
control on the VGA. If it has been more complex than anyone might have
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 653-678
|
|||
---
|
||||
|
||||
## Chapter 35\
|
||||
Bresenham Is Fast, and Fast Is Good {#Heading1}
|
||||
Bresenham Is Fast, and Fast Is Good
|
||||
|
||||
### Implementing and Optimizing Bresenham's Line-Drawing Algorithm {#Heading2}
|
||||
### Implementing and Optimizing Bresenham's Line-Drawing Algorithm
|
||||
|
||||
For all the complexity of graphics design and programming, surprisingly
|
||||
few primitive functions lie at the heart of most graphics software.
|
||||
|
|
@ -73,7 +73,7 @@ Notwithstanding, the line-drawing implementation in Listing 35.3 is
|
|||
plenty fast enough for most purposes, so let's get the discussion
|
||||
underway.
|
||||
|
||||
### The Task at Hand {#Heading3}
|
||||
### The Task at Hand
|
||||
|
||||
There are two important characteristics of any line-drawing function.
|
||||
First, it must draw a reasonable approximation of a line. A computer
|
||||
|
|
@ -124,7 +124,7 @@ floating-point operations, no divides, and no multiplies inside the
|
|||
line-drawing loop. Moreover, it can be implemented with surprisingly
|
||||
little code.
|
||||
|
||||
### Bresenham's Line-Drawing Algorithm {#Heading4}
|
||||
### Bresenham's Line-Drawing Algorithm
|
||||
|
||||
The key to grasping Bresenham's algorithm is to understand that when
|
||||
drawing an approximation of a line on a finite-resolution display, each
|
||||
|
|
@ -243,7 +243,7 @@ under the name *Computer Graphics: Principles and Practice*
|
|||
integer-only, divide-free version of the algorithm, as well as Pascal
|
||||
code for drawing lines in one of the eight possible octants.
|
||||
|
||||
#### Strengths and Weaknesses {#Heading5}
|
||||
#### Strengths and Weaknesses
|
||||
|
||||
The overwhelming strength of Bresenham's line-drawing algorithm is
|
||||
speed. With no divides, no floating-point operations, and no need for
|
||||
|
|
@ -266,7 +266,7 @@ acceptance the algorithm is certainly good enough.
|
|||
Then, too, users hate waiting for their computer to finish drawing. By
|
||||
any standard of drawing performance, Bresenham's algorithm excels.
|
||||
|
||||
### An Implementation in C {#Heading6}
|
||||
### An Implementation in C
|
||||
|
||||
It's time to get down and look at some actual working code. Listing 35.1
|
||||
is a C implementation of Bresenham's line-drawing algorithm for modes
|
||||
|
|
@ -556,7 +556,7 @@ void main()
|
|||
}
|
||||
```
|
||||
|
||||
#### Looking at EVGALine {#Heading7}
|
||||
#### Looking at EVGALine
|
||||
|
||||
The `EVGALine` function itself performs four operations. `EVGALine`
|
||||
first sets up the VGA's hardware so that all pixels drawn will be in the
|
||||
|
|
@ -654,7 +654,7 @@ modularity would improve, speed would suffer markedly.
|
|||
|
||||

|
||||
|
||||
#### Drawing Each Line {#Heading8}
|
||||
#### Drawing Each Line
|
||||
|
||||
The `Octant0` and `Octant1` functions draw lines for which
|
||||
|`DeltaX`| is greater than `DeltaY` and lines for which |`DeltaX`|
|
||||
|
|
@ -685,7 +685,7 @@ coordinate is advanced by either 1 or -1, depending on the value of
|
|||
`XDirection`. (This makes it possible for `Octant1` to draw lines in
|
||||
both octant 1 and octant 2.)
|
||||
|
||||
#### Drawing Each Pixel {#Heading9}
|
||||
#### Drawing Each Pixel
|
||||
|
||||
At the core of `Octant0` and `Octant1` is a pixel-drawing function,
|
||||
`EVGADot`. `EVGADot` draws a pixel at the specified coordinates in
|
||||
|
|
@ -765,7 +765,7 @@ pixel-drawing function for a different adapter, or a fundamentally
|
|||
different mode such as a 256-color SuperVGA mode, remember to remove the
|
||||
hardware-dependent `outportb` lines in `EVGALine` itself.
|
||||
|
||||
### Comments on the C Implementation {#Heading10}
|
||||
### Comments on the C Implementation
|
||||
|
||||
`EVGALine` does no error checking whatsoever. My assumption in writing
|
||||
`EVGALine` was that it would be ultimately used as the lowest-level
|
||||
|
|
@ -806,7 +806,7 @@ times the speed?
|
|||
Given which, a high-speed assembly language version of `EVGALine`
|
||||
would seem to be a logical next step.
|
||||
|
||||
### Bresenham's Algorithm in Assembly {#Heading11}
|
||||
### Bresenham's Algorithm in Assembly
|
||||
|
||||
Listing 35.3 is a high-performance implementation of Bresenham's
|
||||
algorithm, written entirely in assembly language. The code is callable
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 679-693
|
|||
---
|
||||
|
||||
## Chapter 36\
|
||||
The Good, the Bad, and the Run-Sliced {#Heading1}
|
||||
The Good, the Bad, and the Run-Sliced
|
||||
|
||||
### Faster Bresenham Lines with Run-Length Slice Line Drawing {#Heading2}
|
||||
### Faster Bresenham Lines with Run-Length Slice Line Drawing
|
||||
|
||||
Years ago, I worked at a company that asked me to write blazingly fast
|
||||
line-drawing code for an AutoCAD driver. I implemented the basic
|
||||
|
|
@ -107,7 +107,7 @@ algorithm, which just happens to be an excellent example of a minimized
|
|||
state machine. In case you're fuzzy on the good/bad performance thing,
|
||||
that's "good"—as in *fast*.
|
||||
|
||||
### Run-Length Slice Fundamentals {#Heading3}
|
||||
### Run-Length Slice Fundamentals
|
||||
|
||||
First off, I have a confession to make: I'm not sure that the algorithm
|
||||
I'll discuss is actually, precisely Bresenham's run-length slice
|
||||
|
|
@ -171,7 +171,7 @@ redundant work is preferable.
|
|||
|
||||

|
||||
|
||||
### Run-Length Slice Implementation {#Heading4}
|
||||
### Run-Length Slice Implementation
|
||||
|
||||
We know that for any line, a given run will always be one of two
|
||||
possible lengths. How, though, do we know which length to select?
|
||||
|
|
@ -242,7 +242,7 @@ actually write the pixel to display memory.
|
|||
|
||||
That's good.
|
||||
|
||||
### Run-Length Slice Details {#Heading5}
|
||||
### Run-Length Slice Details
|
||||
|
||||
A couple of run-length slice implementation details yet remain. First is
|
||||
the matter of how error-term turnover is detected. This is done in much
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 695-706
|
|||
---
|
||||
|
||||
## Chapter 37\
|
||||
Dead Cats and Lightning Lines {#Heading1}
|
||||
Dead Cats and Lightning Lines
|
||||
|
||||
### Optimizing Run-Length Slice Line Drawing in a Major Way {#Heading2}
|
||||
### Optimizing Run-Length Slice Line Drawing in a Major Way
|
||||
|
||||
As I write this, the wife, the kid, and I are in the throes of yet
|
||||
another lightning-quick transcontinental move, this time to Redmond,
|
||||
|
|
@ -56,7 +56,7 @@ Okay, but what's the point? The point is, if it isn't broken, don't fix
|
|||
it. And if it is broken, maybe that's all right, too. Which brings us,
|
||||
neat as a pin, to the topic of drawing lines in a serious hurry.
|
||||
|
||||
### Fast Run-Length Slice Line Drawing {#Heading3}
|
||||
### Fast Run-Length Slice Line Drawing
|
||||
|
||||
In the last chapter, we examined the principles of run-length slice line
|
||||
drawing, which draws lines a run at a time rather than a pixel at a
|
||||
|
|
@ -418,7 +418,7 @@ _LineDraw endp
|
|||
end
|
||||
```
|
||||
|
||||
#### How Fast Is Fast? {#Heading4}
|
||||
#### How Fast Is Fast?
|
||||
|
||||
Your first question is likely to be the following: Just how fast is
|
||||
Listing 37.1? Is it optimized to the hilt or just pretty fast? The quick
|
||||
|
|
@ -483,7 +483,7 @@ basically a waste of time.
|
|||
|
||||
Profile before you optimize.
|
||||
|
||||
#### Further Optimizations {#Heading5}
|
||||
#### Further Optimizations
|
||||
|
||||
Following is a quick tour of some of the many possible further
|
||||
optimizations to Listing 37.1.
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 707-721
|
|||
---
|
||||
|
||||
## Chapter 38\
|
||||
The Polygon Primeval {#Heading1}
|
||||
The Polygon Primeval
|
||||
|
||||
### Drawing Polygons Efficiently and Quickly {#Heading2}
|
||||
### Drawing Polygons Efficiently and Quickly
|
||||
|
||||
*"Give me but one firm spot on which to stand, and I will move the
|
||||
Earth."*
|
||||
|
|
@ -44,7 +44,7 @@ boost performance into the realm of the sublime.
|
|||
|
||||
And slow computer graphics is scarcely worth the bother.
|
||||
|
||||
### Filled Polygons {#Heading3}
|
||||
### Filled Polygons
|
||||
|
||||
A polygon is simply a shape formed by lines laid end to end to form a
|
||||
continuous, closed path. A polygon is filled by setting all pixels
|
||||
|
|
@ -79,7 +79,7 @@ widely useful and will serve well to introduce some of the subtler
|
|||
complexities of polygon drawing, not the least of which is the slippery
|
||||
concept of "inside."
|
||||
|
||||
#### Which Side Is Inside? {#Heading4}
|
||||
#### Which Side Is Inside?
|
||||
|
||||
The basic principle of polygon filling is decomposing each polygon into
|
||||
a series of horizontal lines, one for each horizontal row of pixels, or
|
||||
|
|
@ -139,7 +139,7 @@ to the boundary lines just won't do for drawing images composed of
|
|||
fitted-together polygons. And because fitting polygons together is
|
||||
exactly what I have in mind, we need a different approach.
|
||||
|
||||
#### How Do You Fit Polygons Together? {#Heading5}
|
||||
#### How Do You Fit Polygons Together?
|
||||
|
||||
How, then, do you fit polygons together? *Very* carefully. First, the
|
||||
line-tracing algorithm must be adjusted so that it selects only those
|
||||
|
|
@ -185,7 +185,7 @@ together seamlessly.
|
|||
For our purposes, nonoverlapping polygons are the way to go, so let's
|
||||
have at them.
|
||||
|
||||
### Filling Non-Overlapping Convex Polygons {#Heading6}
|
||||
### Filling Non-Overlapping Convex Polygons
|
||||
|
||||
Without further ado, Listing 38.1 contains a function,
|
||||
`FillConvexPolygon`, that accepts a list of points that describe a
|
||||
|
|
@ -658,7 +658,7 @@ Once the two edges are scan-converted, the whole line list is passed to
|
|||
|
||||
Finis.
|
||||
|
||||
### Oddball Cases {#Heading7}
|
||||
### Oddball Cases
|
||||
|
||||
Listing 38.1 handles zero-length segments (multiple vertices at the same
|
||||
location) by ignoring them, which will be useful down the road because
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 723-738
|
|||
---
|
||||
|
||||
## Chapter 39\
|
||||
Fast Convex Polygons {#Heading1}
|
||||
Fast Convex Polygons
|
||||
|
||||
### Filling Polygons in a Hurry {#Heading2}
|
||||
### Filling Polygons in a Hurry
|
||||
|
||||
In the previous chapter, we explored the surprisingly intricate process
|
||||
of filling convex polygons. Now we're going to fill them an order of
|
||||
|
|
@ -92,7 +92,7 @@ to understand how things work, especially when they're very visible
|
|||
parts of the software you develop. That said, let's learn more about
|
||||
filling convex polygons.
|
||||
|
||||
### Fast Convex Polygon Filling {#Heading3}
|
||||
### Fast Convex Polygon Filling
|
||||
|
||||
In addressing the topic of filling convex polygons in the previous
|
||||
chapter, the implementation we came up with met all of our functional
|
||||
|
|
@ -118,7 +118,7 @@ time was spent drawing and the other half was spent tracing the polygon
|
|||
edges (the time spent in `FillConvexPolygon` was relatively
|
||||
minuscule), so we have our choice of where to begin optimizing.
|
||||
|
||||
#### Fast Drawing {#Heading4}
|
||||
#### Fast Drawing
|
||||
|
||||
Let's start with drawing, which is easily sped up. The previous
|
||||
chapter's code used a double-nested loop that called a draw-pixel
|
||||
|
|
@ -239,7 +239,7 @@ time. There are cycles yet to be had in the drawing code, but as tracing
|
|||
polygon edges now takes 92 percent of the polygon filling time, it's
|
||||
logical to optimize the tracing code next.
|
||||
|
||||
#### Fast Edge Tracing {#Heading5}
|
||||
#### Fast Edge Tracing
|
||||
|
||||
There's no secret as to why last chapter's `ScanEdge` was so slow: It
|
||||
used floating point calculations. One secret of fast graphics is using
|
||||
|
|
@ -401,7 +401,7 @@ void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart,
|
|||
}
|
||||
```
|
||||
|
||||
### The Finishing Touch: Assembly Language {#Heading6}
|
||||
### The Finishing Touch: Assembly Language
|
||||
|
||||
The C implementation in Listing 39.2 is now nearly 20 times as fast as
|
||||
the original, which is good enough for most purposes. Still, it requires
|
||||
|
|
@ -532,7 +532,7 @@ _DrawHorizontalLineList endp
|
|||
end
|
||||
```
|
||||
|
||||
#### Maximizing REP STOS {#Heading7}
|
||||
#### Maximizing REP STOS
|
||||
|
||||
Listing 39.3 doesn't take the easy way out and use `REP STOSB` to fill
|
||||
each scan line; instead, it uses `REP STOSW` to fill as many pixel
|
||||
|
|
@ -549,7 +549,7 @@ word-at-a-time filling, resulting in extra overhead. For very small or
|
|||
narrow polygons, that overhead might overwhelm the advantage of drawing
|
||||
a word at a time, making plain old `REP STOSB` faster.
|
||||
|
||||
### Faster Edge Tracing {#Heading8}
|
||||
### Faster Edge Tracing
|
||||
|
||||
Finally, Listing 39.4 is an assembly language version of `ScanEdge`.
|
||||
Listing 39.4 is a relatively straightforward translation from C to
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 739-756
|
|||
---
|
||||
|
||||
## Chapter 40\
|
||||
Of Songs, Taxes, and the Simplicity of Complex Polygons {#Heading1}
|
||||
Of Songs, Taxes, and the Simplicity of Complex Polygons
|
||||
|
||||
### Dealing with Irregular Polygonal Areas {#Heading2}
|
||||
### Dealing with Irregular Polygonal Areas
|
||||
|
||||
Every so often, my daughter asks me to sing her to sleep. (If you've
|
||||
ever heard me sing, this may cause you concern about either her hearing
|
||||
|
|
@ -43,7 +43,7 @@ where everything fits together as if preordained.
|
|||
|
||||
Filling arbitrary polygons is such a case.
|
||||
|
||||
### Filling Arbitrary Polygons {#Heading3}
|
||||
### Filling Arbitrary Polygons
|
||||
|
||||
In Chapter 38, I described three types of polygons: convex, nonconvex,
|
||||
and complex. *The RenderMan Companion*, a terrific book by Steve Upstill
|
||||
|
|
@ -73,7 +73,7 @@ all polygons; in such a case, the polygon filler will use the slow
|
|||
complex-fill code even if the polygon is, in fact, a convex polygon. In
|
||||
Chapter 41, I'll discuss one way to improve this situation.
|
||||
|
||||
#### Active Edges {#Heading4}
|
||||
#### Active Edges
|
||||
|
||||
The basic premise of filling a complex polygon is that for a given scan
|
||||
line, we determine all intersections between the polygon's edges and
|
||||
|
|
@ -459,7 +459,7 @@ static void ScanOutAET(int YToScan, int Color) {
|
|||
}
|
||||
```
|
||||
|
||||
### Complex Polygon Filling: An Implementation {#Heading5}
|
||||
### Complex Polygon Filling: An Implementation
|
||||
|
||||
Listing 40.1 just shown presents a function, `FillPolygon()`, that
|
||||
fills polygons of all shapes. If `CONVEX_FILL_LINKED` is defined,
|
||||
|
|
@ -665,7 +665,7 @@ this way elsewhere. The boundary filling approach in Foley and van Dam
|
|||
is similar, but seems to me to not draw all boundary and vertex pixels
|
||||
once and only once.
|
||||
|
||||
#### More on Active Edges {#Heading6}
|
||||
#### More on Active Edges
|
||||
|
||||
Edges of zero height—horizontal edges and edges defined by two vertices
|
||||
at the same location—never even make it into the GET in Listing 40.1. A
|
||||
|
|
@ -674,7 +674,7 @@ never intersect a scan line; it can only run along the scan line, and
|
|||
the span it runs along is defined not by that edge but by the edges that
|
||||
connect to its endpoints.
|
||||
|
||||
#### Performance Considerations {#Heading7}
|
||||
#### Performance Considerations
|
||||
|
||||
How fast is Listing 40.1? When drawing triangles on a 20-MHz 386, it's
|
||||
less than one-fifth the speed of the fast convex polygon fill code.
|
||||
|
|
@ -777,7 +777,7 @@ spent sorting the AET.
|
|||
> example of the need to keep an overall perspective when comparing the
|
||||
> theoretical characteristics of various approaches.
|
||||
|
||||
### Nonconvex Polygons {#Heading8}
|
||||
### Nonconvex Polygons
|
||||
|
||||
Nonconvex polygons can be filled somewhat faster than complex polygons.
|
||||
Because edges never cross or switch positions with other edges once
|
||||
|
|
@ -789,7 +789,7 @@ slopes must be compared to determine which edge is leftmost. This is
|
|||
certainly doable, but because of space limitations and limited
|
||||
performance returns, I haven't implemented this in Listing 40.1.
|
||||
|
||||
#### Details, Details {#Heading9}
|
||||
#### Details, Details
|
||||
|
||||
Every so often, a programming demon that I'd thought I'd forever laid to
|
||||
rest arises to haunt me once again. A minor example of this—an imp, if
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 757-771
|
|||
---
|
||||
|
||||
## Chapter 41\
|
||||
Those Way-Down Polygon Nomenclature Blues {#Heading1}
|
||||
Those Way-Down Polygon Nomenclature Blues
|
||||
|
||||
### Names Do Matter when You Conceptualize a Data Structure {#Heading2}
|
||||
### Names Do Matter when You Conceptualize a Data Structure
|
||||
|
||||
After I wrote the columns on polygons in *Dr. Dobb's Journal* that
|
||||
became Chapters 38-40, long-time reader Bill Huber wrote to take me to
|
||||
|
|
@ -63,7 +63,7 @@ who could be among your most astute readers—those who already have been
|
|||
trained in the same or a related field." Ditto. Likewise. *D'accord*.
|
||||
And *mea culpa* ; I shall endeavor to watch my language in the future.
|
||||
|
||||
### Nomenclature in Action {#Heading3}
|
||||
### Nomenclature in Action
|
||||
|
||||
Just to show you how much difference proper description and interchange
|
||||
of ideas can make, consider the case of identifying convex polygons.
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 773-792
|
|||
---
|
||||
|
||||
## Chapter 42\
|
||||
Wu'ed in Haste; Fried, Stewed at Leisure {#Heading1}
|
||||
Wu'ed in Haste; Fried, Stewed at Leisure
|
||||
|
||||
### Fast Antialiased Lines Using Wu's Algorithm {#Heading2}
|
||||
### Fast Antialiased Lines Using Wu's Algorithm
|
||||
|
||||
The thought first popped into my head as I unenthusiastically picked
|
||||
through the salad bar at a local "family" restaurant, trying to decide
|
||||
|
|
@ -74,7 +74,7 @@ be accomplished on inexpensive, mass-market hardware with the proper
|
|||
programming perspective. In short, it's a splendid example of
|
||||
appropriate technology for PCs.
|
||||
|
||||
### Wu Antialiasing {#Heading3}
|
||||
### Wu Antialiasing
|
||||
|
||||
Antialiasing, as we've been discussing for the past few chapters, is the
|
||||
process of smoothing lines and edges so that they appear less jagged.
|
||||
|
|
@ -128,7 +128,7 @@ possible drawing speed—of an AT-bus VGA. In short, Wu antialiasing is
|
|||
about as fast an antialiased line approach as you could ever hope to
|
||||
find for the VGA.
|
||||
|
||||
### Tracing and Intensity in One {#Heading4}
|
||||
### Tracing and Intensity in One
|
||||
|
||||
Horizontal, vertical, and diagonal lines do not require Wu antialiasing
|
||||
because they pass through the center of every pixel they meet; such
|
||||
|
|
@ -331,7 +331,7 @@ void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels,
|
|||
}
|
||||
```
|
||||
|
||||
### Sample Wu Antialiasing {#Heading5}
|
||||
### Sample Wu Antialiasing
|
||||
|
||||
The true test of any antialiasing technique is how good it looks, so
|
||||
let's have a look at Wu antialiasing in action. Listing 42.1 is a C
|
||||
|
|
@ -929,7 +929,7 @@ _DrawWuLine endp
|
|||
end
|
||||
```
|
||||
|
||||
#### Notes on Wu Antialiasing {#Heading6}
|
||||
#### Notes on Wu Antialiasing
|
||||
|
||||
Wu antialiasing can be applied to any curve for which it's possible to
|
||||
calculate at each step the positions and intensities of two bracketing
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 793-815
|
|||
---
|
||||
|
||||
## Chapter 43\
|
||||
Bit-Plane Animation {#Heading1}
|
||||
Bit-Plane Animation
|
||||
|
||||
### A Simple and Extremely Fast Animation Method for Limited Color {#Heading2}
|
||||
### A Simple and Extremely Fast Animation Method for Limited Color
|
||||
|
||||
When it comes to computers, my first love is animation. There's nothing
|
||||
quite like the satisfaction of fooling the eye and creating a miniature
|
||||
|
|
@ -87,7 +87,7 @@ resources, and that you can do remarkable things if you understand those
|
|||
resources and come up with creative ways to put them to work at specific
|
||||
tasks.
|
||||
|
||||
### Bit-Planes: The Basics {#Heading3}
|
||||
### Bit-Planes: The Basics
|
||||
|
||||
The underlying principle of bit-plane animation is extremely simple. The
|
||||
VGA has four separate bit planes in modes 0DH, 0EH, 10H, and 12H. Plane
|
||||
|
|
@ -141,7 +141,7 @@ You bet.
|
|||
|
||||

|
||||
|
||||
#### Stacking the Palette Registers {#Heading4}
|
||||
#### Stacking the Palette Registers
|
||||
|
||||
Suppose that instead of viewing the four bits per pixel coming out of
|
||||
display memory as selecting one of sixteen colors,we view those bits as
|
||||
|
|
@ -217,7 +217,7 @@ chosen to make plane 0 the highest precedence only because it seems
|
|||
simplest to think of plane 0 as appearing in front of plane 1, which is
|
||||
in front of plane 2, which is in front of plane 3.
|
||||
|
||||
### Bit-Plane Animation in Action {#Heading5}
|
||||
### Bit-Plane Animation in Action
|
||||
|
||||
Without further ado, Listing 43.1 shows bit-plane animation in action.
|
||||
Listing 43.1 animates 13 rather large images (each 32 pixels on a side)
|
||||
|
|
@ -793,7 +793,7 @@ Bit-plane animation with bit-aligned images and internal animation can
|
|||
look truly spectacular. It's a sight worth seeing, particularly for
|
||||
those who doubt the PC's worth when it comes to animation.
|
||||
|
||||
### Limitations of Bit-Plane Animation {#Heading6}
|
||||
### Limitations of Bit-Plane Animation
|
||||
|
||||
As I've said, bit-plane animation is not perfect. For starters,
|
||||
bit-plane animation can only be used in the VGA's planar modes, modes
|
||||
|
|
@ -877,7 +877,7 @@ the images in a given plane marching along in step in a continuous band.
|
|||
The images could never overlap, so bit-plane animation would produce
|
||||
very high image quality.
|
||||
|
||||
### Shearing and Page Flipping {#Heading7}
|
||||
### Shearing and Page Flipping
|
||||
|
||||
As Listing 43.1 runs, you may occasionally see an image shear, with the
|
||||
top and bottom parts of the image briefly offset. This is a consequence
|
||||
|
|
@ -942,7 +942,7 @@ conjunction with page flipping, bit-plane animation looks a little
|
|||
better but is slower, and the overall animation scheme is more difficult
|
||||
to implement and perhaps a bit less reliable on some computers.
|
||||
|
||||
### Beating the Odds in the Jaw-Dropping Contest {#Heading8}
|
||||
### Beating the Odds in the Jaw-Dropping Contest
|
||||
|
||||
Bit-plane animation is neat stuff. Heck, good animation of *any* sort is
|
||||
fun, and the PC is as good a place as any (well, almost any) to make
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 817-837
|
|||
---
|
||||
|
||||
## Chapter 44\
|
||||
Split Screens Save the Page Flipped Day {#Heading1}
|
||||
Split Screens Save the Page Flipped Day
|
||||
|
||||
### 640x480 Page Flipped Animation in 64K...Almost {#Heading2}
|
||||
### 640x480 Page Flipped Animation in 64K...Almost
|
||||
|
||||
Almost doesn't count, they say—at least in horseshoes and maybe a few
|
||||
other things. This is especially true in digital circles, where if you
|
||||
|
|
@ -38,7 +38,7 @@ groundwork first. Or maybe a lot of groundwork.
|
|||
|
||||
No horseshoes here.
|
||||
|
||||
#### A Plethora of Challenges {#Heading3}
|
||||
#### A Plethora of Challenges
|
||||
|
||||
In its simplest terms, computer animation consists of rapidly redrawing
|
||||
similar images at slightly differing locations, so that the eye
|
||||
|
|
@ -66,7 +66,7 @@ invisible to the user; only the end result should ever be seen. Both of
|
|||
these requirements are met by the program presented in Listings 44.1 and
|
||||
44.2.
|
||||
|
||||
#### A Page Flipping Animation Demonstration {#Heading4}
|
||||
#### A Page Flipping Animation Demonstration
|
||||
|
||||
The listings taken together form a sample animation program, in which a
|
||||
single object bounces endlessly off other objects, with instructions and
|
||||
|
|
@ -977,7 +977,7 @@ screens drawn and cross-referencing that to the BIOS timer count
|
|||
periodically, accelerating the overall pace of the animation (moving
|
||||
farther each time and the like) if updates are happening too slowly.
|
||||
|
||||
#### Enter the Split Screen {#Heading9}
|
||||
#### Enter the Split Screen
|
||||
|
||||
So far, I've discussed page flipping in 640x350 mode. There's a reason
|
||||
for that: 640x350 is the highest-resolution standard mode in which
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 839-857
|
|||
---
|
||||
|
||||
## Chapter 45\
|
||||
Dog Hair and Dirty Rectangles {#Heading1}
|
||||
Dog Hair and Dirty Rectangles
|
||||
|
||||
### Different Angles on Animation {#Heading2}
|
||||
### Different Angles on Animation
|
||||
|
||||
We brought our pets with us when we moved to Seattle. At about the same
|
||||
time, our Golden Retriever, Sam, observed his third birthday. Sam is
|
||||
|
|
@ -55,7 +55,7 @@ joyously knocks down any stranger who makes the mistake of glancing in
|
|||
his direction, and will, quite possibly, be booked any day now on
|
||||
suspicion of homicide by licking.
|
||||
|
||||
### Plus ça Change {#Heading3}
|
||||
### Plus ça Change
|
||||
|
||||
Okay, you give up. What exactly does this have to do with graphics? I'm
|
||||
glad you asked. The lesson to be learned from Sam, The Dog With A Brain
|
||||
|
|
@ -86,7 +86,7 @@ least one pair of underwear without a single hole in it. Which brings
|
|||
us, deus ex machina and the creek don't rise, to yet another animation
|
||||
method: dirty-rectangle animation.
|
||||
|
||||
### VGA Access Times {#Heading4}
|
||||
### VGA Access Times
|
||||
|
||||
Actually, before we get to dirty rectangles, I'd like to take you
|
||||
through a quick refresher on VGA memory and I/O access times. I want to
|
||||
|
|
@ -162,7 +162,7 @@ memory if you don't have to. Write each pixel once and only once.
|
|||
It is indeed a strange concept: The key to fast graphics is staying away
|
||||
from the graphics adapter as much as possible.
|
||||
|
||||
### Dirty-Rectangle Animation {#Heading5}
|
||||
### Dirty-Rectangle Animation
|
||||
|
||||
The relative slowness of VGA hardware is part of the appeal of the
|
||||
technique that I call "dirty-rectangle" animation, in which a complete
|
||||
|
|
@ -192,7 +192,7 @@ solves these problems.
|
|||
|
||||

|
||||
|
||||
#### So Why Not Use Page Flipping? {#Heading6}
|
||||
#### So Why Not Use Page Flipping?
|
||||
|
||||
Well, then, if we want good visual quality, why not use page flipping?
|
||||
For one thing, not all adapters and all modes support page flipping. The
|
||||
|
|
@ -234,7 +234,7 @@ happen, it's still possible to have the images in the various dirty
|
|||
rectangles show up non-simultaneously. In my experience, this latter
|
||||
phenomenon is not a serious problem, but do be aware of it.
|
||||
|
||||
### Dirty Rectangles in Action {#Heading7}
|
||||
### Dirty Rectangles in Action
|
||||
|
||||
Listing 45.1 demonstrates dirty-rectangle animation. This is a very
|
||||
simple implementation, in several respects. For one thing, it's written
|
||||
|
|
@ -540,7 +540,7 @@ graphics and by items such as scoreboards and status screens, but look
|
|||
closely and see if the animation region in your favorite game isn't
|
||||
smaller than you thought.
|
||||
|
||||
### Hi-Res VGA Page Flipping {#Heading8}
|
||||
### Hi-Res VGA Page Flipping
|
||||
|
||||
On a standard VGA, hi-res mode is mode 12H, which offers 640x480
|
||||
resolution with 16 colors. That's a nice mode, with plenty of pixels,
|
||||
|
|
@ -762,7 +762,7 @@ The 640x400 mode I've described here isn't exactly earthshaking, but it
|
|||
can come in handy for page flipping and CGA emulation, and I'm sure that
|
||||
some of you will find it useful at one time or another.
|
||||
|
||||
### Another Interesting Twist on Page Flipping {#Heading9}
|
||||
### Another Interesting Twist on Page Flipping
|
||||
|
||||
I've spent a fair amount of time exploring various ways to do animation.
|
||||
I thought I had pegged all the possible ways to do animation:
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 859-874
|
|||
---
|
||||
|
||||
## Chapter 46\
|
||||
Who Was that Masked Image? {#Heading1}
|
||||
Who Was that Masked Image?
|
||||
|
||||
### Optimizing Dirty-Rectangle Animation {#Heading2}
|
||||
### Optimizing Dirty-Rectangle Animation
|
||||
|
||||
Programming is, by and large, a linear process. One statement or
|
||||
instruction follows another, in predictable sequences, with tiny
|
||||
|
|
@ -61,7 +61,7 @@ We're strange thinking machines, but we're the best ones yet invented,
|
|||
and it's worth learning how to tap our full potential. And with that,
|
||||
it's back to dirty-rectangle animation.
|
||||
|
||||
#### Dirty-Rectangle Animation, Continued {#Heading3}
|
||||
#### Dirty-Rectangle Animation, Continued
|
||||
|
||||
In the last chapter, Introduced the idea of dirty-rectangle animation.
|
||||
This technique is an alternative to page flipping that's capable of
|
||||
|
|
@ -618,7 +618,7 @@ RowLoop3:
|
|||
end
|
||||
```
|
||||
|
||||
#### Masked Images {#Heading4}
|
||||
#### Masked Images
|
||||
|
||||
Masked images are rendered by drawing an object's pixels through a mask;
|
||||
pixels are actually drawn only where the mask specifies that drawing is
|
||||
|
|
@ -654,7 +654,7 @@ one color undrawable. Also, with a transparent color, it's not possible
|
|||
to keep the same base image but use different masks, because the mask
|
||||
information is embedded in the image data.
|
||||
|
||||
#### Internal Animation {#Heading5}
|
||||
#### Internal Animation
|
||||
|
||||
I've added another feature essential to producing convincing animation:
|
||||
*internal animation*, which is the process of changing the appearance of
|
||||
|
|
@ -720,7 +720,7 @@ minimal cost. You might then decide to ignore overlapped drawing between
|
|||
different images, which tends to be both less common and more expensive
|
||||
to identify and handle.
|
||||
|
||||
#### Drawing Order and Visual Quality {#Heading7}
|
||||
#### Drawing Order and Visual Quality
|
||||
|
||||
A final note on dirty-rectangle animation concerns the quality of the
|
||||
displayed screen image. In the last chapter, we simply stuffed dirty
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 875-893
|
|||
---
|
||||
|
||||
## Chapter 47\
|
||||
Mode X: 256-Color VGA Magic {#Heading1}
|
||||
Mode X: 256-Color VGA Magic
|
||||
|
||||
### Introducing the VGA's Undocumented "Animation-Optimal" Mode {#Heading2}
|
||||
### Introducing the VGA's Undocumented "Animation-Optimal" Mode
|
||||
|
||||
At a book signing for my book *Zen of Code Optimization*, an attractive
|
||||
young woman came up to me, holding my book, and said, "You're Michael
|
||||
|
|
@ -60,7 +60,7 @@ two chapters are based on the *DDJ* columns that started it all back in
|
|||
spawned a ton of games, and about which I still regularly get letters
|
||||
and e-mail. Ladies and gentlemen, I give you...Mode X.
|
||||
|
||||
### What Makes Mode X Special? {#Heading3}
|
||||
### What Makes Mode X Special?
|
||||
|
||||
Consider the strange case of the VGA's 320x256-color mode—Mode X—which
|
||||
is undeniably complex to program and isn't even documented by IBM—but
|
||||
|
|
@ -143,7 +143,7 @@ animation program that shows many of the features of Mode X in action.
|
|||
|
||||
The mode set code is the logical place to begin.
|
||||
|
||||
### Selecting 320x240 256-Color Mode {#Heading4}
|
||||
### Selecting 320x240 256-Color Mode
|
||||
|
||||
We could, if we wished, write our own mode set code for Mode X from
|
||||
scratch—but why bother? Instead, we'll let the BIOS do most of the work
|
||||
|
|
@ -440,7 +440,7 @@ _ReadPixelX endp
|
|||
end
|
||||
```
|
||||
|
||||
### Designing from a Mode X Perspective {#Heading5}
|
||||
### Designing from a Mode X Perspective
|
||||
|
||||
Listing 47.4 shows Mode X rectangle fill code. The plane is selected for
|
||||
each pixel in turn, with drawing cycling from plane 0 to plane 3, then
|
||||
|
|
@ -711,7 +711,7 @@ _FillRectangleX endp
|
|||
end
|
||||
```
|
||||
|
||||
### Hardware Assist from an Unexpected Quarter {#Heading6}
|
||||
### Hardware Assist from an Unexpected Quarter
|
||||
|
||||
Listing 47.5 illustrates the benefits of designing code from a Mode X
|
||||
perspective; this is the software aspect of Mode X optimization, which
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 895-911
|
|||
---
|
||||
|
||||
## Chapter 48\
|
||||
Mode X Marks the Latch {#Heading1}
|
||||
Mode X Marks the Latch
|
||||
|
||||
### The Internals of Animation's Best Video Display Mode {#Heading2}
|
||||
### The Internals of Animation's Best Video Display Mode
|
||||
|
||||
In the previous chapter, I introduced you to what I call Mode X, an
|
||||
undocumented 320x240 256-color mode of the VGA. Mode X is distinguished
|
||||
|
|
@ -320,7 +320,7 @@ handle, because the latches are four pixels wide; one possible solution
|
|||
is expanding such patterns via repetition until they are
|
||||
multiple-of-four widths.)
|
||||
|
||||
### Allocating Memory in Mode X {#Heading3}
|
||||
### Allocating Memory in Mode X
|
||||
|
||||
Listing 48.2 raises some interesting questions about the allocation of
|
||||
display memory in Mode X. In Listing 48.2, whenever a pattern is to be
|
||||
|
|
@ -365,7 +365,7 @@ the screen, with the screen becoming a scrolling window onto that larger
|
|||
bitmap. This technique has been used to good effect in a number of
|
||||
animated games, with and without the use of Mode X.
|
||||
|
||||
### Copying Pixel Blocks within Display Memory {#Heading4}
|
||||
### Copying Pixel Blocks within Display Memory
|
||||
|
||||
Another fine use for the latches is copying pixels from one place in
|
||||
display memory to another. Whenever both the source and the destination
|
||||
|
|
@ -588,7 +588,7 @@ memory-to-display memory copy routine can do us any good, we must have a
|
|||
way to get pixel patterns from system memory into display memory, so
|
||||
that they can then be copied with the fast copy routine.
|
||||
|
||||
#### Copying to Display Memory {#Heading5}
|
||||
#### Copying to Display Memory
|
||||
|
||||
The final piece of the puzzle is the system memory to
|
||||
display-memory-copy-routine shown in Listing 48.4. This routine assumes
|
||||
|
|
@ -732,7 +732,7 @@ _CopySystemToScreenX endp
|
|||
end
|
||||
```
|
||||
|
||||
### Who Was that Masked Image Copier? {#Heading6}
|
||||
### Who Was that Masked Image Copier?
|
||||
|
||||
At this point, it's getting to be time for us to take all the Mode X
|
||||
tools we've developed, together with one more tool—masked image
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 913-930
|
|||
---
|
||||
|
||||
## Chapter 49\
|
||||
Mode X 256-Color Animation {#Heading1}
|
||||
Mode X 256-Color Animation
|
||||
|
||||
### How to Make the VGA Really Get up and Dance {#Heading2}
|
||||
### How to Make the VGA Really Get up and Dance
|
||||
|
||||
Okay—no amusing stories or informative anecdotes to kick off this
|
||||
chapter; lotta ground to cover, gotta hurry—you're impatient, I can
|
||||
|
|
@ -33,7 +33,7 @@ computing, as Jeff Duntemann's writings make manifest. No lighthearted
|
|||
fluff for us; we have real work to do, for today we animate with 256
|
||||
colors in Mode X.
|
||||
|
||||
### Masked Copying {#Heading3}
|
||||
### Masked Copying
|
||||
|
||||
Over the past two chapters, we've put together most of the tools needed
|
||||
to implement animation in the VGA's undocumented 320x240 256-color Mode
|
||||
|
|
@ -212,7 +212,7 @@ _CopySystemToScreenMaskedX endp
|
|||
end
|
||||
```
|
||||
|
||||
#### Faster Masked Copying {#Heading4}
|
||||
#### Faster Masked Copying
|
||||
|
||||
In the previous chapter we saw how the VGA's latches can be used to copy
|
||||
four pixels at a time from one area of display memory to another in Mode
|
||||
|
|
@ -511,7 +511,7 @@ typedef struct {
|
|||
} MaskedImage;
|
||||
```
|
||||
|
||||
#### Notes on Masked Copying {#Heading5}
|
||||
#### Notes on Masked Copying
|
||||
|
||||
Listings 49.1 and 49.2, like all Mode X code I've presented, perform no
|
||||
clipping, because clipping code would complicate the listings too much.
|
||||
|
|
@ -541,7 +541,7 @@ used separate parameters for simplicity and flexibility.
|
|||
> instruction, and most VGAs respond to `OUT`s much more slowly than to
|
||||
> display memory writes.)
|
||||
|
||||
### Animation {#Heading6}
|
||||
### Animation
|
||||
|
||||
Gosh. There's just no way I can discuss high-level animation
|
||||
fundamentals in any detail here; I could spend an entire (and entirely
|
||||
|
|
@ -564,7 +564,7 @@ Some of the code in this chapter was adapted for Mode X from the code in
|
|||
Chapter 44—yet another reason to read that chapter before finishing this
|
||||
one.
|
||||
|
||||
### Mode X Animation in Action {#Heading7}
|
||||
### Mode X Animation in Action
|
||||
|
||||
Listing 49.5 ties together everything I've discussed about Mode X so far
|
||||
in a compact but surprisingly powerful animation package. Listing 49.5
|
||||
|
|
@ -893,7 +893,7 @@ _ShowPage endp
|
|||
end
|
||||
```
|
||||
|
||||
### Works Fast, Looks Great {#Heading8}
|
||||
### Works Fast, Looks Great
|
||||
|
||||
We now end our exploration of Mode X, although we'll use it again
|
||||
shortly for 3-D animation. Mode X admittedly has its complexities;
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 931-949
|
|||
---
|
||||
|
||||
## Chapter 50\
|
||||
Adding a Dimension {#Heading1}
|
||||
Adding a Dimension
|
||||
|
||||
### 3-D Animation Using Mode X {#Heading2}
|
||||
### 3-D Animation Using Mode X
|
||||
|
||||
When I first started programming micros, more than 11 years ago now,
|
||||
there wasn't much money in it, or visibility, or anything you could call
|
||||
|
|
@ -84,7 +84,7 @@ In a sense, I've saved the best for last, because, to my mind, real-time
|
|||
be done with a computer—and because, with today's hardware, it can in
|
||||
fact be done. Nay, it can be done amazingly well.
|
||||
|
||||
### References on 3-D Drawing {#Heading3}
|
||||
### References on 3-D Drawing
|
||||
|
||||
There are several good sources for information about 3-D graphics. Foley
|
||||
and van Dam's *Computer Graphics: Principles and Practice* (Second
|
||||
|
|
@ -113,7 +113,7 @@ you're just starting out, you might want to look at one and see if it
|
|||
helps you bridge the gap between the theory and implementation of 3-D
|
||||
graphics.
|
||||
|
||||
### The 3-D Drawing Pipeline {#Heading4}
|
||||
### The 3-D Drawing Pipeline
|
||||
|
||||
Each 3-D object that we'll handle will be built out of polygons that
|
||||
represent the surface of the object. Figure 50.1 shows the stages a
|
||||
|
|
@ -160,7 +160,7 @@ twice as far away as a coordinate of (0,0,-500).
|
|||
|
||||

|
||||
|
||||
#### Projection {#Heading5}
|
||||
#### Projection
|
||||
|
||||
Working backward from the final image, we want to take the vertices of a
|
||||
polygon, as transformed into view space, and project them to 2-D
|
||||
|
|
@ -182,7 +182,7 @@ projected X and Y coordinates to integers, appropriately clipped and
|
|||
adjusted as necessary to center the origin on the screen or otherwise
|
||||
map the image into a window, if desired.
|
||||
|
||||
#### Translation {#Heading6}
|
||||
#### Translation
|
||||
|
||||
*Translation* means adding X, Y, and Z offsets to a coordinate to move
|
||||
it linearly through space. Translation is as simple as it seems; it
|
||||
|
|
@ -193,7 +193,7 @@ the object may be located anywhere.
|
|||
|
||||

|
||||
|
||||
#### Rotation {#Heading7}
|
||||
#### Rotation
|
||||
|
||||
*Rotation* is the process of circularly moving coordinates around the
|
||||
origin. For our present purposes, it's necessary only to rotate objects
|
||||
|
|
@ -225,7 +225,7 @@ There's much more to be said about transformations and the supporting
|
|||
matrix math, but, in the interests of getting to working code in this
|
||||
chapter, I'll leave that to be discussed as the need arises.
|
||||
|
||||
### A Simple 3-D Example {#Heading8}
|
||||
### A Simple 3-D Example
|
||||
|
||||
At this point, we know enough to be able to put together a simple
|
||||
working 3-D animation example. The example will do nothing more
|
||||
|
|
@ -755,7 +755,7 @@ void main() {
|
|||
}
|
||||
```
|
||||
|
||||
#### Notes on the 3-D Animation Example {#Heading9}
|
||||
#### Notes on the 3-D Animation Example
|
||||
|
||||
The sample program transforms the polygon's vertices from object space
|
||||
to world space to view space to the screen, as described earlier. In
|
||||
|
|
@ -812,7 +812,7 @@ it rotates. This is temporal aliasing at its finest! We won't address
|
|||
antialiasing further, realtime antialiasing being decidedly nontrivial,
|
||||
but this should give you an idea of why antialiasing is so desirable.
|
||||
|
||||
### An Ongoing Journey {#Heading10}
|
||||
### An Ongoing Journey
|
||||
|
||||
In the next chapter, we'll assign fronts and backs to polygons, and
|
||||
start drawing only those that are facing the viewer. That will enable us
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 951-967
|
|||
---
|
||||
|
||||
## Chapter 51\
|
||||
Sneakers in Space {#Heading1}
|
||||
Sneakers in Space
|
||||
|
||||
### Using Backface Removal to Eliminate Hidden Surfaces {#Heading2}
|
||||
### Using Backface Removal to Eliminate Hidden Surfaces
|
||||
|
||||
As I'm fond of pointing out, computer animation isn't a matter of
|
||||
mathematically exact modeling or raw technical prowess, but rather of
|
||||
|
|
@ -68,7 +68,7 @@ being tipped off—which is, of course, the whole point.)
|
|||
If it's good enough for George Lucas, it's good enough for us. And with
|
||||
that, let's resume our quest for realtime 3-D animation on the PC.
|
||||
|
||||
### One-sided Polygons: Backface Removal {#Heading3}
|
||||
### One-sided Polygons: Backface Removal
|
||||
|
||||
In the previous chapter, we implemented the basic polygon drawing
|
||||
pipeline, transforming a polygon all the way from its basic definition
|
||||
|
|
@ -181,7 +181,7 @@ the reason it's preferable to work in screen space rather than screen
|
|||
coordinates (which suffer from rounding problems), speed considerations
|
||||
aside.
|
||||
|
||||
#### Backface Removal in Action {#Heading4}
|
||||
#### Backface Removal in Action
|
||||
|
||||
Listings 51.1 through 51.5 together form a program that rotates a solid
|
||||
cube in real-time under user control. Listing 51.1 is the main program;
|
||||
|
|
@ -514,7 +514,7 @@ approach; over two-thirds of the overall time is spent in floating-point
|
|||
calculations, and it's there that we'll begin to attack the performance
|
||||
bottleneck we find ourselves up against.
|
||||
|
||||
### Incremental Transformation {#Heading5}
|
||||
### Incremental Transformation
|
||||
|
||||
Listing 51.4 contains three functions; each concatenates an additional
|
||||
rotation around one of the three axes to an existing rotation. To
|
||||
|
|
@ -690,7 +690,7 @@ extern int DisplayedPage, NonDisplayedPage;
|
|||
extern struct Rect EraseRect[];
|
||||
```
|
||||
|
||||
### A Note on Rounding Negative Numbers {#Heading6}
|
||||
### A Note on Rounding Negative Numbers
|
||||
|
||||
In the previous chapter, I added 0.5 and truncated in order to round
|
||||
values from floating-point to integer format. Here, in Listing 51.2,
|
||||
|
|
@ -698,7 +698,7 @@ I've switched to adding 0.5 and using the `floor()` function. For
|
|||
positive values, the two approaches are equivalent; for negative values,
|
||||
only the `floor()` approach works properly.
|
||||
|
||||
### Object Representation {#Heading7}
|
||||
### Object Representation
|
||||
|
||||
Each object consists of a list of vertices and a list of faces, with the
|
||||
vertices of each face defined by pointers into the vertex list; this
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 969-987
|
|||
---
|
||||
|
||||
## Chapter 52\
|
||||
Fast 3-D Animation: Meet X-Sharp {#Heading1}
|
||||
Fast 3-D Animation: Meet X-Sharp
|
||||
|
||||
### The First Iteration of a Generalized 3-D Animation Package {#Heading2}
|
||||
### The First Iteration of a Generalized 3-D Animation Package
|
||||
|
||||
Across the lake from Vermont, a few miles into upstate New York, the
|
||||
Ausable River has carved out a fairly impressive gorge known as "Ausable
|
||||
|
|
@ -68,7 +68,7 @@ easy to add new and different sorts of objects. Taken together, these
|
|||
alterations will let us start to do some really interesting real-time
|
||||
animation.
|
||||
|
||||
### This Chapter's Demo Program {#Heading3}
|
||||
### This Chapter's Demo Program
|
||||
|
||||
Three-dimensional animation is a complicated business, and it takes an
|
||||
astonishing amount of functionality just to get off the launching pad:
|
||||
|
|
@ -795,7 +795,7 @@ extern Object *ObjectList[];
|
|||
extern Point3 CubeVerts[];
|
||||
```
|
||||
|
||||
### A New Animation Framework: X-Sharp {#Heading4}
|
||||
### A New Animation Framework: X-Sharp
|
||||
|
||||
Listings 52.1 through 52.10 shown earlier represent not merely faster
|
||||
animation in library form, but also a nearly complete, extensible,
|
||||
|
|
@ -849,7 +849,7 @@ If you're intending to use X-Sharp in a real project, use the most
|
|||
recent version to be sure that you avail yourself of all new features
|
||||
and bug fixes.
|
||||
|
||||
### Three Keys to Realtime Animation Performance {#Heading5}
|
||||
### Three Keys to Realtime Animation Performance
|
||||
|
||||
As of the previous chapter, we were at the point where we could rotate,
|
||||
move, and draw a solid cube in real time. Not too shabby...but the code
|
||||
|
|
@ -912,7 +912,7 @@ Borland C++, and we're *still* doing sine and cosine via the
|
|||
floating-point emulator. Happily, we're still nowhere near the upper
|
||||
limit on the animation potential of the PC.
|
||||
|
||||
#### Drawbacks {#Heading6}
|
||||
#### Drawbacks
|
||||
|
||||
The techniques we've used to turbocharge 3-D animation are very
|
||||
powerful, but there's a dark side to them as well. Obviously, native 386
|
||||
|
|
@ -939,7 +939,7 @@ the viewer moves, so there's no chance for cumulative error. 3-D
|
|||
clipping with a front clip plane of -1 or less can prevent divide
|
||||
overflow.
|
||||
|
||||
#### Where the Time Goes {#Heading7}
|
||||
#### Where the Time Goes
|
||||
|
||||
The distribution of execution time in the animation code is no longer
|
||||
wildly biased toward transformation, but sine and cosine are certainly
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 989-1003
|
|||
---
|
||||
|
||||
## Chapter 53\
|
||||
Raw Speed and More {#Heading1}
|
||||
Raw Speed and More
|
||||
|
||||
### The Naked Truth About Speed in 3-D Animation {#Heading2}
|
||||
### The Naked Truth About Speed in 3-D Animation
|
||||
|
||||
Years ago, this friend of mine—let's call him Bert—went to Hawaii with
|
||||
three other fellows to celebrate their graduation from high school. This
|
||||
|
|
@ -52,7 +52,7 @@ elevator doors opened again. On the lobby.
|
|||
And with that, we come to this chapter's topics: raw speed and hidden
|
||||
surfaces.
|
||||
|
||||
### Raw Speed, Part 1: Assembly Language {#Heading3}
|
||||
### Raw Speed, Part 1: Assembly Language
|
||||
|
||||
I would like to state, here and for the record, that I am not an
|
||||
assembly language fanatic. Frankly, I prefer programming in C; assembly
|
||||
|
|
@ -478,7 +478,7 @@ ret
|
|||
end
|
||||
```
|
||||
|
||||
### Raw Speed, Part II: Look it Up {#Heading4}
|
||||
### Raw Speed, Part II: Look it Up
|
||||
|
||||
It's a funny thing about Turbo Profiler: Time spent in the Borland C++
|
||||
80x87 emulator doesn't show up directly anywhere that I can see in the
|
||||
|
|
@ -523,7 +523,7 @@ polygon filling soon, but for the moment, we have more than enough
|
|||
horsepower to have some fun with. First, though, we need one more
|
||||
feature: hidden surfaces.
|
||||
|
||||
#### Hidden Surfaces {#Heading5}
|
||||
#### Hidden Surfaces
|
||||
|
||||
So far, we've made a number of simplifying assumptions in order to get
|
||||
the animation to look good; for example, all objects must currently be
|
||||
|
|
@ -662,7 +662,7 @@ void SortObjects()
|
|||
}
|
||||
```
|
||||
|
||||
#### Rounding {#Heading6}
|
||||
#### Rounding
|
||||
|
||||
FIXED.ASM contains the equate **ROUNDING-ON**. When this equate is 1,
|
||||
the results of multiplications and divisions are rounded to the nearest
|
||||
|
|
@ -689,7 +689,7 @@ division is performed only in the course of projection, and the results
|
|||
do not accumulate over time, so it would be reasonable to disable
|
||||
rounding for division.
|
||||
|
||||
#### Having a Ball {#Heading7}
|
||||
#### Having a Ball
|
||||
|
||||
So far in our exploration of 3-D animation, we've had nothing to look at
|
||||
but triangles and cubes. It's time for something a little more visually
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1005-1029
|
|||
---
|
||||
|
||||
## Chapter 54\
|
||||
3-D Shading {#Heading1}
|
||||
3-D Shading
|
||||
|
||||
### Putting Realistic Surfaces on Animated 3-D Objects {#Heading2}
|
||||
### Putting Realistic Surfaces on Animated 3-D Objects
|
||||
|
||||
At the end of the previous chapter, X-Sharp had just acquired basic
|
||||
hidden-surface capability, and performance had been vastly improved
|
||||
|
|
@ -22,7 +22,7 @@ to add quite a bit more: support for 8088 and 80286 PCs, a general color
|
|||
model, and shading. That's an awful lot to cover in one chapter
|
||||
(actually, it'll spill over into the next chapter), so let's get to it!
|
||||
|
||||
### Support for Older Processors {#Heading3}
|
||||
### Support for Older Processors
|
||||
|
||||
To date, X-Sharp has run on only the 386 and 486, because it uses 32-bit
|
||||
multiply and divide instructions that sub-386 processors don't support.
|
||||
|
|
@ -940,7 +940,7 @@ _ConcatXforms endp
|
|||
end
|
||||
```
|
||||
|
||||
#### Shading {#Heading4}
|
||||
#### Shading
|
||||
|
||||
So far, the polygons out of which our animated objects have been built
|
||||
have had colors of fixed intensities. For example, a face of a cube
|
||||
|
|
@ -1168,7 +1168,7 @@ void DrawPObject(PObject * ObjectToXform)
|
|||
}
|
||||
```
|
||||
|
||||
#### Shading: Implementation Details {#Heading7}
|
||||
#### Shading: Implementation Details
|
||||
|
||||
In order to calculate the cosine of the angle between an incoming light
|
||||
source and a polygon's unit normal, we must first have the polygon's
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1031-1044
|
|||
---
|
||||
|
||||
## Chapter 55\
|
||||
Color Modeling in 256-Color Mode {#Heading1}
|
||||
Color Modeling in 256-Color Mode
|
||||
|
||||
### Pondering X-Sharp's Color Model in an RGB State of Mind {#Heading2}
|
||||
### Pondering X-Sharp's Color Model in an RGB State of Mind
|
||||
|
||||
Once she turned six, my daughter wanted some fairly sophisticated books
|
||||
read to her*. Wind in the Willows.* *Little House on the Prairie.*
|
||||
|
|
@ -44,7 +44,7 @@ color on a 256-color display adapter such as the VGA. Coincidentally,
|
|||
VGA color modeling just happens to be this chapter's topic, and the
|
||||
place to start is with color modeling in general.
|
||||
|
||||
#### A Color Model {#Heading3}
|
||||
#### A Color Model
|
||||
|
||||
We've been developing X-Sharp for several chapters now. In the previous
|
||||
chapter, we added illumination sources and shading; that addition makes
|
||||
|
|
@ -340,7 +340,7 @@ To experiment with a different 256-color model in X-Sharp, just change
|
|||
`ModelColorToColorIndex()` to map 24-bit RGB triplets into the palette
|
||||
you've set up. It's that simple, and the results can be striking indeed.
|
||||
|
||||
#### A Bonus from the BitMan {#Heading4}
|
||||
#### A Bonus from the BitMan
|
||||
|
||||
Finally, a note on fast VGA text, which came in from a correspondent who
|
||||
asked to be referred to simply as the BitMan. The BitMan passed along a
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1046-1059
|
|||
---
|
||||
|
||||
## Chapter 56\
|
||||
Pooh and the Space Station {#Heading1}
|
||||
Pooh and the Space Station
|
||||
|
||||
### Using Fast Texture Mapping to Place Pooh on a Polygon {#Heading2}
|
||||
### Using Fast Texture Mapping to Place Pooh on a Polygon
|
||||
|
||||
So, here's where Winnie the Pooh lives: in a space station orbiting
|
||||
Saturn. No, really; I have it straight from my daughter, and an
|
||||
|
|
@ -71,7 +71,7 @@ someone were standing in the window....
|
|||
|
||||
The rest is history.
|
||||
|
||||
### Principles of Quick-and-Dirty Texture Mapping {#Heading3}
|
||||
### Principles of Quick-and-Dirty Texture Mapping
|
||||
|
||||
The key to our texture-mapping approach will be to quickly determine
|
||||
what pixel value to draw for each pixel in the transformed destination
|
||||
|
|
@ -96,7 +96,7 @@ slower.
|
|||

|
||||
|
||||
#### Mapping Textures Made Easy {#Heading4}
|
||||
#### Mapping Textures Made Easy
|
||||
|
||||
To understand how we're going to map textures, consider Figure 56.2,
|
||||
which maps a bitmapped image directly onto an untransformed polygon.
|
||||
|
|
@ -190,7 +190,7 @@ the source image.*](images/56-04.jpg)
|
|||
|
||||

|
||||
|
||||
#### Notes on DDA Texture Mapping {#Heading5}
|
||||
#### Notes on DDA Texture Mapping
|
||||
|
||||
That's all there is to quick-and-dirty texture mapping. This technique
|
||||
basically uses a two-stage digital differential analyzer (DDA) approach
|
||||
|
|
@ -247,7 +247,7 @@ performs DDA texture mapping. First, though, I'd like to take a moment
|
|||
to thank Jim Kent, author of Autodesk Animator and a frequent
|
||||
correspondent, for getting me started with the DDA approach.
|
||||
|
||||
### Fast Texture Mapping: An Implementation {#Heading6}
|
||||
### Fast Texture Mapping: An Implementation
|
||||
|
||||
As you might expect, I've implemented DDA texture mapping in X-Sharp,
|
||||
and the changes are reflected in the X-Sharp archive in this chapter's
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1061-1075
|
|||
---
|
||||
|
||||
## Chapter 57\
|
||||
10,000 Freshly Sheared Sheep on the Screen {#Heading1}
|
||||
10,000 Freshly Sheared Sheep on the Screen
|
||||
|
||||
### The Critical Role of Experience in Implementing Fast, Smooth Texture Mapping {#Heading2}
|
||||
### The Critical Role of Experience in Implementing Fast, Smooth Texture Mapping
|
||||
|
||||
I recently spent an hour or so learning how to shear a sheep. Among
|
||||
other things, I learned—in great detail—about the importance of
|
||||
|
|
@ -42,7 +42,7 @@ graphics-programming approaches and algorithms. Second, computer
|
|||
graphics is a matter of illusion, of convincing the eye to see what you
|
||||
want it to see, and that's very much a black art based on experience.
|
||||
|
||||
#### Visual Quality: A Black Hole ... Er, Art {#Heading3}
|
||||
#### Visual Quality: A Black Hole ... Er, Art
|
||||
|
||||
Pleasing the eye with realtime computer animation is something less than
|
||||
a science, at least at the PC level, where there's a limited color
|
||||
|
|
@ -76,7 +76,7 @@ continuous motion, much like watching a badly flickering movie.
|
|||
> and blend the images together into continuous motion. Only experience
|
||||
> can give you a feel for that sweet spot.
|
||||
|
||||
#### Fixed-Point Arithmetic, Redux {#Heading4}
|
||||
#### Fixed-Point Arithmetic, Redux
|
||||
|
||||
In the previous chapter I added texture mapping to X-Sharp, but lacked
|
||||
space to explain some of its finer points. I'll pick up the thread now
|
||||
|
|
@ -135,7 +135,7 @@ Experience again: It's the difference between knowing which flaws (like
|
|||
small texture shifts) can reasonably be ignored, and which (like those
|
||||
that produce gaps between polygons) must be avoided at all costs.
|
||||
|
||||
#### Texture Mapping: Orientation Independence {#Heading5}
|
||||
#### Texture Mapping: Orientation Independence
|
||||
|
||||
The double-DDA texture-mapping code presented in the previous chapter
|
||||
worked adequately, but there were two things about it that left me less
|
||||
|
|
@ -249,7 +249,7 @@ void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge)
|
|||
}
|
||||
```
|
||||
|
||||
#### Mapping Textures across Multiple Polygons {#Heading6}
|
||||
#### Mapping Textures across Multiple Polygons
|
||||
|
||||
One of the truly nifty things about double-DDA texture mapping is that
|
||||
it is not limited to mapping a texture onto a single polygon. A single
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1077-1093
|
|||
---
|
||||
|
||||
## Chapter 58\
|
||||
Heinlein's Crystal Ball, Spock's Brain, and the 9-Cycle Dare {#Heading1}
|
||||
Heinlein's Crystal Ball, Spock's Brain, and the 9-Cycle Dare
|
||||
|
||||
### Using the Whole-Brain Approach to Accelerate Texture Mapping {#Heading2}
|
||||
### Using the Whole-Brain Approach to Accelerate Texture Mapping
|
||||
|
||||
I've had the pleasure recently of rereading several of the works of
|
||||
Robert A. Heinlein, and I'm as impressed as I was as a teenager—but in a
|
||||
|
|
@ -81,7 +81,7 @@ it.
|
|||
As Exhibit \#1, I present my experience with speeding up the texture
|
||||
mapper in X-Sharp.
|
||||
|
||||
### Texture Mapping Redux {#Heading3}
|
||||
### Texture Mapping Redux
|
||||
|
||||
We've spent the previous several chapters exploring the X Sharp graphics
|
||||
library, something I built over time as a serious exercise in 3-D
|
||||
|
|
@ -105,7 +105,7 @@ But 3 jumps *per pixel?* Hmph!"
|
|||
|
||||
It was the "Hmph" that really got to me.
|
||||
|
||||
#### Left-Brain Optimization {#Heading4}
|
||||
#### Left-Brain Optimization
|
||||
|
||||
That was the first shot of juice for my optimizer (or at least blow to
|
||||
my ego, which can be just as productive). John went on to say he had
|
||||
|
|
@ -223,7 +223,7 @@ don't you just draw vertical rather than horizontal scanlines?"
|
|||
|
||||
Why indeed?
|
||||
|
||||
#### A 90-Degree Shift in Perspective {#Heading5}
|
||||
#### A 90-Degree Shift in Perspective
|
||||
|
||||
As I said earlier, how you look at an optimization problem defines how
|
||||
you'll be able to solve it. In order to boost performance, sometimes
|
||||
|
|
@ -326,7 +326,7 @@ and get caught up in particular implementations; if you bounce your
|
|||
ideas off someone, you may well find them coming back with an
|
||||
unexpected—and welcome—spin.
|
||||
|
||||
### That's Nice—But it Sure as Heck Ain't 9 Cycles {#Heading6}
|
||||
### That's Nice—But it Sure as Heck Ain't 9 Cycles
|
||||
|
||||
Excellent as Chris's suggestion was, I still had work to do: Listing
|
||||
58.2 is still more than twice as slow as John Miles's code.
|
||||
|
|
@ -492,7 +492,7 @@ SCANOFFSET = SCANOFFSET + SCANWIDTH
|
|||
ENDM
|
||||
```
|
||||
|
||||
#### Don't Stop Thinking about Those Cycles {#Heading7}
|
||||
#### Don't Stop Thinking about Those Cycles
|
||||
|
||||
Remember what I said at the outset, that knowing something has been done
|
||||
makes it much easier to do? A corollary is that pushing past that point,
|
||||
|
|
@ -580,7 +580,7 @@ every optimization task as if John Miles has just written to inform you
|
|||
that he's made it faster than your wildest dreams, and you'll be amazed
|
||||
at what you can do!
|
||||
|
||||
### Texture Mapping Notes {#Heading8}
|
||||
### Texture Mapping Notes
|
||||
|
||||
Listing 58.3 contains no 486 pipeline stalls; it has Pentium stalls, but
|
||||
not much can be done for them because of the size prefix on `ADD
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1095-1114
|
|||
---
|
||||
|
||||
## Chapter 59\
|
||||
The Idea of BSP Trees {#Heading1}
|
||||
The Idea of BSP Trees
|
||||
|
||||
### What BSP Trees Are and How to Walk Them {#Heading2}
|
||||
### What BSP Trees Are and How to Walk Them
|
||||
|
||||
The answer is: Wendy Tucker.
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ the most from them.
|
|||
Before we begin, I'd like to thank John Carmack, the technical wizard
|
||||
behind DOOM, for generously sharing his knowledge of BSP trees with me.
|
||||
|
||||
### BSP Trees {#Heading3}
|
||||
### BSP Trees
|
||||
|
||||
A BSP tree is, at heart, nothing more than a tree that subdivides space
|
||||
in order to isolate features of interest. Each node of a BSP tree splits
|
||||
|
|
@ -117,7 +117,7 @@ powerful way to implement Constructive Solid Geometry (CSG). BSP trees
|
|||
can also be used for hit testing, line-of-sight determination, and
|
||||
collision detection.
|
||||
|
||||
#### Visibility Determination {#Heading4}
|
||||
#### Visibility Determination
|
||||
|
||||
For the time being, I'm going to discuss only one of the many uses of
|
||||
BSP trees: The ability of a BSP tree to allow you to traverse a set of
|
||||
|
|
@ -158,7 +158,7 @@ trees.
|
|||
|
||||

|
||||
|
||||
#### Limitations of BSP Trees {#Heading5}
|
||||
#### Limitations of BSP Trees
|
||||
|
||||
Powerful as they are, BSP trees aren't perfect. By far the greatest
|
||||
limitation of BSP trees is that they're time-consuming to build, enough
|
||||
|
|
@ -207,7 +207,7 @@ I'll present in the next chapter, which visually depicts the process of
|
|||
spatial subdivision as a BSP tree is constructed, help a great deal with
|
||||
BSP debugging.
|
||||
|
||||
### Building a BSP Tree {#Heading6}
|
||||
### Building a BSP Tree
|
||||
|
||||
Now that we know a good bit about what a BSP tree is, how it helps in
|
||||
visible surface determination, and what its strengths and weaknesses
|
||||
|
|
@ -280,7 +280,7 @@ treated as a separate wall. As shown in Figure 59.6, each of the split
|
|||
pieces then has a subspace to itself, and each becomes a leaf of the
|
||||
tree. The BSP tree is now complete.
|
||||
|
||||
#### Visibility Ordering {#Heading7}
|
||||
#### Visibility Ordering
|
||||
|
||||
Now that we've successfully built a BSP tree, you might justifiably be a
|
||||
little puzzled as to how any of this helps with visibility ordering. The
|
||||
|
|
@ -379,7 +379,7 @@ void WalkBSPTree(NODE *pNode)
|
|||
> partition space identically and can't occlude one another, so it
|
||||
> suffices to generate one splitting node for each collinear set.
|
||||
|
||||
### Inorder Walks of BSP Trees {#Heading8}
|
||||
### Inorder Walks of BSP Trees
|
||||
|
||||
It was implementing BSP trees that got me to thinking about inorder tree
|
||||
traversal. In inorder traversal, the left subtree of each node gets
|
||||
|
|
@ -472,7 +472,7 @@ fully functional model to follow, with all the problems solved, but they
|
|||
can't make the connection between that model and the code they're trying
|
||||
to implement. Why is this?
|
||||
|
||||
#### Know It *Cold* {#Heading9}
|
||||
#### Know It *Cold*
|
||||
|
||||
The problem is that these people don't understand inorder walking
|
||||
through and through. They understand the concepts of visiting left and
|
||||
|
|
@ -604,7 +604,7 @@ pants.
|
|||
> the model down cold, you can always tell if the implementation is
|
||||
> correct by comparing it with the model.
|
||||
|
||||
#### Measure and Learn {#Heading10}
|
||||
#### Measure and Learn
|
||||
|
||||
How much difference does all this fuss make, anyway? Listing 59.5 is a
|
||||
sample program that builds a tree, then calls `WalkTree` () to walk it
|
||||
|
|
@ -749,7 +749,7 @@ run fast enough to keep up if you just keep at it.
|
|||
|
||||
Depths within depths indeed!
|
||||
|
||||
### Surfing Amidst the Trees {#Heading11}
|
||||
### Surfing Amidst the Trees
|
||||
|
||||
In the next chapter, we'll build a BSP-tree compiler, and after that,
|
||||
we'll put together a rendering system built around the BSP trees the
|
||||
|
|
@ -761,7 +761,7 @@ must investigate at
|
|||
up in the familiar Internet Frequently Asked Questions (FAQ) style, and
|
||||
is very good stuff.
|
||||
|
||||
#### Related Reading {#Heading12}
|
||||
#### Related Reading
|
||||
|
||||
Foley, J., A. van Dam, S. Feiner, and J. Hughes, *Computer Graphics:
|
||||
Principles and Practice (Second Edition)*, Addison Wesley, 1990, pp.
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1115-1129
|
|||
---
|
||||
|
||||
## Chapter 60\
|
||||
Compiling BSP Trees {#Heading1}
|
||||
Compiling BSP Trees
|
||||
|
||||
### Taking BSP Trees from Concept to Reality {#Heading2}
|
||||
### Taking BSP Trees from Concept to Reality
|
||||
|
||||
As long-time readers of my columns know, I tend to move my family around
|
||||
the country quite a bit. Change doesn't come out of the blue, so there's
|
||||
|
|
@ -103,7 +103,7 @@ optimization.
|
|||
|
||||
Onward to compiling BSP trees.
|
||||
|
||||
### Compiling BSP Trees {#Heading3}
|
||||
### Compiling BSP Trees
|
||||
|
||||
As you'll recall from the previous chapter, a BSP tree is nothing more
|
||||
than a series of binary subdivisions that partion space into
|
||||
|
|
@ -139,7 +139,7 @@ choosing splitters shortly, but first let's look at the process of
|
|||
splitting and assigning. To do that, we need to understand parametric
|
||||
lines.
|
||||
|
||||
#### Parametric Lines {#Heading4}
|
||||
#### Parametric Lines
|
||||
|
||||
We're all familiar with lines described in slope-intercept form, with y
|
||||
as a function of x
|
||||
|
|
@ -190,7 +190,7 @@ indeed.
|
|||
|
||||

|
||||
|
||||
#### Parametric Line Clipping {#Heading5}
|
||||
#### Parametric Line Clipping
|
||||
|
||||
In order to assign a line segment to one subspace or the other of a
|
||||
splitter, we must somehow figure out whether the line segment straddles
|
||||
|
|
@ -236,7 +236,7 @@ normal; you could calculate the normal as the cross-product of two of
|
|||
the polygon's edges, or precalculate it when you build the world
|
||||
database.
|
||||
|
||||
#### The BSP Compiler {#Heading6}
|
||||
#### The BSP Compiler
|
||||
|
||||
Listing 60.1 shows the core of a BSP compiler—the code that actually
|
||||
builds the BSP tree. (Note that Listing 60.1 is excerpted from a C++
|
||||
|
|
@ -526,7 +526,7 @@ copying some global variables that it uses.) The complete code is too
|
|||
large to print here in its entirety, but it's on the CD-ROM in file
|
||||
DDJBSP.ZIP.
|
||||
|
||||
### Optimizing the BSP Tree {#Heading7}
|
||||
### Optimizing the BSP Tree
|
||||
|
||||
In the previous chapter, I promised that I'd discuss how to go about
|
||||
deciding which wall to use as the splitter at each node in constructing
|
||||
|
|
@ -583,7 +583,7 @@ surfaces that are being considered for that node. In other words, I
|
|||
choose the wall that splits the fewest of the walls in the subspace it's
|
||||
subdividing.
|
||||
|
||||
### BSP Optimization: an Undiscovered Country {#Heading8}
|
||||
### BSP Optimization: an Undiscovered Country
|
||||
|
||||
Although BSP trees have been around for at least 15 years now, they're
|
||||
still only partially understood and are a ripe area for applied research
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1131-1144
|
|||
---
|
||||
|
||||
## Chapter 61\
|
||||
Frames of Reference {#Heading1}
|
||||
Frames of Reference
|
||||
|
||||
### The Fundamentals of the Math behind 3-D Graphics {#Heading2}
|
||||
### The Fundamentals of the Math behind 3-D Graphics
|
||||
|
||||
Several years ago, I opened a column in *Dr. Dobb's Journal* with a
|
||||
story about singing my daughter to sleep with Beatles' songs. Beatles'
|
||||
|
|
@ -55,7 +55,7 @@ important sort of difference.
|
|||
Before we can talk about transforming between coordinate spaces,
|
||||
however, we need two building blocks: dot products and cross products.
|
||||
|
||||
#### 3-D Math {#Heading3}
|
||||
#### 3-D Math
|
||||
|
||||
At this point in the book, I was originally going to present a BSP-based
|
||||
renderer, to complement the BSP compiler I presented in the previous
|
||||
|
|
@ -89,7 +89,7 @@ chapter examining these basic tools and some of their 3-D applications.
|
|||
If this is old hat to you, my apologies, and I'll return to BSP-based
|
||||
rendering in the next chapter.
|
||||
|
||||
#### Foundation Definitions {#Heading4}
|
||||
#### Foundation Definitions
|
||||
|
||||
The dot and cross products themselves are straightforward and require
|
||||
almost no context to understand, but I need to define some terms I'll
|
||||
|
|
@ -142,7 +142,7 @@ For additional information, you might want to check out Foley & van
|
|||
Dam's *Computer Graphics* (ISBN 0-201-12110-7), or the chapters in this
|
||||
book dealing with my X-Sharp 3-D graphics library.
|
||||
|
||||
### The Dot Product {#Heading5}
|
||||
### The Dot Product
|
||||
|
||||
Now we're ready to move on to the dot product. Given two vectors `U` =
|
||||
[u~1~ u~2~ u~3~] and `V` = [v~1~ v~2~ v~3~], their dot product,
|
||||
|
|
@ -168,7 +168,7 @@ are the lengths of the vectors, as shown in Figure 61.1. Although it's
|
|||
not immediately obvious, equation 3 has a wide variety of applications
|
||||
in 3-D graphics.
|
||||
|
||||
#### Dot Products of Unit Vectors {#Heading6}
|
||||
#### Dot Products of Unit Vectors
|
||||
|
||||
The simplest case of the dot product is when both vectors are *unit
|
||||
vectors*; that is, when their lengths are both one, as calculated as in
|
||||
|
|
@ -208,7 +208,7 @@ three additions—and no explicit cosine calculations—as
|
|||
where `N`~s~ is the surface unit normal and `D`~l~ is the light unit
|
||||
direction vector, as shown in Figure 61.2.
|
||||
|
||||
### Cross Products and the Generation of Polygon Normals {#Heading7}
|
||||
### Cross Products and the Generation of Polygon Normals
|
||||
|
||||
One question equation 6 begs is where the surface unit normal comes
|
||||
from. One approach is to store the end of a surface normal as an extra
|
||||
|
|
@ -287,7 +287,7 @@ orthogonal unit vectors, you'll have to normalize the resulting vector;
|
|||
that is, divide each of the vector's components by the length of the
|
||||
vector, to make it a unit long.
|
||||
|
||||
### Using the Sign of the Dot Product {#Heading8}
|
||||
### Using the Sign of the Dot Product
|
||||
|
||||
The dot product is the cosine of the angle between two vectors, scaled
|
||||
by the magnitudes of the vectors. Magnitudes are always positive, so the
|
||||
|
|
@ -335,7 +335,7 @@ understand the use of the dot product for projection.
|
|||
|
||||

|
||||
|
||||
### Using the Dot Product for Projection {#Heading9}
|
||||
### Using the Dot Product for Projection
|
||||
|
||||
Consider Equation 3 again, but this time make one of the vectors, say
|
||||
`V`, a unit vector. Now the equation reduces to:
|
||||
|
|
@ -407,7 +407,7 @@ void LineIntersectPlane (float *linestart, float *lineend,
|
|||
}
|
||||
```
|
||||
|
||||
### Rotation by Projection {#Heading10}
|
||||
### Rotation by Projection
|
||||
|
||||
We can use the dot product's projection capability to look at rotation
|
||||
in an interesting way. Typically, rotations are represented by matrices.
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1145-1162
|
|||
---
|
||||
|
||||
## Chapter 62\
|
||||
One Story, Two Rules, and a BSP Renderer {#Heading1}
|
||||
One Story, Two Rules, and a BSP Renderer
|
||||
|
||||
### Taking a Compiled BSP Tree from Logical to Visual Reality {#Heading2}
|
||||
### Taking a Compiled BSP Tree from Logical to Visual Reality
|
||||
|
||||
As I've noted before, I'm working on Quake, id Software's follow-up to
|
||||
DOOM. A month or so back, we added page flipping to Quake, and made the
|
||||
|
|
@ -78,7 +78,7 @@ Tools*, AP Professional, ISBN 0-12-627230-1.
|
|||
|
||||
Onward to rendering from a BSP tree.
|
||||
|
||||
#### BSP-based Rendering {#Heading3}
|
||||
#### BSP-based Rendering
|
||||
|
||||
For the last several chapters I've been discussing the nature of BSP
|
||||
(Binary Space Partitioning) trees, and in Chapter 60 I presented a
|
||||
|
|
@ -561,7 +561,7 @@ void UpdateWorld()
|
|||
}
|
||||
```
|
||||
|
||||
#### The Rendering Pipeline {#Heading4}
|
||||
#### The Rendering Pipeline
|
||||
|
||||
Conceptually rendering from a BSP tree really is that simple, but the
|
||||
implementation is a bit more complicated. The full rendering pipeline,
|
||||
|
|
@ -582,7 +582,7 @@ detail. Some sources for further reading are *Computer Graphics*, by
|
|||
Foley and van Dam (ISBN 0-201-12110-7), and the *DDJ Essential Books on
|
||||
Graphics Programming* CD.
|
||||
|
||||
### Moving the Viewer {#Heading5}
|
||||
### Moving the Viewer
|
||||
|
||||
The sample BSP program performs first-person rendering; that is, it
|
||||
renders the world as seen from your eyes as you move about. The rate of
|
||||
|
|
@ -599,7 +599,7 @@ any tilted walls. For simplicity's sake, I have chosen not to implement
|
|||
this in Listing 62.1, but you may find it educational to add it to the
|
||||
program yourself.
|
||||
|
||||
### Transformation into Viewspace {#Heading6}
|
||||
### Transformation into Viewspace
|
||||
|
||||
The viewing angle (which controls direction of movement as well as view
|
||||
direction) can sweep through the full 360 degrees around the viewpoint,
|
||||
|
|
@ -626,7 +626,7 @@ values result in narrower fields of view.
|
|||
|
||||
When this is done the walls are in viewspace, ready to be clipped.
|
||||
|
||||
### Clipping {#Heading7}
|
||||
### Clipping
|
||||
|
||||
In viewspace, the walls may be anywhere relative to the viewpoint: in
|
||||
front, behind, off to the side. We only want to draw those parts of
|
||||
|
|
@ -675,7 +675,7 @@ near clip plane guarantees that no remaining polygon point can have
|
|||
z\<=0, ensuring that when we project we'll always pass valid,
|
||||
y-clippable screenspace vertices to the polygon filler.
|
||||
|
||||
### Projection to Screenspace {#Heading8}
|
||||
### Projection to Screenspace
|
||||
|
||||
At this point, we have viewspace vertices for each wall that's at least
|
||||
partially visible. All we have to do is project these vertices according
|
||||
|
|
@ -686,7 +686,7 @@ as the last step for visible walls in `ClipWalls()`.
|
|||
|
||||

|
||||
|
||||
### Walking the Tree, Backface Culling and Drawing {#Heading9}
|
||||
### Walking the Tree, Backface Culling and Drawing
|
||||
|
||||
Now that we have all the walls clipped to the frustum, with vertices
|
||||
projected into screen coordinates, all we have to do is draw them back
|
||||
|
|
@ -744,7 +744,7 @@ the new frame to the screen. The frame of animation is complete.
|
|||
|
||||

|
||||
|
||||
#### Notes on the BSP Renderer {#Heading10}
|
||||
#### Notes on the BSP Renderer
|
||||
|
||||
Listing 62.1 is far from complete or optimal. There is no such thing as
|
||||
a tiny BSP rendering demo, because 3D rendering, even when based on a
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1163-1175
|
|||
---
|
||||
|
||||
## Chapter 63\
|
||||
Floating-Point for Real-Time 3-D {#Heading1}
|
||||
Floating-Point for Real-Time 3-D
|
||||
|
||||
### Knowing When to Hurl Conventional Math Wisdom Out the Window {#Heading2}
|
||||
### Knowing When to Hurl Conventional Math Wisdom Out the Window
|
||||
|
||||
In a crisis, sometimes it's best to go with the first solution that
|
||||
comes into your head—but not very often.
|
||||
|
|
@ -94,7 +94,7 @@ quickly assumptions that once were completely valid can deteriorate.
|
|||
|
||||
For example, consider floating-point math.
|
||||
|
||||
### Not Your Father's Floating-Point {#Heading3}
|
||||
### Not Your Father's Floating-Point
|
||||
|
||||
Until last year, I had never done any serious floating-point (FP)
|
||||
optimization, for the perfectly good reason that FP math had never been
|
||||
|
|
@ -132,7 +132,7 @@ this chapter I'll examine the basics of Pentium FP optimization, then
|
|||
look at how some key mathematical techniques for 3-D—dot product, cross
|
||||
product, transformation, and projection—can be accelerated.
|
||||
|
||||
### Pentium Floating-Point Optimization {#Heading4}
|
||||
### Pentium Floating-Point Optimization
|
||||
|
||||
I'm going to assume you're already familiar with x86 FP code in general;
|
||||
for additional information, check out Intel's *Pentium Processor User's
|
||||
|
|
@ -188,7 +188,7 @@ instructions starts. There's a more exciting possibility here, though:
|
|||
Given properly structured code, the FPU is capable of averaging 1 cycle
|
||||
per FADD, FSUB, or FMUL. The secret is pipelining.
|
||||
|
||||
#### Pipelining, Latency, and Throughput {#Heading5}
|
||||
#### Pipelining, Latency, and Throughput
|
||||
|
||||
The Pentium's FPU is the first pipelined x86 FPU. *Pipelining* means
|
||||
that the FPU is capable of starting an instruction every cycle, and can
|
||||
|
|
@ -258,7 +258,7 @@ two instructions. When dependencies like this occur, the FPU runs at
|
|||
latency rather than throughput speeds, and performance can drop by as
|
||||
much as two-thirds.
|
||||
|
||||
#### FXCH {#Heading6}
|
||||
#### FXCH
|
||||
|
||||
One piece of the puzzle is still missing. Clearly, to get maximum
|
||||
throughput, we need to interleave FP instructions, such that at any one
|
||||
|
|
@ -297,7 +297,7 @@ multiplications, without incurring any stalls, as shown in Listing 63.1.
|
|||
faddp st(2),st(0) ;starts on cycle 6
|
||||
```
|
||||
|
||||
### The Dot Product {#Heading7}
|
||||
### The Dot Product
|
||||
|
||||
Now we're ready to look at fast FP for common 3-D operations; we'll
|
||||
start by looking at how to speed up the dot product. As discussed in
|
||||
|
|
@ -355,7 +355,7 @@ potential, as we'll see when we discuss transformation.
|
|||
; ends on cycle 14
|
||||
```
|
||||
|
||||
### The Cross Product {#Heading8}
|
||||
### The Cross Product
|
||||
|
||||
When last we looked at the cross product, we found that it's handy for
|
||||
generating a vector that's normal to two other vectors. The cross
|
||||
|
|
@ -442,7 +442,7 @@ of properly managing the Pentium's FP pipeline.
|
|||
; ends on cycle 21
|
||||
```
|
||||
|
||||
### Transformation {#Heading9}
|
||||
### Transformation
|
||||
|
||||
Transforming a point, for example from worldspace to viewspace, is one
|
||||
of the most heavily used FP operations in realtime 3-D. Conceptually,
|
||||
|
|
@ -528,7 +528,7 @@ certainly feasible; at a frame rate of 30 Hz, that's an impressive
|
|||
; ends on cycle 33
|
||||
```
|
||||
|
||||
### Projection {#Heading10}
|
||||
### Projection
|
||||
|
||||
The final optimization we'll look at is projection to screenspace.
|
||||
Projection itself is basically nothing more than a divide (to get 1/z),
|
||||
|
|
@ -554,7 +554,7 @@ precision-related problems, such as clipped values that vary more than
|
|||
you'd expect from the precise clip point, or the need for using larger
|
||||
epsilons in comparisons for point-on-plane tests.
|
||||
|
||||
### Rounding Control {#Heading11}
|
||||
### Rounding Control
|
||||
|
||||
Another useful area that I can note only in passing here is that of
|
||||
leaving the FPU in a particular rounding mode while performing bulk
|
||||
|
|
@ -576,7 +576,7 @@ A final note: There are some speed-ups to be had by manipulating FP
|
|||
variables with integer instructions. Check out Chris Hecker's column in
|
||||
the February/March 1996 issue of *Game Developer* for details.
|
||||
|
||||
### A Farewell to 3-D Fixed-Point {#Heading12}
|
||||
### A Farewell to 3-D Fixed-Point
|
||||
|
||||
As with most optimizations, there are both benefits and hazards to
|
||||
floating-point acceleration, especially pedal-to-the-metal optimizations
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1177-1190
|
|||
---
|
||||
|
||||
## Chapter 64\
|
||||
Quake's Visible-Surface Determination {#Heading1}
|
||||
Quake's Visible-Surface Determination
|
||||
|
||||
### The Challenge of Separating All Things Seen from All Things Unseen {#Heading2}
|
||||
### The Challenge of Separating All Things Seen from All Things Unseen
|
||||
|
||||
Years ago, I was working at Video Seven, a now-vanished video adapter
|
||||
manufacturer, helping to develop a VGA clone. The fellow who was
|
||||
|
|
@ -83,7 +83,7 @@ seemingly outlandish ideas can open up new design possibilities for you.
|
|||
|
||||
Case in point: The evolution of Quake's 3-D graphics engine.
|
||||
|
||||
### VSD: The Toughest 3-D Challenge of All {#Heading3}
|
||||
### VSD: The Toughest 3-D Challenge of All
|
||||
|
||||
I've spent most of my waking hours for the last several months working
|
||||
on Quake, id Software's successor to DOOM, and I suspect I have a few
|
||||
|
|
@ -119,7 +119,7 @@ detailed. Already, a good-sized Quake level contains on the order of
|
|||
10,000 polygons, about three times as many polygons as a comparable DOOM
|
||||
level.
|
||||
|
||||
### The Structure of Quake Levels {#Heading4}
|
||||
### The Structure of Quake Levels
|
||||
|
||||
Before diving into VSD, let me note that each Quake level is stored as a
|
||||
single huge 3-D BSP tree. This BSP tree, like any BSP, subdivides space,
|
||||
|
|
@ -135,7 +135,7 @@ the boundaries of the BSP leaves, facing inward, the polygons in a given
|
|||
leaf can never obscure one another and can be drawn in any order. (This
|
||||
is a general property of convex polyhedra.)
|
||||
|
||||
### Culling and Visible Surface Determination {#Heading5}
|
||||
### Culling and Visible Surface Determination
|
||||
|
||||
The process of VSD would ideally work as follows: First, you would cull
|
||||
all polygons that are completely outside the view frustum (view
|
||||
|
|
@ -172,7 +172,7 @@ the world, more transformations and tests have to be performed to cull
|
|||
polygons that aren't visible; at some point, that will bog considerably
|
||||
performance down.
|
||||
|
||||
#### Nodes Inside and Outside the View Frustum {#Heading6}
|
||||
#### Nodes Inside and Outside the View Frustum
|
||||
|
||||
Happily, there's a good workaround for this particular problem. As
|
||||
discussed earlier, each leaf of a BSP tree represents a convex subspace,
|
||||
|
|
@ -199,7 +199,7 @@ specifically for culling tests.
|
|||
So culling to the frustum isn't a problem, and the BSP can be used to
|
||||
draw back-to- front. What, then, *is* the problem?
|
||||
|
||||
### Overdraw {#Heading7}
|
||||
### Overdraw
|
||||
|
||||
The problem John Carmack, the driving technical force behind DOOM and
|
||||
Quake, faced when he designed Quake was that in a complex world, many
|
||||
|
|
@ -241,7 +241,7 @@ By three months after I arrived, only one element of the original VSD
|
|||
design was anywhere in sight, and John had taken the dictum of "try new
|
||||
things" farther than I'd ever seen it taken.
|
||||
|
||||
### The Beam Tree {#Heading8}
|
||||
### The Beam Tree
|
||||
|
||||
John's original Quake design was to draw front-to-back, using a second
|
||||
BSP tree to keep track of what parts of the screen were already drawn
|
||||
|
|
@ -294,7 +294,7 @@ proved to suffer from much the same malady as the painter's algorithm:
|
|||
The worst case was much worse than the average case, and it didn't scale
|
||||
well with increasing level complexity.
|
||||
|
||||
### 3-D Engine du Jour {#Heading9}
|
||||
### 3-D Engine du Jour
|
||||
|
||||
Once the beam tree was working, John relentlessly worked at speeding up
|
||||
the 3-D engine, always trying to improve the design, rather than
|
||||
|
|
@ -311,7 +311,7 @@ are some of those approaches, presented in minimal detail in the hopes
|
|||
that, like Tom Wilson with the Paradise FIFO, your imagination will be
|
||||
sparked.
|
||||
|
||||
#### Subdividing Raycast {#Heading10}
|
||||
#### Subdividing Raycast
|
||||
|
||||
Rays are cast in an 8x8 screen-pixel grid; this is a highly efficient
|
||||
operation because the first intersection with a surface can be found by
|
||||
|
|
@ -324,7 +324,7 @@ This scales very well, being limited by the number of pixels, with no
|
|||
overdraw. The problem is dropouts; it's quite possible for small
|
||||
polygons to fall between rays and vanish.
|
||||
|
||||
#### Vertex-Free Surfaces {#Heading11}
|
||||
#### Vertex-Free Surfaces
|
||||
|
||||
The world is represented by a set of surface planes. The polygons are
|
||||
implicit in the plane intersections, and are extracted from the planes
|
||||
|
|
@ -332,7 +332,7 @@ as a final step before drawing. This makes for fast clipping and a very
|
|||
small data set (planes are far more compact than polygons), but it's
|
||||
time-consuming to extract polygons from planes.
|
||||
|
||||
#### The Draw-Buffer {#Heading12}
|
||||
#### The Draw-Buffer
|
||||
|
||||
Like a z-buffer, but with 1 bit per pixel, indicating whether the pixel
|
||||
has been drawn yet. This eliminates overdraw, but at the cost of an
|
||||
|
|
@ -344,7 +344,7 @@ for drawing 0-8 pixels, in the process possibly taking advantage of the
|
|||
ability of the x86 to do the perspective floating-point divide in
|
||||
parallel while 8 pixels are processed.
|
||||
|
||||
#### Span-Based Drawing {#Heading13}
|
||||
#### Span-Based Drawing
|
||||
|
||||
Polygons are rasterized into spans, which are added to a global span
|
||||
list and clipped against that list so that only the nearest span at each
|
||||
|
|
@ -353,7 +353,7 @@ because if there's any overlap, the span already in the list is nearer.
|
|||
This eliminates overdraw, but at the cost of a lot of span arithmetic;
|
||||
also, every polygon still has to be turned into spans.
|
||||
|
||||
#### Portals {#Heading14}
|
||||
#### Portals
|
||||
|
||||
The holes where polygons are missing on surfaces are tracked, because
|
||||
it's only through such portals that line-of-sight can extend. Drawing
|
||||
|
|
@ -363,7 +363,7 @@ portals remain visible. Applied recursively, this allows drawing only
|
|||
the visible portions of visible polygons, but at the cost of a
|
||||
considerable amount of portal clipping.
|
||||
|
||||
### Breakthrough! {#Heading15}
|
||||
### Breakthrough!
|
||||
|
||||
In the end, John decided that the beam tree was a sort of second-order
|
||||
structure, reflecting information already implicitly contained in the
|
||||
|
|
@ -421,7 +421,7 @@ still-in-development sorted-edge rasterizer that completely eliminates
|
|||
overdraw, comes remarkably close to meeting the "perfect-world"
|
||||
specifications we laid out at the start.
|
||||
|
||||
### Simplify, and Keep on Trying New Things {#Heading16}
|
||||
### Simplify, and Keep on Trying New Things
|
||||
|
||||
What does it all mean? Exactly what I said up front: Simplify, and keep
|
||||
trying new things. The precalculated PVS is simpler than any of the
|
||||
|
|
@ -463,7 +463,7 @@ things better with less code.
|
|||
|
||||
So far, it seems to have worked out pretty well for him.
|
||||
|
||||
### Learn Now, Pay Forward {#Heading17}
|
||||
### Learn Now, Pay Forward
|
||||
|
||||
There's one other thing I'd like to mention before I close this chapter.
|
||||
Much of what I've learned, and a great deal of what I've written, has
|
||||
|
|
@ -494,7 +494,7 @@ in a vacuum; we all stand on the shoulders of giants such as Wirth and
|
|||
Knuth and thousands of others. Lend your shoulders to building the
|
||||
future!
|
||||
|
||||
### References {#Heading18}
|
||||
### References
|
||||
|
||||
Foley, James D., *et al.*, *Computer Graphics: Principles and Practice*,
|
||||
Addison Wesley, 1990, ISBN 0-201-12110-7 (beams, BSP trees, VSD).
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1191-1208
|
|||
---
|
||||
|
||||
## Chapter 65\
|
||||
3-D Clipping and Other Thoughts {#Heading1}
|
||||
3-D Clipping and Other Thoughts
|
||||
|
||||
### Determining What's Inside Your Field of View {#Heading2}
|
||||
### Determining What's Inside Your Field of View
|
||||
|
||||
Our part of the world is changing, and I'm concerned. By way of
|
||||
explanation, three anecdotes.
|
||||
|
|
@ -100,7 +100,7 @@ circulated a good bit of info about 3-D graphics, and plan to keep on
|
|||
doing it as long as I can. Next, we're going to take a look at 3-D
|
||||
clipping.
|
||||
|
||||
### 3-D Clipping Basics {#Heading3}
|
||||
### 3-D Clipping Basics
|
||||
|
||||
Before I got deeply into 3-D, I kept hearing how difficult 3-D clipping
|
||||
was, so I was pleasantly surprised when I actually got around to doing
|
||||
|
|
@ -137,7 +137,7 @@ ways to speed up clipping under various circumstances, some of which
|
|||
I'll mention, but the material covered below will give you the tools you
|
||||
need to implement functional 3-D clipping.
|
||||
|
||||
#### Intersecting a Line Segment with a Plane {#Heading4}
|
||||
#### Intersecting a Line Segment with a Plane
|
||||
|
||||
The fundamental 3-D clipping operation is clipping a line segment to a
|
||||
plane. There are two parts to this operation: determining if the line is
|
||||
|
|
@ -246,7 +246,7 @@ y, and z lengths of the line segment by that fraction, and add the
|
|||
results to the inside endpoint, we get a new, clipped endpoint at the
|
||||
point of intersection.
|
||||
|
||||
### Polygon Clipping {#Heading5}
|
||||
### Polygon Clipping
|
||||
|
||||
Line clipping is fine for wireframe rendering, but what we really want
|
||||
to do is polygon rendering of solid models, which requires polygon
|
||||
|
|
@ -345,7 +345,7 @@ intersection with the clip plane can be used to advance the texture
|
|||
coordinates as well, so only one extra multiply and one extra add are
|
||||
required for each texture coordinate.
|
||||
|
||||
#### Clipping to the Frustum {#Heading6}
|
||||
#### Clipping to the Frustum
|
||||
|
||||
Given a polygon-clipping function, it's easy to clip to the frustum: set
|
||||
up the four planes for the sides of the frustum, with another one or two
|
||||
|
|
@ -669,7 +669,7 @@ void UpdateWorld()
|
|||
}
|
||||
```
|
||||
|
||||
#### The Lessons of Listing 65.3 {#Heading7}
|
||||
#### The Lessons of Listing 65.3
|
||||
|
||||
There are several interesting points to Listing 65.3. First,
|
||||
floating-point arithmetic is used throughout the clipping process. While
|
||||
|
|
@ -731,7 +731,7 @@ viewspace, then clipping them. However, the decision whether to clip in
|
|||
worldspace or viewspace is not clear-cut and is affected by several
|
||||
factors.
|
||||
|
||||
### Advantages of Viewspace Clipping {#Heading8}
|
||||
### Advantages of Viewspace Clipping
|
||||
|
||||
Although viewspace clipping requires transforming vertices that may not
|
||||
be drawn, it has potential performance advantages. For example, in
|
||||
|
|
@ -771,7 +771,7 @@ the more frustum clipping you're doing, especially if most of the
|
|||
polygons are trivially visible, the more attractive the performance
|
||||
advantages of normalized clipping become.
|
||||
|
||||
### Further Reading {#Heading9}
|
||||
### Further Reading
|
||||
|
||||
You now have the basics of 3-D clipping, but because fast clipping is
|
||||
central to high-performance 3-D, there's a lot more to be learned. One
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1209-1222
|
|||
---
|
||||
|
||||
## Chapter 66\
|
||||
Quake's Hidden-Surface Removal {#Heading1}
|
||||
Quake's Hidden-Surface Removal
|
||||
|
||||
### Struggling with Z-Order Solutions to the Hidden Surface Problem {#Heading2}
|
||||
### Struggling with Z-Order Solutions to the Hidden Surface Problem
|
||||
|
||||
Okay, I admit it: I'm sick and tired of classic rock. Admittedly, it's
|
||||
been a while, about 20 years, since I was last excited to hear anything
|
||||
|
|
@ -55,7 +55,7 @@ I've found that they're often worth considering.
|
|||
Not that I should have needed any reminding, considering the
|
||||
ever-evolving nature of Quake.
|
||||
|
||||
### Creative Flux and Hidden Surfaces {#Heading3}
|
||||
### Creative Flux and Hidden Surfaces
|
||||
|
||||
Back in Chapter 64, I described the creative flux that led to John
|
||||
Carmack's decision to use a precalculated potentially visible set (PVS)
|
||||
|
|
@ -70,7 +70,7 @@ drawing perform the final stage of hidden-surface removal (HSR). This
|
|||
was a terrific idea, but it was far from the end of the road for Quake's
|
||||
design.
|
||||
|
||||
#### Drawing Moving Objects {#Heading4}
|
||||
#### Drawing Moving Objects
|
||||
|
||||
For one thing, there was still the question of how to sort and draw
|
||||
moving objects properly; in fact, this is the single technical question
|
||||
|
|
@ -102,7 +102,7 @@ drawing and z-filling of the world is done, we can simply draw the
|
|||
sprites and polygon models with z-buffering and get perfect sorting all
|
||||
around.
|
||||
|
||||
#### Performance Impact {#Heading5}
|
||||
#### Performance Impact
|
||||
|
||||
Whenever a z-buffer is involved, the questions inevitably are: What's
|
||||
the memory footprint and what's the performance impact? Well, the memory
|
||||
|
|
@ -117,7 +117,7 @@ vastly improved the visual quality and flexibility of the Quake engine,
|
|||
and also simplified the code quite a bit, at an acceptable memory and
|
||||
performance cost.
|
||||
|
||||
#### Leveling and Improving Performance {#Heading6}
|
||||
#### Leveling and Improving Performance
|
||||
|
||||
As I said above, in the Quake architecture, the world itself is drawn
|
||||
first, without z-buffer reads or compares, but filling the z-buffer with
|
||||
|
|
@ -158,7 +158,7 @@ than back-to-front drawing.
|
|||
|
||||
And indeed there is.
|
||||
|
||||
### Sorted Spans {#Heading7}
|
||||
### Sorted Spans
|
||||
|
||||
The ideal final HSR stage for Quake would reject all the polygons in the
|
||||
PVS that are actually invisible, and draw only the visible pixels of the
|
||||
|
|
@ -214,7 +214,7 @@ implement, with a couple of major design choices to be made, a subtle
|
|||
mathematical element, and some tricky gotchas that I'll have to defer
|
||||
until Chapter 67. Let's look at the design choices first.
|
||||
|
||||
### Edges versus Spans {#Heading8}
|
||||
### Edges versus Spans
|
||||
|
||||
The first design choice is whether to sort spans or edges (both of which
|
||||
fall into the general category of "sorted spans"). Although the results
|
||||
|
|
@ -314,7 +314,7 @@ process, I'm going to have to make a few forward references to aspects
|
|||
of edge-sorting that I haven't yet covered in detail; my apologies, but
|
||||
it's unavoidable, and all should become clear by the end of Chapter 67.
|
||||
|
||||
### Edge-Sorting Keys {#Heading9}
|
||||
### Edge-Sorting Keys
|
||||
|
||||
Now that we know we're going to sort edges, using them to emit spans for
|
||||
the polygons nearest the viewer, the question becomes: How can we tell
|
||||
|
|
@ -389,7 +389,7 @@ floating-point math sounds expensive but really isn't, especially on a
|
|||
Pentium, where a plane's 1/z value at any point can be calculated in as
|
||||
little as six cycles in assembly language.
|
||||
|
||||
#### Where That 1/Z Equation Comes From {#Heading10}
|
||||
#### Where That 1/Z Equation Comes From
|
||||
|
||||
For those who are interested, here's a quick derivation of the 1/z
|
||||
equation. The plane equation for a plane is
|
||||
|
|
@ -410,7 +410,7 @@ Inverting and distributing yields:
|
|||
|
||||
We'll see 1/z sorting in action in Chapter 67.
|
||||
|
||||
#### Quake and Z-Sorting {#Heading11}
|
||||
#### Quake and Z-Sorting
|
||||
|
||||
I mentioned earlier that Quake no longer uses BSP order as the sorting
|
||||
key; in fact, it uses 1/z as the key now. Elegant as the gradients are,
|
||||
|
|
@ -438,7 +438,7 @@ so they don't cross any solid world surfaces, to avoid complications
|
|||
associated with interpenetration), along with all the world edges, and
|
||||
1/z sorting takes care of the rest.
|
||||
|
||||
### Decisions Deferred {#Heading12}
|
||||
### Decisions Deferred
|
||||
|
||||
There is, without a doubt, an awful lot of information in the preceding
|
||||
pages, and it may not all connect together yet in your mind. The code
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1223-1241
|
|||
---
|
||||
|
||||
## Chapter 67\
|
||||
Sorted Spans in Action {#Heading1}
|
||||
Sorted Spans in Action
|
||||
|
||||
### Implementing Independent Span Sorting for Rendering without Overdraw {#Heading2}
|
||||
### Implementing Independent Span Sorting for Rendering without Overdraw
|
||||
|
||||
In Chapter 66, we dove headlong into the intricacies of hidden surface
|
||||
removal by way of z-sorted (actually, 1/z-sorted) spans. At the end of
|
||||
|
|
@ -49,7 +49,7 @@ contained herein. Besides, the ultimate reference for any design is
|
|||
working code, which you'll find, in part, in Listing 67.1, and in its
|
||||
entirety in the file DDJZSORT.ZIP on the CD-ROM.
|
||||
|
||||
### Quake and Sorted Spans {#Heading3}
|
||||
### Quake and Sorted Spans
|
||||
|
||||
As you'll recall from Chapter 66, Quake uses sorted spans to get zero
|
||||
overdraw while rendering the world, thereby both improving overall
|
||||
|
|
@ -152,7 +152,7 @@ For the remainder of this chapter, I'm going to look at the three main
|
|||
types of 1/z span sorting, then discuss a sample 3-D app built around
|
||||
1/z span sorting.
|
||||
|
||||
### Types of 1/z Span Sorting {#Heading4}
|
||||
### Types of 1/z Span Sorting
|
||||
|
||||
As a quick refresher: With 1/z span sorting, all the polygons in a scene
|
||||
are treated as sets of screenspace pixel spans, and 1/z (where z is
|
||||
|
|
@ -168,7 +168,7 @@ they are: intersecting, abutting, and independent. (These are names of
|
|||
my own devising; I haven't come across any standard nomenclature in the
|
||||
literature.)
|
||||
|
||||
#### Intersecting Span Sorting {#Heading5}
|
||||
#### Intersecting Span Sorting
|
||||
|
||||
Intersecting span sorting occurs when polygons can interpenetrate. Thus,
|
||||
two spans may cross such that part of each span is visible, in which
|
||||
|
|
@ -183,7 +183,7 @@ detect interpenetration, and additional work must be done to split the
|
|||
spans as necessary. Thus, although intersecting span sorting certainly
|
||||
works, it's not the first choice for performance.
|
||||
|
||||
#### Abutting Span Sorting {#Heading6}
|
||||
#### Abutting Span Sorting
|
||||
|
||||
Abutting span sorting occurs when polygons that are not part of a
|
||||
continuous surface can butt up against one another, but don't
|
||||
|
|
@ -238,7 +238,7 @@ of caching sort results were outweighed by the additional overhead of
|
|||
maintaining the caching information, and every caching variant we tried
|
||||
actually slowed Quake down.
|
||||
|
||||
#### Independent Span Sorting {#Heading7}
|
||||
#### Independent Span Sorting
|
||||
|
||||
Finally, we come to independent span sorting, the simplest and fastest
|
||||
of the three, and the type the sample code in Listing 67.1 uses. Here,
|
||||
|
|
@ -253,7 +253,7 @@ Independent span sorting is ideal for scenes with lots of moving objects
|
|||
that never actually touch each other, such as a space battle. Next,
|
||||
we'll look at an implementation of independent 1/z span sorting.
|
||||
|
||||
### 1/z Span Sorting in Action {#Heading8}
|
||||
### 1/z Span Sorting in Action
|
||||
|
||||
Listing 67.1 is a portion of a program that demonstrates independent 1/z
|
||||
span sorting. This program is based on the sample 3-D clipping program
|
||||
|
|
@ -788,7 +788,7 @@ necessary. As you can see from Listing 67.1, it takes a fair bit of code
|
|||
to implement this, but all that's really going on is a surface stack
|
||||
driven by edge events.
|
||||
|
||||
#### Implementation Notes {#Heading9}
|
||||
#### Implementation Notes
|
||||
|
||||
Finally, a few notes on Listing 67.1. First, you'll notice that although
|
||||
we clip all polygons to the view frustum in worldspace, we nonetheless
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1243-1256
|
|||
---
|
||||
|
||||
## Chapter 68\
|
||||
Quake's Lighting Model {#Heading1}
|
||||
Quake's Lighting Model
|
||||
|
||||
### A Radically Different Approach to Lighting Polygons {#Heading2}
|
||||
### A Radically Different Approach to Lighting Polygons
|
||||
|
||||
It was during my senior year in college that I discovered computer
|
||||
games. Not Wizardry, or Choplifter, or Ultima, because none of those
|
||||
|
|
@ -65,7 +65,7 @@ doing, and if it's getting stale, it's time to learn something new;
|
|||
there's plenty of interesting programming of all sorts to be done.
|
||||
Follow your interests—and don't forget to have fun!
|
||||
|
||||
### The Lighting Conundrum {#Heading3}
|
||||
### The Lighting Conundrum
|
||||
|
||||
I spent about two years working with John Carmack on Quake's 3-D
|
||||
graphics engine. John faced several fundamental design issues while
|
||||
|
|
@ -89,7 +89,7 @@ and rock-solid, complex lighting proved to be difficult to achieve with
|
|||
traditional lighting approaches; ultimately, a dramatically different
|
||||
approach was required.
|
||||
|
||||
### Gouraud Shading {#Heading4}
|
||||
### Gouraud Shading
|
||||
|
||||
The traditional way to do realistic lighting in polygon pipelines is
|
||||
Gouraud shading (also known as *smooth shading*). Gouraud shading
|
||||
|
|
@ -118,7 +118,7 @@ small amount of calculation and a compact data set that's a simple
|
|||
extension of the basic polygon model. However, there are several
|
||||
important drawbacks to Gouraud shading, as well.
|
||||
|
||||
#### Problems with Gouraud Shading {#Heading5}
|
||||
#### Problems with Gouraud Shading
|
||||
|
||||
The quality of Gouraud shading depends heavily on the average size of
|
||||
the polygons being drawn. Linear interpolation is used, so highlights
|
||||
|
|
@ -157,7 +157,7 @@ that increases the rasterization load.
|
|||
|
||||

|
||||
|
||||
#### Perspective Correctness {#Heading6}
|
||||
#### Perspective Correctness
|
||||
|
||||
Another problem is that Gouraud shading isn't perspective-correct. With
|
||||
Gouraud shading, lighting varies linearly across the face of a polygon,
|
||||
|
|
@ -229,7 +229,7 @@ not only would the world still be less than totally solid, because of
|
|||
the limitations of Gouraud shading, but the engine would also be too
|
||||
slow to support the complex worlds we had hoped for in Quake.
|
||||
|
||||
### The Quest for Alternative Lighting {#Heading7}
|
||||
### The Quest for Alternative Lighting
|
||||
|
||||
None of which is to say that Gouraud shading isn't useful in general.
|
||||
Descent uses it to excellent effect, and in fact Quake uses Gouraud
|
||||
|
|
@ -251,7 +251,7 @@ possibilities and continued working with Gouraud shading for lack of a
|
|||
better alternative—until the day John came into work and said, "You
|
||||
know, I have an idea...."
|
||||
|
||||
#### Decoupling Lighting from Rasterization {#Heading8}
|
||||
#### Decoupling Lighting from Rasterization
|
||||
|
||||
John's idea came to him while was looking at a wall that had been carved
|
||||
into several pieces because of a spotlight, with an ugly lighting glitch
|
||||
|
|
@ -303,7 +303,7 @@ because lighting is unrelated to vertices. In short, surface-based
|
|||
lighting meets all of Quake's visual quality goals, which leaves only
|
||||
one question: How does it perform?
|
||||
|
||||
#### Size and Speed {#Heading9}
|
||||
#### Size and Speed
|
||||
|
||||
As it turns out, the raw speed of surface-based lighting is pretty good.
|
||||
Although an extra step is required to build the surface, moving lighting
|
||||
|
|
@ -357,7 +357,7 @@ the combination of surface building and unlit texture mapping a
|
|||
potential performance problem, but that never posed a problem during the
|
||||
development of Quake, thanks to surface caching.
|
||||
|
||||
### Surface Caching {#Heading10}
|
||||
### Surface Caching
|
||||
|
||||
When he thought of surface-based lighting, John immediately realized
|
||||
that surface building would be relatively expensive. (In fact, he
|
||||
|
|
@ -392,7 +392,7 @@ cache initially looked to be very large, on the order of several
|
|||
megabytes, even at 320x200—too much for a game intended to run on an 8
|
||||
MB machine.
|
||||
|
||||
#### Mipmapping To The Rescue {#Heading11}
|
||||
#### Mipmapping To The Rescue
|
||||
|
||||
Two factors combined to solve this problem. First, polygons are drawn
|
||||
through an edge list with no overdraw, as I discussed a few chapters
|
||||
|
|
@ -433,7 +433,7 @@ of texels, all at the mipmap level of the nearest vertex, and would
|
|||
require huge amounts of surface cache space while displaying a great
|
||||
deal of aliasing in distant regions due to a high texel:pixel ratio.
|
||||
|
||||
#### Two Final Notes on Surface Caching {#Heading12}
|
||||
#### Two Final Notes on Surface Caching
|
||||
|
||||
Dynamic lighting has a significant impact on the performance of surface
|
||||
caching, because whenever the lighting on a surface changes, the surface
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ pages: 1257-1271
|
|||
---
|
||||
|
||||
## Chapter 69\
|
||||
Surface Caching and Quake's Triangle Models {#Heading1}
|
||||
Surface Caching and Quake's Triangle Models
|
||||
|
||||
### Probing Hardware-Assisted Surfaces and Fast Model Animation Without Sprites {#Heading2}
|
||||
### Probing Hardware-Assisted Surfaces and Fast Model Animation Without Sprites
|
||||
|
||||
In the late '70s, I spent a summer doing contract programming at a
|
||||
government-funded installation called the Northeast Solar Energy Center
|
||||
|
|
@ -83,7 +83,7 @@ and the more information we have, the better. In that spirit, let's look
|
|||
at more of the stuff that makes Quake tick, starting with what I've
|
||||
recently learned about surface caching.
|
||||
|
||||
### Surface Caching with Hardware Assistance {#Heading3}
|
||||
### Surface Caching with Hardware Assistance
|
||||
|
||||
In Chapter 68, I discussed in detail the surface caching technique that
|
||||
Quake uses to do detailed, high-quality lighting without lots of
|
||||
|
|
@ -135,7 +135,7 @@ the case in software. Second, there are at least two alternatives that
|
|||
preserve the advantages of surface caching without many of the
|
||||
disadvantages noted above.
|
||||
|
||||
#### Letting the Graphics Card Build the Textures {#Heading4}
|
||||
#### Letting the Graphics Card Build the Textures
|
||||
|
||||
One obvious solution is to have the accelerator card build the textures,
|
||||
rather than having the CPU build and then download them. This eliminates
|
||||
|
|
@ -157,7 +157,7 @@ as it stores them in texture memory. Better yet, some accelerators
|
|||
support 8-bpp palettized hardware textures that are expanded to 16-bpp
|
||||
on the fly during texturing.)
|
||||
|
||||
#### The Light Map as Alpha Texture {#Heading5}
|
||||
#### The Light Map as Alpha Texture
|
||||
|
||||
Another appealing non-caching approach is doing unlit texture-mapping in
|
||||
one pass, then lighting from the light map as a second pass, using the
|
||||
|
|
@ -193,7 +193,7 @@ heavily toward hardware accelerators, and at this point it's a tossup
|
|||
whether the engine will use surface caching, Gouraud shading, or
|
||||
two-pass lighting.
|
||||
|
||||
### Drawing Triangle Models {#Heading6}
|
||||
### Drawing Triangle Models
|
||||
|
||||
Most of the last group of chapters in this book discuss how Quake works.
|
||||
If you look closely, though, you'll see that almost all of the
|
||||
|
|
@ -212,7 +212,7 @@ discuss some interesting aspects of our triangle-model architecture, and
|
|||
present code for one useful approach for the rapid drawing of triangle
|
||||
models.
|
||||
|
||||
#### Drawing Triangle Models Fast {#Heading7}
|
||||
#### Drawing Triangle Models Fast
|
||||
|
||||
We would have liked one rendering model, and hence one graphics
|
||||
pipeline, for all drawing in Quake; this would have simplified the code
|
||||
|
|
@ -256,7 +256,7 @@ between the vertices that suffer slight warping.
|
|||
|
||||

|
||||
|
||||
#### Trading Subpixel Precision for Speed {#Heading8}
|
||||
#### Trading Subpixel Precision for Speed
|
||||
|
||||
Another sacrifice at the altar of performance was subpixel precision.
|
||||
Before each triangle is drawn, we snap its vertices to the nearest
|
||||
|
|
@ -290,7 +290,7 @@ so the model is always lit from the same direction. Somewhat
|
|||
surprisingly, in practice this looks considerably better than pure
|
||||
ambient lighting.
|
||||
|
||||
#### An Idea that Didn't Work {#Heading9}
|
||||
#### An Idea that Didn't Work
|
||||
|
||||
As we implemented triangle models, we tried several ideas that didn't
|
||||
work out. One that's notable because it seems so appealing is caching a
|
||||
|
|
@ -328,7 +328,7 @@ if necessary, but the sprite architecture just had the feeling of being
|
|||
fundamentally not the right approach, so we tried thinking along
|
||||
different lines.
|
||||
|
||||
#### An Idea that Did Work {#Heading10}
|
||||
#### An Idea that Did Work
|
||||
|
||||
John Carmack had the notion that it was just way too much effort per
|
||||
pixel to do all the work of scanning out the tiny triangles in distant
|
||||
|
|
@ -519,7 +519,7 @@ D_PolysetRecursiveTriangle (lp3, new, lp2);
|
|||
|
||||

|
||||
|
||||
#### More Ideas that Might Work {#Heading11}
|
||||
#### More Ideas that Might Work
|
||||
|
||||
Useful as subdivision rasterization proved to be, we by no means think
|
||||
that we've maxed out triangle-model drawing, if only because we spent
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pages: 1273-1285
|
|||
---
|
||||
|
||||
## Chapter 70\
|
||||
Quake: A Post-Mortem and a Glimpse into the Future {#Heading1}
|
||||
Quake: A Post-Mortem and a Glimpse into the Future
|
||||
|
||||
*Why did not any of the children in the first group think of this faster
|
||||
method of going across the room? It is simple. They looked at what they
|
||||
|
|
@ -81,7 +81,7 @@ like Knuth, Foley and van Dam, Jim Blinn, Jim Kajiya, and hundreds of
|
|||
others—are you ready to take a shot at making your own contribution to
|
||||
the future?
|
||||
|
||||
### Preprocessing the World {#Heading2}
|
||||
### Preprocessing the World
|
||||
|
||||
For the most part, I'll discuss Quake's 3-D engine in this chapter,
|
||||
although I'll touch on other areas of interest. For 3-D rendering
|
||||
|
|
@ -146,7 +146,7 @@ player can move, is completely surrounded by a solid region. This
|
|||
eliminates a great many irrelevant polygons, and reduces the complexity
|
||||
of the next step, calculating the potentially visible set.
|
||||
|
||||
### The Potentially Visible Set (PVS) {#Heading3}
|
||||
### The Potentially Visible Set (PVS)
|
||||
|
||||
After the BSP tree is built, the potentially visible set (PVS) for each
|
||||
leaf is calculated. The PVS for a leaf consists of all the leaves that
|
||||
|
|
@ -229,7 +229,7 @@ radiosity lighting—a considerably more expensive process, but one that
|
|||
produces highly realistic lighting—is performed, but I'll save that for
|
||||
later.
|
||||
|
||||
### Passages: The Last-Minute Change that Didn't Happen {#Heading4}
|
||||
### Passages: The Last-Minute Change that Didn't Happen
|
||||
|
||||
Earlier, I mentioned that we almost changed 3-D engines again in the
|
||||
last month of Quake's development. Here's what happened: One of the
|
||||
|
|
@ -287,7 +287,7 @@ future engine.
|
|||
The more approaches you try, the larger your toolkit and the broader
|
||||
your understanding will be when you tackle your next project.
|
||||
|
||||
### Drawing the World {#Heading5}
|
||||
### Drawing the World
|
||||
|
||||
Everything described so far is a preprocessing step. When Quake is
|
||||
actually running, the world is drawn as follows: First, the PVS for the
|
||||
|
|
@ -361,7 +361,7 @@ Quake engine, but remains an inelegant solution, and, in the end, it
|
|||
feels like there's something better we didn't hit on. However, as John
|
||||
says, "I'm pragmatic above all else"—and the edge list did the job.
|
||||
|
||||
### Rasterization {#Heading6}
|
||||
### Rasterization
|
||||
|
||||
Once the visible spans are scanned out of the edge list, they must still
|
||||
be drawn, with perspective-correct texture mapping and lighting. This
|
||||
|
|
@ -383,7 +383,7 @@ of 1/z is overlapped with drawing 16 pixels, taking advantage of the
|
|||
Pentium's ability to perform floating-point in parallel with integer
|
||||
instructions, so the FDIV effectively takes only one cycle.
|
||||
|
||||
#### Lighting {#Heading7}
|
||||
#### Lighting
|
||||
|
||||
Lighting is less simple to explain. The traditional way of doing polygon
|
||||
lighting is to calculate the correct light at the vertices and linearly
|
||||
|
|
@ -422,7 +422,7 @@ splattered on a wall could be handled by drawing the splatter image as a
|
|||
sprite into the appropriate surface buffer, so that drawing the surface
|
||||
would draw the splatter as well.
|
||||
|
||||
#### Dynamic Lighting {#Heading8}
|
||||
#### Dynamic Lighting
|
||||
|
||||
Here we come to a feature added to Quake after last year's Computer Game
|
||||
Developer's Conference (CGDC). At that time, Quake did not support
|
||||
|
|
@ -469,7 +469,7 @@ visually very solid and stable. This was an important design goal from
|
|||
the start, both as a point of technical pride and because it greatly
|
||||
improves the player's sense of immersion.
|
||||
|
||||
### Entities {#Heading9}
|
||||
### Entities
|
||||
|
||||
So far, all we've drawn is the static, unchanging (apart from dynamic
|
||||
lighting) world. That's an important foundation, but it's certainly not
|
||||
|
|
@ -477,7 +477,7 @@ a game; now we need to add moving objects. These objects fall into four
|
|||
very different categories: BSP models, polygon models, sprites, and
|
||||
particles.
|
||||
|
||||
#### BSP Models {#Heading10}
|
||||
#### BSP Models
|
||||
|
||||
BSP models are just like the world, except that they can move. Examples
|
||||
include doors, moving bridges, and health and ammo boxes. The way these
|
||||
|
|
@ -518,7 +518,7 @@ if the door opens). This makes BSP models most suitable for fairly
|
|||
simple structures, such as boxes, which have relatively few polygons to
|
||||
clip, and cause relatively few edges to be added to the edge list.
|
||||
|
||||
#### Polygon Models and Z-Buffering {#Heading11}
|
||||
#### Polygon Models and Z-Buffering
|
||||
|
||||
Polygon models, such as monsters, weapons, and projectiles, consist of a
|
||||
triangle mesh with front and back skins stretched over the model. For
|
||||
|
|
@ -592,7 +592,7 @@ could be sent through a special fast path. The biggest breakthrough,
|
|||
though, was a very different sort of rasterizer that John came up with
|
||||
for relatively distant models.
|
||||
|
||||
#### The Subdivision Rasterizer {#Heading12}
|
||||
#### The Subdivision Rasterizer
|
||||
|
||||
This rasterizer, which we call the *subdivision rasterizer*, first draws
|
||||
all the vertices in the model. Then it takes each front-facing triangle,
|
||||
|
|
@ -621,7 +621,7 @@ faster ways yet to rasterize distant models adequately well, but the
|
|||
subdivider was clearly a win, and is a good example of how thinking in a
|
||||
radically different direction can pay off handsomely.
|
||||
|
||||
#### Sprites {#Heading13}
|
||||
#### Sprites
|
||||
|
||||
We had hoped to be able to eliminate sprites completely, making Quake
|
||||
100% 3-D, but sprites—although sometimes very visibly 2-D—were used for
|
||||
|
|
@ -637,7 +637,7 @@ similar to drawing a normal polygon, complete with perspective
|
|||
correction, although of course the inner loop must detect and skip over
|
||||
transparent pixels, and must also perform z-buffering.
|
||||
|
||||
#### Particles {#Heading14}
|
||||
#### Particles
|
||||
|
||||
The last drawing entity type is particles. Each particle is a
|
||||
solid-colored rectangle, scaled by distance from the viewer and drawn
|
||||
|
|
@ -650,7 +650,7 @@ example, providing a trail of fire behind a polygon-model lava ball that
|
|||
flies into the air, or generating an expanding cloud around a sprite
|
||||
explosion core.
|
||||
|
||||
### How We Spent Our Summer Vacation: After Shipping Quake {#Heading15}
|
||||
### How We Spent Our Summer Vacation: After Shipping Quake
|
||||
|
||||
Since shipping Quake in the summer of 1996, we've extended it in several
|
||||
ways: We've worked with Rendition to port it to the Verite accelerator
|
||||
|
|
@ -658,7 +658,7 @@ chip, we've ported it to OpenGL, we've ported it to Win32, we've done
|
|||
QuakeWorld, and we've added features for Quake 2. I'll discuss each of
|
||||
these briefly.
|
||||
|
||||
#### Verite Quake {#Heading16}
|
||||
#### Verite Quake
|
||||
|
||||
Verite Quake (VQuake) was the first hardware-accelerated version of
|
||||
Quake. It looks extremely good, due to bilinear texture filtering, which
|
||||
|
|
@ -719,7 +719,7 @@ it seems most likely that the two approaches will be mixed together,
|
|||
with surface caching used for special surfaces, and two-pass alpha
|
||||
lighting used for most drawing.
|
||||
|
||||
#### GLQuake {#Heading17}
|
||||
#### GLQuake
|
||||
|
||||
The second (and, according to current plans, last) port of Quake to a
|
||||
hardware accelerator was an OpenGL version, GLQuake, a native Win32
|
||||
|
|
@ -809,7 +809,7 @@ are standard equipment on accelerators, and it's a lot of fun seeing
|
|||
what sorts of previously very difficult effects can now be up and
|
||||
working in a matter of hours.
|
||||
|
||||
#### WinQuake {#Heading18}
|
||||
#### WinQuake
|
||||
|
||||
I'm not going to spend much time on the Win32 port of Quake; most of
|
||||
what I learned doing this consists of tedious details that are doubtless
|
||||
|
|
@ -833,7 +833,7 @@ Still, when you get down to it, the future of gaming is a networked
|
|||
Win32 world, and that's that, so if you haven't already moved to Win32,
|
||||
I'd say it's time.
|
||||
|
||||
#### QuakeWorld {#Heading19}
|
||||
#### QuakeWorld
|
||||
|
||||
QuakeWorld is a native Win32 multiplayer-only version of Quake, and was
|
||||
done as a learning experience; it is not a commercial product, but is
|
||||
|
|
@ -941,7 +941,7 @@ tradeoff of smoothness and perceived low latency for the frustration of
|
|||
paradoxes—and that's the way it's going to stay until most people are
|
||||
connected to the Internet by something better than modems.
|
||||
|
||||
#### Quake 2 {#Heading20}
|
||||
#### Quake 2
|
||||
|
||||
I can't talk in detail about Quake 2 as a game, but I can describe some
|
||||
interesting technology features. The Quake 2 rendering engine isn't
|
||||
|
|
@ -1000,7 +1000,7 @@ considers this the game interface of the future.
|
|||
By the way, Quake 2 is currently being developed as a native Win32 app
|
||||
only; no DOS version is planned.
|
||||
|
||||
### Looking Forward {#Heading21}
|
||||
### Looking Forward
|
||||
|
||||
In my address to the Computer Game Developer's Conference in 1996, I
|
||||
said that it wasn't a bad time to start up a game company aimed at
|
||||
|
|
|
|||
2
intro.md
2
intro.md
|
|
@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel
|
|||
Graphics and Multimedia Development'
|
||||
---
|
||||
|
||||
# Introduction {#Heading1}
|
||||
# Introduction
|
||||
|
||||
What was it like working with John Carmack on Quake? Like being strapped
|
||||
onto a rocket during takeoff—in the middle of a hurricane. It seemed
|
||||
|
|
|
|||
Loading…
Reference in a new issue