Use backtick code blocks for syntax highlighting
This commit is contained in:
parent
8ed9dea394
commit
f658bfbb40
203 changed files with 26546 additions and 25801 deletions
2
01-02.md
2
01-02.md
|
|
@ -82,6 +82,7 @@ It's *slow*.
|
||||||
|
|
||||||
**LISTING 1.1 L1-1.C**
|
**LISTING 1.1 L1-1.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/*
|
/*
|
||||||
* Program to calculate the 16-bit checksum of all bytes in the
|
* Program to calculate the 16-bit checksum of all bytes in the
|
||||||
* specified file. Obtains the bytes one at a time via read(),
|
* specified file. Obtains the bytes one at a time via read(),
|
||||||
|
|
@ -122,6 +123,7 @@ It's *slow*.
|
||||||
printf("The checksum is: %u\n", Checksum);
|
printf("The checksum is: %u\n", Checksum);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Table 1.1 shows the time taken for Listing 1.1 to generate a checksum of
|
Table 1.1 shows the time taken for Listing 1.1 to generate a checksum of
|
||||||
the WordPerfect version 4.2 thesaurus file, TH.WP (362,293 bytes in
|
the WordPerfect version 4.2 thesaurus file, TH.WP (362,293 bytes in
|
||||||
|
|
|
||||||
4
01-03.md
4
01-03.md
|
|
@ -30,6 +30,7 @@ disk caching turned off.
|
||||||
|
|
||||||
**LISTING 1.2 L1-2.C**
|
**LISTING 1.2 L1-2.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/*
|
/*
|
||||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* from the specified file. Obtains the bytes one at a time in
|
* from the specified file. Obtains the bytes one at a time in
|
||||||
|
|
@ -62,9 +63,11 @@ disk caching turned off.
|
||||||
printf("The checksum is: %u\n", Checksum);
|
printf("The checksum is: %u\n", Checksum);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 1.3 L1-3.ASM**
|
**LISTING 1.3 L1-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Assembler subroutine to perform a 16-bit checksum on the file
|
; Assembler subroutine to perform a 16-bit checksum on the file
|
||||||
; opened on the passed-in handle. Stores the result in the
|
; opened on the passed-in handle. Stores the result in the
|
||||||
; passed-in checksum variable. Returns 1 for success, 0 for error.
|
; passed-in checksum variable. Returns 1 for success, 0 for error.
|
||||||
|
|
@ -129,6 +132,7 @@ disk caching turned off.
|
||||||
ret
|
ret
|
||||||
_ChecksumFileendp
|
_ChecksumFileendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
The lesson is clear: Optimization makes code faster, but without proper
|
The lesson is clear: Optimization makes code faster, but without proper
|
||||||
design, optimization just creates fast slow code.
|
design, optimization just creates fast slow code.
|
||||||
|
|
|
||||||
2
01-04.md
2
01-04.md
|
|
@ -20,6 +20,7 @@ libraries do their work. In other words, *know the territory*!
|
||||||
|
|
||||||
**LISTING 1.4 L1-4.C**
|
**LISTING 1.4 L1-4.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/*
|
/*
|
||||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* from the specified file. Obtains the bytes one at a time via
|
* from the specified file. Obtains the bytes one at a time via
|
||||||
|
|
@ -53,6 +54,7 @@ libraries do their work. In other words, *know the territory*!
|
||||||
printf("The checksum is: %u\n", Checksum);
|
printf("The checksum is: %u\n", Checksum);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Know When It Matters {#Heading10}
|
#### Know When It Matters {#Heading10}
|
||||||
|
|
||||||
|
|
|
||||||
4
01-05.md
4
01-05.md
|
|
@ -30,6 +30,7 @@ uses no assembly at all.
|
||||||
|
|
||||||
**LISTING 1.5 L1-5.C**
|
**LISTING 1.5 L1-5.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/*
|
/*
|
||||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* from the specified file. Buffers the bytes internally, rather
|
* from the specified file. Buffers the bytes internally, rather
|
||||||
|
|
@ -86,6 +87,7 @@ uses no assembly at all.
|
||||||
printf("The checksum is: %u\n", Checksum);
|
printf("The checksum is: %u\n", Checksum);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
That brings us to the fourth reason: avoiding an internal-buffered
|
That brings us to the fourth reason: avoiding an internal-buffered
|
||||||
implementation like Listing 1.5 because of the difficulty of coding such
|
implementation like Listing 1.5 because of the difficulty of coding such
|
||||||
|
|
@ -122,6 +124,7 @@ the design has been maxed out.
|
||||||
|
|
||||||
**LISTING 1.6 L1-6.C**
|
**LISTING 1.6 L1-6.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/*
|
/*
|
||||||
* Program to calculate the 16-bit checksum of the stream of bytes
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* from the specified file. Buffers the bytes internally, rather
|
* from the specified file. Buffers the bytes internally, rather
|
||||||
|
|
@ -175,3 +178,4 @@ the design has been maxed out.
|
||||||
printf("The checksum is: %u\n", Checksum);
|
printf("The checksum is: %u\n", Checksum);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
2
01-06.md
2
01-06.md
|
|
@ -12,6 +12,7 @@ pages: 018-019
|
||||||
|
|
||||||
**LISTING 1.7 L1-7.ASM**
|
**LISTING 1.7 L1-7.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Assembler subroutine to perform a 16-bit checksum on a block of
|
; Assembler subroutine to perform a 16-bit checksum on a block of
|
||||||
; bytes 1 to 64K in size. Adds checksum for block into passed-in
|
; bytes 1 to 64K in size. Adds checksum for block into passed-in
|
||||||
; checksum.
|
; checksum.
|
||||||
|
|
@ -61,6 +62,7 @@ pages: 018-019
|
||||||
ret
|
ret
|
||||||
_ChecksumChunkendp
|
_ChecksumChunkendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Note that in Table 1.1, optimization makes little difference except in
|
Note that in Table 1.1, optimization makes little difference except in
|
||||||
the case of Listing 1.5, where the design has been refined considerably.
|
the case of Listing 1.5, where the design has been refined considerably.
|
||||||
|
|
|
||||||
4
02-01.md
4
02-01.md
|
|
@ -45,6 +45,7 @@ bytes.) I examined the subroutine line by line, saving a cycle here and
|
||||||
a cycle there, until the code truly seemed to be optimized. When I was
|
a cycle there, until the code truly seemed to be optimized. When I was
|
||||||
done, the key part of the code looked something like this:
|
done, the key part of the code looked something like this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
lodsb ;get the next byte to extract a bit from
|
lodsb ;get the next byte to extract a bit from
|
||||||
and al,ah ;isolate the bit we want
|
and al,ah ;isolate the bit we want
|
||||||
|
|
@ -53,6 +54,7 @@ done, the key part of the code looked something like this:
|
||||||
dec cx ;the next bit goes 1 place to the right
|
dec cx ;the next bit goes 1 place to the right
|
||||||
dec dx ;count down the number of bits
|
dec dx ;count down the number of bits
|
||||||
jnz LoopTop ;process the next bit, if any
|
jnz LoopTop ;process the next bit, if any
|
||||||
|
```
|
||||||
|
|
||||||
Now, it's hard to write code that's much faster than seven instructions,
|
Now, it's hard to write code that's much faster than seven instructions,
|
||||||
only one of which accesses memory, and most programmers would have
|
only one of which accesses memory, and most programmers would have
|
||||||
|
|
@ -68,6 +70,7 @@ total of four separate time-consuming multibit rotations!
|
||||||
|
|
||||||
I changed the code to the following:
|
I changed the code to the following:
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
lodsb ;get the next byte to extract a bit from
|
lodsb ;get the next byte to extract a bit from
|
||||||
and al,ah ;isolate the bit we want
|
and al,ah ;isolate the bit we want
|
||||||
|
|
@ -77,6 +80,7 @@ I changed the code to the following:
|
||||||
jnz LoopTop ;process the next bit, if any
|
jnz LoopTop ;process the next bit, if any
|
||||||
rol bl,cl ;rotate all four bits into their final
|
rol bl,cl ;rotate all four bits into their final
|
||||||
; positions at the same time
|
; positions at the same time
|
||||||
|
```
|
||||||
|
|
||||||
This moved the costly multibit rotation out of the loop so that it was
|
This moved the costly multibit rotation out of the loop so that it was
|
||||||
performed just once, rather than four times. While the code may not look
|
performed just once, rather than four times. While the code may not look
|
||||||
|
|
|
||||||
2
03-02.md
2
03-02.md
|
|
@ -12,6 +12,7 @@ pages: 035-042
|
||||||
|
|
||||||
**LISTING 3.1 PZTIMER.ASM**
|
**LISTING 3.1 PZTIMER.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; The precision Zen timer (PZTIMER.ASM)
|
; The precision Zen timer (PZTIMER.ASM)
|
||||||
;
|
;
|
||||||
; Uses the 8253 timer to time the performance of code that takes
|
; Uses the 8253 timer to time the performance of code that takes
|
||||||
|
|
@ -451,3 +452,4 @@ pages: 035-042
|
||||||
|
|
||||||
Code ends
|
Code ends
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
4
03-05.md
4
03-05.md
|
|
@ -82,6 +82,7 @@ and should contain calls to **ZTimerOn** and **ZTimerOff** .
|
||||||
|
|
||||||
**LISTING 3.2 PZTEST.ASM**
|
**LISTING 3.2 PZTEST.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to measure performance of code that takes less than
|
; Program to measure performance of code that takes less than
|
||||||
; 54 ms to execute. (PZTEST.ASM)
|
; 54 ms to execute. (PZTEST.ASM)
|
||||||
;
|
;
|
||||||
|
|
@ -119,6 +120,7 @@ and should contain calls to **ZTimerOn** and **ZTimerOff** .
|
||||||
Start endp
|
Start endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
||||||
Listing 3.3 shows some sample code to be timed. This listing measures
|
Listing 3.3 shows some sample code to be timed. This listing measures
|
||||||
the time required to execute 1,000 loads of AL from the memory variable
|
the time required to execute 1,000 loads of AL from the memory variable
|
||||||
|
|
@ -130,6 +132,7 @@ after the code in Listing 3.3 has been run.
|
||||||
|
|
||||||
**LISTING 3.3 LST3-3.ASM**
|
**LISTING 3.3 LST3-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Test file;
|
; Test file;
|
||||||
; Measures the performance of 1,000 loads of AL from
|
; Measures the performance of 1,000 loads of AL from
|
||||||
; memory. (Use by renaming to TESTCODE, which is
|
; memory. (Use by renaming to TESTCODE, which is
|
||||||
|
|
@ -154,6 +157,7 @@ after the code in Listing 3.3 has been run.
|
||||||
; Stop timing.
|
; Stop timing.
|
||||||
;
|
;
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
It's worth noting that Listing 3.3 begins by jumping around the memory
|
It's worth noting that Listing 3.3 begins by jumping around the memory
|
||||||
variable **MemVar**. This approach lets us avoid reproducing Listing 3.2
|
variable **MemVar**. This approach lets us avoid reproducing Listing 3.2
|
||||||
|
|
|
||||||
6
03-06.md
6
03-06.md
|
|
@ -25,6 +25,7 @@ and "link" with "tlink" in Listing 3.4. The same is true of Listing
|
||||||
|
|
||||||
**LISTING 3.4 PZTIME.BAT**
|
**LISTING 3.4 PZTIME.BAT**
|
||||||
|
|
||||||
|
```bat
|
||||||
echo off
|
echo off
|
||||||
rem
|
rem
|
||||||
rem *** Listing 3.4 ***
|
rem *** Listing 3.4 ***
|
||||||
|
|
@ -86,11 +87,14 @@ and "link" with "tlink" in Listing 3.4. The same is true of Listing
|
||||||
echo * An error occurred while building the precision Zen timer. *
|
echo * An error occurred while building the precision Zen timer. *
|
||||||
echo ***************************************************************
|
echo ***************************************************************
|
||||||
:end
|
:end
|
||||||
|
```
|
||||||
|
|
||||||
Assuming that Listing 3.3 is named LST3-3.ASM and Listing 3.4 is named
|
Assuming that Listing 3.3 is named LST3-3.ASM and Listing 3.4 is named
|
||||||
PZTIME.BAT, the code in Listing 3.3 would be timed with the command:
|
PZTIME.BAT, the code in Listing 3.3 would be timed with the command:
|
||||||
|
|
||||||
|
```sh
|
||||||
pztime LST3-3.ASM
|
pztime LST3-3.ASM
|
||||||
|
```
|
||||||
|
|
||||||
which performs all assembly and linking, and reports the execution time
|
which performs all assembly and linking, and reports the execution time
|
||||||
of the code in Listing 3.3.
|
of the code in Listing 3.3.
|
||||||
|
|
@ -116,7 +120,9 @@ In order to perform any of the timing tests in this book, enter Listing
|
||||||
and enter Listing 3.4 and name it PZTIME.BAT. Then simply enter the
|
and enter Listing 3.4 and name it PZTIME.BAT. Then simply enter the
|
||||||
listing you wish to run into the file *filename* and enter the command:
|
listing you wish to run into the file *filename* and enter the command:
|
||||||
|
|
||||||
|
```sh
|
||||||
pztime <filename>
|
pztime <filename>
|
||||||
|
```
|
||||||
|
|
||||||
In fact, that's exactly how I timed each of the listings in this book.
|
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
|
Code fragments you write yourself can be timed in just the same way. If
|
||||||
|
|
|
||||||
2
03-07.md
2
03-07.md
|
|
@ -78,6 +78,7 @@ computers.
|
||||||
|
|
||||||
**LISTING 3.5 LZTIMER.ASM**
|
**LISTING 3.5 LZTIMER.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; The long-period Zen timer. (LZTIMER.ASM)
|
; The long-period Zen timer. (LZTIMER.ASM)
|
||||||
; Uses the 8253 timer and the BIOS time-of-day count to time the
|
; Uses the 8253 timer and the BIOS time-of-day count to time the
|
||||||
|
|
@ -712,3 +713,4 @@ computers.
|
||||||
|
|
||||||
Code ends
|
Code ends
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
2
03-08.md
2
03-08.md
|
|
@ -70,6 +70,7 @@ timing.
|
||||||
|
|
||||||
**LISTING 3.6 LZTEST.ASM**
|
**LISTING 3.6 LZTEST.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to measure performance of code that takes longer than
|
; Program to measure performance of code that takes longer than
|
||||||
; 54 ms to execute. (LZTEST.ASM)
|
; 54 ms to execute. (LZTEST.ASM)
|
||||||
;
|
;
|
||||||
|
|
@ -128,6 +129,7 @@ timing.
|
||||||
Start endp
|
Start endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
||||||
As with the precision Zen timer, the program in Listing 3.6 is used by
|
As with the precision Zen timer, the program in Listing 3.6 is used by
|
||||||
naming the file containing the code to be timed TESTCODE, then
|
naming the file containing the code to be timed TESTCODE, then
|
||||||
|
|
|
||||||
8
03-09.md
8
03-09.md
|
|
@ -12,6 +12,7 @@ pages: 067-070
|
||||||
|
|
||||||
**LISTING 3.7 LZTIME.BAT**
|
**LISTING 3.7 LZTIME.BAT**
|
||||||
|
|
||||||
|
```bat
|
||||||
echo off
|
echo off
|
||||||
rem
|
rem
|
||||||
rem *** Listing 3.7 ***
|
rem *** Listing 3.7 ***
|
||||||
|
|
@ -73,6 +74,7 @@ pages: 067-070
|
||||||
echo * An error occurred while building the long-period Zen timer. *
|
echo * An error occurred while building the long-period Zen timer. *
|
||||||
echo ***************************************************************
|
echo ***************************************************************
|
||||||
:end
|
:end
|
||||||
|
```
|
||||||
|
|
||||||
Listing 3.8 shows sample code that can be timed with the test-bed
|
Listing 3.8 shows sample code that can be timed with the test-bed
|
||||||
program of Listing 3.6. Listing 3.8 measures the time required to
|
program of Listing 3.6. Listing 3.8 measures the time required to
|
||||||
|
|
@ -81,6 +83,7 @@ the precision Zen timer to handle on the 8088.
|
||||||
|
|
||||||
**LISTING 3.8 LST3-8.ASM**
|
**LISTING 3.8 LST3-8.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; Measures the performance of 20,000 loads of AL from
|
; Measures the performance of 20,000 loads of AL from
|
||||||
; memory. (Use by renaming to TESTCODE, which is
|
; memory. (Use by renaming to TESTCODE, which is
|
||||||
|
|
@ -108,11 +111,14 @@ the precision Zen timer to handle on the 8088.
|
||||||
; Stop timing.
|
; Stop timing.
|
||||||
;
|
;
|
||||||
callZTimerOff
|
callZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
When LZTIME.BAT is run on a PC with the following command line (assuming
|
When LZTIME.BAT is run on a PC with the following command line (assuming
|
||||||
the code in Listing 3.8 is the file LST3-8.ASM)
|
the code in Listing 3.8 is the file LST3-8.ASM)
|
||||||
|
|
||||||
|
```sh
|
||||||
lztime lst3-8.asm
|
lztime lst3-8.asm
|
||||||
|
```
|
||||||
|
|
||||||
the result is 72,544 µs, or about 3.63 µs per load of AL from memory.
|
the result is 72,544 µs, or about 3.63 µs per load of AL from memory.
|
||||||
This is just slightly longer than the time per load of AL measured by
|
This is just slightly longer than the time per load of AL measured by
|
||||||
|
|
@ -147,4 +153,6 @@ lines from Listing 3.1 that must be changed. These changes convert the
|
||||||
code to use C-style external label names and the small model C code
|
code to use C-style external label names and the small model C code
|
||||||
segment. (In C++, use the "C" specifier, as in
|
segment. (In C++, use the "C" specifier, as in
|
||||||
|
|
||||||
|
```c
|
||||||
extern "C" ZTimerOn(void);
|
extern "C" ZTimerOn(void);
|
||||||
|
```
|
||||||
|
|
|
||||||
6
03-10.md
6
03-10.md
|
|
@ -16,11 +16,13 @@ doesn't occur, and the linker can find the routines' C-style names.)
|
||||||
That's all it takes; after doing this, you'll be able to use the Zen
|
That's all it takes; after doing this, you'll be able to use the Zen
|
||||||
timer from C, as, for example, in:
|
timer from C, as, for example, in:
|
||||||
|
|
||||||
|
```c
|
||||||
ZTimerOn():
|
ZTimerOn():
|
||||||
for (i=0, x=0; i<100; i++)
|
for (i=0, x=0; i<100; i++)
|
||||||
x += i;
|
x += i;
|
||||||
ZTimerOff();
|
ZTimerOff();
|
||||||
ZTimerReport();
|
ZTimerReport();
|
||||||
|
```
|
||||||
|
|
||||||
(I'm talking about the precision timer here. The long-period
|
(I'm talking about the precision timer here. The long-period
|
||||||
timer—Listing 3.5—requires the same modifications, but to different
|
timer—Listing 3.5—requires the same modifications, but to different
|
||||||
|
|
@ -46,12 +48,16 @@ One important safety tip when modifying the Zen timer for use with large
|
||||||
code model C code: Watch out for optimizing assemblers! TASM actually
|
code model C code: Watch out for optimizing assemblers! TASM actually
|
||||||
replaces
|
replaces
|
||||||
|
|
||||||
|
```nasm
|
||||||
call far ptr ReferenceZTimerOn
|
call far ptr ReferenceZTimerOn
|
||||||
|
```
|
||||||
|
|
||||||
with
|
with
|
||||||
|
|
||||||
|
```nasm
|
||||||
push cs
|
push cs
|
||||||
call near ptr ReferenceZTimerOn
|
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
|
**ReferenceZTimerOn** is in the same segment as the calling code. This
|
||||||
|
|
|
||||||
12
04-02.md
12
04-02.md
|
|
@ -53,11 +53,15 @@ byte-sized accesses. That's why the official instruction timings
|
||||||
indicate that for code running on an 8088 an additional 4 cycles are
|
indicate that for code running on an 8088 an additional 4 cycles are
|
||||||
required for every word-sized access to a memory operand. For instance,
|
required for every word-sized access to a memory operand. For instance,
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ax,word ptr [MemVar]
|
mov ax,word ptr [MemVar]
|
||||||
|
```
|
||||||
|
|
||||||
takes 4 cycles longer to read the word at address **MemVar** than
|
takes 4 cycles longer to read the word at address **MemVar** than
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov al,byte ptr [MemVar]
|
mov al,byte ptr [MemVar]
|
||||||
|
```
|
||||||
|
|
||||||
takes to read the byte at address **MemVar.** (Actually, the difference
|
takes to read the byte at address **MemVar.** (Actually, the difference
|
||||||
between the two isn't very likely to be exactly 4 cycles, for reasons
|
between the two isn't very likely to be exactly 4 cycles, for reasons
|
||||||
|
|
@ -72,11 +76,15 @@ prior to adding to it, and one to write the result of the addition back
|
||||||
to the destination operand—and thus incurs not one but two 4-cycle
|
to the destination operand—and thus incurs not one but two 4-cycle
|
||||||
penalties. As a result
|
penalties. As a result
|
||||||
|
|
||||||
|
```nasm
|
||||||
add word ptr [MemVar],ax
|
add word ptr [MemVar],ax
|
||||||
|
```
|
||||||
|
|
||||||
takes about 8 cycles longer to execute than:
|
takes about 8 cycles longer to execute than:
|
||||||
|
|
||||||
|
```nasm
|
||||||
add byte ptr [MemVar],al
|
add byte ptr [MemVar],al
|
||||||
|
```
|
||||||
|
|
||||||
String instructions can suffer from the 8-bit bus cycle-eater to a
|
String instructions can suffer from the 8-bit bus cycle-eater to a
|
||||||
greater extent than other instructions. Believe it or not, a single
|
greater extent than other instructions. Believe it or not, a single
|
||||||
|
|
@ -114,6 +122,7 @@ in all.
|
||||||
|
|
||||||
**LISTING 4.1 LST4-1.ASM**
|
**LISTING 4.1 LST4-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of a loop which uses a
|
; Measures the performance of a loop which uses a
|
||||||
; byte-sized memory variable as the loop counter.
|
; byte-sized memory variable as the loop counter.
|
||||||
;
|
;
|
||||||
|
|
@ -127,9 +136,11 @@ in all.
|
||||||
dec [Counter]
|
dec [Counter]
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.2 LST4-2.ASM**
|
**LISTING 4.2 LST4-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of a loop which uses a
|
; Measures the performance of a loop which uses a
|
||||||
; word-sized memory variable as the loop counter.
|
; word-sized memory variable as the loop counter.
|
||||||
;
|
;
|
||||||
|
|
@ -143,6 +154,7 @@ in all.
|
||||||
dec [Counter]
|
dec [Counter]
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
I'd like to make a brief aside concerning code optimization in the
|
I'd like to make a brief aside concerning code optimization in the
|
||||||
listings in this book. Throughout this book I've modeled the sample code
|
listings in this book. Throughout this book I've modeled the sample code
|
||||||
|
|
|
||||||
12
04-03.md
12
04-03.md
|
|
@ -16,12 +16,16 @@ strive to use byte-sized memory variables whenever possible. That does
|
||||||
manipulate a word-sized memory variable in preference to 1 word-sized
|
manipulate a word-sized memory variable in preference to 1 word-sized
|
||||||
memory access, as, for instance,
|
memory access, as, for instance,
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov dl,byte ptr [MemVar]
|
mov dl,byte ptr [MemVar]
|
||||||
mov dh,byte ptr [MemVar+1]
|
mov dh,byte ptr [MemVar+1]
|
||||||
|
```
|
||||||
|
|
||||||
versus:
|
versus:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov dx,word ptr [MemVar]
|
mov dx,word ptr [MemVar]
|
||||||
|
```
|
||||||
|
|
||||||
Recall that every access to a memory byte takes at least 4 cycles; that
|
Recall that every access to a memory byte takes at least 4 cycles; that
|
||||||
limitation is built right into the 8088. The 8088 is also built so that
|
limitation is built right into the 8088. The 8088 is also built so that
|
||||||
|
|
@ -48,6 +52,7 @@ the second byte of each word.
|
||||||
|
|
||||||
**LISTING 4.3 LST4-3.ASM**
|
**LISTING 4.3 LST4-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of reading 1,000 words
|
; Measures the performance of reading 1,000 words
|
||||||
; from memory with 1,000 word-sized accesses.
|
; from memory with 1,000 word-sized accesses.
|
||||||
;
|
;
|
||||||
|
|
@ -56,9 +61,11 @@ the second byte of each word.
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rep lodsw
|
rep lodsw
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.4 LST4-4.ASM**
|
**LISTING 4.4 LST4-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of reading 1000 words
|
; Measures the performance of reading 1000 words
|
||||||
; from memory with 2,000 byte-sized accesses.
|
; from memory with 2,000 byte-sized accesses.
|
||||||
;
|
;
|
||||||
|
|
@ -67,6 +74,7 @@ the second byte of each word.
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rep lodsb
|
rep lodsb
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
In short, if you must perform a 16-bit memory access, let the 8088 break
|
In short, if you must perform a 16-bit memory access, let the 8088 break
|
||||||
the access into two byte-sized accesses for you. The 8088 is more
|
the access into two byte-sized accesses for you. The 8088 is more
|
||||||
|
|
@ -142,17 +150,21 @@ What makes the prefetch queue cycle-eater tricky is that it's
|
||||||
undocumented and unpredictable. That is, with a word-sized memory
|
undocumented and unpredictable. That is, with a word-sized memory
|
||||||
access, such as
|
access, such as
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov [bx],ax
|
mov [bx],ax
|
||||||
|
```
|
||||||
|
|
||||||
it's well-documented that an extra 4 cycles will always be required to
|
it's well-documented that an extra 4 cycles will always be required to
|
||||||
write the upper byte of AX to memory. Not so with the prefetch queue
|
write the upper byte of AX to memory. Not so with the prefetch queue
|
||||||
cycle-eater lurking nearby. For instance, the instructions
|
cycle-eater lurking nearby. For instance, the instructions
|
||||||
|
|
||||||
|
```nasm
|
||||||
shr ax,1
|
shr ax,1
|
||||||
shr ax,1
|
shr ax,1
|
||||||
shr ax,1
|
shr ax,1
|
||||||
shr ax,1
|
shr ax,1
|
||||||
shr ax,1
|
shr ax,1
|
||||||
|
```
|
||||||
|
|
||||||
should execute in 10 cycles, since each **SHR** takes 2 cycles to
|
should execute in 10 cycles, since each **SHR** takes 2 cycles to
|
||||||
execute, according to Intel's specifications. Those specifications
|
execute, according to Intel's specifications. Those specifications
|
||||||
|
|
|
||||||
4
04-04.md
4
04-04.md
|
|
@ -104,6 +104,7 @@ that the "true" execution time of **SHR** is 8.64 cycles.
|
||||||
|
|
||||||
**LISTING 4.5 LST4-5.ASM**
|
**LISTING 4.5 LST4-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of 1,000 SHR instructions
|
; Measures the performance of 1,000 SHR instructions
|
||||||
; in a row. Since SHR executes in 2 cycles but is
|
; in a row. Since SHR executes in 2 cycles but is
|
||||||
; 2 bytes long, the prefetch queue is always empty,
|
; 2 bytes long, the prefetch queue is always empty,
|
||||||
|
|
@ -115,9 +116,11 @@ that the "true" execution time of **SHR** is 8.64 cycles.
|
||||||
shr ax,1
|
shr ax,1
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.6 LST4-6.ASM**
|
**LISTING 4.6 LST4-6.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of 1,000 MUL/SHR instruction
|
; Measures the performance of 1,000 MUL/SHR instruction
|
||||||
; pairs in a row. The lengthy execution time of MUL
|
; pairs in a row. The lengthy execution time of MUL
|
||||||
; should keep the prefetch queue from ever emptying.
|
; should keep the prefetch queue from ever emptying.
|
||||||
|
|
@ -130,6 +133,7 @@ that the "true" execution time of **SHR** is 8.64 cycles.
|
||||||
shr ax,1
|
shr ax,1
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||

|
Listing 4.5.*](images/04-03.jpg)
|
||||||
|
|
|
||||||
4
04-05.md
4
04-05.md
|
|
@ -43,6 +43,7 @@ Listing 4.6.*](images/04-04.jpg)
|
||||||
|
|
||||||
**LISTING 4.7 LST4-7.ASM**
|
**LISTING 4.7 LST4-7.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of repeated MOV AL,0 instructions,
|
; Measures the performance of repeated MOV AL,0 instructions,
|
||||||
; which take 4 cycles each according to Intel's official
|
; which take 4 cycles each according to Intel's official
|
||||||
; specifications.
|
; specifications.
|
||||||
|
|
@ -53,9 +54,11 @@ Listing 4.6.*](images/04-04.jpg)
|
||||||
mov al,0
|
mov al,0
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.8 LST4-8.ASM**
|
**LISTING 4.8 LST4-8.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of repeated SUB AL,AL instructions,
|
; Measures the performance of repeated SUB AL,AL instructions,
|
||||||
; which take 3 cycles each according to Intel's official
|
; which take 3 cycles each according to Intel's official
|
||||||
; specifications.
|
; specifications.
|
||||||
|
|
@ -66,6 +69,7 @@ Listing 4.6.*](images/04-04.jpg)
|
||||||
sub al,al
|
sub al,al
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
As you can see, it's easy to be drawn into thinking you're saving cycles
|
As you can see, it's easy to be drawn into thinking you're saving cycles
|
||||||
when you're not. You can only improve the performance of a specific bit
|
when you're not. You can only improve the performance of a specific bit
|
||||||
|
|
|
||||||
4
04-07.md
4
04-07.md
|
|
@ -28,6 +28,7 @@ request a memory access from the Bus Interface Unit.)
|
||||||
|
|
||||||
**LISTING 4.9 LST4-9.ASM**
|
**LISTING 4.9 LST4-9.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of repeated MUL instructions,
|
; Measures the performance of repeated MUL instructions,
|
||||||
; which allow the prefetch queue to be full at all times,
|
; which allow the prefetch queue to be full at all times,
|
||||||
; to demonstrate a case in which DRAM refresh has no impact
|
; to demonstrate a case in which DRAM refresh has no impact
|
||||||
|
|
@ -39,6 +40,7 @@ request a memory access from the Bus Interface Unit.)
|
||||||
mul ax
|
mul ax
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
Running Listing 4.9, we find that each **MUL** executes in 24.72 µs, or
|
Running Listing 4.9, we find that each **MUL** executes in 24.72 µs, or
|
||||||
exactly 118 cycles. Since that's the shortest time in which **MUL** can
|
exactly 118 cycles. Since that's the shortest time in which **MUL** can
|
||||||
|
|
@ -56,6 +58,7 @@ to fetch the instruction bytes.
|
||||||
|
|
||||||
**LISTING 4.10 LST4-10.ASM**
|
**LISTING 4.10 LST4-10.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the performance of repeated SHR instructions,
|
; Measures the performance of repeated SHR instructions,
|
||||||
; which empty the prefetch queue, to demonstrate the
|
; which empty the prefetch queue, to demonstrate the
|
||||||
; worst-case impact of DRAM refresh on code performance.
|
; worst-case impact of DRAM refresh on code performance.
|
||||||
|
|
@ -65,6 +68,7 @@ to fetch the instruction bytes.
|
||||||
shr ax,1
|
shr ax,1
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
Since 4 cycles are required to read each instruction byte, we'd expect
|
Since 4 cycles are required to read each instruction byte, we'd expect
|
||||||
each **SHR** to execute in 8 cycles, or 1.676 µs, if there were no DRAM
|
each **SHR** to execute in 8 cycles, or 1.676 µs, if there were no DRAM
|
||||||
|
|
|
||||||
4
04-09.md
4
04-09.md
|
|
@ -86,6 +86,7 @@ long as we had assumed, but a long time nonetheless.
|
||||||
|
|
||||||
**LISTING 4.11 LST4-11.ASM**
|
**LISTING 4.11 LST4-11.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Times speed of memory access to Enhanced Graphics
|
; Times speed of memory access to Enhanced Graphics
|
||||||
; Adapter graphics mode display memory at A000:0000.
|
; Adapter graphics mode display memory at A000:0000.
|
||||||
;
|
;
|
||||||
|
|
@ -114,6 +115,7 @@ long as we had assumed, but a long time nonetheless.
|
||||||
;
|
;
|
||||||
mov ax,0003h
|
mov ax,0003h
|
||||||
int 10h ;return to text mode
|
int 10h ;return to text mode
|
||||||
|
```
|
||||||
|
|
||||||
For comparison, let's see how long the same code takes when accessing
|
For comparison, let's see how long the same code takes when accessing
|
||||||
normal system RAM instead of display memory. The code in Listing 4.12,
|
normal system RAM instead of display memory. The code in Listing 4.12,
|
||||||
|
|
@ -125,6 +127,7 @@ cycle-eater can *more than double* the execution time of 8088 code!
|
||||||
|
|
||||||
**LISTING 4.12 LST4-12.ASM**
|
**LISTING 4.12 LST4-12.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Times speed of memory access to normal system
|
; Times speed of memory access to normal system
|
||||||
; memory.
|
; memory.
|
||||||
;
|
;
|
||||||
|
|
@ -143,6 +146,7 @@ cycle-eater can *more than double* the execution time of 8088 code!
|
||||||
; is just to measure memory access
|
; is just to measure memory access
|
||||||
; times
|
; times
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
Bear in mind that we're talking about a worst case here; the impact of
|
Bear in mind that we're talking about a worst case here; the impact of
|
||||||
the display adapter cycle-eater is proportional to the percent of time a
|
the display adapter cycle-eater is proportional to the percent of time a
|
||||||
|
|
|
||||||
2
05-03.md
2
05-03.md
|
|
@ -12,6 +12,7 @@ pages: 118-121
|
||||||
|
|
||||||
**LISTING 5.1 SEARCH.C**
|
**LISTING 5.1 SEARCH.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Program to search the file specified by the first command-line
|
/* Program to search the file specified by the first command-line
|
||||||
* argument for the string specified by the second command-line
|
* argument for the string specified by the second command-line
|
||||||
* argument. Performs the search by reading and searching blocks
|
* argument. Performs the search by reading and searching blocks
|
||||||
|
|
@ -177,3 +178,4 @@ pages: 118-121
|
||||||
exit(Found); /* Return the found/not found status as the
|
exit(Found); /* Return the found/not found status as the
|
||||||
DOS errorlevel */
|
DOS errorlevel */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
4
06-01.md
4
06-01.md
|
|
@ -120,10 +120,14 @@ For example, suppose you have an array base address in BX and an index
|
||||||
into the array in SI. You could add the two registers together to
|
into the array in SI. You could add the two registers together to
|
||||||
address memory, like this:
|
address memory, like this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
add bx,si
|
add bx,si
|
||||||
mov al,[bx]
|
mov al,[bx]
|
||||||
|
```
|
||||||
|
|
||||||
Or you could let the processor do the arithmetic for you in a single
|
Or you could let the processor do the arithmetic for you in a single
|
||||||
instruction:
|
instruction:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov al,[bx+si]
|
mov al,[bx+si]
|
||||||
|
```
|
||||||
|
|
|
||||||
18
06-02.md
18
06-02.md
|
|
@ -19,11 +19,13 @@ within a loop, however, it's advantageous on the 8088 CPU to perform the
|
||||||
addition outside the loop, if possible, reducing effective address
|
addition outside the loop, if possible, reducing effective address
|
||||||
calculation time inside the loop, as in the following:
|
calculation time inside the loop, as in the following:
|
||||||
|
|
||||||
|
```nasm
|
||||||
add bx,si
|
add bx,si
|
||||||
LoopTop:
|
LoopTop:
|
||||||
mov al,[bx]
|
mov al,[bx]
|
||||||
inc bx
|
inc bx
|
||||||
loop LoopTop
|
loop LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
Here, **MOV AL,[BX]** is two cycles faster than **MOV AL,[BX+SI]**.
|
Here, **MOV AL,[BX]** is two cycles faster than **MOV AL,[BX+SI]**.
|
||||||
|
|
||||||
|
|
@ -73,24 +75,32 @@ source operands.
|
||||||
Imagine that we want to add BX to DI, add two to the result, and store
|
Imagine that we want to add BX to DI, add two to the result, and store
|
||||||
the result in AX. The obvious solution is this:
|
the result in AX. The obvious solution is this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ax,bx
|
mov ax,bx
|
||||||
add ax,di
|
add ax,di
|
||||||
add ax,2
|
add ax,2
|
||||||
|
```
|
||||||
|
|
||||||
(It would be more compact to increment AX twice than to add two to it,
|
(It would be more compact to increment AX twice than to add two to it,
|
||||||
and would probably be faster on an 8088, but that's not what we're after
|
and would probably be faster on an 8088, but that's not what we're after
|
||||||
at the moment.) An elegant alternative solution is simply:
|
at the moment.) An elegant alternative solution is simply:
|
||||||
|
|
||||||
|
```nasm
|
||||||
lea ax,[bx+di+2]
|
lea ax,[bx+di+2]
|
||||||
|
```
|
||||||
|
|
||||||
Likewise, either of the following would copy SI plus two to DI
|
Likewise, either of the following would copy SI plus two to DI
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov di,si
|
mov di,si
|
||||||
add di,2
|
add di,2
|
||||||
|
```
|
||||||
|
|
||||||
or:
|
or:
|
||||||
|
|
||||||
|
```nasm
|
||||||
lea di,[si+2]
|
lea di,[si+2]
|
||||||
|
```
|
||||||
|
|
||||||
Mind you, the only components **LEA** can add are BX or BP, SI or DI,
|
Mind you, the only components **LEA** can add are BX or BP, SI or DI,
|
||||||
and a constant displacement, so it's not going to replace **ADD** most
|
and a constant displacement, so it's not going to replace **ADD** most
|
||||||
|
|
@ -135,14 +145,18 @@ bits, so we can now add up to two 32-bit registers and a constant, *and*
|
||||||
shift (or multiply) one of the registers to some extent—all with a
|
shift (or multiply) one of the registers to some extent—all with a
|
||||||
single instruction. For example,
|
single instruction. For example,
|
||||||
|
|
||||||
|
```nasm
|
||||||
lea edi,TableBase[ecx+edx*4]
|
lea edi,TableBase[ecx+edx*4]
|
||||||
|
```
|
||||||
|
|
||||||
replaces all this
|
replaces all this
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov edi,edx
|
mov edi,edx
|
||||||
shl edi,2
|
shl edi,2
|
||||||
add edi,ecx
|
add edi,ecx
|
||||||
add edi,offset TableBase
|
add edi,offset TableBase
|
||||||
|
```
|
||||||
|
|
||||||
when pointing to an entry in a doubly indexed table.
|
when pointing to an entry in a doubly indexed table.
|
||||||
|
|
||||||
|
|
@ -156,15 +170,19 @@ index on the 386, and can be scaled as the index while being used
|
||||||
unchanged as the base. That means that you can, for example, multiply
|
unchanged as the base. That means that you can, for example, multiply
|
||||||
EBX by 5 with:
|
EBX by 5 with:
|
||||||
|
|
||||||
|
```nasm
|
||||||
lea ebx,[ebx+ebx*4]
|
lea ebx,[ebx+ebx*4]
|
||||||
|
```
|
||||||
|
|
||||||
Without **LEA** and scaling, multiplication of EBX by 5 would require
|
Without **LEA** and scaling, multiplication of EBX by 5 would require
|
||||||
either a relatively slow **MUL**, along with a set-up instruction or
|
either a relatively slow **MUL**, along with a set-up instruction or
|
||||||
two, or three separate instructions along the lines of the following
|
two, or three separate instructions along the lines of the following
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov edx,ebx
|
mov edx,ebx
|
||||||
shl ebx,2
|
shl ebx,2
|
||||||
add ebx,edx
|
add ebx,edx
|
||||||
|
```
|
||||||
|
|
||||||
and would in either case require the destruction of the contents of
|
and would in either case require the destruction of the contents of
|
||||||
another register.
|
another register.
|
||||||
|
|
|
||||||
4
07-02.md
4
07-02.md
|
|
@ -13,13 +13,17 @@ pages: 139-141
|
||||||
By the way, don't fall victim to the lures of **JCXZ** and do something
|
By the way, don't fall victim to the lures of **JCXZ** and do something
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
and cx,ofh ;Isolate the desired field
|
and cx,ofh ;Isolate the desired field
|
||||||
jcxz SkipLoop ;If field is 0, don't bother
|
jcxz SkipLoop ;If field is 0, don't bother
|
||||||
|
```
|
||||||
|
|
||||||
The **AND** instruction has already set the Zero flag, so this
|
The **AND** instruction has already set the Zero flag, so this
|
||||||
|
|
||||||
|
```nasm
|
||||||
and cx,0fh ;Isolate the desired field
|
and cx,0fh ;Isolate the desired field
|
||||||
jz SkipLoop ;If field is 0, don't bother
|
jz SkipLoop ;If field is 0, don't bother
|
||||||
|
```
|
||||||
|
|
||||||
will do just fine and is faster on all processors. Use **JCXZ** only
|
will do just fine and is faster on all processors. Use **JCXZ** only
|
||||||
when the Zero flag isn't already set to reflect the status of CX.
|
when the Zero flag isn't already set to reflect the status of CX.
|
||||||
|
|
|
||||||
2
07-03.md
2
07-03.md
|
|
@ -12,6 +12,7 @@ pages: 141-143
|
||||||
|
|
||||||
**LISTING 7.1 L7-1.ASM**
|
**LISTING 7.1 L7-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate searching through a buffer of a specified
|
; Program to illustrate searching through a buffer of a specified
|
||||||
; length until either a specified byte or a zero byte is
|
; length until either a specified byte or a zero byte is
|
||||||
; encountered.
|
; encountered.
|
||||||
|
|
@ -102,6 +103,7 @@ pages: 141-143
|
||||||
ret
|
ret
|
||||||
SearchMaxLengthendp
|
SearchMaxLengthendp
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
||||||
### Unrolling Loops {#Heading7}
|
### Unrolling Loops {#Heading7}
|
||||||
|
|
||||||
|
|
|
||||||
2
07-04.md
2
07-04.md
|
|
@ -12,6 +12,7 @@ pages: 143-145
|
||||||
|
|
||||||
**LISTING 7.2 L7-2.ASM**
|
**LISTING 7.2 L7-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate searching through a buffer of a specified
|
; Program to illustrate searching through a buffer of a specified
|
||||||
; length until a specified zero byte is encountered.
|
; length until a specified zero byte is encountered.
|
||||||
; A loop unrolled four times and terminated with LOOP is used.
|
; A loop unrolled four times and terminated with LOOP is used.
|
||||||
|
|
@ -140,6 +141,7 @@ pages: 143-145
|
||||||
ret
|
ret
|
||||||
SearchMaxLengthendp
|
SearchMaxLengthendp
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
||||||
How much difference? Listing 7.2 runs in 121 µs—40 percent faster than
|
How much difference? Listing 7.2 runs in 121 µs—40 percent faster than
|
||||||
Listing 7.1, even though Listing 7.2 still uses **LOOP** rather than
|
Listing 7.1, even though Listing 7.2 still uses **LOOP** rather than
|
||||||
|
|
|
||||||
14
07-05.md
14
07-05.md
|
|
@ -19,9 +19,11 @@ simple task of setting bit N of AX to 1.
|
||||||
The obvious way to do this is to place N in CL, rotate the bit into
|
The obvious way to do this is to place N in CL, rotate the bit into
|
||||||
position, and OR it with AX, as follows:
|
position, and OR it with AX, as follows:
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV BX,1
|
MOV BX,1
|
||||||
SHL BX,CL
|
SHL BX,CL
|
||||||
OR AX,BX
|
OR AX,BX
|
||||||
|
```
|
||||||
|
|
||||||
This solution is obvious because it takes good advantage of the special
|
This solution is obvious because it takes good advantage of the special
|
||||||
ability of the x86 family to shift or rotate by the variable number of
|
ability of the x86 family to shift or rotate by the variable number of
|
||||||
|
|
@ -31,6 +33,7 @@ bit number in BX, and look the shifted bit up, as shown in Listing 7.3.
|
||||||
|
|
||||||
**LISTING 7.3 L7-3.ASM**
|
**LISTING 7.3 L7-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
SHL BX,1 ;prepare for word sized look up
|
SHL BX,1 ;prepare for word sized look up
|
||||||
OR AX,ShiftTable[BX] ;look up the bit and OR it in
|
OR AX,ShiftTable[BX] ;look up the bit and OR it in
|
||||||
:
|
:
|
||||||
|
|
@ -40,6 +43,7 @@ bit number in BX, and look the shifted bit up, as shown in Listing 7.3.
|
||||||
DW BIT_PATTERN
|
DW BIT_PATTERN
|
||||||
BIT_PATTERN=BIT_PATTERN SHL 1
|
BIT_PATTERN=BIT_PATTERN SHL 1
|
||||||
ENDM
|
ENDM
|
||||||
|
```
|
||||||
|
|
||||||
Even though it accesses memory, this approach takes only 20 cycles—more
|
Even though it accesses memory, this approach takes only 20 cycles—more
|
||||||
than twice as fast as the variable shift. Once again, we were able to
|
than twice as fast as the variable shift. Once again, we were able to
|
||||||
|
|
@ -55,6 +59,7 @@ code snippet in Listing 7.4.
|
||||||
|
|
||||||
**LISTING 7.4 L7-4.ASM**
|
**LISTING 7.4 L7-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
OR EAX,ShiftTable[EBX*4] ;look up the bit and OR it in
|
OR EAX,ShiftTable[EBX*4] ;look up the bit and OR it in
|
||||||
:
|
:
|
||||||
ShiftTable LABEL DWORD
|
ShiftTable LABEL DWORD
|
||||||
|
|
@ -63,6 +68,7 @@ code snippet in Listing 7.4.
|
||||||
DD BIT_PATTERN
|
DD BIT_PATTERN
|
||||||
BIT_PATTERN=BIT_PATTERN SHL 1
|
BIT_PATTERN=BIT_PATTERN SHL 1
|
||||||
ENDM
|
ENDM
|
||||||
|
```
|
||||||
|
|
||||||
> 
|
> 
|
||||||
> Besides illustrating the advantages of local optimization, this example
|
> Besides illustrating the advantages of local optimization, this example
|
||||||
|
|
@ -119,6 +125,7 @@ addition to the next.
|
||||||
|
|
||||||
**LISTING 7.5 L7-5.ASM**
|
**LISTING 7.5 L7-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
CLC ;clear the Carry for the initial addition
|
CLC ;clear the Carry for the initial addition
|
||||||
LOOP_TOP:
|
LOOP_TOP:
|
||||||
MOV AX,[SI];get next source operand word
|
MOV AX,[SI];get next source operand word
|
||||||
|
|
@ -128,12 +135,14 @@ addition to the next.
|
||||||
INC DI ;point to next dest operand word
|
INC DI ;point to next dest operand word
|
||||||
INC DI
|
INC DI
|
||||||
LOOP LOOP_TOP
|
LOOP LOOP_TOP
|
||||||
|
```
|
||||||
|
|
||||||
If **ADD** were used, the Carry flag would have to be saved between
|
If **ADD** were used, the Carry flag would have to be saved between
|
||||||
additions, with code along the lines shown in Listing 7.6.
|
additions, with code along the lines shown in Listing 7.6.
|
||||||
|
|
||||||
**LISTING 7.6 L7-6.ASM**
|
**LISTING 7.6 L7-6.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
CLC ;clear the carry for the initial addition
|
CLC ;clear the carry for the initial addition
|
||||||
LOOP_TOP:
|
LOOP_TOP:
|
||||||
MOV AX,[SI] ;get next source operand word
|
MOV AX,[SI] ;get next source operand word
|
||||||
|
|
@ -143,6 +152,7 @@ additions, with code along the lines shown in Listing 7.6.
|
||||||
ADD DI,2 ;point to next dest operand word
|
ADD DI,2 ;point to next dest operand word
|
||||||
SAHF ;restore the carry flag
|
SAHF ;restore the carry flag
|
||||||
LOOP LOOP_TOP
|
LOOP LOOP_TOP
|
||||||
|
```
|
||||||
|
|
||||||
It's not that the Listing 7.6 approach is necessarily better or worse;
|
It's not that the Listing 7.6 approach is necessarily better or worse;
|
||||||
that depends on the processor and the situation. The Listing 7.6
|
that depends on the processor and the situation. The Listing 7.6
|
||||||
|
|
@ -182,13 +192,17 @@ an 8088 but are relatively slow instructions on the 486 and Pentium.
|
||||||
There are times when it's a clear liability that **INC** doesn't set the
|
There are times when it's a clear liability that **INC** doesn't set the
|
||||||
Carry flag. For instance
|
Carry flag. For instance
|
||||||
|
|
||||||
|
```nasm
|
||||||
INC AX
|
INC AX
|
||||||
ADC DX,0
|
ADC DX,0
|
||||||
|
```
|
||||||
|
|
||||||
does *not* increment the 32-bit value in DX:AX. To do that, you'd need
|
does *not* increment the 32-bit value in DX:AX. To do that, you'd need
|
||||||
the following:
|
the following:
|
||||||
|
|
||||||
|
```nasm
|
||||||
ADD AX,1
|
ADD AX,1
|
||||||
ADC DX,0
|
ADC DX,0
|
||||||
|
```
|
||||||
|
|
||||||
As always, pay attention!
|
As always, pay attention!
|
||||||
2
08-02.md
2
08-02.md
|
|
@ -91,8 +91,10 @@ deal. You've eliminated the trappings of the compiler—the stack frame
|
||||||
and the restricted register usage—but you're still *thinking* like the
|
and the restricted register usage—but you're still *thinking* like the
|
||||||
compiler. Try this:
|
compiler. Try this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
repnz scasw
|
repnz scasw
|
||||||
jz Match
|
jz Match
|
||||||
|
```
|
||||||
|
|
||||||
It's a simple example—but, I hope, a convincing one. Stretch your brain
|
It's a simple example—but, I hope, a convincing one. Stretch your brain
|
||||||
when you optimize.
|
when you optimize.
|
||||||
|
|
|
||||||
4
08-03.md
4
08-03.md
|
|
@ -12,6 +12,7 @@ pages: 156-160
|
||||||
|
|
||||||
**LISTING 8.1 L8-1.C**
|
**LISTING 8.1 L8-1.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Program to search an array spanning a linked list of variable-
|
/* Program to search an array spanning a linked list of variable-
|
||||||
sized blocks, for all entries with a specified ID number,
|
sized blocks, for all entries with a specified ID number,
|
||||||
and return the average of the values of all such entries. Each of
|
and return the average of the values of all such entries. Each of
|
||||||
|
|
@ -131,6 +132,7 @@ pages: 156-160
|
||||||
else
|
else
|
||||||
return(IDMatchSum / IDMatchCount);
|
return(IDMatchSum / IDMatchCount);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The main body of Listing 8.1 constructs a linked list of memory blocks
|
The main body of Listing 8.1 constructs a linked list of memory blocks
|
||||||
of various sizes and stores an array of structures across those blocks,
|
of various sizes and stores an array of structures across those blocks,
|
||||||
|
|
@ -154,6 +156,7 @@ instruction can be used.
|
||||||
|
|
||||||
**LISTING 8.2 L8-2.COD**
|
**LISTING 8.2 L8-2.COD**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Code generated by Microsoft C for inner loop of FindIDAverage.
|
; Code generated by Microsoft C for inner loop of FindIDAverage.
|
||||||
;|*** for (WorkingBlockCount=0;
|
;|*** for (WorkingBlockCount=0;
|
||||||
;|*** WorkingBlockCount<BlockPointer->BlockCount;
|
;|*** WorkingBlockCount<BlockPointer->BlockCount;
|
||||||
|
|
@ -183,3 +186,4 @@ instruction can be used.
|
||||||
mov WORD PTR [bp-2],di ;IDMatchSum
|
mov WORD PTR [bp-2],di ;IDMatchSum
|
||||||
mov WORD PTR [bp-4],dx ;IDMatchCount
|
mov WORD PTR [bp-4],dx ;IDMatchCount
|
||||||
$FB264:
|
$FB264:
|
||||||
|
```
|
||||||
|
|
|
||||||
6
08-04.md
6
08-04.md
|
|
@ -24,6 +24,7 @@ optimization, isn't it?
|
||||||
|
|
||||||
**LISTING 8.3 L8-3.ASM**
|
**LISTING 8.3 L8-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Typically optimized assembly language version of FindIDAverage.
|
; Typically optimized assembly language version of FindIDAverage.
|
||||||
SearchedForID equ 4 ;Passed parameter offsets in the
|
SearchedForID equ 4 ;Passed parameter offsets in the
|
||||||
BlockPointer equ 6 ; stack frame (skip over pushed BP
|
BlockPointer equ 6 ; stack frame (skip over pushed BP
|
||||||
|
|
@ -37,6 +38,7 @@ optimization, isn't it?
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindIDAverage
|
public _FindIDAverage
|
||||||
|
```
|
||||||
|
|
||||||
| | On 20 MHz 386 | On 10 MHz 286 |
|
| | On 20 MHz 386 | On 10 MHz 286 |
|
||||||
|------------------------------------------------------------|------------------|------------------|
|
|------------------------------------------------------------|------------------|------------------|
|
||||||
|
|
@ -47,6 +49,7 @@ optimization, isn't it?
|
||||||
|
|
||||||
Table: Table 8.1 Execution Times of FindIDAverage.
|
Table: Table 8.1 Execution Times of FindIDAverage.
|
||||||
|
|
||||||
|
```nasm
|
||||||
_FindIDAverage proc near
|
_FindIDAverage proc near
|
||||||
push bp ;Save caller's stack frame
|
push bp ;Save caller's stack frame
|
||||||
mov bp,sp ;Point to our stack frame
|
mov bp,sp ;Point to our stack frame
|
||||||
|
|
@ -90,6 +93,7 @@ Table: Table 8.1 Execution Times of FindIDAverage.
|
||||||
ret
|
ret
|
||||||
_FindIDAverage ENDP
|
_FindIDAverage ENDP
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Listing 8.4 tosses some sophisticated optimization techniques into the
|
Listing 8.4 tosses some sophisticated optimization techniques into the
|
||||||
mix. The loop is unrolled eight times, eliminating a good deal of
|
mix. The loop is unrolled eight times, eliminating a good deal of
|
||||||
|
|
@ -102,6 +106,7 @@ but not a tremendous return for the optimization effort invested.
|
||||||
|
|
||||||
**LISTING 8.4 L8-4.ASM**
|
**LISTING 8.4 L8-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Heavily optimized assembly language version of FindIDAverage.
|
; Heavily optimized assembly language version of FindIDAverage.
|
||||||
; Features an unrolled loop and more efficient pointer use.
|
; Features an unrolled loop and more efficient pointer use.
|
||||||
SearchedForID equ 4 ;Passed parameter offsets in the
|
SearchedForID equ 4 ;Passed parameter offsets in the
|
||||||
|
|
@ -190,3 +195,4 @@ but not a tremendous return for the optimization effort invested.
|
||||||
ret
|
ret
|
||||||
_FindIDAverage ENDP
|
_FindIDAverage ENDP
|
||||||
end
|
end
|
||||||
|
```
|
||||||
6
08-05.md
6
08-05.md
|
|
@ -20,6 +20,7 @@ merely rearranged.
|
||||||
|
|
||||||
**LISTING 8.5 L8-5.C**
|
**LISTING 8.5 L8-5.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Program to search an array spanning a linked list of variable-
|
/* Program to search an array spanning a linked list of variable-
|
||||||
sized blocks, for all entries with a specified ID number,
|
sized blocks, for all entries with a specified ID number,
|
||||||
and return the average of the values of all such entries. Each of
|
and return the average of the values of all such entries. Each of
|
||||||
|
|
@ -38,9 +39,11 @@ merely rearranged.
|
||||||
void exit(int);
|
void exit(int);
|
||||||
extern unsigned int FindIDAverage2(unsigned int,
|
extern unsigned int FindIDAverage2(unsigned int,
|
||||||
struct BlockHeader *);
|
struct BlockHeader *);
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
```c
|
||||||
/* Structure that starts each variable-sized block */
|
/* Structure that starts each variable-sized block */
|
||||||
struct BlockHeader {
|
struct BlockHeader {
|
||||||
struct BlockHeader *NextBlock; /* Pointer to next block, or NULL
|
struct BlockHeader *NextBlock; /* Pointer to next block, or NULL
|
||||||
|
|
@ -93,9 +96,11 @@ merely rearranged.
|
||||||
IDToFind, FindIDAverage2(IDToFind, BaseArrayBlockPointer));
|
IDToFind, FindIDAverage2(IDToFind, BaseArrayBlockPointer));
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 8.6 L8-6.ASM**
|
**LISTING 8.6 L8-6.ASM**
|
||||||
|
|
||||||
|
```asm
|
||||||
; Alternative optimized assembly language version of FindIDAverage
|
; Alternative optimized assembly language version of FindIDAverage
|
||||||
; requires data organized as two arrays within each block rather
|
; requires data organized as two arrays within each block rather
|
||||||
; than as an array of two-value element structures. This allows the
|
; than as an array of two-value element structures. This allows the
|
||||||
|
|
@ -163,6 +168,7 @@ merely rearranged.
|
||||||
ret
|
ret
|
||||||
_FindIDAverage2 ENDP
|
_FindIDAverage2 ENDP
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
The whole point of this rearrangement is to allow us to use **REP
|
The whole point of this rearrangement is to allow us to use **REP
|
||||||
SCASW** to search through each block, and that's exactly what
|
SCASW** to search through each block, and that's exactly what
|
||||||
|
|
|
||||||
14
09-01.md
14
09-01.md
|
|
@ -88,13 +88,17 @@ machine word (32 bits in 386 protected mode, 16 bits otherwise), but it
|
||||||
renders **LEA** useless for multiword operations, which use the Carry
|
renders **LEA** useless for multiword operations, which use the Carry
|
||||||
flag to tie together partial results. For example, these instructions
|
flag to tie together partial results. For example, these instructions
|
||||||
|
|
||||||
|
```nasm
|
||||||
ADD EAX,EBX
|
ADD EAX,EBX
|
||||||
ADC EDX,ECX
|
ADC EDX,ECX
|
||||||
|
```
|
||||||
|
|
||||||
could *not* be replaced
|
could *not* be replaced
|
||||||
|
|
||||||
|
```nasm
|
||||||
LEA EAX,[EAX+EBX]
|
LEA EAX,[EAX+EBX]
|
||||||
ADC EDX,ECX
|
ADC EDX,ECX
|
||||||
|
```
|
||||||
|
|
||||||
because **LEA** doesn't affect the Carry flag.
|
because **LEA** doesn't affect the Carry flag.
|
||||||
|
|
||||||
|
|
@ -103,6 +107,7 @@ performing pointer arithmetic, however. For instance, the following code
|
||||||
uses **LEA** to advance the pointers while adding one 128-bit memory
|
uses **LEA** to advance the pointers while adding one 128-bit memory
|
||||||
variable to another such variable:
|
variable to another such variable:
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV ECX,4 ;# of 32-bit words to add
|
MOV ECX,4 ;# of 32-bit words to add
|
||||||
CLC
|
CLC
|
||||||
;no carry into the initial ADC
|
;no carry into the initial ADC
|
||||||
|
|
@ -113,6 +118,7 @@ variable to another such variable:
|
||||||
LEA ESI,[ESI+4] ;advance one array's pointer
|
LEA ESI,[ESI+4] ;advance one array's pointer
|
||||||
LEA EDI,[EDI+4] ;advance the other array's pointer
|
LEA EDI,[EDI+4] ;advance the other array's pointer
|
||||||
LOOP ADDLOOP
|
LOOP ADDLOOP
|
||||||
|
```
|
||||||
|
|
||||||
(Yes, I could use **LODSD** instead of **MOV/LEA**; I'm just
|
(Yes, I could use **LODSD** instead of **MOV/LEA**; I'm just
|
||||||
illustrating a point here. Besides, **LODS** is only 1 cycle faster than
|
illustrating a point here. Besides, **LODS** is only 1 cycle faster than
|
||||||
|
|
@ -140,9 +146,11 @@ of my favorites:
|
||||||
|
|
||||||
John's code for setting AX to its absolute value is:
|
John's code for setting AX to its absolute value is:
|
||||||
|
|
||||||
|
```nasm
|
||||||
CWD
|
CWD
|
||||||
XOR AX,DX
|
XOR AX,DX
|
||||||
SUB AX,DX
|
SUB AX,DX
|
||||||
|
```
|
||||||
|
|
||||||
This does nothing when bit 15 of AX is 0 (that is, if AX is positive).
|
This does nothing when bit 15 of AX is 0 (that is, if AX is positive).
|
||||||
When AX is negative, the code "nots" it and adds 1, which is exactly how
|
When AX is negative, the code "nots" it and adds 1, which is exactly how
|
||||||
|
|
@ -150,10 +158,12 @@ you perform a two's complement negate. For the case where AX is not
|
||||||
negative, this trick usually beats the stuffing out of the standard
|
negative, this trick usually beats the stuffing out of the standard
|
||||||
absolute value code:
|
absolute value code:
|
||||||
|
|
||||||
|
```nasm
|
||||||
AND AX,AX ;negative?
|
AND AX,AX ;negative?
|
||||||
JNS IsPositive ;no
|
JNS IsPositive ;no
|
||||||
NEG AX ;yes,negate it
|
NEG AX ;yes,negate it
|
||||||
IsPositive:
|
IsPositive:
|
||||||
|
```
|
||||||
|
|
||||||
However, John's code is slower on a 486; as you're no doubt coming to
|
However, John's code is slower on a 486; as you're no doubt coming to
|
||||||
realize (and as I'll explain in Chapters 12 and 13), the 486 is an
|
realize (and as I'll explain in Chapters 12 and 13), the 486 is an
|
||||||
|
|
@ -162,19 +172,23 @@ optimization world unto itself.
|
||||||
Here's how John copies a block of bytes from DS:SI to ES:DI, moving as
|
Here's how John copies a block of bytes from DS:SI to ES:DI, moving as
|
||||||
much data as possible a word at a time:
|
much data as possible a word at a time:
|
||||||
|
|
||||||
|
```nasm
|
||||||
SHR CX,1 ;word count
|
SHR CX,1 ;word count
|
||||||
REP MOVSW ;copy as many words as possible
|
REP MOVSW ;copy as many words as possible
|
||||||
ADC CX,CX ;CX=1 if copy length was odd,
|
ADC CX,CX ;CX=1 if copy length was odd,
|
||||||
;0 else
|
;0 else
|
||||||
REP MOVSB ;copy any odd byte
|
REP MOVSB ;copy any odd byte
|
||||||
|
```
|
||||||
|
|
||||||
(**ADC CX,CX** can be replaced with **RCL CX,1**; which is faster
|
(**ADC CX,CX** can be replaced with **RCL CX,1**; which is faster
|
||||||
depends on the processor type.) It might be hard to believe that the
|
depends on the processor type.) It might be hard to believe that the
|
||||||
above is faster than this:
|
above is faster than this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
SHR CX,1 ;word count
|
SHR CX,1 ;word count
|
||||||
REP MOVSW ;copy as many words as
|
REP MOVSW ;copy as many words as
|
||||||
;possible
|
;possible
|
||||||
JNC CopyDone ;done if even copy length
|
JNC CopyDone ;done if even copy length
|
||||||
MOVSB ;copy the odd byte
|
MOVSB ;copy the odd byte
|
||||||
CopyDone:
|
CopyDone:
|
||||||
|
```
|
||||||
|
|
|
||||||
8
09-02.md
8
09-02.md
|
|
@ -27,6 +27,7 @@ video programmers are undoubtedly familiar with the following code to
|
||||||
multiply AX times 80 (the width in bytes of the bitmap in most PC
|
multiply AX times 80 (the width in bytes of the bitmap in most PC
|
||||||
display modes):
|
display modes):
|
||||||
|
|
||||||
|
```nasm
|
||||||
SHL AX,1 ;*2
|
SHL AX,1 ;*2
|
||||||
SH LAX,1 ;*4
|
SH LAX,1 ;*4
|
||||||
SH LAX,1 ;*8
|
SH LAX,1 ;*8
|
||||||
|
|
@ -35,26 +36,33 @@ display modes):
|
||||||
SH LAX,1 ;*32
|
SH LAX,1 ;*32
|
||||||
SH LAX,1 ;*64
|
SH LAX,1 ;*64
|
||||||
ADD AX,BX ;*80
|
ADD AX,BX ;*80
|
||||||
|
```
|
||||||
|
|
||||||
Using **LEA** on the 386, the above could be reduced to
|
Using **LEA** on the 386, the above could be reduced to
|
||||||
|
|
||||||
|
```nasm
|
||||||
LEA EAX,[EAX*2] ;*2
|
LEA EAX,[EAX*2] ;*2
|
||||||
LEA EAX,[EAX*8] ;*16
|
LEA EAX,[EAX*8] ;*16
|
||||||
LEA EAX,[EAX+EAX*4] ;*80
|
LEA EAX,[EAX+EAX*4] ;*80
|
||||||
|
```
|
||||||
|
|
||||||
which still isn't as fast as using a lookup table like
|
which still isn't as fast as using a lookup table like
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV EAX,MultiplesOf80Table[EAX*4]
|
MOV EAX,MultiplesOf80Table[EAX*4]
|
||||||
|
```
|
||||||
|
|
||||||
but is close and takes a great deal less space.
|
but is close and takes a great deal less space.
|
||||||
|
|
||||||
Of course, on the 386, the shift and add version could also be reduced
|
Of course, on the 386, the shift and add version could also be reduced
|
||||||
to this considerably more efficient code:
|
to this considerably more efficient code:
|
||||||
|
|
||||||
|
```nasm
|
||||||
SH LAX,4 ;*16
|
SH LAX,4 ;*16
|
||||||
MOV BX,AX
|
MOV BX,AX
|
||||||
SHL AX,2 ;*64
|
SHL AX,2 ;*64
|
||||||
ADD AX,BX ;*80
|
ADD AX,BX ;*80
|
||||||
|
```
|
||||||
|
|
||||||
#### Speeding Up Multiplication {#Heading5}
|
#### Speeding Up Multiplication {#Heading5}
|
||||||
|
|
||||||
|
|
|
||||||
2
09-03.md
2
09-03.md
|
|
@ -40,6 +40,7 @@ using **REPZ CMPS** to check scanning matches.
|
||||||
|
|
||||||
**LISTING 9.1 L9-1.ASM**
|
**LISTING 9.1 L9-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to sca"n
|
; Searches a text buffer for a text string. Uses REPNZ SCASB to sca"n
|
||||||
; the buffer for locations that match the first character of the
|
; the buffer for locations that match the first character of the
|
||||||
; searched-for string, then uses REPZ CMPS to check fully only those
|
; searched-for string, then uses REPZ CMPS to check fully only those
|
||||||
|
|
@ -131,3 +132,4 @@ using **REPZ CMPS** to check scanning matches.
|
||||||
ret
|
ret
|
||||||
_FindStringendp
|
_FindStringendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
4
09-04.md
4
09-04.md
|
|
@ -12,6 +12,7 @@ pages: 178-180
|
||||||
|
|
||||||
**LISTING 9.2 L9-2.ASM**
|
**LISTING 9.2 L9-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
||||||
; the buffer for locations that match a specified character of the
|
; the buffer for locations that match a specified character of the
|
||||||
; searched-for string, then uses REPZ CMPS to check fully only those
|
; searched-for string, then uses REPZ CMPS to check fully only those
|
||||||
|
|
@ -108,9 +109,11 @@ pages: 178-180
|
||||||
ret
|
ret
|
||||||
_FindStringendp
|
_FindStringendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 9.3 L9-3.C**
|
**LISTING 9.3 L9-3.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -143,3 +146,4 @@ pages: 178-180
|
||||||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
3
09-05.md
3
09-05.md
|
|
@ -43,7 +43,7 @@ one.
|
||||||
|
|
||||||
**LISTING 9.4 L9-4.ASM**
|
**LISTING 9.4 L9-4.ASM**
|
||||||
|
|
||||||
.
|
```nasm
|
||||||
;--------------------------------------------------------------------------
|
;--------------------------------------------------------------------------
|
||||||
; Sorts an array of ints. C callable (small model). 25 bytes.
|
; Sorts an array of ints. C callable (small model). 25 bytes.
|
||||||
; void sort( int num, int a[] );
|
; void sort( int num, int a[] );
|
||||||
|
|
@ -75,6 +75,7 @@ one.
|
||||||
ret
|
ret
|
||||||
|
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
#### Full 32-Bit Division {#Heading8}
|
#### Full 32-Bit Division {#Heading8}
|
||||||
|
|
||||||
|
|
|
||||||
4
09-06.md
4
09-06.md
|
|
@ -12,6 +12,7 @@ pages: 182-185
|
||||||
|
|
||||||
**LISTING 9.5 L9-5.ASM**
|
**LISTING 9.5 L9-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Divides an arbitrarily long unsigned dividend by a 16-bit unsigned
|
; Divides an arbitrarily long unsigned dividend by a 16-bit unsigned
|
||||||
; divisor. C near-callable as:
|
; divisor. C near-callable as:
|
||||||
; unsigned int Div(unsigned int * Dividend,
|
; unsigned int Div(unsigned int * Dividend,
|
||||||
|
|
@ -78,9 +79,11 @@ pages: 182-185
|
||||||
ret
|
ret
|
||||||
_Divendp
|
_Divendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 9.6 L9-6.C**
|
**LISTING 9.6 L9-6.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Sample use of Div function to perform division when the result
|
/* Sample use of Div function to perform division when the result
|
||||||
doesn't fit in 16 bits */
|
doesn't fit in 16 bits */
|
||||||
|
|
||||||
|
|
@ -97,6 +100,7 @@ pages: 182-185
|
||||||
k = Div((unsigned int *)&i, sizeof(i), j, (unsigned int *)&m);
|
k = Div((unsigned int *)&i, sizeof(i), j, (unsigned int *)&m);
|
||||||
printf("%lu / %u = %lu r %u\n", i, j, m, k);
|
printf("%lu / %u = %lu r %u\n", i, j, m, k);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Sweet Spot Revisited {#Heading9}
|
#### Sweet Spot Revisited {#Heading9}
|
||||||
|
|
||||||
|
|
|
||||||
12
09-07.md
12
09-07.md
|
|
@ -52,11 +52,13 @@ PTR 1000:5**, but you'd be wrong. That won't even assemble. You might
|
||||||
then think to construct in memory a far pointer containing 1000:5, as in
|
then think to construct in memory a far pointer containing 1000:5, as in
|
||||||
the following:
|
the following:
|
||||||
|
|
||||||
|
```nasm
|
||||||
Ptr dd ?
|
Ptr dd ?
|
||||||
:
|
:
|
||||||
mov word ptr [Ptr],5
|
mov word ptr [Ptr],5
|
||||||
mov word ptr [Ptr+2],1000h
|
mov word ptr [Ptr+2],1000h
|
||||||
jmp [Ptr]
|
jmp [Ptr]
|
||||||
|
```
|
||||||
|
|
||||||
That will work, but at a price in performance. On an 8088, **JMP DWORD
|
That will work, but at a price in performance. On an 8088, **JMP DWORD
|
||||||
PTR [*mem*]** (an indirect far jump) takes at least 37 cycles; **JMP
|
PTR [*mem*]** (an indirect far jump) takes at least 37 cycles; **JMP
|
||||||
|
|
@ -87,6 +89,7 @@ solution; if you have one, pass it along.
|
||||||
|
|
||||||
**LISTING 9.7 L9-7.ASM**
|
**LISTING 9.7 L9-7.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to perform a direct far jump to address 1000:5.
|
; Program to perform a direct far jump to address 1000:5.
|
||||||
; *** Do not run this program! It's just an example of how ***
|
; *** Do not run this program! It's just an example of how ***
|
||||||
; *** to build a direct far jump to an absolute address ***
|
; *** to build a direct far jump to an absolute address ***
|
||||||
|
|
@ -103,6 +106,7 @@ solution; if you have one, pass it along.
|
||||||
start:
|
start:
|
||||||
jmp FarLabel
|
jmp FarLabel
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
||||||
By the way, if you're wondering how I figured this out, I merely applied
|
By the way, if you're wondering how I figured this out, I merely applied
|
||||||
my good friend Dan Illowsky's long-standing rule for dealing with MASM:
|
my good friend Dan Illowsky's long-standing rule for dealing with MASM:
|
||||||
|
|
@ -116,12 +120,16 @@ something that does—a rule with plenty of history on its side.
|
||||||
To finish up this chapter, consider these two items. First, in 32-bit
|
To finish up this chapter, consider these two items. First, in 32-bit
|
||||||
protected mode,
|
protected mode,
|
||||||
|
|
||||||
|
```nasm
|
||||||
sub eax,eax
|
sub eax,eax
|
||||||
inc eax
|
inc eax
|
||||||
|
```
|
||||||
|
|
||||||
takes 4 cycles to execute, but is only 3 bytes long, while
|
takes 4 cycles to execute, but is only 3 bytes long, while
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov eax,1
|
mov eax,1
|
||||||
|
```
|
||||||
|
|
||||||
takes only 2 cycles to execute, but is 5 bytes long (because native mode
|
takes only 2 cycles to execute, but is 5 bytes long (because native mode
|
||||||
constants are dwords and the **MOV** instruction doesn't sign-extend).
|
constants are dwords and the **MOV** instruction doesn't sign-extend).
|
||||||
|
|
@ -129,11 +137,15 @@ Both code fragments are ways to set **EAX** to 1 (although the first
|
||||||
affects the flags and the second doesn't); this is a classic trade-off
|
affects the flags and the second doesn't); this is a classic trade-off
|
||||||
of speed for space. Second,
|
of speed for space. Second,
|
||||||
|
|
||||||
|
```nasm
|
||||||
or ebx,-1
|
or ebx,-1
|
||||||
|
```
|
||||||
|
|
||||||
takes 2 cycles to execute and is 3 bytes long, while
|
takes 2 cycles to execute and is 3 bytes long, while
|
||||||
|
|
||||||
|
```nasm
|
||||||
move bx,-1
|
move bx,-1
|
||||||
|
```
|
||||||
|
|
||||||
takes 2 cycles to execute and is 5 bytes long. Both instructions set
|
takes 2 cycles to execute and is 5 bytes long. Both instructions set
|
||||||
**EBX** to -1; this is a classic trade-off of—gee, it's not a trade-off
|
**EBX** to -1; this is a classic trade-off of—gee, it's not a trade-off
|
||||||
|
|
|
||||||
4
10-02.md
4
10-02.md
|
|
@ -64,6 +64,7 @@ default optimization was used. All times measured with the Zen timer
|
||||||
|
|
||||||
**LISTING 10.1 L10-1.C**
|
**LISTING 10.1 L10-1.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Finds and returns the greatest common divisor of two positive
|
/* Finds and returns the greatest common divisor of two positive
|
||||||
integers. Works by trying every integral divisor between the
|
integers. Works by trying every integral divisor between the
|
||||||
smaller of the two integers and 1, until a divisor that divides
|
smaller of the two integers and 1, until a divisor that divides
|
||||||
|
|
@ -86,6 +87,7 @@ default optimization was used. All times measured with the Zen timer
|
||||||
;
|
;
|
||||||
return(trial_divisor);
|
return(trial_divisor);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Wasted Breakthroughs {#Heading5}
|
#### Wasted Breakthroughs {#Heading5}
|
||||||
|
|
||||||
|
|
@ -99,6 +101,7 @@ in Listing 10.2.
|
||||||
|
|
||||||
**LISTING 10.2 L10-2.C**
|
**LISTING 10.2 L10-2.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Finds and returns the greatest common divisor of two positive
|
/* Finds and returns the greatest common divisor of two positive
|
||||||
integers. Works by subtracting the smaller integer from the
|
integers. Works by subtracting the smaller integer from the
|
||||||
larger integer until either the values match (in which case
|
larger integer until either the values match (in which case
|
||||||
|
|
@ -128,3 +131,4 @@ in Listing 10.2.
|
||||||
/* Now recursively call this function to continue the process */
|
/* Now recursively call this function to continue the process */
|
||||||
return(gcd(int1, int2));
|
return(gcd(int1, int2));
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
4
10-03.md
4
10-03.md
|
|
@ -45,6 +45,7 @@ Figure 10.3. Listing 10.3 is an implementation of Euclid's algorithm.
|
||||||
|
|
||||||
**LISTING 10.3 L10-3.C**
|
**LISTING 10.3 L10-3.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Finds and returns the greatest common divisor of two integers.
|
/* Finds and returns the greatest common divisor of two integers.
|
||||||
Uses Euclid's algorithm: divides the larger integer by the
|
Uses Euclid's algorithm: divides the larger integer by the
|
||||||
smaller; if the remainder is 0, the smaller integer is the GCD,
|
smaller; if the remainder is 0, the smaller integer is the GCD,
|
||||||
|
|
@ -88,6 +89,7 @@ Figure 10.3. Listing 10.3 is an implementation of Euclid's algorithm.
|
||||||
continue the process */
|
continue the process */
|
||||||
return(gcd_recurs(smaller_int, temp));
|
return(gcd_recurs(smaller_int, temp));
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
As you can see from Table 10.1, Euclid's algorithm is superior,
|
As you can see from Table 10.1, Euclid's algorithm is superior,
|
||||||
especially for large numbers (and imagine if we were working with large
|
especially for large numbers (and imagine if we were working with large
|
||||||
|
|
@ -129,6 +131,7 @@ recursive operations that Listing 10.3 does.
|
||||||
|
|
||||||
**LISTING 10.4 L10-4.C**
|
**LISTING 10.4 L10-4.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Finds and returns the greatest common divisor of two integers.
|
/* Finds and returns the greatest common divisor of two integers.
|
||||||
Uses Euclid's algorithm: divides the larger integer by the
|
Uses Euclid's algorithm: divides the larger integer by the
|
||||||
smaller; if the remainder is 0, the smaller integer is the GCD,
|
smaller; if the remainder is 0, the smaller integer is the GCD,
|
||||||
|
|
@ -161,6 +164,7 @@ recursive operations that Listing 10.3 does.
|
||||||
int2 = temp;
|
int2 = temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Patient Optimization {#Heading7}
|
#### Patient Optimization {#Heading7}
|
||||||
|
|
||||||
|
|
|
||||||
2
10-04.md
2
10-04.md
|
|
@ -12,6 +12,7 @@ pages: 200-203
|
||||||
|
|
||||||
**LISTING 10.5 L10-5.ASM**
|
**LISTING 10.5 L10-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Finds and returns the greatest common divisor of two integers.
|
; Finds and returns the greatest common divisor of two integers.
|
||||||
; Uses Euclid's algorithm: divides the larger integer by the
|
; Uses Euclid's algorithm: divides the larger integer by the
|
||||||
; smaller; if the remainder is 0, the smaller integer is the GCD,
|
; smaller; if the remainder is 0, the smaller integer is the GCD,
|
||||||
|
|
@ -99,6 +100,7 @@ pages: 200-203
|
||||||
ret
|
ret
|
||||||
_gcd endp
|
_gcd endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Assembly language optimization is pattern matching on a local scale.
|
Assembly language optimization is pattern matching on a local scale.
|
||||||
Frankly, it's also the sort of boring, brute-force work that people are
|
Frankly, it's also the sort of boring, brute-force work that people are
|
||||||
|
|
|
||||||
4
11-01.md
4
11-01.md
|
|
@ -97,13 +97,17 @@ than plain old registers, and can't be set to arbitrary values. That
|
||||||
means that segments can't be used for temporary storage or as part of a
|
means that segments can't be used for temporary storage or as part of a
|
||||||
fast indivisible 32-bit load from memory, as in
|
fast indivisible 32-bit load from memory, as in
|
||||||
|
|
||||||
|
```nasm
|
||||||
les ax,dword ptr [LongVar]
|
les ax,dword ptr [LongVar]
|
||||||
mov dx,es
|
mov dx,es
|
||||||
|
```
|
||||||
|
|
||||||
which loads **LongVar** into DX:AX faster than this:
|
which loads **LongVar** into DX:AX faster than this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ax,word ptr [LongVar]
|
mov ax,word ptr [LongVar]
|
||||||
mov dx,word ptr [LongVar+2]
|
mov dx,word ptr [LongVar+2]
|
||||||
|
```
|
||||||
|
|
||||||
Protected mode uses those altered segment registers to offer access to a
|
Protected mode uses those altered segment registers to offer access to a
|
||||||
great deal more memory than real mode: The 286 supports 16 megabytes of
|
great deal more memory than real mode: The 286 supports 16 megabytes of
|
||||||
|
|
|
||||||
2
11-02.md
2
11-02.md
|
|
@ -126,6 +126,7 @@ shortly.)
|
||||||
|
|
||||||
**LISTING 11.1 L11-1.ASM**
|
**LISTING 11.1 L11-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; *** Listing 11.1 ***
|
; *** Listing 11.1 ***
|
||||||
;
|
;
|
||||||
|
|
@ -145,6 +146,7 @@ shortly.)
|
||||||
mov [WordVar],0
|
mov [WordVar],0
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
What does this mean? It means that, practically speaking, the 286 as
|
What does this mean? It means that, practically speaking, the 286 as
|
||||||
used in the AT doesn't have a 16-bit bus. From a performance
|
used in the AT doesn't have a 16-bit bus. From a performance
|
||||||
|
|
|
||||||
4
11-03.md
4
11-03.md
|
|
@ -101,6 +101,7 @@ refresh.
|
||||||
|
|
||||||
**LISTING 11.2 L11-2.ASM**
|
**LISTING 11.2 L11-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; *** Listing 11.2 ***
|
; *** Listing 11.2 ***
|
||||||
;
|
;
|
||||||
|
|
@ -118,6 +119,7 @@ refresh.
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rep movsw
|
rep movsw
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
On the other hand, Listing 11.3, which is exactly the same as Listing
|
On the other hand, Listing 11.3, which is exactly the same as Listing
|
||||||
11.2 save that the memory accesses are word-aligned (start at even
|
11.2 save that the memory accesses are word-aligned (start at even
|
||||||
|
|
@ -128,6 +130,7 @@ we predicted.
|
||||||
|
|
||||||
**LISTING 11.3 L11-3.ASM**
|
**LISTING 11.3 L11-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; *** Listing 11.3 ***
|
; *** Listing 11.3 ***
|
||||||
;
|
;
|
||||||
|
|
@ -144,6 +147,7 @@ we predicted.
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rep movsw
|
rep movsw
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
The data alignment cycle-eater has intriguing implications for speeding
|
The data alignment cycle-eater has intriguing implications for speeding
|
||||||
up 286/386 code. The expenditure of a little care and a few bytes to
|
up 286/386 code. The expenditure of a little care and a few bytes to
|
||||||
|
|
|
||||||
7
11-04.md
7
11-04.md
|
|
@ -15,12 +15,13 @@ AT clone to verify the basic functionality of the timer by measuring the
|
||||||
performance of simple instruction sequences. I was cruising along with
|
performance of simple instruction sequences. I was cruising along with
|
||||||
no problems until I timed the following code:
|
no problems until I timed the following code:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov cx,1000
|
mov cx,1000
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
LoopTop:
|
LoopTop:
|
||||||
loop LoopTop
|
loop LoopTop
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -39,12 +40,14 @@ resided at the start of the next word-aligned word.
|
||||||
One simple change brought the execution time down to a reasonable 12.5
|
One simple change brought the execution time down to a reasonable 12.5
|
||||||
cycles per loop:
|
cycles per loop:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov cx,1000
|
mov cx,1000
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
even
|
even
|
||||||
LoopTop:
|
LoopTop:
|
||||||
loop LoopTop
|
loop LoopTop
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
While word-aligning branch destinations can improve branching
|
While word-aligning branch destinations can improve branching
|
||||||
performance, it's a nuisance and can increase code size a good deal, so
|
performance, it's a nuisance and can increase code size a good deal, so
|
||||||
|
|
@ -60,9 +63,11 @@ branch destination.
|
||||||
I recommend that you only go out of your way to word-align the start
|
I recommend that you only go out of your way to word-align the start
|
||||||
offsets of your subroutines, as in:
|
offsets of your subroutines, as in:
|
||||||
|
|
||||||
|
```nasm
|
||||||
even
|
even
|
||||||
FindChar proc near
|
FindChar proc near
|
||||||
:
|
:
|
||||||
|
```
|
||||||
|
|
||||||
In my experience, this simple practice is the one form of code alignment
|
In my experience, this simple practice is the one form of code alignment
|
||||||
that consistently provides a reasonable return for bytes and effort
|
that consistently provides a reasonable return for bytes and effort
|
||||||
|
|
|
||||||
2
11-06.md
2
11-06.md
|
|
@ -113,6 +113,7 @@ Theory confirmed.
|
||||||
|
|
||||||
**LISTING 11.4 L11-4.ASM**
|
**LISTING 11.4 L11-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; *** Listing 11.4 ***
|
; *** Listing 11.4 ***
|
||||||
;
|
;
|
||||||
|
|
@ -125,3 +126,4 @@ Theory confirmed.
|
||||||
add dx,100h
|
add dx,100h
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
|
||||||
2
11-07.md
2
11-07.md
|
|
@ -12,6 +12,7 @@ pages: 224-226
|
||||||
|
|
||||||
**LISTING 11.5 L11-5.ASM**
|
**LISTING 11.5 L11-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; *** Listing 11.5 ***
|
; *** Listing 11.5 ***
|
||||||
;
|
;
|
||||||
|
|
@ -31,6 +32,7 @@ pages: 224-226
|
||||||
add [WordVar]100h
|
add [WordVar]100h
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
What's going on? Simply this: Instruction fetching is controlling
|
What's going on? Simply this: Instruction fetching is controlling
|
||||||
overall execution time on *both* processors. Both the 8088 in a PC and
|
overall execution time on *both* processors. Both the 8088 in a PC and
|
||||||
|
|
|
||||||
8
11-08.md
8
11-08.md
|
|
@ -43,6 +43,7 @@ a segment and an offset. We'll also branch backward so that the address
|
||||||
pushed on the stack will point to the instruction we want to continue
|
pushed on the stack will point to the instruction we want to continue
|
||||||
with. The code works out like this:
|
with. The code works out like this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
jmpshort popfskip
|
jmpshort popfskip
|
||||||
popfiret:
|
popfiret:
|
||||||
iret; branches to the instruction after the
|
iret; branches to the instruction after the
|
||||||
|
|
@ -59,6 +60,7 @@ with. The code works out like this:
|
||||||
; the word that was on top of the stack when JMP SHORT POPFSKIP
|
; the word that was on top of the stack when JMP SHORT POPFSKIP
|
||||||
; was reached has been popped into the FLAGS register, just as
|
; was reached has been popped into the FLAGS register, just as
|
||||||
; if a POPF instruction had been executed.
|
; if a POPF instruction had been executed.
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -68,6 +70,7 @@ The **POPF** workaround can best be implemented as a macro; we can also
|
||||||
emulate a far call by pushing CS and performing a near call, thereby
|
emulate a far call by pushing CS and performing a near call, thereby
|
||||||
shrinking the workaround code by 1 byte:
|
shrinking the workaround code by 1 byte:
|
||||||
|
|
||||||
|
```nasm
|
||||||
EMULATE_POPF macro
|
EMULATE_POPF macro
|
||||||
local popfskip, popfiret
|
local popfskip, popfiret
|
||||||
jmp short popfskip
|
jmp short popfskip
|
||||||
|
|
@ -77,17 +80,20 @@ shrinking the workaround code by 1 byte:
|
||||||
push cs
|
push cs
|
||||||
call popfiret
|
call popfiret
|
||||||
endm
|
endm
|
||||||
|
```
|
||||||
|
|
||||||
By the way, the flags can be popped much more quickly if you're willing
|
By the way, the flags can be popped much more quickly if you're willing
|
||||||
to alter a register in the process. For example, the following macro
|
to alter a register in the process. For example, the following macro
|
||||||
emulates **POPF** with just one branch, but wipes out AX:
|
emulates **POPF** with just one branch, but wipes out AX:
|
||||||
|
|
||||||
|
```nasm
|
||||||
EMULATE_POPF_TRASH_AX macro
|
EMULATE_POPF_TRASH_AX macro
|
||||||
push cs
|
push cs
|
||||||
mov ax,offset $+5
|
mov ax,offset $+5
|
||||||
push ax
|
push ax
|
||||||
iret
|
iret
|
||||||
endm
|
endm
|
||||||
|
```
|
||||||
|
|
||||||
It's not a perfect substitute for **POPF**, since **POPF** doesn't alter
|
It's not a perfect substitute for **POPF**, since **POPF** doesn't alter
|
||||||
any registers, but it's faster and shorter than **EMULATE\_POPF** when
|
any registers, but it's faster and shorter than **EMULATE\_POPF** when
|
||||||
|
|
@ -96,6 +102,7 @@ you can use which is shorter still, alters no registers, and branches
|
||||||
just once. (Of course, this version of **EMULATE\_POPF** won't work on
|
just once. (Of course, this version of **EMULATE\_POPF** won't work on
|
||||||
an 8088.)
|
an 8088.)
|
||||||
|
|
||||||
|
```nasm
|
||||||
.286
|
.286
|
||||||
:
|
:
|
||||||
EMULATE_POPFmacro
|
EMULATE_POPFmacro
|
||||||
|
|
@ -103,6 +110,7 @@ an 8088.)
|
||||||
pushoffset $+4
|
pushoffset $+4
|
||||||
iret
|
iret
|
||||||
endm
|
endm
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
4
12-01.md
4
12-01.md
|
|
@ -128,14 +128,17 @@ matter what register is used.) **MOV AX, [BX+DI]** and **MOV CL,
|
||||||
|
|
||||||
As an example, you might adhere to this rule by replacing the code
|
As an example, you might adhere to this rule by replacing the code
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
add ax,[bx+si]
|
add ax,[bx+si]
|
||||||
add si,2
|
add si,2
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
with this
|
with this
|
||||||
|
|
||||||
|
```nasm
|
||||||
add si,bx
|
add si,bx
|
||||||
LoopTop:
|
LoopTop:
|
||||||
add ax,[si]
|
add ax,[si]
|
||||||
|
|
@ -143,3 +146,4 @@ with this
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
sub si,bx
|
sub si,bx
|
||||||
|
```
|
||||||
|
|
|
||||||
12
12-02.md
12
12-02.md
|
|
@ -58,8 +58,10 @@ isn't known until the instruction starts, and that's exactly the case
|
||||||
when the preceding instruction modifies one of the target instruction's
|
when the preceding instruction modifies one of the target instruction's
|
||||||
addressing registers. For example, in the code
|
addressing registers. For example, in the code
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV BX,OFFSET MemVar
|
MOV BX,OFFSET MemVar
|
||||||
MOV AX,[BX]
|
MOV AX,[BX]
|
||||||
|
```
|
||||||
|
|
||||||
there's no way that the 486 can calculate the address referenced by
|
there's no way that the 486 can calculate the address referenced by
|
||||||
**MOV AX,[BX]** until **MOV BX,OFFSET MemVar** finishes, so pipelining
|
**MOV AX,[BX]** until **MOV BX,OFFSET MemVar** finishes, so pipelining
|
||||||
|
|
@ -68,19 +70,23 @@ rearranging your code so that at least one instruction lies between the
|
||||||
loading of the memory pointer and its use. For example,
|
loading of the memory pointer and its use. For example,
|
||||||
postdecrementing, as in the following
|
postdecrementing, as in the following
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
add ax,[si]
|
add ax,[si]
|
||||||
add si,2
|
add si,2
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
is faster than preincrementing, as in:
|
is faster than preincrementing, as in:
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
add si,2
|
add si,2
|
||||||
add ax,[SI]
|
add ax,[SI]
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
Now that we understand what Intel means by this rule, let me make a very
|
Now that we understand what Intel means by this rule, let me make a very
|
||||||
important comment: My observations indicate that for real-mode code, the
|
important comment: My observations indicate that for real-mode code, the
|
||||||
|
|
@ -108,28 +114,34 @@ instructions, but, because some 486 instructions take more than 1 cycle,
|
||||||
the 2 are not always equivalent) before it's used to point to memory, 1
|
the 2 are not always equivalent) before it's used to point to memory, 1
|
||||||
cycle is lost. Therefore, whereas this code
|
cycle is lost. Therefore, whereas this code
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bx,offset MemVar
|
mov bx,offset MemVar
|
||||||
mov ax,[bx]
|
mov ax,[bx]
|
||||||
inc dx
|
inc dx
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
loses two cycles from interrupting the address calculation pipeline,
|
loses two cycles from interrupting the address calculation pipeline,
|
||||||
this code
|
this code
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bx,offset MemVar
|
mov bx,offset MemVar
|
||||||
inc dx
|
inc dx
|
||||||
mov ax,[bx]
|
mov ax,[bx]
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
loses only one cycle, and this code
|
loses only one cycle, and this code
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bx,offset MemVar
|
mov bx,offset MemVar
|
||||||
inc dx
|
inc dx
|
||||||
dec cx
|
dec cx
|
||||||
mov ax,[bx]
|
mov ax,[bx]
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
loses no cycles at all. Apparently, the 486's addressing calculation
|
loses no cycles at all. Apparently, the 486's addressing calculation
|
||||||
pipeline actually starts 2 cycles ahead, as shown in Figure 12.2. (In
|
pipeline actually starts 2 cycles ahead, as shown in Figure 12.2. (In
|
||||||
|
|
|
||||||
14
12-03.md
14
12-03.md
|
|
@ -41,22 +41,26 @@ pops up only spottily when the stack pointer is involved.
|
||||||
|
|
||||||
For example, you'd certainly expect a sequence such as
|
For example, you'd certainly expect a sequence such as
|
||||||
|
|
||||||
|
```nasm
|
||||||
:
|
:
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
pop ax
|
pop ax
|
||||||
et
|
et
|
||||||
:
|
:
|
||||||
|
```
|
||||||
|
|
||||||
to exhibit the addressing pipeline interruption phenomenon (SP is both
|
to exhibit the addressing pipeline interruption phenomenon (SP is both
|
||||||
destination and addressing register for both instructions, according to
|
destination and addressing register for both instructions, according to
|
||||||
Intel), but this code runs in six cycles per **POP/RET** pair, matching
|
Intel), but this code runs in six cycles per **POP/RET** pair, matching
|
||||||
the official execution times exactly. Likewise, a sequence like
|
the official execution times exactly. Likewise, a sequence like
|
||||||
|
|
||||||
|
```nasm
|
||||||
pop dx
|
pop dx
|
||||||
pop cx
|
pop cx
|
||||||
pop bx
|
pop bx
|
||||||
pop ax
|
pop ax
|
||||||
|
```
|
||||||
|
|
||||||
runs in one cycle per instruction, just as it should.
|
runs in one cycle per instruction, just as it should.
|
||||||
|
|
||||||
|
|
@ -65,15 +69,19 @@ destination—for example, to deallocate local variables—and then using
|
||||||
**PUSH**, **POP**, or **RET**, definitely can interrupt the addressing
|
**PUSH**, **POP**, or **RET**, definitely can interrupt the addressing
|
||||||
pipeline. For example
|
pipeline. For example
|
||||||
|
|
||||||
|
```nasm
|
||||||
add sp,10h
|
add sp,10h
|
||||||
ret
|
ret
|
||||||
|
```
|
||||||
|
|
||||||
loses two cycles because SP is the explicit destination of one
|
loses two cycles because SP is the explicit destination of one
|
||||||
instruction and then the implied addressing register for the next, and
|
instruction and then the implied addressing register for the next, and
|
||||||
the sequence
|
the sequence
|
||||||
|
|
||||||
|
```nasm
|
||||||
add sp,10h
|
add sp,10h
|
||||||
pop ax
|
pop ax
|
||||||
|
```
|
||||||
|
|
||||||
loses two cycles for the same reason.
|
loses two cycles for the same reason.
|
||||||
|
|
||||||
|
|
@ -101,22 +109,26 @@ during the next instruction.
|
||||||
|
|
||||||
So, for example, it would be a bad idea to do this
|
So, for example, it would be a bad idea to do this
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ah,o
|
mov ah,o
|
||||||
:
|
:
|
||||||
mov cx,[MemVar1]
|
mov cx,[MemVar1]
|
||||||
mov al,[MemVar2]
|
mov al,[MemVar2]
|
||||||
add cx,ax
|
add cx,ax
|
||||||
|
```
|
||||||
|
|
||||||
because AL is loaded by one instruction, then AX is used as the source
|
because AL is loaded by one instruction, then AX is used as the source
|
||||||
register for the next instruction. A cycle can be saved simply by
|
register for the next instruction. A cycle can be saved simply by
|
||||||
rearranging the instructions so that the byte register load isn't
|
rearranging the instructions so that the byte register load isn't
|
||||||
immediately followed by the word register usage, like so:
|
immediately followed by the word register usage, like so:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ah,o
|
mov ah,o
|
||||||
:
|
:
|
||||||
mov al,[MemVar2]
|
mov al,[MemVar2]
|
||||||
mov cx,[MemVar1]
|
mov cx,[MemVar1]
|
||||||
add cx,ax
|
add cx,ax
|
||||||
|
```
|
||||||
|
|
||||||
Strange as it may seem, this rule is neither arbitrary nor nonsensical.
|
Strange as it may seem, this rule is neither arbitrary nor nonsensical.
|
||||||
Basically, when a byte destination register is part of a word source
|
Basically, when a byte destination register is part of a word source
|
||||||
|
|
@ -135,10 +147,12 @@ works.
|
||||||
In case you're curious, there's no such penalty for the typical **XLAT**
|
In case you're curious, there's no such penalty for the typical **XLAT**
|
||||||
sequence like
|
sequence like
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bx,offset MemTable
|
mov bx,offset MemTable
|
||||||
:
|
:
|
||||||
mov al,[si]
|
mov al,[si]
|
||||||
xlat
|
xlat
|
||||||
|
```
|
||||||
|
|
||||||
even though AL must be converted to a word by **XLAT** before it can be
|
even though AL must be converted to a word by **XLAT** before it can be
|
||||||
added to BX and used to address memory. In fact, none of the penalties
|
added to BX and used to address memory. In fact, none of the penalties
|
||||||
|
|
|
||||||
13
12-04.md
13
12-04.md
|
|
@ -24,9 +24,11 @@ This, the last of this chapter's rules, is the strangest of the lot. If
|
||||||
any byte register is loaded, and then two cycles later any register is
|
any byte register is loaded, and then two cycles later any register is
|
||||||
used to point to memory, one cycle is lost. So, for example, this code
|
used to point to memory, one cycle is lost. So, for example, this code
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov al,bl
|
mov al,bl
|
||||||
mov cx,dx
|
mov cx,dx
|
||||||
mov si,[di]
|
mov si,[di]
|
||||||
|
```
|
||||||
|
|
||||||
takes four rather than the expected three cycles to execute. Note that
|
takes four rather than the expected three cycles to execute. Note that
|
||||||
it is *not* required that the byte register be part of the register used
|
it is *not* required that the byte register be part of the register used
|
||||||
|
|
@ -35,17 +37,20 @@ to address memory; any byte register will do the trick.
|
||||||
Worse still, loading byte registers both one and two cycles before a
|
Worse still, loading byte registers both one and two cycles before a
|
||||||
register is used to address memory costs two cycles, as in
|
register is used to address memory costs two cycles, as in
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bl,al
|
mov bl,al
|
||||||
mov cl,3
|
mov cl,3
|
||||||
mov bx,[si]
|
mov bx,[si]
|
||||||
|
```
|
||||||
which takes five rather than three cycles to run. However, there is *no*
|
which takes five rather than three cycles to run. However, there is *no*
|
||||||
penalty if a byte register is loaded one cycle but not two cycles before
|
penalty if a byte register is loaded one cycle but not two cycles before
|
||||||
a register is used to address memory. Therefore,
|
a register is used to address memory. Therefore,
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov cx,3
|
mov cx,3
|
||||||
mov dl,al
|
mov dl,al
|
||||||
mov si,[bx]
|
mov si,[bx]
|
||||||
|
```
|
||||||
|
|
||||||
runs in the expected three cycles.
|
runs in the expected three cycles.
|
||||||
|
|
||||||
|
|
@ -57,9 +62,11 @@ its interaction with the other rules—could lead to considerable
|
||||||
performance loss in seemingly air-tight code. For instance, a casual
|
performance loss in seemingly air-tight code. For instance, a casual
|
||||||
observer would expect the following code to run in 3 cycles:
|
observer would expect the following code to run in 3 cycles:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bx,offset MemVar
|
mov bx,offset MemVar
|
||||||
mov cl,al
|
mov cl,al
|
||||||
mov ax,[bx]
|
mov ax,[bx]
|
||||||
|
```
|
||||||
|
|
||||||
A more sophisticated programmer would expect to lose one cycle, because
|
A more sophisticated programmer would expect to lose one cycle, because
|
||||||
BX is loaded two cycles before being used to address memory. In fact,
|
BX is loaded two cycles before being used to address memory. In fact,
|
||||||
|
|
@ -97,6 +104,7 @@ addressing memory.
|
||||||
|
|
||||||
**LISTING 12.1 LST12-1.ASM**
|
**LISTING 12.1 LST12-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the effect of loading a byte register 2 cycles before
|
; Measures the effect of loading a byte register 2 cycles before
|
||||||
; using a register to address memory.
|
; using a register to address memory.
|
||||||
mov bp,2 ;run the test code twice to make sure
|
mov bp,2 ;run the test code twice to make sure
|
||||||
|
|
@ -114,9 +122,11 @@ addressing memory.
|
||||||
jz Done
|
jz Done
|
||||||
jmp CacheFillLoop
|
jmp CacheFillLoop
|
||||||
Done:
|
Done:
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 12.2 LST12-2.ASM**
|
**LISTING 12.2 LST12-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Measures the effect of loading a byte register 1 cycle before
|
; Measures the effect of loading a byte register 1 cycle before
|
||||||
; using a register to address memory.
|
; using a register to address memory.
|
||||||
mov bp,2 ;run the test code twice to make sure
|
mov bp,2 ;run the test code twice to make sure
|
||||||
|
|
@ -134,6 +144,7 @@ addressing memory.
|
||||||
jz Done
|
jz Done
|
||||||
jmp CacheFillLoop
|
jmp CacheFillLoop
|
||||||
Done:
|
Done:
|
||||||
|
```
|
||||||
|
|
||||||
Note that Listings 12.1 and 12.2 each repeat the timing of the code
|
Note that Listings 12.1 and 12.2 each repeat the timing of the code
|
||||||
under test a second time, to make sure that the instructions are in the
|
under test a second time, to make sure that the instructions are in the
|
||||||
|
|
|
||||||
4
13-01.md
4
13-01.md
|
|
@ -78,10 +78,12 @@ also lines, at a rate of three instructions for every two characters!
|
||||||
|
|
||||||
**LISTING 13.1 L13-1.ASM**
|
**LISTING 13.1 L13-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
mov bl,[di] ;get the state value for the pair
|
mov bl,[di] ;get the state value for the pair
|
||||||
add dx,[bx+8000h] ;increment word and line count
|
add dx,[bx+8000h] ;increment word and line count
|
||||||
; appropriately for the pair
|
; appropriately for the pair
|
||||||
|
```
|
||||||
|
|
||||||
Listing 13.1 looks as tight as it could be, with just two one-cycle
|
Listing 13.1 looks as tight as it could be, with just two one-cycle
|
||||||
instructions, one two-cycle instruction, and no branches. It *is* tight,
|
instructions, one two-cycle instruction, and no branches. It *is* tight,
|
||||||
|
|
@ -113,10 +115,12 @@ the intervening instruction takes two cycles, there's no penalty at all.
|
||||||
|
|
||||||
**LISTING 13.2 L13-2.ASM**
|
**LISTING 13.2 L13-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bl,[di] ;get the state value for the pair
|
mov bl,[di] ;get the state value for the pair
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
add dx,[bx+8000h] ;increment word and line count
|
add dx,[bx+8000h] ;increment word and line count
|
||||||
; appropriately for the pair
|
; appropriately for the pair
|
||||||
|
```
|
||||||
|
|
||||||
At this point, Terje had nearly doubled the performance of this code
|
At this point, Terje had nearly doubled the performance of this code
|
||||||
simply by moving one instruction. (Note that swapping the instructions
|
simply by moving one instruction. (Note that swapping the instructions
|
||||||
|
|
|
||||||
8
13-02.md
8
13-02.md
|
|
@ -22,18 +22,22 @@ throughput of two cycles/char."
|
||||||
|
|
||||||
**LISTING 13.3 L13-3.ASM**
|
**LISTING 13.3 L13-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bl,[di] ;get the state value for the pair
|
mov bl,[di] ;get the state value for the pair
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
mov ax,[bx+8000h] ;increment word and line count
|
mov ax,[bx+8000h] ;increment word and line count
|
||||||
add dx,ax ; appropriately for the pair
|
add dx,ax ; appropriately for the pair
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 13.4 L13-4.ASM**
|
**LISTING 13.4 L13-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bl,[di] ;get the state value for the pair
|
mov bl,[di] ;get the state value for the pair
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
add dx,ax ;increment word and line count
|
add dx,ax ;increment word and line count
|
||||||
; appropriately for the pair
|
; appropriately for the pair
|
||||||
mov ax,[bx+8000h] ;get increments for next time
|
mov ax,[bx+8000h] ;get increments for next time
|
||||||
|
```
|
||||||
|
|
||||||
I'd like to point out two fairly remarkable things. First, the single
|
I'd like to point out two fairly remarkable things. First, the single
|
||||||
cycle that Terje saved in Listing 13.4 sped up his entire word-counting
|
cycle that Terje saved in Listing 13.4 sped up his entire word-counting
|
||||||
|
|
@ -57,9 +61,11 @@ and 16-bit registers are not valid operands.) The obvious use of
|
||||||
first in memory, also called *little endian*) to Motorola format (most
|
first in memory, also called *little endian*) to Motorola format (most
|
||||||
significant byte first in memory, or *big endian*), like so:
|
significant byte first in memory, or *big endian*), like so:
|
||||||
|
|
||||||
|
```nasm
|
||||||
lodsd
|
lodsd
|
||||||
bswap
|
bswap
|
||||||
stosd
|
stosd
|
||||||
|
```
|
||||||
|
|
||||||
**BSWAP** can also be useful for reversing the order of pixel bits from
|
**BSWAP** can also be useful for reversing the order of pixel bits from
|
||||||
a bitmap so that they can be rotated 32 bits at a time with an
|
a bitmap so that they can be rotated 32 bits at a time with an
|
||||||
|
|
@ -94,6 +100,7 @@ memory, isn't it?
|
||||||
|
|
||||||
**LISTING 13.5 L13-5.ASM**
|
**LISTING 13.5 L13-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov cx,[initialskip]
|
mov cx,[initialskip]
|
||||||
shl ecx,16 ;put skip value in upper half of ECX
|
shl ecx,16 ;put skip value in upper half of ECX
|
||||||
mov cx,100 ;put loop count in CX
|
mov cx,100 ;put loop count in CX
|
||||||
|
|
@ -105,6 +112,7 @@ memory, isn't it?
|
||||||
ror ecx,16 ;put loop count in CX
|
ror ecx,16 ;put loop count in CX
|
||||||
dec cx ;count down loop
|
dec cx ;count down loop
|
||||||
jnz looptop
|
jnz looptop
|
||||||
|
```
|
||||||
|
|
||||||
Not necessarily. Shifts and rotates are among the worst performing
|
Not necessarily. Shifts and rotates are among the worst performing
|
||||||
instructions of the 486, taking 2 to 3 cycles to execute. Thus, it takes
|
instructions of the 486, taking 2 to 3 cycles to execute. Thus, it takes
|
||||||
|
|
|
||||||
8
13-03.md
8
13-03.md
|
|
@ -21,6 +21,7 @@ registers into one 32-bit register much more useful.
|
||||||
|
|
||||||
**LISTING 13.6 L13-6.ASM**
|
**LISTING 13.6 L13-6.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov cx,[initialskip]
|
mov cx,[initialskip]
|
||||||
bswap ecx ;put skip value in upper half of ECX
|
bswap ecx ;put skip value in upper half of ECX
|
||||||
mov cx,100 ;put loop count in CX
|
mov cx,100 ;put loop count in CX
|
||||||
|
|
@ -32,6 +33,7 @@ registers into one 32-bit register much more useful.
|
||||||
bswap ecx ;put loop count in CX
|
bswap ecx ;put loop count in CX
|
||||||
dec cx ;count down loop
|
dec cx ;count down loop
|
||||||
jnz looptop
|
jnz looptop
|
||||||
|
```
|
||||||
|
|
||||||
### Pushing and Popping Memory {#Heading5}
|
### Pushing and Popping Memory {#Heading5}
|
||||||
|
|
||||||
|
|
@ -44,12 +46,16 @@ contrast, loading a memory location into a register takes only one
|
||||||
cycle, and pushing a register takes just 1 more cycle, for a total of
|
cycle, and pushing a register takes just 1 more cycle, for a total of
|
||||||
two cycles. Therefore,
|
two cycles. Therefore,
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ax,[bx]
|
mov ax,[bx]
|
||||||
push ax
|
push ax
|
||||||
|
```
|
||||||
|
|
||||||
is twice as fast as
|
is twice as fast as
|
||||||
|
|
||||||
|
```nasm
|
||||||
push word ptr [bx]
|
push word ptr [bx]
|
||||||
|
```
|
||||||
|
|
||||||
and the only cost is that the previous contents of AX are destroyed.
|
and the only cost is that the previous contents of AX are destroyed.
|
||||||
|
|
||||||
|
|
@ -98,9 +104,11 @@ disassembly in a debugger or by having the assembler generate a listing
|
||||||
file. You could then insert the n-bit version of **SHL AX,1** in your
|
file. You could then insert the n-bit version of **SHL AX,1** in your
|
||||||
code as follows:
|
code as follows:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ax,1
|
mov ax,1
|
||||||
db 0c1h, 0e0h, 001h
|
db 0c1h, 0e0h, 001h
|
||||||
mov dx,ax
|
mov dx,ax
|
||||||
|
```
|
||||||
|
|
||||||
At the end of this sequence, DX will contain 2, and the fast n-bit
|
At the end of this sequence, DX will contain 2, and the fast n-bit
|
||||||
version of **SHL AX,1** will have executed. If you use this approach,
|
version of **SHL AX,1** will have executed. If you use this approach,
|
||||||
|
|
|
||||||
6
13-04.md
6
13-04.md
|
|
@ -17,7 +17,9 @@ register may serve as the base memory addressing register, and almost
|
||||||
any register may serve as the potentially scaled index register. For
|
any register may serve as the potentially scaled index register. For
|
||||||
example,
|
example,
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov al,BaseTable[ecx+edx*4]
|
mov al,BaseTable[ecx+edx*4]
|
||||||
|
```
|
||||||
|
|
||||||
uses a perfectly valid 32-bit address, with the byte accessed being the
|
uses a perfectly valid 32-bit address, with the byte accessed being the
|
||||||
one at the offset in DS pointed to by the sum of EDX times 4 plus the
|
one at the offset in DS pointed to by the sum of EDX times 4 plus the
|
||||||
|
|
@ -82,14 +84,17 @@ registers when they're needed, but if you find yourself using them
|
||||||
inside key loops, you should see if it's possible to move the index
|
inside key loops, you should see if it's possible to move the index
|
||||||
calculation outside the loop as, for example, in a loop like this:
|
calculation outside the loop as, for example, in a loop like this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
add ax,DataTable[ebx*2]
|
add ax,DataTable[ebx*2]
|
||||||
inc ebx
|
inc ebx
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
You could change this to the following for greater performance:
|
You could change this to the following for greater performance:
|
||||||
|
|
||||||
|
```nasm
|
||||||
add ebx,ebx ;ebx*2
|
add ebx,ebx ;ebx*2
|
||||||
LoopTop:
|
LoopTop:
|
||||||
add ax,DataTable[ebx]
|
add ax,DataTable[ebx]
|
||||||
|
|
@ -97,6 +102,7 @@ You could change this to the following for greater performance:
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
shr ebx,1 ;ebx*2/2
|
shr ebx,1 ;ebx*2/2
|
||||||
|
```
|
||||||
|
|
||||||
I'll end this chapter with two more quirks of 32-bit addressing. First,
|
I'll end this chapter with two more quirks of 32-bit addressing. First,
|
||||||
as with 16-bit addressing, addressing that uses EBP as a base register
|
as with 16-bit addressing, addressing that uses EBP as a base register
|
||||||
|
|
|
||||||
4
14-04.md
4
14-04.md
|
|
@ -12,6 +12,7 @@ pages: 268-271
|
||||||
|
|
||||||
**LISTING 14.1 L14-1.C**
|
**LISTING 14.1 L14-1.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Searches a buffer for a specified pattern. In case of a mismatch,
|
/* Searches a buffer for a specified pattern. In case of a mismatch,
|
||||||
uses the value of the mismatched byte to skip across as many
|
uses the value of the mismatched byte to skip across as many
|
||||||
potential match locations as possible (partial Boyer-Moore).
|
potential match locations as possible (partial Boyer-Moore).
|
||||||
|
|
@ -97,9 +98,11 @@ pages: 268-271
|
||||||
BufferPtr += Skip;
|
BufferPtr += Skip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 14.2 L14-2.C**
|
**LISTING 14.2 L14-2.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Program to exercise buffer-search routines in Listings 14.1 & 14.3.
|
/* Program to exercise buffer-search routines in Listings 14.1 & 14.3.
|
||||||
(Must be modified to put copy of pattern as sentinel at end of the
|
(Must be modified to put copy of pattern as sentinel at end of the
|
||||||
search buffer in order to be used with Listing 14.4.) */
|
search buffer in order to be used with Listing 14.4.) */
|
||||||
|
|
@ -154,6 +157,7 @@ pages: 268-271
|
||||||
}
|
}
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Well, architecture carries a lot of weight, but it sure as heck isn't
|
Well, architecture carries a lot of weight, but it sure as heck isn't
|
||||||
destiny. I had simply fallen into the trap of figuring that the
|
destiny. I had simply fallen into the trap of figuring that the
|
||||||
|
|
|
||||||
2
14-05.md
2
14-05.md
|
|
@ -12,6 +12,7 @@ pages: 271-274
|
||||||
|
|
||||||
**LISTING 14.3 L14-3.ASM**
|
**LISTING 14.3 L14-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||||
; uses the value of the mismatched byte to skip across as many
|
; uses the value of the mismatched byte to skip across as many
|
||||||
; potential match locations as possible (partial Boyer-Moore).
|
; potential match locations as possible (partial Boyer-Moore).
|
||||||
|
|
@ -172,3 +173,4 @@ pages: 271-274
|
||||||
ret
|
ret
|
||||||
_FindString endp
|
_FindString endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
2
14-06.md
2
14-06.md
|
|
@ -35,6 +35,7 @@ about 60 percent faster than Listing 14.3.
|
||||||
|
|
||||||
**LISTING 14.4 L14-4.ASM**
|
**LISTING 14.4 L14-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||||
; uses the value of the mismatched byte to skip across as many
|
; uses the value of the mismatched byte to skip across as many
|
||||||
; potential match locations as possible (partial Boyer-Moore).
|
; potential match locations as possible (partial Boyer-Moore).
|
||||||
|
|
@ -181,6 +182,7 @@ about 60 percent faster than Listing 14.3.
|
||||||
ret
|
ret
|
||||||
_FindString endp
|
_FindString endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Note that Table 14.1 includes the time required to build the skip table
|
Note that Table 14.1 includes the time required to build the skip table
|
||||||
each time **FindString** is called. This time could be eliminated for
|
each time **FindString** is called. This time could be eliminated for
|
||||||
|
|
|
||||||
8
15-02.md
8
15-02.md
|
|
@ -12,6 +12,7 @@ pages: 284-287
|
||||||
|
|
||||||
**LISTING 15.1 L15-1.C**
|
**LISTING 15.1 L15-1.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Deletes the node in a linked list that follows the indicated node.
|
/* Deletes the node in a linked list that follows the indicated node.
|
||||||
Assumes list is headed by a dummy node, so no special testing for
|
Assumes list is headed by a dummy node, so no special testing for
|
||||||
the head-of-list pointer is required. Returns the same pointer
|
the head-of-list pointer is required. Returns the same pointer
|
||||||
|
|
@ -24,9 +25,11 @@ pages: 284-287
|
||||||
NodeToDeleteAfter->NextNode->NextNode;
|
NodeToDeleteAfter->NextNode->NextNode;
|
||||||
return(NodeToDeleteAfter);
|
return(NodeToDeleteAfter);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 15.2 LLIST.H**
|
**LISTING 15.2 LLIST.H**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Linked list header file. */
|
/* Linked list header file. */
|
||||||
#define MAX_TEXT_LENGTH 100 /* longest allowed Text field */
|
#define MAX_TEXT_LENGTH 100 /* longest allowed Text field */
|
||||||
#define SENTINEL 32767 /* largest possible Value field */
|
#define SENTINEL 32767 /* largest possible Value field */
|
||||||
|
|
@ -42,9 +45,11 @@ pages: 284-287
|
||||||
struct LinkNode *InitLinkedList(void);
|
struct LinkNode *InitLinkedList(void);
|
||||||
struct LinkNode *InsertNodeSorted(struct LinkNode *,
|
struct LinkNode *InsertNodeSorted(struct LinkNode *,
|
||||||
struct LinkNode *);
|
struct LinkNode *);
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 15.3 L15-3.C**
|
**LISTING 15.3 L15-3.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Deletes the node in the specified linked list that follows the
|
/* Deletes the node in the specified linked list that follows the
|
||||||
indicated node. List is headed by a head-of-list pointer; if the
|
indicated node. List is headed by a head-of-list pointer; if the
|
||||||
pointer to the node to delete after points to the head-of-list
|
pointer to the node to delete after points to the head-of-list
|
||||||
|
|
@ -63,6 +68,7 @@ pages: 284-287
|
||||||
}
|
}
|
||||||
return(NodeToDeleteAfter);
|
return(NodeToDeleteAfter);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
However, it is true that if you're going to store a variety of types of
|
However, it is true that if you're going to store a variety of types of
|
||||||
structures in your linked lists, you should start each node with the
|
structures in your linked lists, you should start each node with the
|
||||||
|
|
@ -123,6 +129,7 @@ value has to perform two tests in the inner loop, as shown in Listing
|
||||||
|
|
||||||
**LISTING 15.4 L15-4.C**
|
**LISTING 15.4 L15-4.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Finds the first node in a linked list with a value field greater
|
/* Finds the first node in a linked list with a value field greater
|
||||||
than or equal to a key value, and returns a pointer to the node
|
than or equal to a key value, and returns a pointer to the node
|
||||||
preceding that node (to facilitate insertion and deletion), or a
|
preceding that node (to facilitate insertion and deletion), or a
|
||||||
|
|
@ -145,6 +152,7 @@ value has to perform two tests in the inner loop, as shown in Listing
|
||||||
return(NodePtr); /* success; return pointer to node preceding
|
return(NodePtr); /* success; return pointer to node preceding
|
||||||
node that was >= */
|
node that was >= */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Suppose, however, that we make the tail node a *sentinel* by giving it a
|
Suppose, however, that we make the tail node a *sentinel* by giving it a
|
||||||
value that is guaranteed to terminate the search, as shown in Figure
|
value that is guaranteed to terminate the search, as shown in Figure
|
||||||
|
|
|
||||||
4
15-03.md
4
15-03.md
|
|
@ -12,6 +12,7 @@ pages: 287-290
|
||||||
|
|
||||||
**LISTING 15.5 L15-5.C**
|
**LISTING 15.5 L15-5.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Finds the first node in a value-sorted linked list that
|
/* Finds the first node in a value-sorted linked list that
|
||||||
has a Value field greater than or equal to a key value, and
|
has a Value field greater than or equal to a key value, and
|
||||||
returns a pointer to the node preceding that node (to facilitate
|
returns a pointer to the node preceding that node (to facilitate
|
||||||
|
|
@ -33,6 +34,7 @@ pages: 287-290
|
||||||
return(NodePtr); /* success; return pointer to node preceding
|
return(NodePtr); /* success; return pointer to node preceding
|
||||||
node that was >= */
|
node that was >= */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -80,6 +82,7 @@ before you write a single line of code.
|
||||||
|
|
||||||
**LISTING 15.6 L15-6.C**
|
**LISTING 15.6 L15-6.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Suite of functions for maintaining a linked list sorted by
|
/* Suite of functions for maintaining a linked list sorted by
|
||||||
ascending order of the Value field. The list is circular; that
|
ascending order of the Value field. The list is circular; that
|
||||||
is,it has a dummy node as both the head and the tail of the list.
|
is,it has a dummy node as both the head and the tail of the list.
|
||||||
|
|
@ -145,3 +148,4 @@ before you write a single line of code.
|
||||||
NodePtr->NextNode = NodeToInsert;
|
NodePtr->NextNode = NodeToInsert;
|
||||||
return(NodePtr);
|
return(NodePtr);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
6
15-04.md
6
15-04.md
|
|
@ -12,6 +12,7 @@ pages: 290-293
|
||||||
|
|
||||||
**LISTING 15.7 L15-7.ASM**
|
**LISTING 15.7 L15-7.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; C near-callable assembly function for inserting a new node in a
|
; C near-callable assembly function for inserting a new node in a
|
||||||
; linked list sorted by ascending order of the Value field. The list
|
; linked list sorted by ascending order of the Value field. The list
|
||||||
; is circular; that is, it has a dummy node as both the head and the
|
; is circular; that is, it has a dummy node as both the head and the
|
||||||
|
|
@ -71,9 +72,11 @@ pages: 290-293
|
||||||
ret
|
ret
|
||||||
_InsertNodeSorted endp
|
_InsertNodeSorted endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 15.8 L15-8.C**
|
**LISTING 15.8 L15-8.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Sample linked list program. Tested with Borland C++. */
|
/* Sample linked list program. Tested with Borland C++. */
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -155,6 +158,7 @@ pages: 290-293
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Hi/Lo in 24 Bytes {#Heading6}
|
### Hi/Lo in 24 Bytes {#Heading6}
|
||||||
|
|
||||||
|
|
@ -182,6 +186,7 @@ reads closely enough.
|
||||||
|
|
||||||
**LISTING 15.9 L15-9.ASM**
|
**LISTING 15.9 L15-9.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Find the greatest or smallest unsigned int.
|
; Find the greatest or smallest unsigned int.
|
||||||
; C callable (small model); 24 bytes.
|
; C callable (small model); 24 bytes.
|
||||||
; By David Stafford.
|
; By David Stafford.
|
||||||
|
|
@ -209,6 +214,7 @@ reads closely enough.
|
||||||
jnz top
|
jnz top
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
```
|
||||||
|
|
||||||
Before I end this chapter, let me say that I get a lot of feedback from
|
Before I end this chapter, let me say that I get a lot of feedback from
|
||||||
my readers, and it's much appreciated. Keep those cards, letters, and
|
my readers, and it's much appreciated. Keep those cards, letters, and
|
||||||
|
|
|
||||||
2
16-01.md
2
16-01.md
|
|
@ -98,6 +98,7 @@ timed from a RAM disk on a 20 MHz 386.
|
||||||
|
|
||||||
**LISTING 16.1 L16-1.C**
|
**LISTING 16.1 L16-1.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Word-counting program. Tested with Borland C++ in C
|
/* Word-counting program. Tested with Borland C++ in C
|
||||||
compilation mode and the small model. */
|
compilation mode and the small model. */
|
||||||
|
|
||||||
|
|
@ -170,3 +171,4 @@ timed from a RAM disk on a 20 MHz 386.
|
||||||
printf("\nTotal words in file: %lu\n", WordCount);
|
printf("\nTotal words in file: %lu\n", WordCount);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
4
16-02.md
4
16-02.md
|
|
@ -22,6 +22,7 @@ generates.
|
||||||
|
|
||||||
**LISTING 16.2 L16-2.C**
|
**LISTING 16.2 L16-2.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* Word-counting program incorporating assembly language. Tested
|
/* Word-counting program incorporating assembly language. Tested
|
||||||
with Borland C++ in C compilation mode & the small model. */
|
with Borland C++ in C compilation mode & the small model. */
|
||||||
|
|
||||||
|
|
@ -80,9 +81,11 @@ generates.
|
||||||
printf("\nTotal words in file: %lu\n", WordCount);
|
printf("\nTotal words in file: %lu\n", WordCount);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 16.3 L16-3.ASM**
|
**LISTING 16.3 L16-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
||||||
; length BufferLength, counting words and updating WordCount as
|
; length BufferLength, counting words and updating WordCount as
|
||||||
; appropriate. BufferLength must be > 0. *CharFlag and *WordCount
|
; appropriate. BufferLength must be > 0. *CharFlag and *WordCount
|
||||||
|
|
@ -163,6 +166,7 @@ generates.
|
||||||
ret
|
ret
|
||||||
_ScanBuffer endp
|
_ScanBuffer endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
#### Which Way to Go from Here? {#Heading4}
|
#### Which Way to Go from Here? {#Heading4}
|
||||||
|
|
||||||
|
|
|
||||||
3
16-03.md
3
16-03.md
|
|
@ -12,6 +12,7 @@ pages: 303-305
|
||||||
|
|
||||||
**LISTING 16.4 L16-4.ASM**
|
**LISTING 16.4 L16-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
||||||
; length BufferLength, counting words and updating WordCount as
|
; length BufferLength, counting words and updating WordCount as
|
||||||
; appropriate, using a lookup table-based approach. BufferLength
|
; appropriate, using a lookup table-based approach. BufferLength
|
||||||
|
|
@ -102,7 +103,7 @@ pages: 303-305
|
||||||
jmp Done
|
jmp Done
|
||||||
_ScanBuffer endp
|
_ScanBuffer endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Listing 16.4 features several interesting tricks. First, it uses
|
Listing 16.4 features several interesting tricks. First, it uses
|
||||||
**LODSB** and **XLAT** in succession, a very neat way to get a
|
**LODSB** and **XLAT** in succession, a very neat way to get a
|
||||||
|
|
|
||||||
4
16-04.md
4
16-04.md
|
|
@ -40,9 +40,9 @@ over Terje's 386 native-mode code, and found the critical inner loop,
|
||||||
which was indeed as tight as one could imagine, consisting of just a few
|
which was indeed as tight as one could imagine, consisting of just a few
|
||||||
386 native-mode instructions. However, one of the instructions was this:
|
386 native-mode instructions. However, one of the instructions was this:
|
||||||
|
|
||||||
|
```nasm
|
||||||
CMP DH,[EBX+EAX]
|
CMP DH,[EBX+EAX]
|
||||||
|
```
|
||||||
|
|
||||||
Harmless enough, save for two things. First, EBX happened to be zero at
|
Harmless enough, save for two things. First, EBX happened to be zero at
|
||||||
this point (a leftover from an earlier version of the code, as it turned
|
this point (a leftover from an earlier version of the code, as it turned
|
||||||
|
|
|
||||||
2
16-05.md
2
16-05.md
|
|
@ -36,6 +36,7 @@ Table: Table 16.2 The top four word-counting entries.
|
||||||
|
|
||||||
**LISTING 16.5 QSCAN3.ASM**
|
**LISTING 16.5 QSCAN3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; QSCAN3.ASM
|
; QSCAN3.ASM
|
||||||
; David Stafford
|
; David Stafford
|
||||||
|
|
||||||
|
|
@ -214,3 +215,4 @@ Table: Table 16.2 The top four word-counting entries.
|
||||||
.fardata WordTable
|
.fardata WordTable
|
||||||
include qscan3.inc ;built by MAKETAB
|
include qscan3.inc ;built by MAKETAB
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
2
16-07.md
2
16-07.md
|
|
@ -12,6 +12,7 @@ pages: 313-316
|
||||||
|
|
||||||
**Listing 16.6 OPT2.ASM**
|
**Listing 16.6 OPT2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
;
|
;
|
||||||
; Opt2 Final optimization word count
|
; Opt2 Final optimization word count
|
||||||
; Written by Michael Abrash
|
; Written by Michael Abrash
|
||||||
|
|
@ -116,6 +117,7 @@ pages: 313-316
|
||||||
ret
|
ret
|
||||||
_ScanBuffer endp
|
_ScanBuffer endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Level 2: A New Perspective {#Heading11}
|
### Level 2: A New Perspective {#Heading11}
|
||||||
|
|
||||||
|
|
|
||||||
6
16-08.md
6
16-08.md
|
|
@ -12,6 +12,7 @@ pages: 316-319
|
||||||
|
|
||||||
**Listing 16.7 L16-7.ASM**
|
**Listing 16.7 L16-7.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
ScanLoop:
|
ScanLoop:
|
||||||
lodsw ;get the next 2 bytes (AL = first, AH = 2nd)
|
lodsw ;get the next 2 bytes (AL = first, AH = 2nd)
|
||||||
xlat ;look up first's char/not status
|
xlat ;look up first's char/not status
|
||||||
|
|
@ -25,7 +26,7 @@ pages: 316-319
|
||||||
mov dl,al
|
mov dl,al
|
||||||
dec dx
|
dec dx
|
||||||
jnz ScanLoop
|
jnz ScanLoop
|
||||||
|
```
|
||||||
|
|
||||||
John later divides the transition count by two to get the word count.
|
John later divides the transition count by two to get the word count.
|
||||||
(Food for thought: It's also possible to use **CMP** and **ADC** to
|
(Food for thought: It's also possible to use **CMP** and **ADC** to
|
||||||
|
|
@ -66,6 +67,7 @@ report a "location counter overflow" warning; ignore it.)
|
||||||
|
|
||||||
**LISTING 16.8 MAKETAB.C**
|
**LISTING 16.8 MAKETAB.C**
|
||||||
|
|
||||||
|
```c
|
||||||
// MAKETAB.C — Build QSCAN3.INC for QSCAN3.ASM
|
// MAKETAB.C — Build QSCAN3.INC for QSCAN3.ASM
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -98,7 +100,7 @@ report a "location counter overflow" warning; ignore it.)
|
||||||
|
|
||||||
fclose( t );
|
fclose( t );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
David's approach is simplicity itself, although his implementation
|
David's approach is simplicity itself, although his implementation
|
||||||
arguably is not. Consider any three sequential bytes in the buffer.
|
arguably is not. Consider any three sequential bytes in the buffer.
|
||||||
|
|
|
||||||
4
17-02.md
4
17-02.md
|
|
@ -12,6 +12,7 @@ pages: 325-329
|
||||||
|
|
||||||
**LISTING 17.1 L17-1.CPP**
|
**LISTING 17.1 L17-1.CPP**
|
||||||
|
|
||||||
|
```cpp
|
||||||
/* C++ Game of Life implementation for any mode for which mode set
|
/* C++ Game of Life implementation for any mode for which mode set
|
||||||
and draw pixel functions can be provided.
|
and draw pixel functions can be provided.
|
||||||
Tested with Borland C++ in the small model. */
|
Tested with Borland C++ in the small model. */
|
||||||
|
|
@ -206,9 +207,11 @@ pages: 325-329
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 17.2 L17-2.CPP**
|
**LISTING 17.2 L17-2.CPP**
|
||||||
|
|
||||||
|
```cpp
|
||||||
/* VGA mode 13h functions for Game of Life.
|
/* VGA mode 13h functions for Game of Life.
|
||||||
Tested with Borland C++. */
|
Tested with Borland C++. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -265,3 +268,4 @@ pages: 325-329
|
||||||
gotoxy(TEXT_X_OFFSET + x, y);
|
gotoxy(TEXT_X_OFFSET + x, y);
|
||||||
puts(text);
|
puts(text);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
2
17-04.md
2
17-04.md
|
|
@ -48,6 +48,7 @@ improvement.
|
||||||
|
|
||||||
**LISTING 17.3 L17-3.CPP**
|
**LISTING 17.3 L17-3.CPP**
|
||||||
|
|
||||||
|
```cpp
|
||||||
/* cellmap class definition, constructor, copy_cells(), set_cell(),
|
/* cellmap class definition, constructor, copy_cells(), set_cell(),
|
||||||
clear_cell(), cell_state(), count_neighbors(), and
|
clear_cell(), cell_state(), count_neighbors(), and
|
||||||
next_generation() for fast, hard-wired neighbor count approach.
|
next_generation() for fast, hard-wired neighbor count approach.
|
||||||
|
|
@ -209,3 +210,4 @@ improvement.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
2
17-05.md
2
17-05.md
|
|
@ -51,6 +51,7 @@ Listing 17.3.
|
||||||
|
|
||||||
**LISTING 17.4 L17-4.CPP**
|
**LISTING 17.4 L17-4.CPP**
|
||||||
|
|
||||||
|
```cpp
|
||||||
/* next_generation(), implemented using fast, all-in-one hard-wired
|
/* next_generation(), implemented using fast, all-in-one hard-wired
|
||||||
neighbor count/update/draw function. Otherwise, the same as
|
neighbor count/update/draw function. Otherwise, the same as
|
||||||
Listing 17.3. */
|
Listing 17.3. */
|
||||||
|
|
@ -131,6 +132,7 @@ Listing 17.3.
|
||||||
row_cell_ptr += width_in_bytes; // point to start of next row
|
row_cell_ptr += width_in_bytes; // point to start of next row
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Listing 17.4 and Listing 17.3 are functionally the same; the only
|
Listing 17.4 and Listing 17.3 are functionally the same; the only
|
||||||
difference lies in how **next\_generation()** is implemented. (Only
|
difference lies in how **next\_generation()** is implemented. (Only
|
||||||
|
|
|
||||||
2
17-07.md
2
17-07.md
|
|
@ -12,6 +12,7 @@ pages: 340-345
|
||||||
|
|
||||||
**LISTING 17.5 L17-5.CPP**
|
**LISTING 17.5 L17-5.CPP**
|
||||||
|
|
||||||
|
```cpp
|
||||||
/* C++ Game of Life implementation for any mode for which mode set
|
/* C++ Game of Life implementation for any mode for which mode set
|
||||||
and draw pixel functions can be provided. The cellmap stores the
|
and draw pixel functions can be provided. The cellmap stores the
|
||||||
neighbor count for each cell as well as the state of each cell;
|
neighbor count for each cell as well as the state of each cell;
|
||||||
|
|
@ -285,3 +286,4 @@ pages: 340-345
|
||||||
}
|
}
|
||||||
} while (—init_length);
|
} while (—init_length);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
4
18-03.md
4
18-03.md
|
|
@ -12,13 +12,16 @@ pages: 352-361
|
||||||
|
|
||||||
**LISTING 18.1 BUILD.BAT**
|
**LISTING 18.1 BUILD.BAT**
|
||||||
|
|
||||||
|
```bat
|
||||||
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 lcomp.c
|
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 lcomp.c
|
||||||
lcomp > qlife.asm
|
lcomp > qlife.asm
|
||||||
tasmx /mx /kh30000 qlife
|
tasmx /mx /kh30000 qlife
|
||||||
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 qlife.obj main.c video.c
|
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 qlife.obj main.c video.c
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 18.2 LCOMP.C**
|
**LISTING 18.2 LCOMP.C**
|
||||||
|
|
||||||
|
```c
|
||||||
// LCOMP.C
|
// LCOMP.C
|
||||||
//
|
//
|
||||||
// Life compiler, ver 1.3
|
// Life compiler, ver 1.3
|
||||||
|
|
@ -512,3 +515,4 @@ pages: 352-361
|
||||||
|
|
||||||
printf( "LIFE ends\nend\n" );
|
printf( "LIFE ends\nend\n" );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
6
18-04.md
6
18-04.md
|
|
@ -12,6 +12,7 @@ pages: 361-365
|
||||||
|
|
||||||
**LISTING 18.3 MAIN.C**
|
**LISTING 18.3 MAIN.C**
|
||||||
|
|
||||||
|
```c
|
||||||
// MAIN.C
|
// MAIN.C
|
||||||
//
|
//
|
||||||
// David Stafford
|
// David Stafford
|
||||||
|
|
@ -102,9 +103,11 @@ pages: 361-365
|
||||||
printf( "Time: %f generations/second\n",
|
printf( "Time: %f generations/second\n",
|
||||||
(double)generation / (double)end_time * 18.2 );
|
(double)generation / (double)end_time * 18.2 );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 18.4 VIDEO.C**
|
**LISTING 18.4 VIDEO.C**
|
||||||
|
|
||||||
|
```c
|
||||||
/* VGA mode 13h functions for Game of Life.
|
/* VGA mode 13h functions for Game of Life.
|
||||||
Tested with Borland C++. */
|
Tested with Borland C++. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -141,9 +144,11 @@ pages: 361-365
|
||||||
gotoxy(TEXT_X_OFFSET + x, y);
|
gotoxy(TEXT_X_OFFSET + x, y);
|
||||||
puts(text);
|
puts(text);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 18.5 LIFE.H**
|
**LISTING 18.5 LIFE.H**
|
||||||
|
|
||||||
|
```c
|
||||||
void far NextGen( void );
|
void far NextGen( void );
|
||||||
|
|
||||||
extern unsigned short CellMap[];
|
extern unsigned short CellMap[];
|
||||||
|
|
@ -161,6 +166,7 @@ pages: 361-365
|
||||||
#define WRAPRIGHT (LEFT * (WIDTH - 1))
|
#define WRAPRIGHT (LEFT * (WIDTH - 1))
|
||||||
#define WRAPUP (DOWN * (HEIGHT - 1))
|
#define WRAPUP (DOWN * (HEIGHT - 1))
|
||||||
#define WRAPDOWN (UP * (HEIGHT - 1))
|
#define WRAPDOWN (UP * (HEIGHT - 1))
|
||||||
|
```
|
||||||
|
|
||||||
### Keeping Track of Change with a Change List {#Heading5}
|
### Keeping Track of Change with a Change List {#Heading5}
|
||||||
|
|
||||||
|
|
|
||||||
2
18-05.md
2
18-05.md
|
|
@ -103,9 +103,11 @@ and branching to the routine at that address. As with everything in this
|
||||||
amazing program, this represents the least possible work to accomplish
|
amazing program, this represents the least possible work to accomplish
|
||||||
the desired result—just three instructions:
|
the desired result—just three instructions:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov dh,[bp+1]
|
mov dh,[bp+1]
|
||||||
or dh,1
|
or dh,1
|
||||||
jmp dx
|
jmp dx
|
||||||
|
```
|
||||||
|
|
||||||
These suffice to select the proper, minimum-work code to process the
|
These suffice to select the proper, minimum-work code to process the
|
||||||
next cell triplet that has changed, and all potentially affected
|
next cell triplet that has changed, and all potentially affected
|
||||||
|
|
|
||||||
4
19-02.md
4
19-02.md
|
|
@ -102,6 +102,7 @@ pipelines mean that an AGI can now slow down execution of an instruction
|
||||||
that's *three* instructions away from the AGI (because four instructions
|
that's *three* instructions away from the AGI (because four instructions
|
||||||
can execute in two cycles). So, for example, the code sequence
|
can execute in two cycles). So, for example, the code sequence
|
||||||
|
|
||||||
|
```nasm
|
||||||
add edx,4 ;U-pipe cycle 1
|
add edx,4 ;U-pipe cycle 1
|
||||||
mov ecx,[ebx] ;V-pipe cycle 1
|
mov ecx,[ebx] ;V-pipe cycle 1
|
||||||
add ebx,4 ;U-pipe cycle 2
|
add ebx,4 ;U-pipe cycle 2
|
||||||
|
|
@ -109,6 +110,7 @@ can execute in two cycles). So, for example, the code sequence
|
||||||
; due to AGI
|
; due to AGI
|
||||||
; (would have been
|
; (would have been
|
||||||
; V-pipe cycle 2)
|
; V-pipe cycle 2)
|
||||||
|
```
|
||||||
|
|
||||||
takes three cycles rather than the two cycles it should take, because
|
takes three cycles rather than the two cycles it should take, because
|
||||||
EDX was modified on cycle 1 and an attempt was made to use it on cycle
|
EDX was modified on cycle 1 and an attempt was made to use it on cycle
|
||||||
|
|
@ -116,10 +118,12 @@ two, before the AGI had time to clear—even though there are two
|
||||||
instructions between the instructions that are actually involved in the
|
instructions between the instructions that are actually involved in the
|
||||||
AGI. Rearranging the code like
|
AGI. Rearranging the code like
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ecx,[ebx] ;U-pipe cycle 1
|
mov ecx,[ebx] ;U-pipe cycle 1
|
||||||
add ebx,4 ;V-pipe cycle 1
|
add ebx,4 ;V-pipe cycle 1
|
||||||
mov [edx+4],ecx ;U-pipe cycle 2
|
mov [edx+4],ecx ;U-pipe cycle 2
|
||||||
add edx,4 ;V-pipe cycle 2
|
add edx,4 ;V-pipe cycle 2
|
||||||
|
```
|
||||||
|
|
||||||
makes it functionally identical, but cuts the cycles to 2—a 50 percent
|
makes it functionally identical, but cuts the cycles to 2—a 50 percent
|
||||||
improvement. Clearly, avoiding AGIs becomes a much more challenging and
|
improvement. Clearly, avoiding AGIs becomes a much more challenging and
|
||||||
|
|
|
||||||
2
19-03.md
2
19-03.md
|
|
@ -38,9 +38,11 @@ word-aligned, dword operands should be dword-aligned, and qword operands
|
||||||
(double-precision variables) should be qword-aligned. Spanning a dword
|
(double-precision variables) should be qword-aligned. Spanning a dword
|
||||||
boundary, as in
|
boundary, as in
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov ebx,3
|
mov ebx,3
|
||||||
:
|
:
|
||||||
mov eax,[ebx]
|
mov eax,[ebx]
|
||||||
|
```
|
||||||
|
|
||||||
costs three cycles. On the other hand, as noted above, branch targets
|
costs three cycles. On the other hand, as noted above, branch targets
|
||||||
can now span cache lines with impunity, so on the Pentium there's no
|
can now span cache lines with impunity, so on the Pentium there's no
|
||||||
|
|
|
||||||
14
20-02.md
14
20-02.md
|
|
@ -40,6 +40,7 @@ waits until the first instruction is done, then executes in the U-pipe,
|
||||||
possibly pairing with the next instruction in line if all pairing
|
possibly pairing with the next instruction in line if all pairing
|
||||||
conditions are met.
|
conditions are met.
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV reg,reg (1 cycle)
|
MOV reg,reg (1 cycle)
|
||||||
mem,reg (1 cycle)
|
mem,reg (1 cycle)
|
||||||
reg,mem (1 cycle)
|
reg,mem (1 cycle)
|
||||||
|
|
@ -75,6 +76,7 @@ conditions are met.
|
||||||
|
|
||||||
JMP/CALL near (1 cycle if predicted correctly;
|
JMP/CALL near (1 cycle if predicted correctly;
|
||||||
3 cycles otherwise)
|
3 cycles otherwise)
|
||||||
|
```
|
||||||
|
|
||||||
† Can't execute in V-pipe if address contains a displacement
|
† Can't execute in V-pipe if address contains a displacement
|
||||||
|
|
||||||
|
|
@ -100,6 +102,7 @@ each of 2 cycles, for 2\*0.5 = 1 cycle total execution time), as shown
|
||||||
in Figure 20.3—a full cycle *faster* than **PUSH [*mem*]**, which takes
|
in Figure 20.3—a full cycle *faster* than **PUSH [*mem*]**, which takes
|
||||||
2 cycles.
|
2 cycles.
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV reg,reg (1 cycle)
|
MOV reg,reg (1 cycle)
|
||||||
mem,reg (1 cycle)
|
mem,reg (1 cycle)
|
||||||
reg,mem (1 cycle)
|
reg,mem (1 cycle)
|
||||||
|
|
@ -132,8 +135,9 @@ in Figure 20.3—a full cycle *faster* than **PUSH [*mem*]**, which takes
|
||||||
SHL/SHR/SAL/SAR reg,immediate (1 cycle)††
|
SHL/SHR/SAL/SAR reg,immediate (1 cycle)††
|
||||||
|
|
||||||
ROL/ROR/RCL/RCR reg,1 (1 cycle)
|
ROL/ROR/RCL/RCR reg,1 (1 cycle)
|
||||||
|
```
|
||||||
|
|
||||||
† Can't pair if address contains a displacement
|
† Can't pair if address contains a displacement\
|
||||||
†† Includes shift-by-1 forms of instructions
|
†† Includes shift-by-1 forms of instructions
|
||||||
|
|
||||||
**Table 20.2 Instructions that, when executed in the U-pipe, allow
|
**Table 20.2 Instructions that, when executed in the U-pipe, allow
|
||||||
|
|
@ -155,25 +159,33 @@ One downside of this "RISCification" (turning complex instructions into
|
||||||
simple, RISC-like ones) of Pentium-optimized code is that it makes for
|
simple, RISC-like ones) of Pentium-optimized code is that it makes for
|
||||||
substantially larger code. For example,
|
substantially larger code. For example,
|
||||||
|
|
||||||
|
```nasm
|
||||||
push dword ptr [esi]
|
push dword ptr [esi]
|
||||||
|
```
|
||||||
|
|
||||||
is one byte smaller than this sequence:
|
is one byte smaller than this sequence:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov eax,[esi]
|
mov eax,[esi]
|
||||||
push eax
|
push eax
|
||||||
|
```
|
||||||
|
|
||||||

|
cycle.*](images/20-03.jpg)
|
||||||
|
|
||||||
A more telling example is the following
|
A more telling example is the following
|
||||||
|
|
||||||
|
```nasm
|
||||||
add [MemVar],eax
|
add [MemVar],eax
|
||||||
|
```
|
||||||
|
|
||||||
versus the equivalent:
|
versus the equivalent:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov edx,[MemVar]
|
mov edx,[MemVar]
|
||||||
add edx,eax
|
add edx,eax
|
||||||
mov [MemVar],edx
|
mov [MemVar],edx
|
||||||
|
```
|
||||||
|
|
||||||
The single complex instruction takes 3 cycles and is 6 bytes long; with
|
The single complex instruction takes 3 cycles and is 6 bytes long; with
|
||||||
proper sequencing, interleaving the simple instructions with other
|
proper sequencing, interleaving the simple instructions with other
|
||||||
|
|
|
||||||
8
20-03.md
8
20-03.md
|
|
@ -79,23 +79,29 @@ the same values for address
|
||||||
bits 2, 3, and 4 (fall in the same bank) in tight loops, and you should
|
bits 2, 3, and 4 (fall in the same bank) in tight loops, and you should
|
||||||
also avoid sequences like
|
also avoid sequences like
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bl,[esi]
|
mov bl,[esi]
|
||||||
mov bh,[esi+1]
|
mov bh,[esi+1]
|
||||||
|
```
|
||||||
|
|
||||||
because both operands will generally be in the same bank. An alternative
|
because both operands will generally be in the same bank. An alternative
|
||||||
is to place another instruction between the two instructions that access
|
is to place another instruction between the two instructions that access
|
||||||
the same bank, as in this sequence:
|
the same bank, as in this sequence:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bl,[esi]
|
mov bl,[esi]
|
||||||
mov edi,edx
|
mov edi,edx
|
||||||
mov bh,[esi+1]
|
mov bh,[esi+1]
|
||||||
|
```
|
||||||
|
|
||||||
By the way, the reason a code sequence that takes two instructions to
|
By the way, the reason a code sequence that takes two instructions to
|
||||||
load a single word is attractive in a 32-bit segment is because it takes
|
load a single word is attractive in a 32-bit segment is because it takes
|
||||||
only one cycle when the two instructions can be paired with other
|
only one cycle when the two instructions can be paired with other
|
||||||
instructions; by contrast, the obvious way of loading BX
|
instructions; by contrast, the obvious way of loading BX
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov bx,[esi]
|
mov bx,[esi]
|
||||||
|
```
|
||||||
|
|
||||||
takes 1.5 to two cycles because the size prefix can't pair, as described
|
takes 1.5 to two cycles because the size prefix can't pair, as described
|
||||||
below. This is yet another example of how different Pentium optimization
|
below. This is yet another example of how different Pentium optimization
|
||||||
|
|
@ -116,5 +122,7 @@ but that turns out to not always be the case. Two two-cycle instructions
|
||||||
execute in two cycles, so it's okay to pair two instructions such as
|
execute in two cycles, so it's okay to pair two instructions such as
|
||||||
these:
|
these:
|
||||||
|
|
||||||
|
```nasm
|
||||||
add esi,[SourceSkip] ;U-pipe cycles 1 and 2
|
add esi,[SourceSkip] ;U-pipe cycles 1 and 2
|
||||||
add edi,[DestinationSkip] ;V-pipe cycles 1 and 2
|
add edi,[DestinationSkip] ;V-pipe cycles 1 and 2
|
||||||
|
```
|
||||||
|
|
|
||||||
4
20-04.md
4
20-04.md
|
|
@ -41,13 +41,17 @@ it's the only way to get both pipes running at capacity.
|
||||||
You may well ask why it's necessary to interleave operations, as is done
|
You may well ask why it's necessary to interleave operations, as is done
|
||||||
in Figure 20.7. It seems simpler just to turn
|
in Figure 20.7. It seems simpler just to turn
|
||||||
|
|
||||||
|
```nasm
|
||||||
and [ebx],al
|
and [ebx],al
|
||||||
|
```
|
||||||
|
|
||||||
into
|
into
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov dl,[ebx]
|
mov dl,[ebx]
|
||||||
and dl,al
|
and dl,al
|
||||||
mov [ebx],dl
|
mov [ebx],dl
|
||||||
|
```
|
||||||
|
|
||||||
and be done with it. The problem here is one of dependency. Before the
|
and be done with it. The problem here is one of dependency. Before the
|
||||||
Pentium can execute **AND DL,AL,**, it must first know what is in DL,
|
Pentium can execute **AND DL,AL,**, it must first know what is in DL,
|
||||||
|
|
|
||||||
4
21-01.md
4
21-01.md
|
|
@ -65,7 +65,9 @@ areas of AGIs and register dependencies.
|
||||||
AGIs are *Address Generation Interlocks*, a fancy way of saying that if
|
AGIs are *Address Generation Interlocks*, a fancy way of saying that if
|
||||||
a register is used to address memory, as is EBX in this instruction
|
a register is used to address memory, as is EBX in this instruction
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov [ebx],eax
|
mov [ebx],eax
|
||||||
|
```
|
||||||
|
|
||||||
and the value of the register is not set far enough ahead for the
|
and the value of the register is not set far enough ahead for the
|
||||||
Pentium to perform the addressing calculations before the instruction
|
Pentium to perform the addressing calculations before the instruction
|
||||||
|
|
@ -101,6 +103,7 @@ As an example of a sort of AGI that's new to the Pentium, consider the
|
||||||
following test for a NULL pointer, followed by the use of the pointer if
|
following test for a NULL pointer, followed by the use of the pointer if
|
||||||
it's not NULL:
|
it's not NULL:
|
||||||
|
|
||||||
|
```nasm
|
||||||
push ebx ;U-pipe cycle 1
|
push ebx ;U-pipe cycle 1
|
||||||
mov ebx,[Ptr] ;V-pipe cycle 1
|
mov ebx,[Ptr] ;V-pipe cycle 1
|
||||||
and ebx,ebx ;U-pipe cycle 2
|
and ebx,ebx ;U-pipe cycle 2
|
||||||
|
|
@ -109,6 +112,7 @@ it's not NULL:
|
||||||
mov edx,[ebp-8] ;V-pipe cycle 3 lockstep idle
|
mov edx,[ebp-8] ;V-pipe cycle 3 lockstep idle
|
||||||
;U-pipe cycle 4 mov eax,[ebx]
|
;U-pipe cycle 4 mov eax,[ebx]
|
||||||
;V-pipe cycle 4 mov edx,[ebp-8]
|
;V-pipe cycle 4 mov edx,[ebp-8]
|
||||||
|
```
|
||||||
|
|
||||||
This commonplace code loses a U-pipe cycle to the AGI caused by **AND
|
This commonplace code loses a U-pipe cycle to the AGI caused by **AND
|
||||||
EBX,EBX**, followed by the attempt two instructions later to use EBX to
|
EBX,EBX**, followed by the attempt two instructions later to use EBX to
|
||||||
|
|
|
||||||
24
21-02.md
24
21-02.md
|
|
@ -15,16 +15,22 @@ stack pointer. Implicit modifiers of ESP, such as **PUSH** and **POP**,
|
||||||
are special-cased so you don't have to worry about AGIs. However, if you
|
are special-cased so you don't have to worry about AGIs. However, if you
|
||||||
explicitly modify ESP with this instruction
|
explicitly modify ESP with this instruction
|
||||||
|
|
||||||
|
```nasm
|
||||||
sub esp,100h
|
sub esp,100h
|
||||||
|
```
|
||||||
|
|
||||||
for example, or with the popular
|
for example, or with the popular
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov esp,ebp
|
mov esp,ebp
|
||||||
|
```
|
||||||
|
|
||||||
you can then get AGIs if you attempt to use ESP to address memory,
|
you can then get AGIs if you attempt to use ESP to address memory,
|
||||||
either explicitly with instructions like this one
|
either explicitly with instructions like this one
|
||||||
|
|
||||||
|
```nasm
|
||||||
moveax,[esp+20h]
|
moveax,[esp+20h]
|
||||||
|
```
|
||||||
|
|
||||||
or via **PUSH**, **POP**, or other instructions that implicitly use ESP
|
or via **PUSH**, **POP**, or other instructions that implicitly use ESP
|
||||||
as an addressing register.
|
as an addressing register.
|
||||||
|
|
@ -32,25 +38,31 @@ as an addressing register.
|
||||||
On the 486, any instruction that had both a constant value and an
|
On the 486, any instruction that had both a constant value and an
|
||||||
addressing displacement, such as
|
addressing displacement, such as
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov dword ptr [ebp+16],1
|
mov dword ptr [ebp+16],1
|
||||||
|
```
|
||||||
|
|
||||||
suffered a 1-cycle penalty, taking a total of 2 cycles. Such
|
suffered a 1-cycle penalty, taking a total of 2 cycles. Such
|
||||||
instructions take only one cycle on the Pentium, but they cannot pair,
|
instructions take only one cycle on the Pentium, but they cannot pair,
|
||||||
so they're still the most expensive sort of **MOV**. Knowing this can
|
so they're still the most expensive sort of **MOV**. Knowing this can
|
||||||
speed up something as simple as zeroing two memory variables, as in
|
speed up something as simple as zeroing two memory variables, as in
|
||||||
|
|
||||||
|
```nasm
|
||||||
sub eax,eax ;U-pipe 1
|
sub eax,eax ;U-pipe 1
|
||||||
;any V-pipe pairable
|
;any V-pipe pairable
|
||||||
; instruction can go here,
|
; instruction can go here,
|
||||||
; or SUB could be in V-pipe
|
; or SUB could be in V-pipe
|
||||||
mov [MemVar1],eax ;U-pipe 2
|
mov [MemVar1],eax ;U-pipe 2
|
||||||
mov [MemVar2],eax ;V-pipe 2
|
mov [MemVar2],eax ;V-pipe 2
|
||||||
|
```
|
||||||
|
|
||||||
which should never be slower and should potentially be 0.5 cycles
|
which should never be slower and should potentially be 0.5 cycles
|
||||||
faster, and six bytes smaller than this sequence:
|
faster, and six bytes smaller than this sequence:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov [MemVar1],0 ;U-pipe 1
|
mov [MemVar1],0 ;U-pipe 1
|
||||||
mov [MemVar2],0 ;U-pipe 2
|
mov [MemVar2],0 ;U-pipe 2
|
||||||
|
```
|
||||||
|
|
||||||
Note, however, that my experiments thus far indicate that the two writes
|
Note, however, that my experiments thus far indicate that the two writes
|
||||||
in the first case don't actually pair (possibly because the memory
|
in the first case don't actually pair (possibly because the memory
|
||||||
|
|
@ -66,10 +78,12 @@ optimization: register contention. The basic premise here is simple: You
|
||||||
can't use the same register in two inherently sequential ways in a
|
can't use the same register in two inherently sequential ways in a
|
||||||
single cycle. For example, you can't execute
|
single cycle. For example, you can't execute
|
||||||
|
|
||||||
|
```nasm
|
||||||
inc eax ;U-pipe cycle 1
|
inc eax ;U-pipe cycle 1
|
||||||
;V-pipe idle cycle 1
|
;V-pipe idle cycle 1
|
||||||
; due to dependency
|
; due to dependency
|
||||||
and ebx,eax ;U-pipe cycle 2
|
and ebx,eax ;U-pipe cycle 2
|
||||||
|
```
|
||||||
|
|
||||||
in a single cycle; **AND EBX,EAX** can't execute until the value in EAX
|
in a single cycle; **AND EBX,EAX** can't execute until the value in EAX
|
||||||
is known, and that can't happen until **INC EAX** is done. Consequently,
|
is known, and that can't happen until **INC EAX** is done. Consequently,
|
||||||
|
|
@ -86,10 +100,12 @@ to write to the same register on the same cycle. While that may not seem
|
||||||
like a particularly useful operation in general, it can happen when
|
like a particularly useful operation in general, it can happen when
|
||||||
subregisters are being set, as in the following
|
subregisters are being set, as in the following
|
||||||
|
|
||||||
|
```nasm
|
||||||
sub eax,eax ;U-pipe cycle 1
|
sub eax,eax ;U-pipe cycle 1
|
||||||
;V-pipe idle cycle 1
|
;V-pipe idle cycle 1
|
||||||
; due to register contention
|
; due to register contention
|
||||||
mov al,[Var] ;U-pipe cycle 2
|
mov al,[Var] ;U-pipe cycle 2
|
||||||
|
```
|
||||||
|
|
||||||
where an attempt is made to set both EAX and its AL subregister on the
|
where an attempt is made to set both EAX and its AL subregister on the
|
||||||
same cycle. Write-after-write contention implies that the two
|
same cycle. Write-after-write contention implies that the two
|
||||||
|
|
@ -103,8 +119,10 @@ Intel has special-cased some very useful exceptions to register
|
||||||
contention. Happily, write-after-read operations do *not* cause
|
contention. Happily, write-after-read operations do *not* cause
|
||||||
contention. Such operations, as in
|
contention. Such operations, as in
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov eax,edx ;U-pipe cycle 1
|
mov eax,edx ;U-pipe cycle 1
|
||||||
sub edx,edxX ;V-pipe cycle 1
|
sub edx,edxX ;V-pipe cycle 1
|
||||||
|
```
|
||||||
|
|
||||||
are free of charge.
|
are free of charge.
|
||||||
|
|
||||||
|
|
@ -117,12 +135,14 @@ register contention on ESP—but not AGIs—with instructions that use ESP
|
||||||
explicitly, such as **MOV EAX,[ESP+4]**.) Without this special case, the
|
explicitly, such as **MOV EAX,[ESP+4]**.) Without this special case, the
|
||||||
following sequence would hardly use the V-pipe at all:
|
following sequence would hardly use the V-pipe at all:
|
||||||
|
|
||||||
|
```nasm
|
||||||
mov eax,[MemVar] ;U-pipe cycle 1
|
mov eax,[MemVar] ;U-pipe cycle 1
|
||||||
push esi ;V-pipe cycle 1
|
push esi ;V-pipe cycle 1
|
||||||
push eax ;U-pipe cycle 2
|
push eax ;U-pipe cycle 2
|
||||||
push edi ;V-pipe cycle 2
|
push edi ;V-pipe cycle 2
|
||||||
push ebx ;U-pipe cycle 3
|
push ebx ;U-pipe cycle 3
|
||||||
call FooTilde ;V-pipe cycle 3
|
call FooTilde ;V-pipe cycle 3
|
||||||
|
```
|
||||||
|
|
||||||
But in fact, all the instructions pair, even though ESP is modified five
|
But in fact, all the instructions pair, even though ESP is modified five
|
||||||
times in the space of six instructions.
|
times in the space of six instructions.
|
||||||
|
|
@ -133,11 +153,13 @@ pair only in the V-pipe: branches. Any near call or conditional or
|
||||||
unconditional near jump can execute in the V-pipe paired with any
|
unconditional near jump can execute in the V-pipe paired with any
|
||||||
pairable U-pipe instruction, as illustrated by this sequence:
|
pairable U-pipe instruction, as illustrated by this sequence:
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
mov [esi],eax ;U-pipe cycle 1
|
mov [esi],eax ;U-pipe cycle 1
|
||||||
add esi,4 ;V-pipe cycle 1
|
add esi,4 ;V-pipe cycle 1
|
||||||
dec ecx ;U-pipe cycle 2
|
dec ecx ;U-pipe cycle 2
|
||||||
jnz LoopTop ;V-pipe cycle 2
|
jnz LoopTop ;V-pipe cycle 2
|
||||||
|
```
|
||||||
|
|
||||||
Branches can't pair in the U-pipe; a branch that executes in the U-pipe
|
Branches can't pair in the U-pipe; a branch that executes in the U-pipe
|
||||||
runs alone, with the V-pipe idle. If a call or jump is correctly
|
runs alone, with the V-pipe idle. If a call or jump is correctly
|
||||||
|
|
@ -159,6 +181,7 @@ instruction, the other instructions will go through different pipes than
|
||||||
previously, and cause the loop as a whole to take 50 percent longer,
|
previously, and cause the loop as a whole to take 50 percent longer,
|
||||||
even though we only added 25 percent more cycles:
|
even though we only added 25 percent more cycles:
|
||||||
|
|
||||||
|
```nasm
|
||||||
LoopTop:
|
LoopTop:
|
||||||
inc edx ;U-pipe cycle 1
|
inc edx ;U-pipe cycle 1
|
||||||
mov [esi],eax ;V-pipe cycle 1
|
mov [esi],eax ;V-pipe cycle 1
|
||||||
|
|
@ -168,3 +191,4 @@ even though we only added 25 percent more cycles:
|
||||||
;V-pipe idle cycle 3
|
;V-pipe idle cycle 3
|
||||||
; because JNZ can't
|
; because JNZ can't
|
||||||
; pair in the U-pipe
|
; pair in the U-pipe
|
||||||
|
```
|
||||||
|
|
|
||||||
2
21-03.md
2
21-03.md
|
|
@ -40,6 +40,7 @@ right?
|
||||||
|
|
||||||
**LISTING 21.1 L21-1.ASM**
|
**LISTING 21.1 L21-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; starting at ESI, of length ECX words.
|
; starting at ESI, of length ECX words.
|
||||||
; Returns checksum in AX.
|
; Returns checksum in AX.
|
||||||
|
|
@ -68,6 +69,7 @@ right?
|
||||||
add esi,2 ;cycle 5 V-pipe
|
add esi,2 ;cycle 5 V-pipe
|
||||||
dec ecx ;cycle 6 U-pipe
|
dec ecx ;cycle 6 U-pipe
|
||||||
jnz ckloop ;cycle 6 V-pipe
|
jnz ckloop ;cycle 6 V-pipe
|
||||||
|
```
|
||||||
|
|
||||||
Wrong, wrong, wrong! As detailed in Listing 21.1, this loop should take
|
Wrong, wrong, wrong! As detailed in Listing 21.1, this loop should take
|
||||||
6 cycles per checksummed word in 32-bit protected mode, a ridiculously
|
6 cycles per checksummed word in 32-bit protected mode, a ridiculously
|
||||||
|
|
|
||||||
4
21-04.md
4
21-04.md
|
|
@ -27,6 +27,7 @@ same cache data bank, as discussed in the last chapter).
|
||||||
|
|
||||||
**LISTING 21.2 L21-2.ASM**
|
**LISTING 21.2 L21-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; starting at ESI, of length ECX words.
|
; starting at ESI, of length ECX words.
|
||||||
; Returns checksum in AX.
|
; Returns checksum in AX.
|
||||||
|
|
@ -53,6 +54,7 @@ same cache data bank, as discussed in the last chapter).
|
||||||
ckloopend:
|
ckloopend:
|
||||||
add ax,dx ;checksum the last word
|
add ax,dx ;checksum the last word
|
||||||
adc eax,0
|
adc eax,0
|
||||||
|
```
|
||||||
|
|
||||||
Listing 21.3 is a more sophisticated attempt to speed up the checksum
|
Listing 21.3 is a more sophisticated attempt to speed up the checksum
|
||||||
calculation. Here we see a hallmark of Pentium optimization: two
|
calculation. Here we see a hallmark of Pentium optimization: two
|
||||||
|
|
@ -68,6 +70,7 @@ placement of **ADD ESI,4** to avoid an AGI.
|
||||||
|
|
||||||
**LISTING 21.3 L21-3.ASM**
|
**LISTING 21.3 L21-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; starting at ESI, of length ECX words.
|
; starting at ESI, of length ECX words.
|
||||||
; Returns checksum in AX.
|
; Returns checksum in AX.
|
||||||
|
|
@ -114,6 +117,7 @@ placement of **ADD ESI,4** to avoid an AGI.
|
||||||
add ax,dx
|
add ax,dx
|
||||||
adc eax,0
|
adc eax,0
|
||||||
ckloopdone:
|
ckloopdone:
|
||||||
|
```
|
||||||
|
|
||||||
The checksum loop in Listing 21.3 takes longer than the loop in Listing
|
The checksum loop in Listing 21.3 takes longer than the loop in Listing
|
||||||
21.2, at 6 cycles versus 4 cycles for Listing 21.2—but Listing 21.3 does
|
21.2, at 6 cycles versus 4 cycles for Listing 21.2—but Listing 21.3 does
|
||||||
|
|
|
||||||
4
21-05.md
4
21-05.md
|
|
@ -12,6 +12,7 @@ pages: 409-411
|
||||||
|
|
||||||
**LISTING 21.4 L21-4.ASM**
|
**LISTING 21.4 L21-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; starting at ESI, of length ECX words.
|
; starting at ESI, of length ECX words.
|
||||||
; Returns checksum in AX.
|
; Returns checksum in AX.
|
||||||
|
|
@ -42,6 +43,7 @@ pages: 409-411
|
||||||
shr edx,16 ; into a 16-bit checksum
|
shr edx,16 ; into a 16-bit checksum
|
||||||
add ax,dx
|
add ax,dx
|
||||||
adc eax,0
|
adc eax,0
|
||||||
|
```
|
||||||
|
|
||||||
Listing 21.5 improves upon Listing 21.4 by processing 2 dwords per loop,
|
Listing 21.5 improves upon Listing 21.4 by processing 2 dwords per loop,
|
||||||
thereby bringing the time per checksummed word down to exactly 1 cycle.
|
thereby bringing the time per checksummed word down to exactly 1 cycle.
|
||||||
|
|
@ -53,6 +55,7 @@ more registers.
|
||||||
|
|
||||||
**LISTING 21.5 L21-5.ASM**
|
**LISTING 21.5 L21-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; starting at ESI, of length ECX words.
|
; starting at ESI, of length ECX words.
|
||||||
; Returns checksum in AX.
|
; Returns checksum in AX.
|
||||||
|
|
@ -93,6 +96,7 @@ more registers.
|
||||||
shr edx,16 ; into a 16-bit checksum
|
shr edx,16 ; into a 16-bit checksum
|
||||||
add ax,dx
|
add ax,dx
|
||||||
adc eax,0
|
adc eax,0
|
||||||
|
```
|
||||||
|
|
||||||
Listing 21.5 is undeniably intricate code, and not the sort of thing one
|
Listing 21.5 is undeniably intricate code, and not the sort of thing one
|
||||||
would choose to write as a matter of course. On the other hand, it's
|
would choose to write as a matter of course. On the other hand, it's
|
||||||
|
|
|
||||||
2
22-01.md
2
22-01.md
|
|
@ -55,6 +55,7 @@ are mine.
|
||||||
|
|
||||||
**LISTING 22.1 L22-1.ASM**
|
**LISTING 22.1 L22-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
OnStack struc ;data that's stored on the stack after PUSH BP
|
OnStack struc ;data that's stored on the stack after PUSH BP
|
||||||
OldBP dw ? ;caller's BP
|
OldBP dw ? ;caller's BP
|
||||||
RetAddr dw ? ;return address
|
RetAddr dw ? ;return address
|
||||||
|
|
@ -89,6 +90,7 @@ are mine.
|
||||||
pop bp ; and caller's BP
|
pop bp ; and caller's BP
|
||||||
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
||||||
ClearS endp
|
ClearS endp
|
||||||
|
```
|
||||||
|
|
||||||
The first thing you'll notice about Listing 22.1 is that **ClearS** uses
|
The first thing you'll notice about Listing 22.1 is that **ClearS** uses
|
||||||
a **REP STOSW** instruction. That means that we're not going to improve
|
a **REP STOSW** instruction. That means that we're not going to improve
|
||||||
|
|
|
||||||
4
22-02.md
4
22-02.md
|
|
@ -12,6 +12,7 @@ pages: 417-418
|
||||||
|
|
||||||
**LISTING 22.2 L22-2.ASM**
|
**LISTING 22.2 L22-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
ClearS proc near
|
ClearS proc near
|
||||||
push bp ;save caller's BP
|
push bp ;save caller's BP
|
||||||
mov bp,sp ;point to stack frame
|
mov bp,sp ;point to stack frame
|
||||||
|
|
@ -33,6 +34,7 @@ pages: 417-418
|
||||||
pop bp ;restore caller's BP
|
pop bp ;restore caller's BP
|
||||||
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
||||||
ClearS endp
|
ClearS endp
|
||||||
|
```
|
||||||
|
|
||||||
(The **OnStack** structure definition doesn't change in any of our
|
(The **OnStack** structure definition doesn't change in any of our
|
||||||
examples, so I'm not going clutter up this chapter by reproducing it for
|
examples, so I'm not going clutter up this chapter by reproducing it for
|
||||||
|
|
@ -47,6 +49,7 @@ loading ES and DI as shown in Listing 22.3.
|
||||||
|
|
||||||
**LISTING 22.3 L22-3.ASM**
|
**LISTING 22.3 L22-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
ClearS proc near
|
ClearS proc near
|
||||||
push bp ;save caller's BP
|
push bp ;save caller's BP
|
||||||
mov bp,sp ;point to stack frame
|
mov bp,sp ;point to stack frame
|
||||||
|
|
@ -68,6 +71,7 @@ loading ES and DI as shown in Listing 22.3.
|
||||||
pop bp ;restore caller's BP
|
pop bp ;restore caller's BP
|
||||||
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
||||||
ClearS endp
|
ClearS endp
|
||||||
|
```
|
||||||
|
|
||||||
That's good for another three bytes. We're down to 43 bytes, and
|
That's good for another three bytes. We're down to 43 bytes, and
|
||||||
counting.
|
counting.
|
||||||
|
|
|
||||||
6
22-03.md
6
22-03.md
|
|
@ -12,6 +12,7 @@ pages: 419-420
|
||||||
|
|
||||||
**LISTING 22.5 L22-5.ASM**
|
**LISTING 22.5 L22-5.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
ClearS proc near
|
ClearS proc near
|
||||||
push bp ;save caller's BP
|
push bp ;save caller's BP
|
||||||
mov bp,sp ;point to stack frame
|
mov bp,sp ;point to stack frame
|
||||||
|
|
@ -29,6 +30,7 @@ pages: 419-420
|
||||||
pop bp ;restore caller's BP
|
pop bp ;restore caller's BP
|
||||||
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
||||||
ClearS endp
|
ClearS endp
|
||||||
|
```
|
||||||
|
|
||||||
(We could get rid of yet another instruction by having the calling code
|
(We could get rid of yet another instruction by having the calling code
|
||||||
pack both the attribute and the fill value into the same word, but
|
pack both the attribute and the fill value into the same word, but
|
||||||
|
|
@ -44,6 +46,7 @@ shown in Listing 22.6.
|
||||||
|
|
||||||
**LISTING 22.6 L22-6.ASM**
|
**LISTING 22.6 L22-6.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
ClearS proc near
|
ClearS proc near
|
||||||
push bp ;save caller's BP
|
push bp ;save caller's BP
|
||||||
mov bp,sp ;point to stack frame
|
mov bp,sp ;point to stack frame
|
||||||
|
|
@ -60,6 +63,7 @@ shown in Listing 22.6.
|
||||||
pop bp ;restore caller's BP
|
pop bp ;restore caller's BP
|
||||||
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
ret EndMrk-RetAddr-2 ;return, clearing the parms from the stack
|
||||||
ClearS endp
|
ClearS endp
|
||||||
|
```
|
||||||
|
|
||||||
Well. Now we're down to 28 bytes, having reduced the size of this
|
Well. Now we're down to 28 bytes, having reduced the size of this
|
||||||
subroutine by nearly 50 percent. Only 13 instructions remain.
|
subroutine by nearly 50 percent. Only 13 instructions remain.
|
||||||
|
|
@ -103,6 +107,7 @@ With that problem dealt with, Listing 22.7 shows the Zenned version of
|
||||||
|
|
||||||
**LISTING 22.7 L22-7.ASM**
|
**LISTING 22.7 L22-7.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
ClearS procnear
|
ClearS procnear
|
||||||
pop dx ;get the return address
|
pop dx ;get the return address
|
||||||
pop ax ;put fill char into AL
|
pop ax ;put fill char into AL
|
||||||
|
|
@ -119,6 +124,7 @@ With that problem dealt with, Listing 22.7 shows the Zenned version of
|
||||||
Bye:
|
Bye:
|
||||||
jmp dx ;return to the calling code
|
jmp dx ;return to the calling code
|
||||||
ClearS endp
|
ClearS endp
|
||||||
|
```
|
||||||
|
|
||||||
At long last, we're down to the bare metal. This version of **ClearS**
|
At long last, we're down to the bare metal. This version of **ClearS**
|
||||||
is just 19 bytes long. That's just 37 percent as long as the original
|
is just 19 bytes long. That's just 37 percent as long as the original
|
||||||
|
|
|
||||||
2
23-03.md
2
23-03.md
|
|
@ -72,6 +72,7 @@ seen the features in action.
|
||||||
|
|
||||||
**LISTING 23.1 L23-1.ASM**
|
**LISTING 23.1 L23-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Sample VGA program.
|
; Sample VGA program.
|
||||||
; Animates four balls bouncing around a playfield by using
|
; Animates four balls bouncing around a playfield by using
|
||||||
; page flipping. Playfield is panned smoothly both horizontally
|
; page flipping. Playfield is panned smoothly both horizontally
|
||||||
|
|
@ -627,3 +628,4 @@ seen the features in action.
|
||||||
AdjustPanning endp
|
AdjustPanning endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
2
24-02.md
2
24-02.md
|
|
@ -12,6 +12,7 @@ pages: 453-458
|
||||||
|
|
||||||
**LISTING 24.1 L24-1.ASM**
|
**LISTING 24.1 L24-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate operation of ALUs and latches of the VGA's
|
; Program to illustrate operation of ALUs and latches of the VGA's
|
||||||
; Graphics Controller. Draws a variety of patterns against
|
; Graphics Controller. Draws a variety of patterns against
|
||||||
; a horizontally striped background, using each of the 4 available
|
; a horizontally striped background, using each of the 4 available
|
||||||
|
|
@ -263,3 +264,4 @@ pages: 453-458
|
||||||
DrawVerticalBox endp
|
DrawVerticalBox endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
5
24-03.md
5
24-03.md
|
|
@ -90,16 +90,19 @@ program code and a few cycles of execution time. DX is being loaded with
|
||||||
a word value that's composed of two independent immediate byte values.
|
a word value that's composed of two independent immediate byte values.
|
||||||
The obvious way to implement this would be with
|
The obvious way to implement this would be with
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV DL,VALUE1
|
MOV DL,VALUE1
|
||||||
MOV DH,VALUE2
|
MOV DH,VALUE2
|
||||||
|
```
|
||||||
which requires four instruction bytes. By shifting the value destined
|
which requires four instruction bytes. By shifting the value destined
|
||||||
for the high byte into the high byte with MASM's shift-left operator,
|
for the high byte into the high byte with MASM's shift-left operator,
|
||||||
**SHL** (\*100H would work also), and then logically combining the
|
**SHL** (\*100H would work also), and then logically combining the
|
||||||
values with MASM's **OR** operator (or the **ADD** operator), both
|
values with MASM's **OR** operator (or the **ADD** operator), both
|
||||||
halves of DX can be loaded with a single instruction, as in
|
halves of DX can be loaded with a single instruction, as in
|
||||||
|
|
||||||
|
```nasm
|
||||||
MOV DX,(VALUE2 SHL 8) OR VALUE1
|
MOV DX,(VALUE2 SHL 8) OR VALUE1
|
||||||
|
```
|
||||||
|
|
||||||
which takes only three bytes and is faster, being a single instruction.
|
which takes only three bytes and is faster, being a single instruction.
|
||||||
(Note, though, that in 32-bit protected mode, there's a size and
|
(Note, though, that in 32-bit protected mode, there's a size and
|
||||||
|
|
|
||||||
2
25-02.md
2
25-02.md
|
|
@ -12,6 +12,7 @@ pages: 466-470
|
||||||
|
|
||||||
**LISTING 25.1 L25-1.ASM**
|
**LISTING 25.1 L25-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate operation of data rotate and bit mask
|
; Program to illustrate operation of data rotate and bit mask
|
||||||
; features of Graphics Controller. Draws 8x8 character at
|
; features of Graphics Controller. Draws 8x8 character at
|
||||||
; specified location, using VGA's 8x8 ROM font. Designed
|
; specified location, using VGA's 8x8 ROM font. Designed
|
||||||
|
|
@ -242,6 +243,7 @@ pages: 466-470
|
||||||
;
|
;
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
||||||
The bit mask can be used for much more than bit-aligned fonts. For
|
The bit mask can be used for much more than bit-aligned fonts. For
|
||||||
example, the bit mask is useful for fast pixel drawing, such as that
|
example, the bit mask is useful for fast pixel drawing, such as that
|
||||||
|
|
|
||||||
2
25-04.md
2
25-04.md
|
|
@ -12,6 +12,7 @@ pages: 472-474
|
||||||
|
|
||||||
**LISTING 25.2 L25-2.ASM**
|
**LISTING 25.2 L25-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate operation of Map Mask register when drawing
|
; Program to illustrate operation of Map Mask register when drawing
|
||||||
; to memory that already contains data.
|
; to memory that already contains data.
|
||||||
; By Michael Abrash.
|
; By Michael Abrash.
|
||||||
|
|
@ -93,6 +94,7 @@ pages: 472-474
|
||||||
start endp
|
start endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
||||||
#### Setting All Planes to a Single Color {#Heading6}
|
#### Setting All Planes to a Single Color {#Heading6}
|
||||||
|
|
||||||
|
|
|
||||||
4
25-05.md
4
25-05.md
|
|
@ -12,6 +12,7 @@ pages: 474-478
|
||||||
|
|
||||||
**LISTING 25.3 L25-3.ASM**
|
**LISTING 25.3 L25-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate operation of set/reset circuitry to force
|
; Program to illustrate operation of set/reset circuitry to force
|
||||||
; setting of memory that already contains data.
|
; setting of memory that already contains data.
|
||||||
; By Michael Abrash.
|
; By Michael Abrash.
|
||||||
|
|
@ -120,6 +121,7 @@ pages: 474-478
|
||||||
start endp
|
start endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
||||||
#### Manipulating Planes Individually {#Heading7}
|
#### Manipulating Planes Individually {#Heading7}
|
||||||
|
|
||||||
|
|
@ -142,6 +144,7 @@ be used to control individual pixels.
|
||||||
|
|
||||||
**LISTING 25.4 L25-4.ASM**
|
**LISTING 25.4 L25-4.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate operation of set/reset circuitry in conjunction
|
; Program to illustrate operation of set/reset circuitry in conjunction
|
||||||
; with CPU data to modify setting of memory that already contains data.
|
; with CPU data to modify setting of memory that already contains data.
|
||||||
; By Michael Abrash.
|
; By Michael Abrash.
|
||||||
|
|
@ -250,3 +253,4 @@ be used to control individual pixels.
|
||||||
start endp
|
start endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
2
26-02.md
2
26-02.md
|
|
@ -12,6 +12,7 @@ pages: 484-489
|
||||||
|
|
||||||
**LISTING 26.1 L26-1.ASM**
|
**LISTING 26.1 L26-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate operation of write mode 3 of the VGA.
|
; Program to illustrate operation of write mode 3 of the VGA.
|
||||||
; Draws 8x8 characters at arbitrary locations without disturbing
|
; Draws 8x8 characters at arbitrary locations without disturbing
|
||||||
; the background, using VGA's 8x8 ROM font. Designed
|
; the background, using VGA's 8x8 ROM font. Designed
|
||||||
|
|
@ -305,3 +306,4 @@ pages: 484-489
|
||||||
;
|
;
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
2
26-03.md
2
26-03.md
|
|
@ -80,6 +80,7 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
|
|
||||||
**LISTING 26.2 L26-2.ASM**
|
**LISTING 26.2 L26-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate high-speed text-drawing operation of
|
; Program to illustrate high-speed text-drawing operation of
|
||||||
; write mode 3 of the VGA.
|
; write mode 3 of the VGA.
|
||||||
; Draws a string of 8x14 characters at arbitrary locations
|
; Draws a string of 8x14 characters at arbitrary locations
|
||||||
|
|
@ -398,6 +399,7 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
;
|
;
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
||||||
In this chapter, I've tried to give you a feel for how write mode 3
|
In this chapter, I've tried to give you a feel for how write mode 3
|
||||||
works and what it might be used for, rather than providing polished,
|
works and what it might be used for, rather than providing polished,
|
||||||
|
|
|
||||||
2
27-02.md
2
27-02.md
|
|
@ -52,6 +52,7 @@ image.
|
||||||
|
|
||||||
**LISTING 27.1 L27-1.ASM**
|
**LISTING 27.1 L27-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
||||||
; animating the image of an "A" drawn by copying it from a chunky
|
; animating the image of an "A" drawn by copying it from a chunky
|
||||||
; bit-map in system memory to a planar bit-map in VGA or EGA memory.
|
; bit-map in system memory to a planar bit-map in VGA or EGA memory.
|
||||||
|
|
@ -269,3 +270,4 @@ image.
|
||||||
DrawFromChunkyBitmap endp
|
DrawFromChunkyBitmap endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
2
27-03.md
2
27-03.md
|
|
@ -53,6 +53,7 @@ the CPU byte in write mode 2 to select the color in which to draw.
|
||||||
|
|
||||||
**LISTING 27.2 L27-2.ASM**
|
**LISTING 27.2 L27-2.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
||||||
; drawing lines in color patterns.
|
; drawing lines in color patterns.
|
||||||
;
|
;
|
||||||
|
|
@ -379,3 +380,4 @@ the CPU byte in write mode 2 to select the color in which to draw.
|
||||||
Start endp
|
Start endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
2
27-05.md
2
27-05.md
|
|
@ -53,6 +53,7 @@ rewarding!) VGA to cover.
|
||||||
|
|
||||||
**LISTING 27.3 L27-3.ASM**
|
**LISTING 27.3 L27-3.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate flipping from bit-mapped graphics mode to
|
; Program to illustrate flipping from bit-mapped graphics mode to
|
||||||
; text mode and back without losing any of the graphics bit-map.
|
; text mode and back without losing any of the graphics bit-map.
|
||||||
;
|
;
|
||||||
|
|
@ -244,3 +245,4 @@ rewarding!) VGA to cover.
|
||||||
Start endp
|
Start endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
2
28-02.md
2
28-02.md
|
|
@ -12,6 +12,7 @@ pages: 526-530
|
||||||
|
|
||||||
**LISTING 28.1 L28-1.ASM**
|
**LISTING 28.1 L28-1.ASM**
|
||||||
|
|
||||||
|
```nasm
|
||||||
; Program to illustrate the use of the Read Map register in read mode 0.
|
; Program to illustrate the use of the Read Map register in read mode 0.
|
||||||
; Animates by copying a 16-color image from VGA memory to system memory,
|
; Animates by copying a 16-color image from VGA memory to system memory,
|
||||||
; one plane at a time, then copying the image back to a new location
|
; one plane at a time, then copying the image back to a new location
|
||||||
|
|
@ -247,3 +248,4 @@ pages: 526-530
|
||||||
GetImageOffset endp
|
GetImageOffset endp
|
||||||
code ends
|
code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue