Compare commits

...
Author SHA1 Message Date
James Gregory
4028269fe1
Merge pull request #43 from thp/code-fixes
Code fixes
2020-05-21 19:30:04 +10:00
Thomas Perl
d54e14ca74 CurrentColordb -> CurrentColor db 2020-05-21 11:25:45 +02:00
Thomas Perl
4b5c169695 COLOR_ENTRY_LENGTHequ -> COLOR_ENTRY_LENGTH equ 2020-05-21 11:25:15 +02:00
Thomas Perl
8d29240973 loopDrawDiagonalLoop -> loop DrawDiagonalLoop 2020-05-21 11:24:43 +02:00
Thomas Perl
7388cf370c loopVLineLoop -> loop VLineLoop 2020-05-21 11:24:18 +02:00
Thomas Perl
beff664727 labelbyte -> label byte 2020-05-21 11:23:42 +02:00
Thomas Perl
b9bf2eb0ed _DATAsegmentpublic -> _DATA segment public 2020-05-21 11:14:12 +02:00
Thomas Perl
1a87045a95 pus h -> push 2020-05-21 11:13:30 +02:00
Thomas Perl
28e420e665 Fix ASM OCR of macros 2020-05-21 11:13:06 +02:00
Thomas Perl
51194d4928 not40h -> not 40h 2020-05-21 11:12:28 +02:00
Thomas Perl
b96792917b Fix more ASM calling (OCR) 2020-05-21 11:12:10 +02:00
Thomas Perl
73e56070b4 Fix function calling (OCR) 2020-05-21 11:11:50 +02:00
Thomas Perl
6adf8f912b Fix cmpcx->cmp cx 2020-05-21 11:11:16 +02:00
Thomas Perl
f2025ac6e8 Add missing ; for ASM comment 2020-05-21 11:10:54 +02:00
Thomas Perl
700514a488 Code OCR fix: lodsbyte -> lods byte 2020-05-21 11:10:09 +02:00
Thomas Perl
f87c4484c2 Fix db512->db 512 2020-05-21 11:09:07 +02:00
Thomas Perl
2342c2d49d Fix typo 2020-05-21 11:08:49 +02:00
Thomas Perl
670082d347 Fix MAXIMUM_SCAN_LINEequ 2020-05-21 11:08:17 +02:00
Thomas Perl
03978b9651 Fix looping/calling OCR bugs 2020-05-21 11:07:47 +02:00
Thomas Perl
a4df877739 Datasegment -> Data segment 2020-05-21 11:07:02 +02:00
Thomas Perl
b3144a2145 Startproc near -> Start proc near 2020-05-21 11:06:40 +02:00
Thomas Perl
9072e5d500 Fix various (ends) code OCR errors 2020-05-21 11:06:10 +02:00
Thomas Perl
97d436fb73 Fix 'endp' -> ' endp'
Automatically done using:

    sed -i '' -e 's/\([^ ]\)endp/\1 endp/g' src/*.md
2020-05-21 10:55:36 +02:00
Thomas Perl
9373e5d2ee Fix 'procnear' -> ' proc near'
Automatically done using:

    sed -i '' -e 's/procnear/ proc near/g' src/*.md
2020-05-21 10:54:08 +02:00
James Gregory
264f0b2624 Sourced a higher-res 13-01 image
Fixes #18
2019-08-13 22:40:23 +10:00
James Gregory
d34abb2619
Merge pull request #37 from ntwk/master
Replace Pandoc -S switch with +smart extension and update documentation
2019-08-13 21:44:38 +10:00
James Gregory
b61c1dc6eb
Merge branch 'master' into master 2019-08-13 21:44:22 +10:00
James Gregory
9892775079
Merge pull request #34 from ayghor/ayghor-listing-1.3
Listing 1.3: fix typos
2019-08-13 21:29:24 +10:00
James Gregory
5f35d647df
Merge pull request #25 from TheFakeMontyOnTheRun/fix/fixTypoInCycleEaters
Fix OCR error/typo in chapter 4
2019-08-13 21:27:53 +10:00
James Gregory
205e20d4a9
Merge pull request #26 from amroibrahim/patch-1
Update chapter-02.md
2019-08-13 21:27:31 +10:00
James Gregory
28da7c0a8d
Merge pull request #35 from mtexier/fix-59.4
Fix listing 59.4
2019-08-13 21:23:50 +10:00
James Gregory
6c9b1ebee5
Merge pull request #27 from shlomif/correct-typos
Fix spelling, capitalisation and grammar.
2019-08-13 21:22:07 +10:00
James Gregory
67ef5ef0ba
Merge pull request #32 from thp/milliseconds
Fix time unit: Milliseconds instead of microseconds
2019-08-13 21:21:09 +10:00
rnndxb wxcy
c1e37c257b Update README.md to specify Pandoc 2.0 or greater 2018-09-06 10:14:06 -04:00
rnndxb wxcy
44f7df0b28 Use Pandoc +smart extension in Makefile
The --smart/-S option has been removed from Pandoc 2.0.  Use the +smart
extension instead.
2018-09-06 09:59:58 -04:00
Matthieu Texier
6140424123 Fix listing 59.4 2018-07-24 14:44:41 +02:00
Igor de Sant'Ana Fontana
c25e885362
Listing 1.3: fix typos 2018-07-23 16:40:27 -03:00
Thomas Perl
e5e611fe7a Fix time unit: Milliseconds instead of microseconds
DRAM refresh is usually measured in milliseconds, and
from the calculations below one can see that it must
be milliseconds.

And same for the other changes: Multiplying microsecond
values by a factor of ~ 1000 will result in milliseconds.

This is also cross-checked with the PDF available here:
http://twimgs.com/ddj/abrashblackbook/gpbb4.pdf
2018-06-22 12:34:08 +02:00
Shlomi Fish
4f23917dbe Fix spelling, capitalisation and grammar.
In README.md. I hereby put all my changes under CC0/Public-Domain.
2017-09-02 10:32:43 +03:00
Amro Ibrahim
ff14db2bac Update chapter-02.md 2017-08-31 10:00:41 -07:00
TheFakeMontyOnTheRun
3f04c2c77f Fix OCR error/typo in chapter 4
Not only cycles were eaten ;-P
2017-08-27 20:29:36 +01:00
James Gregory
5162407cef Merge pull request #19 from ntwk/master
Correct typos in listing 3.1
2017-08-05 13:17:43 +10:00
James Gregory
fd5e7e94ff Merge pull request #21 from dennis714/master
minor listings formatting
2017-08-05 13:16:52 +10:00
James Gregory
766cc22965 Merge pull request #23 from dgholz/dgholz-typo-1
fix typo 'new' -> 'knew' in about.md
2017-08-05 13:15:12 +10:00
Daniel George Holz
7f2f1f5689 fix typo 'new' -> 'knew' in about.md 2017-08-01 10:38:28 +01:00
Dennis Yurichev
f1c36f03da ... 2016-08-11 03:11:13 +03:00
Dennis Yurichev
c92b4e7d49 mering 2016-08-11 03:07:57 +03:00
Dennis Yurichev
93fa15fd19 ... 2016-08-11 03:07:21 +03:00
Dennis Yurichev
5ece9ece8c minor listing formatting 2016-08-11 03:04:43 +03:00
rnndxb wxcy
37a1456cc8 Correct typos and formatting in listing 3.8 2016-03-03 14:14:09 -05:00
rnndxb wxcy
879524acd5 Correct typos and formatting in listing 3.6 2016-03-03 13:56:41 -05:00
rnndxb wxcy
6d7fcf71b1 Correct typos in listing 3.2 2016-03-03 13:21:25 -05:00
rnndxb wxcy
101714d905 Correct typos in listing 3.1 2016-03-03 13:07:27 -05:00
James Gregory
9b1713b961 Merge pull request #13 from dennis714/master
typos
2015-09-15 13:35:09 +10:00
James Gregory
788a63738f Merge pull request #14 from joseprous/remove_duplicate_text
remove duplicate text
2015-09-15 13:34:20 +10:00
James Gregory
2426d214b6 Merge pull request #15 from joseprous/typo
typo
2015-09-15 13:34:00 +10:00
Jose Prous
30b83be20d typo 2015-09-13 19:41:36 -04:00
Jose Prous
b85ec11c00 remove duplicate text 2015-09-13 19:33:56 -04:00
Dennis Yurichev
a07f8de883 typos 2015-08-14 01:18:56 +03:00
Dennis Yurichev
897e853a42 typos 2015-08-13 06:16:59 +03:00
James Gregory
f69d425cac Merge pull request #8 from brohee/master
misaligned brace in listing 1.5
2014-02-01 15:09:52 -08:00
Bruno Rohée
e93ef444d4 misaligned brace in listing 1.5 2014-02-01 14:39:18 +01:00
James Gregory
419f4be93f Merge pull request #6 from steets/patch-1
Delete errant space in chapter 6
2014-01-31 16:24:21 -08:00
Andrew Steets
1d697446a5 Delete errant space 2014-01-31 10:09:26 -06:00
James Gregory
3c4c99501b Change permission to blessing. The rights situation is ambiguous.
Michael is happy with this project, and has given permission as much as
he is able; sadly, the actual rights to the book are ambiguous, with
the publisher going out of business and the PDF being released by Dr.
Dobbs Journal.

Removed the word permission as to not imply that Michael Abrash has the
ability to grant legal permission. He doesn't.
2014-01-31 13:48:03 +11:00
James Gregory
810786fdc1 Merge pull request #5 from picomancer/master
Fix typos; MathML -> MathJAX
2014-01-30 18:39:19 -08:00
picomancer
0bcbb14c0f README.md: MathML -> MathJax 2014-01-30 17:05:42 -05:00
picomancer
836b1c13a6 README.md: Fix typos; improve grammar and spelling. 2014-01-30 17:05:38 -05:00
James Gregory
9ebbb12fc9 Remove line about rights. They're complicated. 2014-01-30 20:50:01 +11:00
James Gregory
548f7146d7 Revert By->My Gosh, it was right originally. 2014-01-28 19:58:11 +11:00
James Gregory
a17b3067ed Merge pull request #4 from mstum/master
Chapter 1-3 corrections
2014-01-28 00:46:46 -08:00
Michael Stum
753657938b Chapter 3 typos (and 1 Chapter 2 one) 2014-01-28 00:18:10 -08:00
Michael Stum
e54d0fa5d2 Chapter 2 typos 2014-01-27 23:04:19 -08:00
Michael Stum
340429d486 Chapter 1 typos 2014-01-27 22:49:56 -08:00
James Gregory
983124da7c Mention the releases and online versions 2014-01-20 20:34:29 +11:00
35 changed files with 535 additions and 536 deletions

4
Makefile Executable file → Normal file
View file

@ -7,12 +7,12 @@ all: html epub mobi
html:
rm -rf out/html && mkdir -p out/html
cp -r images html/book.css out/html/
pandoc -S --to html5 -o out/html/black-book.html --section-divs --toc --standalone --template=html/template.html $(FILES)
pandoc --to html5+smart -o out/html/black-book.html --section-divs --toc --standalone --template=html/template.html $(FILES)
epub:
mkdir -p out
rm -f out/black-book.epub
pandoc -S --to epub3 -o out/black-book.epub --epub-cover-image images/cover.png --toc --epub-chapter-level=2 --data-dir=epub --template=epub/template.html $(FILES)
pandoc --to epub3+smart -o out/black-book.epub --epub-cover-image images/cover.png --toc --epub-chapter-level=2 --data-dir=epub --template=epub/template.html $(FILES)
mobi:
rm -f out/black-book.mobi

View file

@ -2,36 +2,38 @@
This is the source for an ebook version of Michael Abrash's Black Book of Graphics Programming (Special Edition), originally published in 1997 and [released online for free in 2001](http://www.drdobbs.com/parallel/graphics-programming-black-book/184404919).
Reproduced with permission of Michael Abrash, converted and maintained by [James Gregory](mailto:james@jagregory.com).
Reproduced with blessing of Michael Abrash, converted and maintained by [James Gregory](mailto:james@jagregory.com).
The [GitHub releases list](https://github.com/jagregory/abrash-black-book/releases) has an EPUB and Mobi version available for download, and you can find a mirror of the HTML version at [www.jagregory.com/abrash-black-book](http://www.jagregory.com/abrash-black-book/).
## How does this differ from the previously released versions?
The book is now out of print, and hard to come by. Last time I checked it was going for over $200 on ebay.
The book is now out of print, and hard to come by. Last time I checked, it was going for over $200 on eBay.
The version which Michael and Dr. Dobbs released in 2001 was as a collection PDFs. This version is [still available](http://www.drdobbs.com/parallel/graphics-programming-black-book/184404919); however, the structure (multiple files) and the format (PDF) doesn't lend itself well to reading on a ebook reader or other mobile device.
The version which Michael and Dr. Dobbs released in 2001 was a collection of PDF files. That version is [still available](http://www.drdobbs.com/parallel/graphics-programming-black-book/184404919). However, the structure (multiple files) and the format (PDF) result in a poor user experience on an ebook reader or other mobile device.
This version has been thoroughly cleaned of artefacts and condensed into something which can be easily converted into a ebook friendly format. You can read this version online at Github, or download any of the Epub or Mobi releases. You can clone the repository and generate your own version with [pandoc](http://johnmacfarlane.net/pandoc/) if necessary.
This version has been thoroughly cleaned of artifacts and condensed into something which can easily be converted into an ebook-friendly format. You can read this version online at GitHub, or download any of the EPUB or Mobi releases. You can clone the repository and generate your own version with [pandoc](http://johnmacfarlane.net/pandoc/) if necessary.
## Contributing
Changes are welcome, especially conversion related ones. If you spot any issues whilst reading, please submit an issue and I'll correct it. Pull Requests are always welcome.
Changes are welcome, especially conversion-related ones. If you spot any problems while reading, please [submit an issue](https://github.com/jagregory/abrash-black-book/issues) and I'll correct it. Pull requests are always welcome.
There's some larger changes that could be made to help preserve the content longer term. I'd love to see some of the images converted to a vector representation so we can provide higher-resolution versions, and similarly formulas and maths could be represented in MathML.
Some larger changes could be made to improve the content. I'd love to see some of the images converted to a vector representation so we can provide higher-resolution versions. Formulas and equations could be typeset with [MathJax](http://www.mathjax.org/).
## Generating your own ebook
You need to have the following software installed and on your `PATH` before you begin:
* [pandoc](http://johnmacfarlane.net/pandoc/) for Markdown to HTML and Epub conversion.
* [pandoc](http://johnmacfarlane.net/pandoc/) version 2.0 or greater for Markdown to HTML and EPUB conversion.
* [kindlegen](http://www.amazon.com/gp/feature.html?docId=1000765211) for Epub to Mobi conversion.
To generate an e-reader friendly version of the book, you can use `make` with one of the following options:
* `html` - build a HTML5 single-page version of the book
* `epub` - build an Epub3 ebook
* `html` - build an HTML5 single-page version of the book
* `epub` - build an EPUB3 ebook
* `mobi` - build a Kindle-friendly Mobi
* `all` - do all of the above
Once complete, there'll be an `out` directory with a `black-book.epub`, a `black-book.mobi` and a `html` directory with a `black-book.html` file.
Once complete, there will be an `out` directory with a `black-book.epub`, a `black-book.mobi` and an `html` directory with a `black-book.html` file.
> Note: Generating a mobi requires an epub to already exist. Also, mobi generation can be *slow* because of compression. If you want a quick mobi conversion you can just run `kindlegen out/black-book.epub`.
> Note: Generating a Mobi requires an EPUB to already exist. Also, Mobi generation can be *slow* because of compression. If you want a quick Mobi conversion you can just run `kindlegen out/black-book.epub`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

BIN
images/13-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View file

@ -23,7 +23,7 @@ learn in the space of a few months on the PC.
The biggest benefit to me of actually making money as a programmer was
the ability to buy all the books and magazines I wanted. I bought a lot.
I was in territory that I new almost nothing about, so I read
I was in territory that I knew almost nothing about, so I read
*everything* that I could get my hands on. Feature articles, editorials,
even advertisements held information for me to assimilate.

View file

@ -254,7 +254,7 @@ requires over two and one-half minutes to checksum *one* file!
These results make it clear that it's folly to rely on your compiler's
optimization to make your programs fast. Listing 1.1 is simply poorly
designed, and no amount of compiler optimization will compensate for
that failing. To drive home the point, conListings 1.2 and 1.3, which
that failing. To drive home the point, Listings 1.2 and 1.3, which
together are equivalent to Listing 1.1 except that the entire checksum
loop is written in tight assembly code. The assembly language
implementation is indeed faster than any of the C versions, as shown in
@ -363,12 +363,12 @@ _ChecksumFile proc near
ChecksumLoop:
mov ah,3fh ;DOS read file function #
int 21h ;read the byte
jcErrorEnd;an error occurred
jc ErrorEnd ;an error occurred
and ax,ax ;any bytes read?
jz Success ;no-end of file reached-we're done
add si,[TempWord] ;add the byte into the
;checksum total
jmpChecksumLoop
jmp ChecksumLoop
ErrorEnd:
sub ax,ax ;error
jmp short Done
@ -381,7 +381,7 @@ Done:
pop si ;restore C's register variable
pop bp
ret
_ChecksumFileendp
_ChecksumFile endp
end
```
@ -500,7 +500,7 @@ your programs that directly affect response time. Notice, for example,
that I haven't bothered to implement a version of the checksum program
entirely in assembly; Listings 1.2 and 1.6 call assembly subroutines
that handle the time-critical operations, but C is still used for
checking command-line parameters, operning files, printing, and the
checking command-line parameters, opening files, printing, and the
like.
> ![](images/i.jpg)
@ -521,7 +521,7 @@ 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
why Listing 1.4 is so much better than Listing 1.1. Like `read()`,
`getc()` calls DOS to read from the file; the speed improvement of
Listing 1.4 over Listing 1.1 occurs because `getc()` eads many bytes
Listing 1.4 over Listing 1.1 occurs because `getc()` reads many bytes
at once via DOS, then manages those bytes for us. That's faster than
reading them one at a time using `read()`—but there's no reason to
think that it's faster than having our program read and manage blocks
@ -563,7 +563,7 @@ The third reason is often fallacious. C library functions are not always
written in assembly, nor are they always particularly well-optimized.
(In fact, they're often written for *portability*, which has nothing to
do with optimization.) What's more, they're general-purpose functions,
and often can be outperformed by well-but-not- brilliantly-written code
and often can be outperformed by well-but-not-brilliantly-written code
that is well-matched to a specific task. As an example, consider Listing
1.5, which uses internal buffering to handle blocks of bytes at a time.
Table 1.1 shows that Listing 1.5 is 2.5 to 4 times faster than Listing
@ -629,7 +629,7 @@ main(int argc, char *argv[]) {
while ( LengthCount-- ) {
/* Add each byte in turn into the checksum accumulator */
Checksum += (unsigned int) *WorkingPtr++;
}
}
} while ( WorkingLength );
/* Report the result */
@ -667,7 +667,7 @@ indeed make a significant difference. Table 1.1 indicates that the
optimized version of Listing 1.5 produced by Microsoft C outperforms an
unoptimized version of the same code by more than 60 percent. What's
more, a mostly-assembly version of Listing 1.5, shown in Listings 1.6
and 1.7, outperforms even the best-optimized C version of List1.5 by 26
and 1.7, outperforms even the best-optimized C version of Listing 1.5 by 26
percent. These are considerable improvements, well worth pursuing—once
the design has been maxed out.
@ -754,12 +754,12 @@ Parms struc
Buffer dw ?
BufferLength dw ?
Checksum dw ?
Parmsends
Parms ends
;
.model small
.code
public _ChecksumChunk
_ChecksumChunkprocnear
_ChecksumChunk proc near
push bp
mov bp,sp
push si ;save C's register variable
@ -779,7 +779,7 @@ ChecksumLoop:
pop si ;restore C's register variable
pop bp
ret
_ChecksumChunkendp
_ChecksumChunk endp
end
```

View file

@ -89,7 +89,7 @@ much different from the original, and in fact still contains exactly the
same number of instructions, the performance of the entire subroutine
improved by about 10 percent from just this one change. (Incidentally,
that wasn't the end of the optimization; I eliminated the `DEC` and
`JNJ` instructions by expanding the four iterations of the loop—but
`JNZ` instructions by expanding the four iterations of the loop—but
that's a tale for another chapter.)
The point is this: To write truly superior assembly programs, you need
@ -200,7 +200,7 @@ enough.
The single most critical aspect of the hardware, and the one about which
it is hardest to learn, is the CPU. The x86 family CPUs have a complex,
irregular instruction set, and, unlike most processors, they are neither
straightforward nor wellregarding true code performance. What's more,
straightforward nor well-documented true code performance. What's more,
assembly is so difficult to learn that most articles and books that
present assembly code settle for code that just works, rather than code
that pushes the CPU to its limits. In fact, since most articles and
@ -293,7 +293,7 @@ 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 1percent to 10 percent of code—usually consisting of small,
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

View file

@ -163,34 +163,34 @@ presented in Chapter K on the companion CD-ROM.
; in when ZTimerOn was called.
;
Code segment word public CODE'
assumecs: Code, ds:nothing
Code segment word public 'CODE'
assume cs:Code, ds:nothing
public ZTimerOn, ZTimerOff, ZTimerReport
;
; Base address of the 8253 timer chip.
;
BASE_8253equ40h
BASE_8253 equ 40h
;
; The address of the timer 0 count registers in the 8253.
;
TIMER_0_8253 equBASE_8253 + 0
TIMER_0_8253 equ BASE_8253 + 0
;
; The address of the mode register in the 8253.
;
MODE_8253 equBASE_8253 + 3
MODE_8253 equ BASE_8253 + 3
;
; The address of Operation Command Word 3 in the 8259 Programmable
; Interrupt Controller (PIC) (write only, and writable only when
; bit 4 of the byte written to this address is 0 and bit 3 is 1).
;
OCW3 equ20h
OCW3 equ 20h
;
; The address of the Interrupt Request register in the 8259 PIC
; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
; of OCW3 = 0).
;
IRR equ20h
IRR equ 20h
;
; Macro to emulate a POPF instruction in order to fix the bug in some
; 80286 chips which allows interrupts to occur during a POPF even when
@ -220,7 +220,7 @@ OriginalFlags db ? ; storage for upper byte of
; ZTimerOn called
TimedCount dw ? ; timer 0 count when the timer
; is stopped
ReferenceCount dw ; number of counts required to
ReferenceCount dw ? ; number of counts required to
; execute timer overhead code
OverflowFlag db ? ; used to indicate whether the
; timer overflowed during the
@ -229,28 +229,28 @@ OverflowFlag db ? ; used to indicate whether the
; String printed to report results.
;
OutputStr label byte
db 0dh, 0ah, Timed count: , 5 dup (?)
ASCIICountEnd labelbyte
db microseconds', 0dh, 0ah
db $'
db 0dh, 0ah, 'Timed count: ', 5 dup (?)
ASCIICountEnd label byte
db ' microseconds', 0dh, 0ah
db '$'
;
; String printed to report timer overflow.
;
OverflowStr label byte
db 0dh, 0ah
db ****************************************************'
db '****************************************************'
db 0dh, 0ah
db * The timer overflowed, so the interval timed was *'
db '* The timer overflowed, so the interval timed was *'
db 0dh, 0ah
db * too long for the precision timer to measure. *'
db '* too long for the precision timer to measure. *'
db 0dh, 0ah
db * Please perform the timing test again with the *'
db0dh, 0ah
db * long-period timer. *'
db '* Please perform the timing test again with the *'
db 0dh, 0ah
db ****************************************************'
db '* long-period timer. *'
db 0dh, 0ah
db $'
db '****************************************************'
db 0dh, 0ah
db '$'
; ********************************************************************
; * Routine called to start timing. *
@ -350,9 +350,9 @@ ZTimerOff proc near
; timer interrupt.
;
mov al,00001010b ; OCW3, set up to read
out OCW3,al; Int errupt Request register
out OCW3,al ; Interrupt Request register
DELAY
ina l,IRR; read Interrupt Request
in al,IRR ; read Interrupt Request
; register
and al,1 ; set AL to 1 if IRQ0 (the
; timer interrupt) is pending
@ -418,7 +418,7 @@ ZTimerOff endp
; Called by ZTimerOff to start timer for overhead measurements.
;
ReferenceZTimerOnproc near
ReferenceZTimerOn proc near
;
; Save the context of the program being timed.
;
@ -445,7 +445,7 @@ ReferenceZTimerOnproc near
pop ax
ret
ReferenceZTimerOnendp
ReferenceZTimerOn endp
;
; Called by ZTimerOff to stop timer and add result to ReferenceCount
@ -488,7 +488,7 @@ ReferenceZTimerOff endp
; * Routine called to report timing results. *
; ********************************************************************
ZTimerReport procnear
ZTimerReport proc near
pushf
push ax
@ -541,7 +541,7 @@ CTSLoop:
;
mov ah, 9
mov dx, offset OutputStr
int 21h
int 21h
;
EndZTimerReport:
pop ds
@ -671,7 +671,7 @@ count reaches zero, the timer turns over and starts counting down again
without stopping, and a pulse is generated for a single clock period.
While the pulse is not held for nearly as long as in square wave mode,
it doesn't matter, since the 8259 interrupt controller is configured in
the PC to be edgeand hence cares only about the existence of a pulse
the PC to be edge-triggered and hence cares only about the existence of a pulse
from timer 0, not the duration of the pulse. As a result, timer 0
continues to generate timer interrupts in divide-by-N mode, and the
system clock continues to maintain good time.
@ -688,7 +688,7 @@ the Zen timer shown in Listing 3.1 supports.
In fact, the Zen timer shown in Listing 3.1 can only time intervals of
up to about 54 ms in length, since that is the period of time that can
be measured by timer 0 before its count turns over and repeats.
fifty-four ms may not seem like a very long time, but even a CPU as slow
Fifty-four ms may not seem like a very long time, but even a CPU as slow
as the 8088 can perform more than 1,000 divides in 54 ms, and division
is the single instruction that the 8088 performs most slowly. If a
measured period turns out to be longer than 54 ms (that is, if timer 0
@ -730,11 +730,11 @@ restart until the timing interval ends, losing time all the while.
The effects on the system time of the Zen timer aren't a matter for
great concern, as they are temporary, lasting only until the next warm
or cold boot. System that have batteryclocks, (AT-style machines; that
or cold boot. System that have battery-backed clocks, (AT-style machines; that
is, virtually all machines in common use) automatically reset the
correct time whenever the computer is booted, and systems without
battery-clocks prompt for the correct date and time when booted.
Also,repeated use of the Zen timer usually makes the system clock slow
battery-backed clocks prompt for the correct date and time when booted.
Also, repeated use of the Zen timer usually makes the system clock slow
by at most a total of a few seconds, unless code that takes much longer
than 54 ms to run is timed (in which case the Zen timer will notify you
that the code is too long to time).
@ -789,8 +789,8 @@ from timer counts to microseconds, and prints the resulting time in
microseconds to the standard output.
Note that `ZTimerReport` need not be called immediately after
`ZTimerOff`. In fact, after a given call to `ZTimerOff,
ZTimerReport` can be called at any time right up until the next call to
`ZTimerOff`. In fact, after a given call to `ZTimerOff`,
`ZTimerReport` can be called at any time right up until the next call to
`ZTimerOn`.
You may want to use the Zen timer to measure several portions of a
@ -880,7 +880,7 @@ performance will be similar even on different IBM models; in fact, quite
the opposite is true. For example, every PS/2 computer, even the
relatively slow Model 30, executes code much faster than does a PC or
XT. As another example, I set out to do the timings for my earlier book
*Zen of Assembly Language* on an XTcomputer, only to find that the
*Zen of Assembly Language* on an XT-compatible computer, only to find that the
computer wasn't quite IBM-compatible regarding code performance. The
differences were minor, mind you, but my experience illustrates the risk
of assuming that a specific make of computer will perform in a certain
@ -913,13 +913,13 @@ and should contain calls to `ZTimerOn` and `ZTimerOff` .
;
; By Michael Abrash
;
mystack segment para stack STACK'
mystack segment para stack 'STACK'
db 512 dup(?)
mystack ends
;
Code segment para public CODE'
Code segment para public 'CODE'
assume cs:Code, ds:Code
extrnZTimerOn:near, ZTimerOff:near, ZTimerReport:near
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
Start proc near
push cs
pop ds ; set DS to point to the code segment,
@ -996,7 +996,7 @@ timing interval.
Listing 3.3 is used by naming it TESTCODE, assembling both Listing 3.2
(which includes TESTCODE) and Listing 3.1 with TASM or MASM, and linking
the two resulting OBJ files together by way of the Borland orMicrosoft
the two resulting OBJ files together by way of the Borland or Microsoft
linker. Listing 3.4 shows a batch file, PZTIME.BAT, which does all that;
when run, this batch file generates and runs the executable file
PZTEST.EXE. PZTIME.BAT (Listing 3.4) assumes that the file PZTIMER.ASM
@ -1111,8 +1111,8 @@ pztime <filename>
In fact, that's exactly how I timed each of the listings in this book.
Code fragments you write yourself can be timed in just the same way. If
you wish to time code directly in place in your programs, rather than in
the test-bed program of Listing 3.2, simply insert calls to `ZTimerOn,
ZTimerOff`, and `ZTimerReport` in the appropriate places and link
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
@ -1303,8 +1303,8 @@ computers.
; All registers and all flags are preserved by all routines.
;
Code segment word public CODE'
assume cs: Code, ds:nothing
Code segment word public 'CODE'
assume cs:Code, ds:nothing
public ZTimerOn, ZTimerOff, ZTimerReport
;
@ -1326,24 +1326,24 @@ Code segment word public CODE'
; which support the undocumented timer-stopping feature of the
; 8253. The choice is yours.
;
PS2 equ1
PS2 equ 1
;
; Base address of the 8253 timer chip.
;
BASE_8253 equ40h
BASE_8253 equ 40h
;
; The address of the timer 0 count registers in the 8253.
;
TIMER_0_8253 equBASE_8253 + 0
TIMER_0_8253 equ BASE_8253 + 0
;
; The address of the mode register in the 8253.
;
MODE_8253 equBASE_8253 + 3
MODE_8253 equ BASE_8253 + 3
;
; The address of the BIOS timer count variable in the BIOS
; data segment.
;
TIMER_COUNT equ46ch
TIMER_COUNT equ 46ch
;
; Macro to emulate a POPF instruction in order to fix the bug in some
; 80286 chips which allows interrupts to occur during a POPF even when
@ -1353,9 +1353,9 @@ MPOPF macro
local p1, p2
jmp short p2
p1: iret ;jump to pushed address & pop flags
p2: pushcs ;construct far return address to
p2: push cs ;construct far return address to
call p1 ; the next instruction
endm
endm
;
; Macro to delay briefly to ensure that enough time has elapsed
@ -1383,11 +1383,11 @@ ReferenceCount dw ? ;number of counts required to
;
; String printed to report results.
;
OutputStr labelbyte
db 0dh, 0ah, Timed count:
TimedCountStr db10 dup (?)
db' microseconds', 0dh, 0ah
db $'
OutputStr label byte
db 0dh, 0ah, 'Timed count: '
TimedCountStr db 10 dup (?)
db ' microseconds', 0dh, 0ah
db '$'
;
; Temporary storage for timed count as it's divided down by powers
; of ten when converting from doubleword binary to ASCII.
@ -1398,7 +1398,7 @@ CurrentCountHigh dw ?
; Powers of ten table used to perform division by 10 when doing
; doubleword conversion from binary to ASCII.
;
PowersOfTenlabelword
PowersOfTen label word
dd 1
dd 10
dd 100
@ -1409,33 +1409,33 @@ PowersOfTenlabelword
dd 10000000
dd 100000000
dd 1000000000
PowersOfTenEnd labelword
PowersOfTenEnd label word
;
; String printed to report that the high word of the BIOS count
; changed while timing (an hour elapsed or midnight was crossed),
; and so the count is invalid and the test needs to be rerun.
;
TurnOverStrlabelbyte
db 0dh, 0ah
db ****************************************************'
TurnOverStr label byte
db 0dh, 0ah
db'* Either midnight passed or an hour or more passed *'
db '****************************************************'
db 0dh, 0ah
db'* while timing was in progress. If the former was *'
db '* Either midnight passed or an hour or more passed *'
db 0dh, 0ah
db'* the case, please rerun the test; if the latter *'
db '* while timing was in progress. If the former was *'
db 0dh, 0ah
db'* was the case, the test code takes too long to *'
db '* the case, please rerun the test; if the latter *'
db 0dh, 0ah
db'* run to be timed by the long-period Zen timer. *'
db '* was the case, the test code takes too long to *'
db 0dh, 0ah
db * Suggestions: use the DOS TIME command, the DOS *'
db '* run to be timed by the long-period Zen timer. *'
db 0dh, 0ah
db * time function, or a watch. *'
db '* Suggestions: use the DOS TIME command, the DOS *'
db 0dh, 0ah
db ****************************************************'
db '* time function, or a watch. *'
db 0dh, 0ah
db'$'
db '****************************************************'
db 0dh, 0ah
db '$'
;********************************************************************
;* Routine called to start timing. *
@ -1447,7 +1447,7 @@ ZTimerOn proc near
; Save the context of the program being timed.
;
push ax
pus hf
pushf
;
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
; linear counting rather than count-by-two counting. Also stops
@ -1463,10 +1463,10 @@ ZTimerOn proc near
; clock count each time it is executed.
;
DELAY
subal,al
outTIMER_0_8253,al ;lsb
sub al,al
out TIMER_0_8253,al ;lsb
DELAY
outTIMER_0_8253,al ;msb
out TIMER_0_8253,al ;msb
;
; In case interrupts are disabled, enable interrupts briefly to allow
; the interrupt generated when switching from mode 3 to mode 2 to be
@ -1488,12 +1488,12 @@ ZTimerOn proc near
; interrupts in order to avoid getting a half-changed count.)
;
push ds
subax, ax
movds, ax
movax, ds:[TIMER_COUNT+2]
movcs: [StartBIOSCountHigh],ax
movax, ds:[TIMER_COUNT]
movcs: [StartBIOSCountLow],ax
sub ax, ax
mov ds, ax
mov ax, ds:[TIMER_COUNT+2]
mov cs:[StartBIOSCountHigh],ax
mov ax, ds:[TIMER_COUNT]
mov cs:[StartBIOSCountLow],ax
pop ds
;
; Set the timer count to 0 again to start the timing interval.
@ -1501,7 +1501,7 @@ ZTimerOn proc near
mov al,00110100b ;set up to load initial
out MODE_8253,al ; timer count
DELAY
subal, al
sub al, al
out TIMER_0_8253,al; load count lsb
DELAY
out TIMER_0_8253,al; load count msb
@ -1509,23 +1509,23 @@ ZTimerOn proc near
; Restore the context of the program being timed and return to it.
;
MPOPF
popax
pop ax
ret
ZTimerOnendp
ZTimerOn endp
;********************************************************************
;* Routine called to stop timing and get count. *
;********************************************************************
ZTimerOff procnear
ZTimerOff proc near
;
; Save the context of the program being timed.
;
pushf
pushax
pushcx
push ax
push cx
;
; In case interrupts are disabled, enable interrupts briefly to allow
; any pending timer interrupt to be handled. Interrupts must be
@ -1618,7 +1618,7 @@ ife PS2
endif
sti;let the BIOS count continue
sti ;let the BIOS count continue
;
; Time a zero-length code fragment, to get a reference for how
; much overhead this routine has. Time it 16 times and average it,
@ -1633,14 +1633,14 @@ RefLoop:
call ReferenceZTimerOff
loop RefLoop
sti
add cs:[ReferenceCount],8; total + (0.5 * 16)
add cs:[ReferenceCount],8 ;total + (0.5 * 16)
mov cl,4
shr cs:[ReferenceCount],cl;(total) / 16 + 0.5
shr cs:[ReferenceCount],cl ;(total) / 16 + 0.5
;
; Restore the context of the program being timed and return to it.
;
popcx
popax
pop cx
pop ax
MPOPF
ret
@ -1650,11 +1650,11 @@ ZTimerOff endp
; Called by ZTimerOff to start the timer for overhead measurements.
;
ReferenceZTimerOnprocnear
ReferenceZTimerOn proc near
;
; Save the context of the program being timed.
;
pushax
push ax
pushf
;
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
@ -1674,10 +1674,10 @@ ReferenceZTimerOnprocnear
; Restore the context of the program being timed and return to it.
;
MPOPF
popax
pop ax
ret
ReferenceZTimerOnendp
ReferenceZTimerOn endp
;
; Called by ZTimerOff to stop the timer and add the result to
@ -1686,20 +1686,20 @@ ReferenceZTimerOnendp
; isn't going to take anywhere near 54 ms.
;
ReferenceZTimerOff procnear
ReferenceZTimerOff proc near
;
; Save the context of the program being timed.
;
pushf
pushax
pushcx
push ax
push cx
;
; Match the interrupt-window delay in ZTimerOff.
;
sti
rept10
jmp$+2
rept 10
jmp $+2
endm
mov al,00000000b
@ -1720,8 +1720,8 @@ ReferenceZTimerOff procnear
;
; Restore the context and return.
;
popcx
popax
pop cx
pop ax
MPOPF
ret
@ -1731,7 +1731,7 @@ ReferenceZTimerOff endp
;* Routine called to report timing results. *
;********************************************************************
ZTimerReportprocnear
ZTimerReport proc near
pushf
push ax
@ -1741,7 +1741,7 @@ ZTimerReportprocnear
push si
push di
push ds
;
;
push cs ;DOS functions require that DS point
pop ds ; to text to be displayed on the screen
assume ds :Code
@ -1780,7 +1780,7 @@ TestTooLong:
; Convert the BIOS time to microseconds.
;
CalcBIOSTime:
mov ax,[EndBIOSCountLow]
mov ax,[EndBIOSCountLow]
sub ax,[StartBIOSCountLow]
mov dx,54925 ;number of microseconds each
; BIOS count represents
@ -1808,7 +1808,7 @@ CalcBIOSTime:
mov si,8381 ;convert the reference count
mul si ; to microseconds
mov si,10000
div si;* .8381 = * 8381 / 10000
div si ;* .8381 = * 8381 / 10000
sub bx,ax
sbb cx,0
mov [CurrentCountLow],bx
@ -1896,7 +1896,7 @@ substantially.
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.
we've just discussed apply *only* to the long-period Zen timer.
### Example Use of the Long-Period Zen Timer
@ -1932,25 +1932,25 @@ timing.
;
; By Michael Abrash
;
mystack segment para stack STACK'
mystack segment para stack 'STACK'
db 512 dup(?)
mystack ends
mystack ends
;
Code segment para public CODE'
Code segment para public 'CODE'
assume cs:Code, ds:Code
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
Startproc near
push cs
pop ds ;point DS to the code segment,
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
Start proc near
push cs
pop ds ;point DS to the code segment,
; so data as well as code can easily
; be included in TESTCODE
;
; Delay for 6-7 seconds, to let the Enter keystroke that started the
; program come back up.
;
mov ah,2ch
int 21h ;get the current time
mov bh,dh ;set the current time aside
mov ah,2ch
int 21h ;get the current time
mov bh,dh ;set the current time aside
DelayLoop:
mov ah,2ch
push bx ;preserve start time
@ -1962,12 +1962,12 @@ DelayLoop:
add dh,60 ;yes, a minute must have turned over,
; so add one minute
CheckDelayTime:
sub dh,bh ;get time that's passed
cmp dh,7 ;has it been more than 6 seconds yet?
jb DelayLoop ;not yet
sub dh,bh ;get time that's passed
cmp dh,7 ;has it been more than 6 seconds yet?
jb DelayLoop ;not yet
;
include TESTCODE ;code to be measured, including calls
; to ZTimerOn and ZTimerOff
include TESTCODE ;code to be measured, including calls
; to ZTimerOn and ZTimerOff
;
; Display the results.
;
@ -1975,8 +1975,8 @@ CheckDelayTime:
;
; Terminate the program.
;
mov ah,4ch
int 21h
mov ah,4ch
int 21h
Start endp
Code ends
end Start
@ -2075,23 +2075,23 @@ the precision Zen timer to handle on the 8088.
; Note: takes about ten minutes to assemble on a slow PC if
;you are using MASM
;
jmpSkip;jump around defined data
jmp Skip ;jump around defined data
;
MemVardb?
MemVar db ?
;
Skip:
;
; Start timing.
;
callZTimerOn
call ZTimerOn
;
rept20000
moval,[MemVar]
endm
rept 20000
mov al,[MemVar]
endm
;
; Stop timing.
;
callZTimerOff
call ZTimerOff
```
When LZTIME.BAT is run on a PC with the following command line (assuming
@ -2126,7 +2126,7 @@ be dealt with here: small code model and large; I'll tackle the simpler
one, the small code model, first.
Altering the Zen timer for linking to a small code model C program
involves the following steps: `C` hange `ZTimerOn` to
involves the following steps: Change `ZTimerOn` to
`_ZTimerOn`, change `ZTimerOff` to `_ZTimerOff`, change
`ZTimerReport` to `_ZTimerReport`, and change `Code` to
`_TEXT` . Figure 3.2 shows the line numbers and new states of all
@ -2187,16 +2187,16 @@ push cs
call near ptr ReferenceZTimerOn
```
(and likewise for `ReferenceZTimerOff` ), which works because
(and likewise for `ReferenceZTimerOff`), which works because
`ReferenceZTimerOn` is in the same segment as the calling code. This
is normally a great optimization, being both smaller and faster than a
far call. However, it's not so great for the Zen
far call.
![**Figure 3.3**  *Changes for use with large code model C.*](images/03-03.jpg)
timer, because our purpose in calling the reference timing code is to
However, it's not so great for the Zen timer, because our purpose in calling the reference timing code is to
determine exactly how much time is taken by overhead code—including the
far calls to `ZTimerOn` and `ZTimerOf`f! By converting the far calls
far calls to `ZTimerOn` and `ZTimerOf`! By converting the far calls
to push/near call pairs within the Zen timer module, TASM makes it
impossible to emulate exactly the overhead of the Zen timer, and makes
timings slightly (about 16 cycles on a 386) less accurate.
@ -2255,7 +2255,7 @@ processor cache at the start of the code being timed, because the timing
code is not necessarily fetched and does not necessarily access memory
in exactly the same time sequence as the code immediately preceding the
code under measurement normally does. This prefetch effect can introduce
as much as 3 to 4 µ of inaccuracy. Similarly, the state of the prefetch
as much as 3 to 4 µs of inaccuracy. Similarly, the state of the prefetch
queue at the end of the code being timed affects how long the code that
stops the timer takes to execute. Consequently, the Zen timer tends to
be more accurate for longer code sequences, since the relative magnitude

View file

@ -878,8 +878,8 @@ the PC must be completely refreshed about once every four milliseconds
in order to ensure the integrity of the data it stores. Obviously, it's
highly desirable that the memory in the PC retain the correct data
indefinitely, so each DRAM chip in the PC *must* always be refreshed
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
within 4 ms of the last refresh. Since there's no guarantee that a given
program will access each and every DRAM block once every 4 ms, the PC
contains special circuitry and programming for providing DRAM refresh.
#### How DRAM Refresh Works in the PC
@ -900,8 +900,8 @@ purpose of refreshing the DRAM; the data that is read isn't used.)
The 256 addresses accessed by the refresh DMA accesses are arranged so
that taken together they properly refresh all the memory in the PC. By
accessing one of the 256 addresses every 15.08 µs, all of the PC's DRAM
is refreshed in 256 x 15.08 µs, or 3.86 µs, which is just about the
desired 4 µs time I mentioned earlier. (Only the first 640K of memory is
is refreshed in 256 x 15.08 µs, or 3.86 ms, which is just about the
desired 4 ms time I mentioned earlier. (Only the first 640K of memory is
refreshed in the PC; video adapters and other adapters above 640K
containing memory that requires refreshing must provide their own DRAM
refresh in pre-AT systems.)
@ -1053,7 +1053,7 @@ reduction can vary considerably and unpredictably, depending on how the
DRAM refreshes interact with your code's pattern of memory accesses.
When you use the Zen timer and get a fractional cycle count for the
execution time of an instruction, that's often the DRAM refresh
cycle-eater at work. (The display adapter cycleis another possible
cycle-eater at work. (The display adapter cycle is another possible
culprit, and, on 386s and later processors, cache misses and pipeline
execution hazards produce this sort of effect as well.) Whenever you get
two timing results that differ less or more than they seemingly should,
@ -1223,7 +1223,7 @@ display, and even with the display adapter cycle-eater it just doesn't
take that long to manipulate 4,000 bytes. Even if the display adapter
cycle-eater were to cause the 8088 to take as much as 5µs per display
memory access—more than five times normal—it would still take only
4,000x 2x 5µs, or 40 µs, to read and write every byte of display memory.
4,000x 2x 5µs, or 40 ms, to read and write every byte of display memory.
That's a lot of time as measured in 8088 cycles, but it's less than the
blink of an eye in human time, and video performance only matters in
human time. After all, the whole point of drawing graphics is to convey
@ -1261,7 +1261,7 @@ seriously impact code performance, even as measured in human time.
For example, if we assume the same 5 µs per display memory access for
the EGA's high-resolution graphics mode that we assumed for text mode,
it would take 26,000 x 2 x 5 µs, or 260 µs, to scroll the screen once in
it would take 26,000 x 2 x 5 µs, or 260 ms, to scroll the screen once in
the EGA's high-resolution graphics mode, mode 10H. That's more than
one-quarter of a second—noticeable by human standards, an eternity by
computer standards.

View file

@ -188,7 +188,7 @@ standard memory addressing operand, but does nothing more than store the
calculated memory offset in the specified register, which may be any
general-purpose register. The operation of `LEA` is illustrated in
Figure 6.1, which also shows the operation of register-to-register
`ADD`, for comparis on.
`ADD`, for comparison.
What does that give us? Two things that `ADD` doesn't provide: the
ability to perform addition with either two or three operands, and the

View file

@ -245,7 +245,7 @@ the whole point.)
.stack 100h
.data
; Sample string to search through.
SampleString labelbyte
SampleString label byte
db This is a sample string of a long enough length '
db so that raw searching speed can outweigh any '
db extra set-up time that may be required.',0
@ -263,7 +263,7 @@ NoByteFoundMsg db 0dh,0ah
db Buffer exhausted with no match.', 0dh, 0ah, $'
.code
Startprocnear
Start proc near
mov ax,@data ;point to standard data segment
mov ds,ax
mov dx,offset Prompt
@ -289,7 +289,7 @@ PrintStatus:
int 21h ;report status
mov ah,4ch ;return to DOS
int 21h
Startendp
Start endp
; Function to search a buffer of a specified length until either a
; specified byte or a zero byte is encountered.
@ -306,7 +306,7 @@ Startendp
; byte is found
; Carry Flag = set if searched-for byte found, reset otherwise
SearchMaxLengthprocnear
SearchMaxLength proc near
cld
SearchMaxLengthLoop:
lodsb ;get the next byte
@ -324,7 +324,7 @@ ByteFound:
;we found the searched-for byte
stc ;return "found" status
ret
SearchMaxLengthendp
SearchMaxLength endp
end Start
```
@ -350,7 +350,7 @@ all the difference.
.stack 100h
.data
; Sample string to search through.
SampleStringlabelbyte
SampleString label byte
db This is a sample string of a long enough length '
db so that raw searching speed can outweigh any '
db extra set-up time that may be required.',0
@ -403,7 +403,7 @@ PrintStatus:
mov ah,4ch ;return to DOS
int 21h
Startendp
Start endp
; Function to search a buffer of a specified length until either a
; specified byte or a zero byte is encountered.
@ -468,7 +468,7 @@ ByteFound:
; we found the searched-for byte
stc ;return "found" status
ret
SearchMaxLengthendp
SearchMaxLength endp
end Start
```

View file

@ -213,13 +213,13 @@ display modes):
```nasm
SHL AX,1 ;*2
SH LAX,1 ;*4
SH LAX,1 ;*8
SH LAX,1 ;*16
MO VBX,AX
SH LAX,1 ;*32
SH LAX,1 ;*64
ADD AX,BX ;*80
SHL AX,1 ;*4
SHL AX,1 ;*8
SHL AX,1 ;*16
MOV BX,AX
SHL AX,1 ;*32
SHL AX,1 ;*64
ADD AX,BX ;*80
```
Using `LEA` on the 386, the above could be reduced to
@ -242,7 +242,7 @@ Of course, on the 386, the shift and add version could also be reduced
to this considerably more efficient code:
```nasm
SH LAX,4 ;*16
SHL AX,4 ;*16
MOV BX,AX
SHL AX,2 ;*64
ADD AX,BX ;*80
@ -395,7 +395,7 @@ Parmsends
.model small
.code
public _FindString
_FindStringprocnear
_FindString proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller's register variables
@ -458,7 +458,7 @@ FindStringDone:
pop si
pop bp ;restore caller's stack frame
ret
_FindStringendp
_FindString endp
end
```
@ -492,7 +492,7 @@ Parmsends
.model small
.code
public _FindString
_FindStringprocnear
_FindString proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller's register variables
@ -559,7 +559,7 @@ FindStringDone:
pop si
pop bp ;restore caller's stack frame
ret
_FindStringendp
_FindString endp
end
```
@ -743,7 +743,7 @@ parmsends
.model small
.code
public _Div
_Divprocnear
_Div proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller's register variables
@ -766,9 +766,9 @@ _Divprocnear
sub dx,dx ;convert initial divisor word to a 32-bit
;value for DIV
DivLoop:
lod sw ;get next most significant word of divisor
lodsw ;get next most significant word of divisor
div bx
sto sw ;save this word of the quotient
stosw ;save this word of the quotient
;DX contains the remainder at this point,
; ready to prepend to the next divisor word
loop DivLoop
@ -778,7 +778,7 @@ DivLoop:
pop si
pop bp ;restore caller's stack frame
ret
_Divendp
_Div endp
end
```

View file

@ -1020,7 +1020,7 @@ pushed on the stack will point to the instruction we want to continue
with. The code works out like this:
```nasm
jmpshort popfskip
jmp short popfskip
popfiret:
iret; branches to the instruction after the
; call, popping the word below the address
@ -1082,8 +1082,8 @@ an 8088.)
.286
:
EMULATE_POPFmacro
pushcs
pushoffset $+4
push cs
push offset $+4
iret
endm
```

View file

@ -346,7 +346,7 @@ For example, you'd certainly expect a sequence such as
pop ax
ret
pop ax
et
ret
:
```

View file

@ -107,7 +107,7 @@ from the use of DI to address memory (remember, the loop is unrolled, so
the last instruction is followed by the first instruction), but because
the intervening instruction takes two cycles, there's no penalty at all.
![**Figure 13.1**  *Cycle-eaters in the original WC.*](images/13-01.jpg)
![**Figure 13.1**  *Cycle-eaters in the original WC.*](images/13-01.png)
> ![](images/i.jpg)
> Remember, pipeline penalties diminish with increasing number of cycles,

View file

@ -302,8 +302,7 @@ the pike. The success or failure of the search can then be determined
outside the loop, if necessary, by checking for the tail node's special
pointer—but the inside of the loop is streamlined to just one test, as
shown in Listing 15.5. Not all linked lists lend themselves to
sentinels, but the performance benefits are considerable for those lend
themselves to sentinels, but the performance benefits are considerable
sentinels, but the performance benefits are considerable
for those that do.
![**Figure 15.3**  *Representing an empty list.*](images/15-03.jpg)

View file

@ -237,7 +237,7 @@ contention. Such operations, as in
```nasm
mov eax,edx ;U-pipe cycle 1
sub edx,edxX ;V-pipe cycle 1
sub edx,edx ;V-pipe cycle 1
```
are free of charge.
@ -475,33 +475,33 @@ ckloopsetup:
mov dx,[esi] ;load most of 1st word to
mov bl,[esi+2] ; checksum (last byte loaded in loop)
dec ecx ;any more dwords to checksum?
jz short ckloopend ;no
jz short ckloopend ;no
ckloop:
mov bh,[esi+3] ;cycle 1 U-pipe
add esi,4 ;cycle 1 V-pipe
shl ebx,16 ;cycle 2 U-pipe
;cycle 2 V-pipe idle
; (register contention)
or ebx,edx ;cycle 3 U-pipe
mov dl,[esi] ;cycle 3 V-pipe
add eax,ebx ;cycle 4 U-pipe
mov bl,[esi+2] ;cycle 4 V-pipe
adc eax,0 ;cycle 5 U-pipe
mov dh,[esi+1] ;cycle 5 V-pipe
dec ecx ;cycle 6 U-pipe
jnz ckloop ;cycle 6 V-pipe
mov bh,[esi+3] ;cycle 1 U-pipe
add esi,4 ;cycle 1 V-pipe
shl ebx,16 ;cycle 2 U-pipe
;cycle 2 V-pipe idle
; (register contention)
or ebx,edx ;cycle 3 U-pipe
mov dl,[esi] ;cycle 3 V-pipe
add eax,ebx ;cycle 4 U-pipe
mov bl,[esi+2] ;cycle 4 V-pipe
adc eax,0 ;cycle 5 U-pipe
mov dh,[esi+1] ;cycle 5 V-pipe
dec ecx ;cycle 6 U-pipe
jnz ckloop ;cycle 6 V-pipe
ckloopend:
mov bh,[esi+3] ;checksum the last dword
add ax,dx
adc ax,bx
adc ax,0
mov bh,[esi+3] ;checksum the last dword
add ax,dx
adc ax,bx
adc ax,0
mov edx,eax ;compress the 32-bit checksum
shr edx,16 ; into a 16-bit checksum
add ax,dx
adc eax,0
mov edx,eax ;compress the 32-bit checksum
shr edx,16 ; into a 16-bit checksum
add ax,dx
adc eax,0
ckloopdone:
```
@ -598,29 +598,29 @@ more registers.
jz short ckloopdone ;no, done
add esi,4 ;point to the next dword
noodddword:
mov edx,[esi] ;preload the first dword
mov ebx,[esi+4] ;preload the second dword
dec ecx ;we'll do 1 checksum outside the loop
jz short ckloopend ;only 1 checksum to do
add esi,8 ;point to the next dword
mov edx,[esi] ;preload the first dword
mov ebx,[esi+4] ;preload the second dword
dec ecx ;we'll do 1 checksum outside the loop
jz short ckloopend ;only 1 checksum to do
add esi,8 ;point to the next dword
ckloop:
add eax,edx ;cycle 1 U-pipe
mov edx,[esi] ;cycle 1 V-pipe
adc eax,ebx ;cycle 2 U-pipe
mov ebx,[esi+4] ;cycle 2 V-pipe
adc eax,0 ;cycle 3 U-pipe
add esi,8 ;cycle 3 V-pipe
dec ecx ;cycle 4 U-pipe
jnz ckloop ;cycle 4 V-pipe
add eax,edx ;cycle 1 U-pipe
mov edx,[esi] ;cycle 1 V-pipe
adc eax,ebx ;cycle 2 U-pipe
mov ebx,[esi+4] ;cycle 2 V-pipe
adc eax,0 ;cycle 3 U-pipe
add esi,8 ;cycle 3 V-pipe
dec ecx ;cycle 4 U-pipe
jnz ckloop ;cycle 4 V-pipe
ckloopend:
add eax,edx ;checksum the last two dwords
add eax,edx ;checksum the last two dwords
adc eax,ebx
adc eax,0
ckloopdone:
mov edx,eax ;compress the 32-bit checksum
shr edx,16 ; into a 16-bit checksum
mov edx,eax ;compress the 32-bit checksum
shr edx,16 ; into a 16-bit checksum
add ax,dx
adc eax,0
```

