Like being strapped onto a rocket during takeoff-in the middle of a hurricane.
- Michael Abrash's Graphics Programming Black Book - Download link?
- Michael Abrash's graphics programming black book ( edition) | Open Library.
- Create an account or sign in to comment.
See more about this book on Archive. Copy and paste this code into your Wikipedia page. Need help?
Graphics Programming Black Book Special Edition (with CD-ROM)
Last edited by Clean Up Bot. July 5, History. Add another edition? Michael Abrash's graphics programming black book Michael Abrash. Want to Read.
Michael Abrash - Wikipedia
Are you sure you want to remove Michael Abrash's graphics programming black book from your list? Michael Abrash's graphics programming black book Special ed.
As with Zen of any sort, mastering that Zen of assembly language is more a matter of learning than of being taught. You will have to find your own path of learning, although I will start you on your way with this book. The subtle facts and examples I provide will help you gain the necessary experience, but you must continue the journey on your own. Each program you create will expand your programming horizons and increase the options available to you in meeting the next challenge. The ability of your mind to find surprising new and better ways to craft superior code from a concept—the flexible mind, if you will—is the linchpin of good assembler code, and you will develop this skill only by doing.
Never underestimate the importance of the flexible mind.
Good assembly code is better than good compiled code. High-level languages are the best choice for the majority of programmers, and for the bulk of the code of most applications. When the best code—the fastest or smallest code possible—is needed, though, assembly is the only way to go. Simple logic dictates that no compiler can know as much about what a piece of code needs to do or adapt as well to those needs as the person who wrote the code. Given that superior information and adaptability, an assembly language programmer can generate better code than a compiler, all the more so given that compilers are constrained by the limitations of high-level languages and by the process of transformation from high-level to machine language.
Consequently, carefully optimized assembly is not just the language of choice but the only choice for the 1 percent to 10 percent of code—usually consisting of small, well-defined subroutines—that determines overall program performance, and it is the only choice for code that must be as compact as possible, as well. In the run-of-the-mill, non-time-critical portions of your programs, it makes no sense to waste time and effort on writing optimized assembly code—concentrate your efforts on loops and the like instead; but in those areas where you need the finest code quality, accept no substitutes.
Shop by category
Note that I said that an assembly programmer can generate better code than a compiler, not will generate better code. While it is true that good assembly code is better than good compiled code, it is also true that bad assembly code is often much worse than bad compiled code; since the assembly programmer has so much control over the program, he or she has virtually unlimited opportunities to waste cycles and bytes.
The sword cuts both ways, and good assembly code requires more, not less, forethought and planning than good code written in a high-level language. The gist of all this is simply that good assembly programming is done in 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.
To summarize, the skill of assembly language optimization is a combination of knowledge, perspective, and a way of thought that makes possible the genesis of absolutely the fastest or the smallest code. With that in mind, what should the first step be? Development of the flexible mind is an obvious step. Still, the flexible mind is no better than the knowledge at its disposal. The first step in the journey toward mastering optimization at that exalted level, then, would seem to be learning how to learn.
The author had, however, chosen a small, well-defined assembly language routine to refine, consisting of about 30 instructions that did nothing more than expand 8 bits to 16 bits by duplicating each bit. In short, he had used all the information at his disposal to improve his code, and had, as a result, saved cycles by the bushel. There was, in fact, only one slight problem with the optimized version of the routine…. As diligent as the author had been, he had nonetheless committed a cardinal sin of x86 assembly language programming: He had assumed that the information available to him was both correct and complete.
While the execution times provided by Intel for its processors are indeed correct, they are incomplete; the other—and often more important—part of code performance is instruction fetch time, a topic to which I will return in later chapters. Assume nothing. I cannot emphasize this strongly enough—when you care about performance, do your best to improve the code and then measure the improvement.
Ignorance about true performance can be costly. When I wrote video games for a living, I spent days at a time trying to wring more performance from my graphics drivers. I rewrote whole sections of code just to save a few cycles, juggled registers, and relied heavily on blurry-fast register-to-register shifts and adds. As I was writing my last game, I discovered that the program ran perceptibly faster if I used look-up tables instead of shifts and adds for my calculations. In truth, instruction fetching was rearing its head again, as it often does, and the fetching of the shifts and adds was taking as much as four times the nominal execution time of those instructions.
Ignorance can also be responsible for considerable wasted effort. The letter-writers counted every cycle in their timing loops, just as the author in the story that started this chapter had. Like that author, the letter-writers had failed to take the prefetch queue into account. In fact, they had neglected the effects of video wait states as well, so the code they discussed was actually much slower than their estimates.
The proper test would, of course, have been to run the code to see if snow resulted, since the only true measure of code performance is observing it in action. Clearly, one key to mastering Zen-class optimization is a tool with which to measure code performance. The can be started at the beginning of a block of code of interest and stopped at the end of that code, with the resulting count indicating how long the code took to execute with an accuracy of about 1 microsecond.
To be precise, the counts once every A nanosecond is one billionth of a second, and is abbreviated ns. Listing 3. On the other hand, it is by no means essential that you understand exactly how the Zen timer works. Interesting, yes; essential, no. ZTimerOn is called at the start of a segment of code to be timed. ZTimerOn saves the context of the calling code, disables interrupts, sets timer 0 of the to mode 2 divide-by-N mode , sets the initial timer count to 0, restores the context of the calling code, and returns. Two aspects of ZTimerOn are worth discussing further.
One point of interest is that ZTimerOn disables interrupts. Were interrupts not disabled by ZTimerOn , keyboard, mouse, timer, and other interrupts could occur during the timing interval, and the time required to service those interrupts would incorrectly and erratically appear to be part of the execution time of the code being measured.
As a result, code timed with the Zen timer should not expect 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. A second interesting point about ZTimerOn is that it may introduce some small inaccuracy into the system clock time whenever it is called.
The actually contains three timers, as shown in Figure 3. Each of the three timers counts down in a programmable way, generating a signal on its output pin when it counts down to 0. Timer 2 drives the speaker, although it can be used for other timing purposes when the speaker is not in use. As shown in Figure 3. On the other hand, the output of timer 2 is connected to nothing other than the speaker. Timer 1 is dedicated to providing dynamic RAM refresh, and should not be tampered with lest system crashes result.
Finally, timer 0 is used to drive the system clock. A millisecond is one-thousandth of a second, and is abbreviated ms. This line is connected to the hardware interrupt 0 IRQ0 line on the system board, so every Each timer channel of the can operate in any of six modes. Timer 0 normally operates in mode 3: square wave mode. In square wave mode, the initial count is counted down two at a time; when the count reaches zero, the output state is changed. The initial count is again counted down two at a time, and the output state is toggled back when the count reaches zero.
The result is a square wave that changes state more slowly than the input clock by a factor of the initial count. In its normal mode of operation, timer 0 generates an output pulse that is low for about Square wave mode is not very useful for precision timing because it counts down by two twice per timer interrupt, thereby rendering exact timings impossible.
Fortunately, the offers another timer mode, mode 2 divide-by-N mode , which is both a good substitute for square wave mode and a perfect mode for precision timing. Divide-by-N mode counts down by one from the initial count. When the count reaches zero, the timer turns over and starts counting down again without stopping, and a pulse is generated for a single clock period.
As a result, timer 0 continues to generate timer interrupts in divide-by-N mode, and the system clock continues to maintain good time. Why not use timer 2 instead of timer 0 for precision timing? We need the interrupt generated by the output of timer 0 to tell us when the count has overflowed, and we will see shortly that the timer interrupt also makes it possible to time much longer periods than the Zen timer shown in Listing 3.
In fact, the Zen timer shown in Listing 3. Fifty-four ms may not seem like a very long time, but even a CPU as slow as the can perform more than 1, divides in 54 ms, and division is the single instruction that the performs most slowly.
Cookies on oxfam
If a measured period turns out to be longer than 54 ms that is, if timer 0 has counted down and turned over , the Zen timer will display a message to that effect. A long-period Zen timer for use in such cases will be presented later in this chapter. The Zen timer determines whether timer 0 has turned over by checking to see whether an IRQ0 interrupt is pending.
Remember, interrupts are off while the Zen timer runs, so the timer interrupt cannot be recognized until the Zen timer stops and enables interrupts.