View file

@ -68,29 +68,29 @@ BufSeg dw ? ;buffer segment
EndMrk db ? ;marker for the end of the stack frame
OnStack ends
;
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ax,[bp].Attrib ;load AX with attribute parameter
and ax,0ff00h ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
and bx,0ffh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
mov bx,[bp].BufOfs ;load DI with target buffer offset
mov di,bx
mov bx,[bp].BufSeg ;load ES with target buffer segment
mov es,bx
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:mov sp,bp ;restore original stack pointer
pop bp ; and caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ax,[bp].Attrib ;load AX with attribute parameter
and ax,0ff00h ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
and bx,0ffh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
mov bx,[bp].BufOfs ;load DI with target buffer offset
mov di,bx
mov bx,[bp].BufSeg ;load ES with target buffer segment
mov es,bx
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye: mov sp,bp ;restore original stack pointer
pop bp ; and caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
```
The first thing you'll notice about Listing 22.1 is that `ClearS` uses
@ -121,27 +121,27 @@ DI directly as shown in Listing 22.2.
**LISTING 22.2 L22-2.ASM**
```nasm
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ax,[bp].Attrib ;load AX with attribute parameter
and ax,0ff00h ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
and bx,0ffh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
mov di,[bp].BufOfs ;load DI with target buffer offset
mov es,[bp].BufSeg ;load ES with target buffer segment
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
mov ax,[bp].Attrib ;load AX with attribute parameter
and ax,0ff00h ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
and bx,0ffh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
mov di,[bp].BufOfs ;load DI with target buffer offset
mov es,[bp].BufSeg ;load ES with target buffer segment
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
```
(The `OnStack` structure definition doesn't change in any of our
@ -158,27 +158,27 @@ loading ES and DI as shown in Listing 22.3.
**LISTING 22.3 L22-3.ASM**
```nasm
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ax,[bp].Attrib ;load AX with attribute parameter
and ax,0ff00h ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
and bx,0ffh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer
mov ax,[bp].Attrib ;load AX with attribute parameter
and ax,0ff00h ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
and bx,0ffh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer
;segment:offset
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
```
That's good for another three bytes. We're down to 43 bytes, and
@ -190,27 +190,27 @@ values as shown in Listing 22.4.
**LISTING 22.4 L22-4.ASM**
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ax,[bp].Attrib ;load AX with attribute parameter
sub al,al ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
sub bh,bh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer
;segment:offset
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ax,[bp].Attrib ;load AX with attribute parameter
sub al,al ;prepare for merging with fill char
mov bx,[bp].Filler ;load BX with fill char
sub bh,bh ;prepare for merging with attribute
or ax,bx ;combine attribute and fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer
;segment:offset
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
Now we're down to 40 bytes—more than 20 percent smaller than the
original code. That's pretty much it for simple instruction
@ -232,23 +232,23 @@ shows the new code.
**LISTING 22.5 L22-5.ASM**
```nasm
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
mov al,byte ptr [bp].Filler ;load AL with fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer segment:offset
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
jne Start ; pointer is passed
cmp word ptr [bp].BufOfs,0
je Bye
Start: cld ;make STOSW count up
mov ah,byte ptr [bp].Attrib[1] ;load AH with attribute
mov al,byte ptr [bp].Filler ;load AL with fill char
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer segment:offset
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
```
(We could get rid of yet another instruction by having the calling code
@ -266,22 +266,22 @@ shown in Listing 22.6.
**LISTING 22.6 L22-6.ASM**
```nasm
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer;segment:offset
mov ax,es ;put segment where we can test it
or ax,di ;is it a null pointer?
je Bye ;yes, so we're done
Start: cld ;make STOSW count up
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
mov al,byte ptr [bp].Filler ;load AL with fill char
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
ClearS proc near
push bp ;save caller's BP
mov bp,sp ;point to stack frame
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer;segment:offset
mov ax,es ;put segment where we can test it
or ax,di ;is it a null pointer?
je Bye ;yes, so we're done
Start: cld ;make STOSW count up
mov ah,byte ptr [bp].Attrib[1] ;load AH with attribute
mov al,byte ptr [bp].Filler ;load AL with fill char
mov cx,[bp].BufSize ;load CX with buffer size
rep stosw ;fill the buffer
Bye:
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
pop bp ;restore caller's BP
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
ClearS endp
```
Well. Now we're down to 28 bytes, having reduced the size of this
@ -327,22 +327,22 @@ With that problem dealt with, Listing 22.7 shows the Zenned version of
**LISTING 22.7 L22-7.ASM**
```nasm
ClearS procnear
pop dx ;get the return address
pop ax ;put fill char into AL
pop bx ;get the attribute
mov ah,bh ;put attribute into AH
pop cx ;get the buffer size
pop di ;get the offset of the buffer origin
pop es ;get the segment of the buffer origin
mov bx,es ;put the segment where we can test it
or bx,di ;null pointer?
je Bye ;yes, so we're done
cld ;make STOSW count up
rep stosw ;do the string store
ClearS proc near
pop dx ;get the return address
pop ax ;put fill char into AL
pop bx ;get the attribute
mov ah,bh ;put attribute into AH
pop cx ;get the buffer size
pop di ;get the offset of the buffer origin
pop es ;get the segment of the buffer origin
mov bx,es ;put the segment where we can test it
or bx,di ;null pointer?
je Bye ;yes, so we're done
cld ;make STOSW count up
rep stosw ;do the string store
Bye:
jmp dx ;return to the calling code
ClearS endp
jmp dx ;return to the calling code
ClearS endp
```
At long last, we're down to the bare metal. This version of `ClearS`

View file

@ -85,12 +85,12 @@ registers.
;
; By Michael Abrash
;
stacksegmentword stack 'STACK'
db512 dup (?)
stackends
stack segment word stack 'STACK'
db 512 dup (?)
stack ends
;
datasegment word 'DATA'
IMAGE_WIDTHEQU 4 ;in bytes
data segment word 'DATA'
IMAGE_WIDTH EQU 4 ;in bytes
IMAGE_HEIGHT EQU 32 ;in pixels
LEFT_BOUND EQU 10 ;in bytes
RIGHT_BOUND EQU 66 ;in bytes
@ -105,11 +105,11 @@ READ_MAP EQU 4 ;Read Map register index in GC
;
PatternPlane0 label byte
db 32 dup (0ffh,0ffh,0,0)
PatternPlane1 labelbyte
PatternPlane1 label byte
db 32 dup (0ffh,0,0ffh,0)
PatternPlane2 labelbyte
PatternPlane2 label byte
db 32 dup (0f0h,0f0h,0f0h,0f0h)
PatternPlane3 labelbyte
PatternPlane3 label byte
db 32 dup (0cch,0cch,0cch,0cch)
;
; Temporary storage for 16-color image during animation.
@ -124,7 +124,7 @@ ImagePlane3 db 32*4 dup (?)
ImageX dw 40 ;in bytes
ImageY dw 100 ;in pixels
ImageXDirection dw 1 ;in bytes
dataends
data ends
;
code segment word 'CODE'
assume cs:code,ds:data
@ -197,12 +197,12 @@ DelayLoop:
int 10h
mov ah,4ch
int 21h
Startendp
Start endp
;
; Draws the image at offset DS:SI to the current image location in
; VGA memory.
;
DrawImageprocnear
DrawImage proc near
mov ax,VGA_SEGMENT
mov es,ax
call GetImageOffset ;ES:DI is the destination address for the
@ -233,7 +233,7 @@ DrawImageLoop:
cmp al,10h ;have we done all four planes?
jnz DrawImagePlaneLoop
ret
DrawImageendp
DrawImage endp
;
; Copies the image from its current location in VGA memory into the
; buffer at DS:DI.
@ -274,7 +274,7 @@ GetImageLoop:
push es
pop ds ;restore original DS
ret
GetImageendp
GetImage endp
;
; Erases the image at its current location.
;
@ -410,7 +410,7 @@ Start proc near
mov al,1 ;blue is color 1
call SelectSetResetColor ;set to draw in blue
mov ax,VGA_SEGMENT
move s,ax
mov es,ax
sub di,di
mov cx,7000h
rep stosb ;the value written actually doesn't
@ -435,7 +435,7 @@ VLineLoop:
; provides the data written to display
; memory, and AL is actually ignored)
add di,SCREEN_WIDTH-1 ;point to the next scan line
loopVLineLoop
loop VLineLoop
;
; Select write mode 0 and read mode 1.
;
@ -513,12 +513,12 @@ WaitKeyLoop:
int 10h ;return to text mode
mov ah,4ch
int 21h ;done
Startendp
Start endp
;
; Enables set/reset for all planes, and sets the set/reset color
; to AL.
;
SelectSetResetColorprocnear
SelectSetResetColor proc near
mov dx,GC_INDEX
push ax ;preserve color
mov al,SET_RESET
@ -533,7 +533,7 @@ SelectSetResetColorprocnear
mov al,0fh
out dx,al ;enable set/reset for all planes
ret
SelectSetResetColorendp
SelectSetResetColor endp
code ends
end Start
```
@ -636,7 +636,7 @@ COLOR_DONT_CARE EQU 7 ;Color Don't Care register index in GC
;
code segment word 'CODE'
assume cs:code
Startprocnear
Start proc near
;
; Select graphics mode 12h.
;
@ -698,7 +698,7 @@ DrawDiagonalLoop:
; point to the next scan line
ror al,1 ;move the pixel mask one pixel to the right
adc bx,0 ;advance to the next byte if the pixel mask wrapped
loopDrawDiagonalLoop
loop DrawDiagonalLoop
;
; Wait for a key to be pressed to end, then return to text mode and
; return to DOS.
@ -713,7 +713,7 @@ WaitKeyLoop:
int 10h ;return to text mode
mov ah,4ch
int 21h ;done
Startendp
Start endp
code ends
end Start
```

View file

@ -262,7 +262,7 @@ RestoreTheScreen:
RestoreLoop:
mov dx,SC_INDEX
mov al,MAP_MASK ;set SC Index to Map Mask register
outdx,al
out dx,al
inc dx
mov cl,[Plane] ;get the # of the plane we want
; to restore
@ -557,14 +557,14 @@ x= x+1
; Used to label the colors of the color bars. (Color values are
; filled in on the fly.)
;
ColorNumberslabelbyte
ColorNumbers label byte
rept 16
db 000h', 0ah, 8, 8, 8, 8
endm
COLOR_ENTRY_LENGTHequ($-ColorNumbers)/16
COLOR_ENTRY_LENGTH equ ($-ColorNumbers)/16
db $'
;
CurrentColordb?
CurrentColor db ?
;
; Space for the array of 16 colors we'll pass to the BIOS, plus
; an overscan setting of black.
@ -574,7 +574,7 @@ Data ends
;
Code segment
assume cs:Code, ds:Data
Start procnear
Start proc near
cld
mov ax,Data
mov ds,ax
@ -764,7 +764,7 @@ ColorNumberLoop:
mov dx,offset ColorNumbers
int 21h ;put up the attribute numbers
ret
ColorNumbersUpendp
ColorNumbersUp endp
;
Start endp
Code ends
@ -820,7 +820,7 @@ WAIT_KEY macro
endm
;
stack segment para stack STACK'
db512 dup (?)
db 512 dup (?)
stack ends
;
Data segment word DATA'

View file

@ -146,7 +146,7 @@ SCREEN_WIDTH equ 640
SCREEN_HEIGHT equ 350
CRTC_INDEX equ 3d4h ;CRT Controller Index register
OVERFLOW equ 7 ;index of Overflow reg in CRTC
MAXIMUM_SCAN_LINEequ 9 ;index of Maximum Scan Line register
MAXIMUM_SCAN_LINE equ 9 ;index of Maximum Scan Line register
; in CRTC
START_ADDRESS_HIGH equ 0ch ;index of Start Address High register
; in CRTC
@ -318,7 +318,7 @@ CountVerticalSyncsLoop:
int 10h ;return to text mode
mov ah,4ch
int 21h ;return to DOS
Startendp
Start endp
;*********************************************************************
; Waits for the leading edge of the vertical sync pulse.
;
@ -328,7 +328,7 @@ Startendp
;
; Registers altered: AL, DX
;
WaitForVerticalSyncStartprocnear
WaitForVerticalSyncStart proc near
mov dx,INPUT_STATUS_0
WaitNotVerticalSync:
in al,dx
@ -349,7 +349,7 @@ WaitForVerticalSyncStart endp
;
; Registers altered: AL, DX
;
WaitForVerticalSyncEndprocnear
WaitForVerticalSyncEnd proc near
mov dx,INPUT_STATUS_0
WaitVerticalSync2:
in al,dx
@ -360,7 +360,7 @@ WaitNotVerticalSync2:
test al,08h
jnz WaitNotVerticalSync2
ret
WaitForVerticalSyncEndendp
WaitForVerticalSyncEnd endp
;*********************************************************************
; Sets the start address to the value specifed by StartAddress.
; Wait for the trailing edge of vertical sync before setting so that
@ -751,7 +751,7 @@ MyStack segment para stack 'STACK'
db 512 dup (0)
MyStack ends
;*********************************************************************
Datasegment
Data segment
SplitScreenLine dw ? ;line the split screen currently
; starts after
StartAddress dw ? ;display memory offset at which
@ -763,7 +763,7 @@ Data ends
Code segment
assume cs:Code, ds:Data
;*********************************************************************
Startproc near
Start proc near
mov ax,Data
mov ds,ax
;
@ -810,7 +810,7 @@ RowLoop:
mov cx,LOGICAL_SCREEN_WIDTH/8/4
;fill 1 scan line
ColumnLoop:
sto sw ;draw part of a diagonal line
stosw ;draw part of a diagonal line
mov word ptr es:[di],0 ;make vertical blank spaces so
; panning effects can be seen easily
inc di
@ -832,12 +832,12 @@ RowLoop2:
mov cx,LOGICAL_SCREEN_WIDTH/8/4
;fill 1 scan line
ColumnLoop2:
sto sw ;draw part of a diagonal line
stosw ;draw part of a diagonal line
mov word ptr es:[di],0 ;make vertical blank spaces so
; panning effects can be seen easily
inc di
inc di
loopColumnLoop2
loop ColumnLoop2
ror ax,1 ;shift pattern word
dec dx
jnz RowLoop2
@ -847,7 +847,7 @@ loopColumnLoop2
; screen jerks back and forth as the pel panning setting cycles.
;
mov cx,200 ;pan 200 pixels to the left
callPanRight
call PanRight
;
; Wait for a key press (don't echo character).
;
@ -904,7 +904,7 @@ endif
int 10h ;return to text mode
mov ah,4ch
int 21h ;return to DOS
Startendp
Start endp
;*********************************************************************
; Waits for the leading edge of the vertical sync pulse.
;
@ -997,7 +997,7 @@ SetPelPan proc near
mov al,[PelPan]
out dx,al ;load the new Pel Pan setting
ret
SetPelPanendp
SetPelPan endp
;*********************************************************************
; Sets the scan line the split screen starts after to the scan line
; specified by SplitScreenLine.
@ -1104,8 +1104,8 @@ DoSetStartAddress:
ret
PanRight endp
;*********************************************************************
Codeends
endStart
Code ends
end Start
```
### Notes on Setting and Reading Registers

View file

@ -37,7 +37,7 @@ the way up to 360x480—and that's with the vanilla IBM VGA!
In this chapter, I'm going to focus on one of my favorite 256-color
modes, which provides 320x400 resolution and two graphics pages and can
be set up with very little reof the VGA. In the next chapter, I'll
be set up with very little reprogramming of the VGA. In the next chapter, I'll
discuss higher-resolution 256-color modes, and starting in Chapter 47,
I'll cover the high-performance "Mode X" 256-color programming that many
games use.
@ -283,7 +283,7 @@ endif
;
; Macro to output a constant value to an indexed VGA register.
;
CONSTANT_TO_INDEXED_REGISTERmacroADDRESS, INDEX, VALUE
CONSTANT_TO_INDEXED_REGISTER macro ADDRESS, INDEX, VALUE
mov dx,ADDRESS
mov ax,(VALUE shl 8) + INDEX
OUT_WORD
@ -309,7 +309,7 @@ ColorLoop:
; line descriptor list
LineLoop:
mov cx,[si+StartX] ;set the initial X coordinate
cmpcx,-1
cmp cx,-1
jz LinesDone ;a descriptor with a -1 X
; coordinate marks the end
; of the list
@ -341,7 +341,7 @@ LinesDone:
;
call GetNextKey
mov ax,0003h
int 10h text mode
int 10h ;text mode
mov ah,4ch
int 21h ;done
;
@ -373,22 +373,22 @@ Set320By400Mode proc near
mov al,MEMORY_MODE
out dx,al
inc dx
ina l,dx
in al,dx
and al,not 08h ;turn off chain 4
ora l,04h ;turn off odd/even
or al,04h ;turn off odd/even
out dx,al
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
ina l,dx
in al,dx
and al,not 10h ;turn off odd/even
out dx,al
dec dx
mov al,MISCELLANEOUS
out dx,al
inc dx
ina l,dx
in al,dx
and al,not 02h ;turn off chain
out dx,al
;
@ -477,7 +477,7 @@ WritePixel proc near
; the pixel
mov es:[di],bl ;draw the pixel
ret
WritePixelendp
WritePixel endp
;
; Reads the color of the pixel at the specified location in 320x400
; 256-color mode.
@ -491,7 +491,7 @@ WritePixelendp
;
; Registers altered: AX, CX, DX, SI, ES
;
ReadPixelprocnear
ReadPixel proc near
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
mov ax,SCREEN_WIDTH/4
@ -511,9 +511,9 @@ ReadPixelprocnear
mov dx,GC_INDEX
OUT_WORD ;set to read from the proper plane for
; the pixel
lodsbyte ptr es:[si] ;read the pixel
lods byte ptr es:[si] ;read the pixel
ret
ReadPixelendp
ReadPixel endp
;
; Waits for the next key and returns it in AX.
;
@ -677,20 +677,20 @@ stack ends
;
OUT_WORDmacro
if WORD_OUTS_OK
outdx,ax
out dx,ax
else
out dx,al
inc dx
xch gah,al
xchg ah,al
out dx,al
dec dx
xch gah,al
xchg ah,al
endif
endm
;
; Macro to output a constant value to an indexed VGA register.
;
CONSTANT_TO_INDEXED_REGISTERmacroADDRESS, INDEX, VALUE
CONSTANT_TO_INDEXED_REGISTER macro ADDRESS, INDEX, VALUE
mov dx,ADDRESS
mov ax,(VALUE shl 8) + INDEX
OUT_WORD
@ -702,7 +702,7 @@ Start proc near
;
; Set 320x400 256-color mode.
;
callSet320By400Mode
call Set320By400Mode
;
; We're in 320x400 256-color mode, with page 0 displayed.
; Let's fill page 0 with color bars slanting down and to the right.
@ -722,7 +722,7 @@ callSet320By400Mode
;
; Wait for a key and flip to page 1 when one is pressed.
;
callGetNextKey
call GetNextKey
CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,80h
;set the Start Address High register
; to 80h, for a start address of 8000h
@ -735,7 +735,7 @@ callSet320By400Mode
;
; Wait for another key and flip back to page 0 when one is pressed.
;
callGetNextKey
call GetNextKey
CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,00h
;set the Start Address High register
; to 00h, for a start address of 0000h
@ -757,7 +757,7 @@ Start endp
;
; Output: none
;
Set320By400Modeprocnear
Set320By400Mode proc near
;
; First, go to normal 320x200 256-color mode, which is really a
; 320x400 256-color mode with each line scanned twice.
@ -777,22 +777,22 @@ Set320By400Modeprocnear
mov al,MEMORY_MODE
out dx,al
inc dx
ina l,dx
in al,dx
and al,not 08h ;turn off chain 4
ora l,04h ;turn off odd/even
or al,04h ;turn off odd/even
out dx,al
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
ina l,dx
in al,dx
and al,not 10h ;turn off odd/even
out dx,al
dec dx
mov al,MISCELLANEOUS
out dx,al
inc dx
ina l,dx
in al,dx
and al,not 02h ;turn off chain
out dx,al
;
@ -830,8 +830,8 @@ CONSTANT_TO_INDEXED_REGISTER SC_INDEX,MAP_MASK,0fh
mov al,UNDERLINE
out dx,al
inc dx
ina l,dx
and al,not40h ;turn off doubleword
in al,dx
and al,not 40h ;turn off doubleword
out dx,al
dec dx
mov al,MODE_CONTROL
@ -853,7 +853,7 @@ Set320By400Mode endp
; make them slant down and to the left, 0 to make
; them vertical.
;
ColorBarsUpprocnear
ColorBarsUp proc near
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
sub bh,bh ;start with color 0
@ -867,7 +867,7 @@ RowLoop:
;4 pixels at each address, so
; each 320-pixel row is 80 bytes wide
; in each plane
pus h bx ;save the row-start color
push bx ;save the row-start color
ColumnLoop:
MAP_SELECT = 1
rept 4 ;do all 4 pixels at this address with
@ -887,11 +887,11 @@ MAP_SELECT = MAP_SELECT shl 1
dec si ;count down lines on the screen
jnz RowLoop
ret
ColorBarsUpendp
ColorBarsUp endp
;
; Waits for the next key and returns it in AX.
;
GetNextKeyprocnear
GetNextKey proc near
WaitKey:
mov ah,1
int 16h
@ -901,9 +901,9 @@ WaitKey:
ret
GetNextKey endp
;
Codeends
Code ends
;
endStart
end Start
```
When you run Listing 31.2, note the extremely smooth edges and fine

View file

@ -148,7 +148,7 @@ WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;
_DATAsegmentpublic byte DATA'
_DATA segment public byte DATA'
;
; 360x480 256-color mode CRT Controller register settings.
; (Courtesy of John Bridges.)
@ -171,7 +171,7 @@ vptbl dw 06b00h ; horz total
dw 00616h ; v blank end
dw 0e317h ; turn on byte mode
vpend label word
_DATAends
_DATA ends
;
; Macro to output a word value to a port.
;
@ -199,7 +199,7 @@ _TEXTsegment byte public CODE'
; Returns: nothing
;
public _Set360x480Mode
_Set360x480Modeprocnear
_Set360x480Mode proc near
push si ;preserve C register vars
push di
mov ax,12h ; start with mode 12h
@ -239,7 +239,7 @@ _Set360x480Modeprocnear
pop di ;restore C register vars
pop si
ret
_Set360x480Modeendp
_Set360x480Mode endp
;
; Draws a pixel in the specified color at the specified
; location in 360x480 256-color mode.
@ -258,7 +258,7 @@ Color dw ? ;color in which to draw (in the
DParms ends
;
public _Draw360x480Dot
_Draw360x480Dotprocnear
_Draw360x480Dot proc near
push bp ;preserve caller's BP
mov bp,sp ;point to stack frame
push si ;preserve C register vars
@ -289,7 +289,7 @@ _Draw360x480Dotprocnear
pop si
pop bp ;restore caller's BP
ret
_Draw360x480Dotendp
_Draw360x480Dot endp
;
; Reads the color of the pixel at the specified
; location in 360x480 256-color mode.
@ -306,7 +306,7 @@ ReadY dw ? ;Y coordinate from which to read
RParms ends
;
public _Read360x480Dot
_Read360x480Dotprocnear
_Read360x480Dot proc near
push bp ;preserve caller's BP
mov bp,sp ;point to stack frame
push si ;preserve C register vars
@ -336,7 +336,7 @@ _Read360x480Dotprocnear
pop bp ;restore caller's BP
ret
_Read360x480Dot endp
_TEX Tends
_TEXT ends
end
```

View file

@ -532,7 +532,7 @@ FillVertLoop:
jnz FillHorzLoop ;no, do the next column
ret;
endStart
end Start
```
Note the jagged lines at the corners of the screen when you run Listing

View file

@ -390,7 +390,7 @@ RowLoop:
ColumnLoop:
stosb ;draw a pixel
add al,1 ;increment the attribute
ad cal,0 ;if the attribute just turned
adc al,0 ;if the attribute just turned
; over to 0, increment it to 1
; because we're not going to
; cycle DAC location 0, so

View file

@ -104,7 +104,7 @@ _LineDraw proc near
cld
push bp ;preserve caller's stack frame
mov bp,sp ;point to our stack frame
sub sp, LOCAL_SIZE ;allocate space for local variables
sub sp, LOCAL_SIZE ;allocate space for local variables
push si ;preserve C register variables
push di
push ds ;preserve caller's DS
@ -115,7 +115,7 @@ _LineDraw proc near
jle LineIsTopToBottom
xchg [bp].YEnd,ax; swap endpoints
mov [bp].YStart,ax
movbx, [bp].XStart
mov bx,[bp].XStart
xchg [bp].XEnd,bx
mov [bp].XStart,bx
LineIsTopToBottom:

View file

@ -718,7 +718,7 @@ DiagonalSkipEntry:
jmp ScanEdgeDone
align 2
YMajor:
pushbp ;preserve stack frame pointer
push bp ;preserve stack frame pointer
mov si,[bp+X1] ;starting X coordinate
cmp [bp+SkipFirst],1 ;skip the first point?
mov bp,bx ;put Height in BP for error term calcs
@ -734,7 +734,7 @@ YMajorSkipEntry:
YMajorNoAdvance:
dec bx ;count off this scan line
jnz YMajorLoop
popbp ;restore stack frame pointer
pop bp ;restore stack frame pointer
ScanEdgeDone:
cmp [bp+SetXStart],1 ;were we working with XStart field?
jz UpdateHLinePtr ;yes, DI points to the next XStart

View file

@ -443,7 +443,7 @@ Dataends
;
; Macro to output a word value to a port.
;
OUT_WORDmacro
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
@ -459,9 +459,9 @@ endif
; Macro to output a constant value to an indexed VGA
; register.
;
CONSTANT_TO_INDEXED_REGISTERmacro ADDRESS, INDEX, VALUE
movdx, ADDRESS
movax, (VALUE shl 8) + INDEX
CONSTANT_TO_INDEXED_REGISTER macro ADDRESS, INDEX, VALUE
mov dx, ADDRESS
mov ax, (VALUE shl 8) + INDEX
OUT_WORD
endm
;
@ -704,7 +704,7 @@ DrawObject proc near
mov dx,ax ;# of lines in the image
lodsw ;# of bytes across the image
mov bp,SCREEN_WIDTH
subbp,ax ;# of bytes to add to the display
sub bp,ax ;# of bytes to add to the display
; memory offset after copying a line
; of the image to display memory in
; order to point to the address
@ -718,9 +718,9 @@ DrawLoop:
; next line will go in display
; memory
dec dx ;count down the lines of the image
jnzDrawLoop
jnz DrawLoop
ret
DrawObjectendp
DrawObject endp
;
Code ends
end Start

View file

@ -447,7 +447,7 @@ DrawRectParms ends
mov dh,RightMask[bx] ;set the right-edge clip mask
mov bx,LeftX[bp]
and bx,NOT 7 ;intrapixel address of left edge
su si,bx
sub si,bx
shr si,1
shr si,1
shr si,1 ;# of bytes across spanned by rectangle - 1
@ -455,7 +455,7 @@ DrawRectParms ends
and dl,dh ; combine the masks
MasksSet:
mov bx,BottomY[bp]
su bx,TopY[bp] ;# of scan lines to fill - 1
sub bx,TopY[bp] ;# of scan lines to fill - 1
FillLoop:
push di ;remember line start offset
mov al,dl ;left edge clip mask
@ -661,7 +661,7 @@ TextUpDone:
CharUp: ;draws the character in AL at ES:DI
lds si,[BIOS8x8Ptr] ;point to the 8x8 font start
mov bl,al
su bh,bh
sub bh,bh
shl bx,1
shl bx,1
shl bx,1 ;*8 to look up character offset in font

View file

@ -499,7 +499,7 @@ parms ends
les di,[bp+BufferPtr]
mov dx,[bp+RectHeight]
mov bx,[bp+BufferWidth]
su bx,[bp+RectWidth] ;distance from end of one dest scan
sub bx,[bp+RectWidth] ;distance from end of one dest scan
; to start of next
mov al,byte ptr [bp+Color]
mov ah,al ;double the color for REP STOSW
@ -544,7 +544,7 @@ parms2 ends
mov bx,[bp+Pixels]
mov dx,[bp+ImageHeight]
mov ax,[bp+BufferWidth2]
su ax,[bp+ImageWidth] ;distance from end of one dest scan
sub ax,[bp+ImageWidth] ;distance from end of one dest scan
mov [bp+BufferWidth2],ax ; to start of next
RowLoop2:
mov cx,[bp+ImageWidth]
@ -556,7 +556,7 @@ ColumnLoop:
mov es:[di],al
SkipPixel:
inc bx ;point to next source pixel
inc d ;point to next dest pixel
inc di ;point to next dest pixel
dec cx
jnz ColumnLoop
add di,[bp+BufferWidth2] ;point to next scan to fill
@ -596,9 +596,9 @@ parms3 ends
lds si,[bp+SrcBufferPtr]
mov dx,[bp+CopyHeight]
mov bx,[bp+DestBufferWidth] ;distance from end of one dest scan
su bx,[bp+CopyWidth] ; of copy to the next
sub bx,[bp+CopyWidth] ; of copy to the next
mov ax,[bp+SrcBufferWidth] ;distance from end of one source scan
su ax,[bp+CopyWidth] ; of copy to the next
sub ax,[bp+CopyWidth] ; of copy to the next
RowLoop3:
mov cx,[bp+CopyWidth] ;# of bytes to copy
shr cx,1

View file

@ -521,7 +521,7 @@ MasksSet:
mov [bp+SourceNextScanOffset],ax
mov [bp+RectAddrWidth],cx ;remember width in addresses - 1
;----------------------BUG FIX
mov dx,SC_INDEX
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al ;point SC Index reg to Map Mask
inc dx ;point to SC Data reg

View file

@ -174,7 +174,7 @@ else ; !ROUNDING-ON
sub eax,eax
shrd eax,edx,16 ;position so that result ends up
sar edx,16 ; in EAX
idivdword ptr [bp+Divisor]
idiv dword ptr [bp+Divisor]
endif ;ROUNDING-ON
shld edx,eax,16 ;whole part of result in DX;
; fractional part is already in AX
@ -199,7 +199,7 @@ SCparms ends
align ALIGNMENT
public -CosSin
-CosSinprocnear
-CosSin proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
@ -224,9 +224,9 @@ jg MakeInRange
ja Quadrant1
;quadrant 0
shl bx,2
move ax,CosTable[bx] ;look up sine
mov eax,CosTable[bx] ;look up sine
neg bx ;sin(Angle) = cos(90-Angle)
move dx,CosTable[bx+90*10*4] ;look up cosine
mov edx,CosTable[bx+90*10*4] ;look up cosine
jmp short CSDone
align ALIGNMENT
@ -237,7 +237,7 @@ Quadrant1:
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
move dx,CosTable[bx+90*10*4] ;look up cosine
mov edx,CosTable[bx+90*10*4] ;look up cosine
jmp short CSDone
align ALIGNMENT
@ -248,11 +248,11 @@ BottomHalf: ;quadrant 2 or 3
ja Quadrant2
;quadrant 3
shl bx, 2
mov eax,CosTable[bx] ;look up cosine
neg bx ;sin(Angle) = cos(90-Angle)
movedx,CosTable[90*10*4+bx] ;look up sine
nege dx ;negative in this quadrant
jmp short CSDone
mov eax,CosTable[bx] ;look up cosine
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,CosTable[90*10*4+bx] ;look up sine
neg edx ;negative in this quadrant
jmp short CSDone
align ALIGNMENT
Quadrant2:
@ -262,8 +262,8 @@ Quadrant2:
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
move dx,CosTable[90*10*4+bx] ;look up sine
nege dx ;negative in this quadrant
mov edx,CosTable[90*10*4+bx] ;look up sine
neg edx ;negative in this quadrant
CSDone:
mov bx,[bp].Cos
mov [bx],eax
@ -272,7 +272,7 @@ CSDone:
pop bp;restore stack frame
ret
-CosSinendp
-CosSin endp
;=====================================================================
; Matrix multiplies Xform by SourceVec, and stores the result in
; DestVec. Multiplies a 4x4 matrix times a 4x1 matrix; the result
@ -347,11 +347,11 @@ soff=soff+16
doff=doff+4
ENDM
popdi;restore register variables
popsi
popbp;restore stack frame
pop di;restore register variables
pop si
pop bp;restore stack frame
ret
-XformVecendp
-XformVec endp
;=====================================================================
; Matrix multiplies SourceXform1 by SourceXform2 and stores the
; result in DestXform. Multiplies a 4x4 matrix times a 4x4 matrix;
@ -391,7 +391,7 @@ CXparms ends
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
push si ;preserve register variables
pushdi
push di
mov bx,[bp].SourceXform2 ;BX points to xform2 matrix
mov si,[bp].SourceXform1 ;SI points to xform1 matrix
@ -407,7 +407,7 @@ coff=0 ;column offset
imul dword ptr [bx+coff];times row 0 entry in column
if ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adcedx,0;whole part of result is in DX
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
mov ecx,eax ;set running total
@ -471,11 +471,11 @@ coff=coff+4 ;point to next col in xform2 & dest
roff=roff+16 ;point to next col in xform2 & dest
ENDM
popdi;restore register variables
popsi
popbp;restore stack frame
pop di;restore register variables
pop si
pop bp;restore stack frame
ret
-ConcatXformsendp
-ConcatXforms endp
end
```

View file

@ -203,14 +203,14 @@ _FixedMul endp
; Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
FDparms struc
dw 2 dup(?) ;return address & pushed BP
Dividend dd?
Divisor dd?
Dividend dd ?
Divisor dd ?
FDparms ends
alignALIGNMENT
public_FixedDiv
align ALIGNMENT
public _FixedDiv
_FixedDivproc near
pushbp
movbp,sp
push bp
mov bp,sp
if USE386
@ -268,13 +268,13 @@ else ;!USE386
; projection can't be performed for points closer to the viewpoint than Z=1.
;figure out signs, so we can use
; unsigned divisions
subcx, cx ;assume both operands positive
sub cx,cx ;assume both operands positive
mov ax,word ptr [bp+Dividend+2]
and ax,ax;first operand negative?
jns CheckSecondOperandD ;no
neg ax ;yes, so negate first operand
neg word ptr [bp+Dividend]
sbbax,0
sbb ax,0
inc cx ;mark that first operand is negative
CheckSecondOperandD:
mov bx,word ptr [bp+Divisor+2]
@ -339,7 +339,7 @@ SCparms ends
alignALIGNMENT
public _CosSin
_CosSin procnear
_CosSin proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
@ -366,9 +366,9 @@ CheckInRange:
ja Quadrant1
;quadrant 0
shl bx,2
move ax,CosTable[bx] ;look up sine
mov eax,CosTable[bx] ;look up sine
neg bx;sin(Angle) = cos(90-Angle)
move dx,CosTable[bx+90*10*4] ;look up cosine
mov edx,CosTable[bx+90*10*4] ;look up cosine
jmp short CSDone
align ALIGNMENT
@ -376,10 +376,10 @@ Quadrant1:
neg bx
add bx,180*10 ;convert to angle between 0 and 90
shl bx,2
move ax,CosTable[bx] ;look up cosine
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
move dx,CosTable[bx+90*10*4] ;look up cosine
mov edx,CosTable[bx+90*10*4] ;look up cosine
jmp short CSDone
align ALIGNMENT
@ -390,9 +390,9 @@ BottomHalf: ;quadrant 2 or 3
ja Quadrant2
;quadrant 3
shl bx,2
move ax,CosTable[bx] ;look up cosine
mov eax,CosTable[bx] ;look up cosine
neg bx;sin(Angle) = cos(90-Angle)
move dx,CosTable[90*10*4+bx] ;look up sine
mov edx,CosTable[90*10*4+bx] ;look up sine
neg edx ;negative in this quadrant
jmp short CSDone
@ -401,10 +401,10 @@ Quadrant2:
neg bx
add bx,180*10 ;convert to angle between 0 and 90
shl bx,2
move ax,CosTable[bx] ;look up cosine
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
move dx,CosTable[90*10*4+bx] ;look up sine
mov edx,CosTable[90*10*4+bx] ;look up sine
neg edx ;negative in this quadrant
CSDone:
mov bx,[bp].Cos
@ -597,7 +597,7 @@ FixedMulDone:
align ALIGNMENT
public _XformVec
_XformVecprocnear
_XformVec proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
push si ;preserve register variables
@ -619,9 +619,9 @@ if MUL-ROUNDING-ON
adc edx,0 ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
move cx,eax ;set running total
mov ecx,eax ;set running total
move ax,[si+soff+4] ;column 1 entry on this row
mov eax,[si+soff+4] ;column 1 entry on this row
imul dword ptr [bx+4] ;xform entry times source Y entry
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
@ -630,7 +630,7 @@ endif ;MUL-ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total for this row
move ax,[si+soff+8] ;column 2 entry on this row
mov eax,[si+soff+8] ;column 2 entry on this row
imul dword ptr [bx+8] ;xform entry times source Z entry
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
@ -704,7 +704,7 @@ endif ;USE386
pop si
pop bp ;restore stack frame
ret
_XformVecendp
_XformVec endp
;=====================================================================
; Matrix multiplies SourceXform1 by SourceXform2 and stores the
@ -741,7 +741,7 @@ CXparms ends
align ALIGNMENT
public _ConcatXforms
_ConcatXformsprocnear
_ConcatXforms proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
push si ;preserve register variables
@ -759,17 +759,17 @@ coff=0 ;column offset
REPT 3 ;once for each of the first 3 columns,
; assuming 0 as the bottom entry (no
; translation)
move ax,[si+roff] ;column 0 entry on this row
mov eax,[si+roff] ;column 0 entry on this row
imul dword ptr [bx+coff] ;times row 0 entry in column
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
move cx,eax ;set running total
mov ecx,eax ;set running total
move ax,[si+roff+4] ;column 1 entry on this row
imuld word ptr [bx+coff+16] ;times row 1 entry in col
mov eax,[si+roff+4] ;column 1 entry on this row
imul dword ptr [bx+coff+16] ;times row 1 entry in col
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
@ -777,8 +777,8 @@ endif ;MUL-ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total
move ax,[si+roff+8] ;column 2 entry on this row
imuld word ptr [bx+coff+32] ;times row 2 entry in col
mov eax,[si+roff+8] ;column 2 entry on this row
imul dword ptr [bx+coff+32] ;times row 2 entry in col
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
@ -792,17 +792,17 @@ coff=coff+4 ;point to next col in xform2 & dest
;now do the fourth column, assuming
; 1 as the bottom entry, causing
; translation to be performed
move ax,[si+roff] ;column 0 entry on this row
imuld word ptr [bx+coff] ;times row 0 entry in column
mov eax,[si+roff] ;column 0 entry on this row
imul dword ptr [bx+coff] ;times row 0 entry in column
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;MUL-ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
move cx,eax ;set running total
mov ecx,eax ;set running total
move ax,[si+roff+4] ;column 1 entry on this row
imuld word ptr [bx+coff+16] ;times row 1 entry in col
mov eax,[si+roff+4] ;column 1 entry on this row
imul dword ptr [bx+coff+16] ;times row 1 entry in col
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
@ -810,8 +810,8 @@ endif ;MUL-ROUNDING-ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total
move ax,[si+roff+8] ;column 2 entry on this row
imuld word ptr [bx+coff+32] ;times row 2 entry in col
mov eax,[si+roff+8] ;column 2 entry on this row
imul dword ptr [bx+coff+32] ;times row 2 entry in col
if MUL-ROUNDING-ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX

View file

@ -547,7 +547,7 @@ void WalkTree(NODE *pNode)
// Pop the next node from the stack so
// we can visit it and see if it has a
// right subtree to be traversed
if ((pNode = *pNodeStack) == NULL)
if ((pNode = *--pNodeStack) == NULL)
{
// Stack is empty and the current node
// has no right child; we're done

View file

@ -1,7 +1,5 @@
# About this version
All rights belong to Michael Abrash. Reproduced with permission.
This version was extracted from the PDFs which were [released by Michael Abrash and Dr. Dobbs in 2001](http://www.drdobbs.com/parallel/graphics-programming-black-book/184404919). The intention is to maintain a canonical electronic version of the book, and make it easier to read in other formats and on other devices than were available when the book was released online.
For comments, suggestions, and improvements contact James Gregory at [james@jagregory.com](mailto:james@jagregory.com).