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
20
01-02.md
20
01-02.md
|
|
@ -82,15 +82,16 @@ 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
|
/*
|
||||||
* specified file. Obtains the bytes one at a time via read(),
|
* Program to calculate the 16-bit checksum of all bytes in the
|
||||||
* letting DOS perform all data buffering.
|
* specified file. Obtains the bytes one at a time via read(),
|
||||||
*/
|
* letting DOS perform all data buffering.
|
||||||
#include <stdio.h>
|
*/
|
||||||
#include <fcntl.h>
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
int Handle;
|
int Handle;
|
||||||
unsigned char Byte;
|
unsigned char Byte;
|
||||||
unsigned int Checksum;
|
unsigned int Checksum;
|
||||||
|
|
@ -121,7 +122,8 @@ It's *slow*.
|
||||||
/* Report the result */
|
/* Report the result */
|
||||||
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
|
||||||
|
|
|
||||||
86
01-03.md
86
01-03.md
|
|
@ -30,16 +30,17 @@ 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
|
/*
|
||||||
* from the specified file. Obtains the bytes one at a time in
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* assembler, via direct calls to DOS.
|
* from the specified file. Obtains the bytes one at a time in
|
||||||
*/
|
* assembler, via direct calls to DOS.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
int Handle;
|
int Handle;
|
||||||
unsigned char Byte;
|
unsigned char Byte;
|
||||||
unsigned int Checksum;
|
unsigned int Checksum;
|
||||||
|
|
@ -61,74 +62,77 @@ disk caching turned off.
|
||||||
/* Report the result */
|
/* Report the result */
|
||||||
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**
|
||||||
|
|
||||||
; Assembler subroutine to perform a 16-bit checksum on the file
|
```nasm
|
||||||
; opened on the passed-in handle. Stores the result in the
|
; Assembler subroutine to perform a 16-bit checksum on the file
|
||||||
; passed-in checksum variable. Returns 1 for success, 0 for error.
|
; opened on the passed-in handle. Stores the result in the
|
||||||
;
|
; passed-in checksum variable. Returns 1 for success, 0 for error.
|
||||||
; Call as:
|
;
|
||||||
; int ChecksumFile(unsigned int Handle, unsigned int *Checksum);
|
; Call as:
|
||||||
;
|
; int ChecksumFile(unsigned int Handle, unsigned int *Checksum);
|
||||||
; where:
|
;
|
||||||
; Handle = handle # under which file to checksum is open
|
; where:
|
||||||
; Checksum = pointer to unsigned int variable checksum is
|
; Handle = handle # under which file to checksum is open
|
||||||
; to be stored in
|
; Checksum = pointer to unsigned int variable checksum is
|
||||||
;
|
; to be stored in
|
||||||
; Parameter structure:
|
;
|
||||||
;
|
; Parameter structure:
|
||||||
Parms struc
|
;
|
||||||
|
Parms struc
|
||||||
dw ? ;pushed BP
|
dw ? ;pushed BP
|
||||||
dw ? ;return address
|
dw ? ;return address
|
||||||
Handle dw ?
|
Handle dw ?
|
||||||
Checksum dw ?
|
Checksum dw ?
|
||||||
Parms ends
|
Parms ends
|
||||||
;
|
;
|
||||||
.model small
|
.model small
|
||||||
.data
|
.data
|
||||||
TempWord label word
|
TempWord label word
|
||||||
TempByte db ? ;each byte read by DOS will be stored here
|
TempByte db ? ;each byte read by DOS will be stored here
|
||||||
db 0 ;high byte of TempWord is always 0
|
db 0 ;high byte of TempWord is always 0
|
||||||
;for 16-bit adds
|
;for 16-bit adds
|
||||||
;
|
;
|
||||||
.code
|
.code
|
||||||
public _ChecksumFile
|
public _ChecksumFile
|
||||||
_ChecksumFile proc near
|
_ChecksumFile proc near
|
||||||
push bp
|
push bp
|
||||||
mov bp,sp
|
mov bp,sp
|
||||||
push si ;save C's register variable
|
push si ;save C's register variable
|
||||||
;
|
;
|
||||||
mov bx,[bp+Handle] ;get file handle
|
mov bx,[bp+Handle] ;get file handle
|
||||||
sub si,si ;zero the checksum ;accumulator
|
sub si,si ;zero the checksum ;accumulator
|
||||||
mov cx,1 ;request one byte on each ;read
|
mov cx,1 ;request one byte on each ;read
|
||||||
mov dx,offset TempByte ;point DX to the byte in
|
mov dx,offset TempByte ;point DX to the byte in
|
||||||
;which DOS should store
|
;which DOS should store
|
||||||
;each byte read
|
;each byte read
|
||||||
ChecksumLoop:
|
ChecksumLoop:
|
||||||
mov ah,3fh ;DOS read file function #
|
mov ah,3fh ;DOS read file function #
|
||||||
int 21h ;read the byte
|
int 21h ;read the byte
|
||||||
jcErrorEnd;an error occurred
|
jcErrorEnd;an error occurred
|
||||||
and ax,ax ;any bytes read?
|
and ax,ax ;any bytes read?
|
||||||
jz Success ;no-end of file reached-we're done
|
jz Success ;no-end of file reached-we're done
|
||||||
add si,[TempWord] ;add the byte into the
|
add si,[TempWord] ;add the byte into the
|
||||||
;checksum total
|
;checksum total
|
||||||
jmpChecksumLoop
|
jmpChecksumLoop
|
||||||
ErrorEnd:
|
ErrorEnd:
|
||||||
sub ax,ax ;error
|
sub ax,ax ;error
|
||||||
jmp short Done
|
jmp short Done
|
||||||
Success:
|
Success:
|
||||||
mov bx,[bp+Checksum] ;point to the checksum variable
|
mov bx,[bp+Checksum] ;point to the checksum variable
|
||||||
mov [bx],si ;save the new checksum
|
mov [bx],si ;save the new checksum
|
||||||
mov ax,1 ;success
|
mov ax,1 ;success
|
||||||
;
|
;
|
||||||
Done:
|
Done:
|
||||||
pop si ;restore C's register variable
|
pop si ;restore C's register variable
|
||||||
pop bp
|
pop bp
|
||||||
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.
|
||||||
|
|
|
||||||
18
01-04.md
18
01-04.md
|
|
@ -20,14 +20,15 @@ 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
|
/*
|
||||||
* from the specified file. Obtains the bytes one at a time via
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* getc(), allowing C to perform data buffering.
|
* from the specified file. Obtains the bytes one at a time via
|
||||||
*/
|
* getc(), allowing C to perform data buffering.
|
||||||
#include <stdio.h>
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
FILE *CheckFile;
|
FILE *CheckFile;
|
||||||
int Byte;
|
int Byte;
|
||||||
unsigned int Checksum;
|
unsigned int Checksum;
|
||||||
|
|
@ -52,7 +53,8 @@ libraries do their work. In other words, *know the territory*!
|
||||||
/* Report the result */
|
/* Report the result */
|
||||||
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}
|
||||||
|
|
||||||
|
|
|
||||||
50
01-05.md
50
01-05.md
|
|
@ -30,19 +30,20 @@ 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
|
/*
|
||||||
* from the specified file. Buffers the bytes internally, rather
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* than letting C or DOS do the work.
|
* from the specified file. Buffers the bytes internally, rather
|
||||||
*/
|
* than letting C or DOS do the work.
|
||||||
#include <stdio.h>
|
*/
|
||||||
#include <fcntl.h>
|
#include <stdio.h>
|
||||||
#include <alloc.h> /* alloc.h for Borland,
|
#include <fcntl.h>
|
||||||
|
#include <alloc.h> /* alloc.h for Borland,
|
||||||
malloc.h for Microsoft */
|
malloc.h for Microsoft */
|
||||||
|
|
||||||
#define BUFFER_SIZE 0x8000 /* 32Kb data buffer */
|
#define BUFFER_SIZE 0x8000 /* 32Kb data buffer */
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
int Handle;
|
int Handle;
|
||||||
unsigned int Checksum;
|
unsigned int Checksum;
|
||||||
unsigned char *WorkingBuffer, *WorkingPtr;
|
unsigned char *WorkingBuffer, *WorkingPtr;
|
||||||
|
|
@ -85,7 +86,8 @@ uses no assembly at all.
|
||||||
/* Report the result */
|
/* Report the result */
|
||||||
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,20 +124,21 @@ 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
|
/*
|
||||||
* from the specified file. Buffers the bytes internally, rather
|
* Program to calculate the 16-bit checksum of the stream of bytes
|
||||||
* than letting C or DOS do the work, with the time-critical
|
* from the specified file. Buffers the bytes internally, rather
|
||||||
* portion of the code written in optimized assembler.
|
* than letting C or DOS do the work, with the time-critical
|
||||||
*/
|
* portion of the code written in optimized assembler.
|
||||||
#include <stdio.h>
|
*/
|
||||||
#include <fcntl.h>
|
#include <stdio.h>
|
||||||
#include <alloc.h> /* alloc.h for Borland,
|
#include <fcntl.h>
|
||||||
|
#include <alloc.h> /* alloc.h for Borland,
|
||||||
malloc.h for Microsoft */
|
malloc.h for Microsoft */
|
||||||
|
|
||||||
#define BUFFER_SIZE 0x8000 /* 32K data buffer */
|
#define BUFFER_SIZE 0x8000 /* 32K data buffer */
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
int Handle;
|
int Handle;
|
||||||
unsigned int Checksum;
|
unsigned int Checksum;
|
||||||
unsigned char *WorkingBuffer;
|
unsigned char *WorkingBuffer;
|
||||||
|
|
@ -174,4 +177,5 @@ the design has been maxed out.
|
||||||
/* Report the result */
|
/* Report the result */
|
||||||
printf("The checksum is: %u\n", Checksum);
|
printf("The checksum is: %u\n", Checksum);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
56
01-06.md
56
01-06.md
|
|
@ -12,55 +12,57 @@ pages: 018-019
|
||||||
|
|
||||||
**LISTING 1.7 L1-7.ASM**
|
**LISTING 1.7 L1-7.ASM**
|
||||||
|
|
||||||
; Assembler subroutine to perform a 16-bit checksum on a block of
|
```nasm
|
||||||
; bytes 1 to 64K in size. Adds checksum for block into passed-in
|
; Assembler subroutine to perform a 16-bit checksum on a block of
|
||||||
; checksum.
|
; bytes 1 to 64K in size. Adds checksum for block into passed-in
|
||||||
;
|
; checksum.
|
||||||
; Call as:
|
;
|
||||||
; void ChecksumChunk(unsigned char *Buffer,
|
; Call as:
|
||||||
; unsigned int BufferLength, unsigned int *Checksum);
|
; void ChecksumChunk(unsigned char *Buffer,
|
||||||
;
|
; unsigned int BufferLength, unsigned int *Checksum);
|
||||||
; where:
|
;
|
||||||
; Buffer = pointer to start of block of bytes to checksum
|
; where:
|
||||||
; BufferLength = # of bytes to checksum (0 means 64K, not 0)
|
; Buffer = pointer to start of block of bytes to checksum
|
||||||
; Checksum = pointer to unsigned int variable checksum is
|
; BufferLength = # of bytes to checksum (0 means 64K, not 0)
|
||||||
;stored in
|
; Checksum = pointer to unsigned int variable checksum is
|
||||||
;
|
;stored in
|
||||||
; Parameter structure:
|
;
|
||||||
;
|
; Parameter structure:
|
||||||
Parms struc
|
;
|
||||||
|
Parms struc
|
||||||
dw ? ;pushed BP
|
dw ? ;pushed BP
|
||||||
dw ? ;return address
|
dw ? ;return address
|
||||||
Buffer dw ?
|
Buffer dw ?
|
||||||
BufferLength dw ?
|
BufferLength dw ?
|
||||||
Checksum dw ?
|
Checksum dw ?
|
||||||
Parmsends
|
Parmsends
|
||||||
;
|
;
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _ChecksumChunk
|
public _ChecksumChunk
|
||||||
_ChecksumChunkprocnear
|
_ChecksumChunkprocnear
|
||||||
push bp
|
push bp
|
||||||
mov bp,sp
|
mov bp,sp
|
||||||
push si ;save C's register variable
|
push si ;save C's register variable
|
||||||
;
|
;
|
||||||
cld ;make LODSB increment SI
|
cld ;make LODSB increment SI
|
||||||
mov si,[bp+Buffer] ;point to buffer
|
mov si,[bp+Buffer] ;point to buffer
|
||||||
mov cx,[bp+BufferLength] ;get buffer length
|
mov cx,[bp+BufferLength] ;get buffer length
|
||||||
mov bx,[bp+Checksum] ;point to checksum variable
|
mov bx,[bp+Checksum] ;point to checksum variable
|
||||||
mov dx,[bx] ;get the current checksum
|
mov dx,[bx] ;get the current checksum
|
||||||
sub ah,ah ;so AX will be a 16-bit value after LODSB
|
sub ah,ah ;so AX will be a 16-bit value after LODSB
|
||||||
ChecksumLoop:
|
ChecksumLoop:
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
add dx,ax ;add it into the checksum total
|
add dx,ax ;add it into the checksum total
|
||||||
loop ChecksumLoop ;continue for all bytes in block
|
loop ChecksumLoop ;continue for all bytes in block
|
||||||
mov [bx],dx ;save the new checksum
|
mov [bx],dx ;save the new checksum
|
||||||
;
|
;
|
||||||
pop si ;restore C's register variable
|
pop si ;restore C's register variable
|
||||||
pop bp
|
pop bp
|
||||||
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.
|
||||||
|
|
|
||||||
8
02-01.md
8
02-01.md
|
|
@ -45,7 +45,8 @@ 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:
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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
|
||||||
rol al,cl ;rotate the bit into the desired position
|
rol al,cl ;rotate the bit into the desired position
|
||||||
|
|
@ -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,7 +70,8 @@ total of four separate time-consuming multibit rotations!
|
||||||
|
|
||||||
I changed the code to the following:
|
I changed the code to the following:
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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
|
||||||
or bl,al ;insert the bit into the final nibble
|
or bl,al ;insert the bit into the final nibble
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
456
03-02.md
456
03-02.md
|
|
@ -12,125 +12,126 @@ pages: 035-042
|
||||||
|
|
||||||
**LISTING 3.1 PZTIMER.ASM**
|
**LISTING 3.1 PZTIMER.ASM**
|
||||||
|
|
||||||
; The precision Zen timer (PZTIMER.ASM)
|
```nasm
|
||||||
;
|
; The precision Zen timer (PZTIMER.ASM)
|
||||||
; Uses the 8253 timer to time the performance of code that takes
|
;
|
||||||
; less than about 54 milliseconds to execute, with a resolution
|
; Uses the 8253 timer to time the performance of code that takes
|
||||||
; of better than 10 microseconds.
|
; less than about 54 milliseconds to execute, with a resolution
|
||||||
;
|
; of better than 10 microseconds.
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
; Externally callable routines:
|
;
|
||||||
;
|
; Externally callable routines:
|
||||||
; ZTimerOn: Starts the Zen timer, with interrupts disabled.
|
;
|
||||||
;
|
; ZTimerOn: Starts the Zen timer, with interrupts disabled.
|
||||||
; ZTimerOff: Stops the Zen timer, saves the timer count,
|
;
|
||||||
; times the overhead code, and restores interrupts to the
|
; ZTimerOff: Stops the Zen timer, saves the timer count,
|
||||||
; state they were in when ZTimerOn was called.
|
; times the overhead code, and restores interrupts to the
|
||||||
;
|
; state they were in when ZTimerOn was called.
|
||||||
; ZTimerReport: Prints the net time that passed between starting
|
;
|
||||||
; and stopping the timer.
|
; ZTimerReport: Prints the net time that passed between starting
|
||||||
;
|
; and stopping the timer.
|
||||||
; Note: If longer than about 54 ms passes between ZTimerOn and
|
;
|
||||||
; ZTimerOff calls, the timer turns over and the count is
|
; Note: If longer than about 54 ms passes between ZTimerOn and
|
||||||
; inaccurate. When this happens, an error message is displayed
|
; ZTimerOff calls, the timer turns over and the count is
|
||||||
; instead of a count. The long-period Zen timer should be used
|
; inaccurate. When this happens, an error message is displayed
|
||||||
; in such cases.
|
; instead of a count. The long-period Zen timer should be used
|
||||||
;
|
; in such cases.
|
||||||
; Note: Interrupts *MUST* be left off between calls to ZTimerOn
|
;
|
||||||
; and ZTimerOff for accurate timing and for detection of
|
; Note: Interrupts *MUST* be left off between calls to ZTimerOn
|
||||||
; timer overflow.
|
; and ZTimerOff for accurate timing and for detection of
|
||||||
;
|
; timer overflow.
|
||||||
; Note: These routines can introduce slight inaccuracies into the
|
;
|
||||||
; system clock count for each code section timed even if
|
; Note: These routines can introduce slight inaccuracies into the
|
||||||
; timer 0 doesn't overflow. If timer 0 does overflow, the
|
; system clock count for each code section timed even if
|
||||||
; system clock can become slow by virtually any amount of
|
; timer 0 doesn't overflow. If timer 0 does overflow, the
|
||||||
; time, since the system clock can't advance while the
|
; system clock can become slow by virtually any amount of
|
||||||
; precison timer is timing. Consequently, it's a good idea
|
; time, since the system clock can't advance while the
|
||||||
; to reboot at the end of each timing session. (The
|
; precison timer is timing. Consequently, it's a good idea
|
||||||
; battery-backed clock, if any, is not affected by the Zen
|
; to reboot at the end of each timing session. (The
|
||||||
; timer.)
|
; battery-backed clock, if any, is not affected by the Zen
|
||||||
;
|
; timer.)
|
||||||
; All registers, and all flags except the interrupt flag, are
|
;
|
||||||
; preserved by all routines. Interrupts are enabled and then disabled
|
; All registers, and all flags except the interrupt flag, are
|
||||||
; by ZTimerOn, and are restored by ZTimerOff to the state they were
|
; preserved by all routines. Interrupts are enabled and then disabled
|
||||||
; in when ZTimerOn was called.
|
; by ZTimerOn, and are restored by ZTimerOff to the state they were
|
||||||
;
|
; in when ZTimerOn was called.
|
||||||
|
;
|
||||||
|
|
||||||
Code segment word public ‘CODE'
|
Code segment word public ‘CODE'
|
||||||
assumecs: Code, ds:nothing
|
assumecs: Code, ds:nothing
|
||||||
public ZTimerOn, ZTimerOff, ZTimerReport
|
public ZTimerOn, ZTimerOff, ZTimerReport
|
||||||
|
|
||||||
;
|
;
|
||||||
; Base address of the 8253 timer chip.
|
; Base address of the 8253 timer chip.
|
||||||
;
|
;
|
||||||
BASE_8253equ40h
|
BASE_8253equ40h
|
||||||
;
|
;
|
||||||
; The address of the timer 0 count registers in the 8253.
|
; The address of the timer 0 count registers in the 8253.
|
||||||
;
|
;
|
||||||
TIMER_0_8253 equBASE_8253 + 0
|
TIMER_0_8253 equBASE_8253 + 0
|
||||||
;
|
;
|
||||||
; The address of the mode register in the 8253.
|
; The address of the mode register in the 8253.
|
||||||
;
|
;
|
||||||
MODE_8253 equBASE_8253 + 3
|
MODE_8253 equBASE_8253 + 3
|
||||||
;
|
;
|
||||||
; The address of Operation Command Word 3 in the 8259 Programmable
|
; The address of Operation Command Word 3 in the 8259 Programmable
|
||||||
; Interrupt Controller (PIC) (write only, and writable only when
|
; Interrupt Controller (PIC) (write only, and writable only when
|
||||||
; bit 4 of the byte written to this address is 0 and bit 3 is 1).
|
; bit 4 of the byte written to this address is 0 and bit 3 is 1).
|
||||||
;
|
;
|
||||||
OCW3 equ20h
|
OCW3 equ20h
|
||||||
;
|
;
|
||||||
; The address of the Interrupt Request register in the 8259 PIC
|
; The address of the Interrupt Request register in the 8259 PIC
|
||||||
; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
|
; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
|
||||||
; of OCW3 = 0).
|
; of OCW3 = 0).
|
||||||
;
|
;
|
||||||
IRR equ20h
|
IRR equ20h
|
||||||
;
|
;
|
||||||
; Macro to emulate a POPF instruction in order to fix the bug in some
|
; Macro to emulate a POPF instruction in order to fix the bug in some
|
||||||
; 80286 chips which allows interrupts to occur during a POPF even when
|
; 80286 chips which allows interrupts to occur during a POPF even when
|
||||||
; interrupts remain disabled.
|
; interrupts remain disabled.
|
||||||
;
|
;
|
||||||
MPOPF macro
|
MPOPF macro
|
||||||
local p1, p2
|
local p1, p2
|
||||||
jmp short p2
|
jmp short p2
|
||||||
p1: iret ; jump to pushed address & pop flags
|
p1: iret ; jump to pushed address & pop flags
|
||||||
p2: push cs ; construct far return address to
|
p2: push cs ; construct far return address to
|
||||||
call p1 ; the next instruction
|
call p1 ; the next instruction
|
||||||
endm
|
endm
|
||||||
|
|
||||||
;
|
;
|
||||||
; Macro to delay briefly to ensure that enough time has elapsed
|
; Macro to delay briefly to ensure that enough time has elapsed
|
||||||
; between successive I/O accesses so that the device being accessed
|
; between successive I/O accesses so that the device being accessed
|
||||||
; can respond to both accesses even on a very fast PC.
|
; can respond to both accesses even on a very fast PC.
|
||||||
;
|
;
|
||||||
DELAY macro
|
DELAY macro
|
||||||
jmp $+2
|
jmp $+2
|
||||||
jmp $+2
|
jmp $+2
|
||||||
jmp $+2
|
jmp $+2
|
||||||
endm
|
endm
|
||||||
|
|
||||||
OriginalFlags db ? ; storage for upper byte of
|
OriginalFlags db ? ; storage for upper byte of
|
||||||
; FLAGS register when
|
; FLAGS register when
|
||||||
; ZTimerOn called
|
; ZTimerOn called
|
||||||
TimedCount dw ? ; timer 0 count when the timer
|
TimedCount dw ? ; timer 0 count when the timer
|
||||||
; is stopped
|
; is stopped
|
||||||
ReferenceCount dw ; number of counts required to
|
ReferenceCount dw ; number of counts required to
|
||||||
; execute timer overhead code
|
; execute timer overhead code
|
||||||
OverflowFlag db ? ; used to indicate whether the
|
OverflowFlag db ? ; used to indicate whether the
|
||||||
; timer overflowed during the
|
; timer overflowed during the
|
||||||
; timing interval
|
; timing interval
|
||||||
;
|
;
|
||||||
; String printed to report results.
|
; String printed to report results.
|
||||||
;
|
;
|
||||||
OutputStr label byte
|
OutputStr label byte
|
||||||
db 0dh, 0ah, ‘Timed count: ‘, 5 dup (?)
|
db 0dh, 0ah, ‘Timed count: ‘, 5 dup (?)
|
||||||
ASCIICountEnd labelbyte
|
ASCIICountEnd labelbyte
|
||||||
db ‘ microseconds', 0dh, 0ah
|
db ‘ microseconds', 0dh, 0ah
|
||||||
db ‘$'
|
db ‘$'
|
||||||
;
|
;
|
||||||
; String printed to report timer overflow.
|
; String printed to report timer overflow.
|
||||||
;
|
;
|
||||||
OverflowStr label byte
|
OverflowStr label byte
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘****************************************************'
|
db ‘****************************************************'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
|
|
@ -139,22 +140,22 @@ pages: 035-042
|
||||||
db ‘* too long for the precision timer to measure. *'
|
db ‘* too long for the precision timer to measure. *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘* Please perform the timing test again with the *'
|
db ‘* Please perform the timing test again with the *'
|
||||||
db0dh, 0ah
|
db0dh, 0ah
|
||||||
db ‘* long-period timer. *'
|
db ‘* long-period timer. *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘****************************************************'
|
db ‘****************************************************'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘$'
|
db ‘$'
|
||||||
|
|
||||||
; ********************************************************************
|
; ********************************************************************
|
||||||
; * Routine called to start timing. *
|
; * Routine called to start timing. *
|
||||||
; ********************************************************************
|
; ********************************************************************
|
||||||
|
|
||||||
ZTimerOn proc near
|
ZTimerOn proc near
|
||||||
|
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
push ax
|
push ax
|
||||||
pushf
|
pushf
|
||||||
pop ax ; get flags so we can keep
|
pop ax ; get flags so we can keep
|
||||||
|
|
@ -165,47 +166,47 @@ pages: 035-042
|
||||||
and ah,0fdh ; set pushed interrupt flag
|
and ah,0fdh ; set pushed interrupt flag
|
||||||
; to 0
|
; to 0
|
||||||
push ax
|
push ax
|
||||||
;
|
;
|
||||||
; Turn on interrupts, so the timer interrupt can occur if it's
|
; Turn on interrupts, so the timer interrupt can occur if it's
|
||||||
; pending.
|
; pending.
|
||||||
;
|
;
|
||||||
sti
|
sti
|
||||||
;
|
;
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
||||||
; linear counting rather than count-by-two counting. Also
|
; linear counting rather than count-by-two counting. Also
|
||||||
; leaves the 8253 waiting for the initial timer 0 count to
|
; leaves the 8253 waiting for the initial timer 0 count to
|
||||||
; be loaded.
|
; be loaded.
|
||||||
;
|
;
|
||||||
mov al,00110100b ;mode 2
|
mov al,00110100b ;mode 2
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
;
|
;
|
||||||
; Set the timer count to 0, so we know we won't get another
|
; Set the timer count to 0, so we know we won't get another
|
||||||
; timer interrupt right away.
|
; timer interrupt right away.
|
||||||
; Note: this introduces an inaccuracy of up to 54 ms in the system
|
; Note: this introduces an inaccuracy of up to 54 ms in the system
|
||||||
; clock count each time it is executed.
|
; clock count each time it is executed.
|
||||||
;
|
;
|
||||||
DELAY
|
DELAY
|
||||||
sub al,al
|
sub al,al
|
||||||
out TIMER_0_8253,al ;lsb
|
out TIMER_0_8253,al ;lsb
|
||||||
DELAY
|
DELAY
|
||||||
out TIMER_0_8253,al ;msb
|
out TIMER_0_8253,al ;msb
|
||||||
;
|
;
|
||||||
; Wait before clearing interrupts to allow the interrupt generated
|
; Wait before clearing interrupts to allow the interrupt generated
|
||||||
; when switching from mode 3 to mode 2 to be recognized. The delay
|
; when switching from mode 3 to mode 2 to be recognized. The delay
|
||||||
; must be at least 210 ns long to allow time for that interrupt to
|
; must be at least 210 ns long to allow time for that interrupt to
|
||||||
; occur. Here, 10 jumps are used for the delay to ensure that the
|
; occur. Here, 10 jumps are used for the delay to ensure that the
|
||||||
; delay time will be more than long enough even on a very fast PC.
|
; delay time will be more than long enough even on a very fast PC.
|
||||||
;
|
;
|
||||||
rept 10
|
rept 10
|
||||||
jmp $+2
|
jmp $+2
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
; Disable interrupts to get an accurate count.
|
; Disable interrupts to get an accurate count.
|
||||||
;
|
;
|
||||||
cli
|
cli
|
||||||
;
|
;
|
||||||
; Set the timer count to 0 again to start the timing interval.
|
; Set the timer count to 0 again to start the timing interval.
|
||||||
;
|
;
|
||||||
mov al,00110100b ; set up to load initial
|
mov al,00110100b ; set up to load initial
|
||||||
out MODE_8253,al ; timer count
|
out MODE_8253,al ; timer count
|
||||||
DELAY
|
DELAY
|
||||||
|
|
@ -213,36 +214,36 @@ pages: 035-042
|
||||||
out TIMER_0_8253,al ; load count lsb
|
out TIMER_0_8253,al ; load count lsb
|
||||||
DELAY
|
DELAY
|
||||||
out TIMER_0_8253,al; load count msb
|
out TIMER_0_8253,al; load count msb
|
||||||
;
|
;
|
||||||
; Restore the context and return.
|
; Restore the context and return.
|
||||||
;
|
;
|
||||||
MPOPF ; keeps interrupts off
|
MPOPF ; keeps interrupts off
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerOn endp
|
ZTimerOn endp
|
||||||
|
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
;* Routine called to stop timing and get count. *
|
;* Routine called to stop timing and get count. *
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
|
|
||||||
ZTimerOff proc near
|
ZTimerOff proc near
|
||||||
|
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
push ax
|
push ax
|
||||||
push cx
|
push cx
|
||||||
pushf
|
pushf
|
||||||
;
|
;
|
||||||
; Latch the count.
|
; Latch the count.
|
||||||
;
|
;
|
||||||
mov al,00000000b ; latch timer 0
|
mov al,00000000b ; latch timer 0
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
;
|
;
|
||||||
; See if the timer has overflowed by checking the 8259 for a pending
|
; See if the timer has overflowed by checking the 8259 for a pending
|
||||||
; timer interrupt.
|
; timer interrupt.
|
||||||
;
|
;
|
||||||
mov al,00001010b ; OCW3, set up to read
|
mov al,00001010b ; OCW3, set up to read
|
||||||
out OCW3,al; Int errupt Request register
|
out OCW3,al; Int errupt Request register
|
||||||
DELAY
|
DELAY
|
||||||
|
|
@ -252,13 +253,13 @@ pages: 035-042
|
||||||
; timer interrupt) is pending
|
; timer interrupt) is pending
|
||||||
mov cs:[OverflowFlag],al; store the timer overflow
|
mov cs:[OverflowFlag],al; store the timer overflow
|
||||||
; status
|
; status
|
||||||
;
|
;
|
||||||
; Allow interrupts to happen again.
|
; Allow interrupts to happen again.
|
||||||
;
|
;
|
||||||
sti
|
sti
|
||||||
;
|
;
|
||||||
; Read out the count we latched earlier.
|
; Read out the count we latched earlier.
|
||||||
;
|
;
|
||||||
in al,TIMER_0_8253 ; least significant byte
|
in al,TIMER_0_8253 ; least significant byte
|
||||||
DELAY
|
DELAY
|
||||||
mov ah,al
|
mov ah,al
|
||||||
|
|
@ -268,10 +269,10 @@ pages: 035-042
|
||||||
; remaining to elapsed
|
; remaining to elapsed
|
||||||
; count
|
; count
|
||||||
mov cs:[TimedCount],ax
|
mov cs:[TimedCount],ax
|
||||||
; Time a zero-length code fragment, to get a reference for how
|
; Time a zero-length code fragment, to get a reference for how
|
||||||
; much overhead this routine has. Time it 16 times and average it,
|
; much overhead this routine has. Time it 16 times and average it,
|
||||||
; for accuracy, rounding the result.
|
; for accuracy, rounding the result.
|
||||||
;
|
;
|
||||||
mov cs:[ReferenceCount],0
|
mov cs:[ReferenceCount],0
|
||||||
mov cx,16
|
mov cx,16
|
||||||
cli ; interrupts off to allow a
|
cli ; interrupts off to allow a
|
||||||
|
|
@ -284,9 +285,9 @@ pages: 035-042
|
||||||
add cs:[ReferenceCount],8; total + (0.5 * 16)
|
add cs:[ReferenceCount],8; total + (0.5 * 16)
|
||||||
mov cl,4
|
mov cl,4
|
||||||
shr cs:[ReferenceCount],cl; (total) / 16 + 0.5
|
shr cs:[ReferenceCount],cl; (total) / 16 + 0.5
|
||||||
;
|
;
|
||||||
; Restore original interrupt state.
|
; Restore original interrupt state.
|
||||||
;
|
;
|
||||||
pop ax ; retrieve flags when called
|
pop ax ; retrieve flags when called
|
||||||
mov ch,cs:[OriginalFlags] ; get back the original upper
|
mov ch,cs:[OriginalFlags] ; get back the original upper
|
||||||
; byte of the FLAGS register
|
; byte of the FLAGS register
|
||||||
|
|
@ -297,65 +298,65 @@ pages: 035-042
|
||||||
or ah,ch ; make flags word with original
|
or ah,ch ; make flags word with original
|
||||||
; interrupt flag
|
; interrupt flag
|
||||||
push ax ; prepare flags to be popped
|
push ax ; prepare flags to be popped
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
MPOPF ; restore the flags with the
|
MPOPF ; restore the flags with the
|
||||||
; original interrupt state
|
; original interrupt state
|
||||||
pop cx
|
pop cx
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerOff endp
|
ZTimerOff endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Called by ZTimerOff to start timer for overhead measurements.
|
; Called by ZTimerOff to start timer for overhead measurements.
|
||||||
;
|
;
|
||||||
|
|
||||||
ReferenceZTimerOnproc near
|
ReferenceZTimerOnproc near
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
push ax
|
push ax
|
||||||
pushf ; interrupts are already off
|
pushf ; interrupts are already off
|
||||||
;
|
;
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
||||||
; linear counting rather than count-by-two counting.
|
; linear counting rather than count-by-two counting.
|
||||||
;
|
;
|
||||||
mov al,00110100b ; set up to load
|
mov al,00110100b ; set up to load
|
||||||
out MODE_8253,al ; initial timer count
|
out MODE_8253,al ; initial timer count
|
||||||
DELAY
|
DELAY
|
||||||
;
|
;
|
||||||
; Set the timer count to 0.
|
; Set the timer count to 0.
|
||||||
;
|
;
|
||||||
sub al,al
|
sub al,al
|
||||||
out TIMER_0_8253,al; load count lsb
|
out TIMER_0_8253,al; load count lsb
|
||||||
DELAY
|
DELAY
|
||||||
out TIMER_0_8253,al; load count msb
|
out TIMER_0_8253,al; load count msb
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
MPOPF
|
MPOPF
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ReferenceZTimerOnendp
|
ReferenceZTimerOnendp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Called by ZTimerOff to stop timer and add result to ReferenceCount
|
; Called by ZTimerOff to stop timer and add result to ReferenceCount
|
||||||
; for overhead measurements.
|
; for overhead measurements.
|
||||||
;
|
;
|
||||||
|
|
||||||
ReferenceZTimerOff proc near
|
ReferenceZTimerOff proc near
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
push ax
|
push ax
|
||||||
push cx
|
push cx
|
||||||
pushf
|
pushf
|
||||||
;
|
;
|
||||||
; Latch the count and read it.
|
; Latch the count and read it.
|
||||||
;
|
;
|
||||||
mov al,00000000b ; latch timer 0
|
mov al,00000000b ; latch timer 0
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
DELAY
|
DELAY
|
||||||
|
|
@ -368,21 +369,21 @@ pages: 035-042
|
||||||
; remaining to amount
|
; remaining to amount
|
||||||
; counted down
|
; counted down
|
||||||
add cs:[ReferenceCount],ax
|
add cs:[ReferenceCount],ax
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
MPOPF
|
MPOPF
|
||||||
pop cx
|
pop cx
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ReferenceZTimerOff endp
|
ReferenceZTimerOff endp
|
||||||
|
|
||||||
; ********************************************************************
|
; ********************************************************************
|
||||||
; * Routine called to report timing results. *
|
; * Routine called to report timing results. *
|
||||||
; ********************************************************************
|
; ********************************************************************
|
||||||
|
|
||||||
ZTimerReport procnear
|
ZTimerReport procnear
|
||||||
|
|
||||||
pushf
|
pushf
|
||||||
push ax
|
push ax
|
||||||
|
|
@ -391,53 +392,53 @@ pages: 035-042
|
||||||
push dx
|
push dx
|
||||||
push si
|
push si
|
||||||
push ds
|
push ds
|
||||||
;
|
;
|
||||||
push cs ; DOS functions require that DS point
|
push cs ; DOS functions require that DS point
|
||||||
pop ds ; to text to be displayed on the screen
|
pop ds ; to text to be displayed on the screen
|
||||||
assume ds :Code
|
assume ds :Code
|
||||||
;
|
;
|
||||||
; Check for timer 0 overflow.
|
; Check for timer 0 overflow.
|
||||||
;
|
;
|
||||||
cmp [OverflowFlag],0
|
cmp [OverflowFlag],0
|
||||||
jz PrintGoodCount
|
jz PrintGoodCount
|
||||||
mov dx,offset OverflowStr
|
mov dx,offset OverflowStr
|
||||||
mov ah,9
|
mov ah,9
|
||||||
int 21h
|
int 21h
|
||||||
jmp short EndZTimerReport
|
jmp short EndZTimerReport
|
||||||
;
|
;
|
||||||
; Convert net count to decimal ASCII in microseconds.
|
; Convert net count to decimal ASCII in microseconds.
|
||||||
;
|
;
|
||||||
PrintGoodCount:
|
PrintGoodCount:
|
||||||
mov ax,[TimedCount]
|
mov ax,[TimedCount]
|
||||||
sub ax,[ReferenceCount]
|
sub ax,[ReferenceCount]
|
||||||
mov si,offset ASCIICountEnd - 1
|
mov si,offset ASCIICountEnd - 1
|
||||||
;
|
;
|
||||||
; Convert count to microseconds by multiplying by .8381.
|
; Convert count to microseconds by multiplying by .8381.
|
||||||
;
|
;
|
||||||
mov dx, 8381
|
mov dx, 8381
|
||||||
mul dx
|
mul dx
|
||||||
mov bx, 10000
|
mov bx, 10000
|
||||||
div bx ;* .8381 = * 8381 / 10000
|
div bx ;* .8381 = * 8381 / 10000
|
||||||
;
|
;
|
||||||
; Convert time in microseconds to 5 decimal ASCII digits.
|
; Convert time in microseconds to 5 decimal ASCII digits.
|
||||||
;
|
;
|
||||||
mov bx, 10
|
mov bx, 10
|
||||||
mov cx, 5
|
mov cx, 5
|
||||||
CTSLoop:
|
CTSLoop:
|
||||||
sub dx, dx
|
sub dx, dx
|
||||||
div bx
|
div bx
|
||||||
add dl,'0'
|
add dl,'0'
|
||||||
mov [si],dl
|
mov [si],dl
|
||||||
dec si
|
dec si
|
||||||
loop CTSLoop
|
loop CTSLoop
|
||||||
;
|
;
|
||||||
; Print the results.
|
; Print the results.
|
||||||
;
|
;
|
||||||
mov ah, 9
|
mov ah, 9
|
||||||
mov dx, offset OutputStr
|
mov dx, offset OutputStr
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
EndZTimerReport:
|
EndZTimerReport:
|
||||||
pop ds
|
pop ds
|
||||||
pop si
|
pop si
|
||||||
pop dx
|
pop dx
|
||||||
|
|
@ -447,7 +448,8 @@ pages: 035-042
|
||||||
MPOPF
|
MPOPF
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerReport endp
|
ZTimerReport endp
|
||||||
|
|
||||||
Code ends
|
Code ends
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
90
03-05.md
90
03-05.md
|
|
@ -82,43 +82,45 @@ and should contain calls to **ZTimerOn** and **ZTimerOff** .
|
||||||
|
|
||||||
**LISTING 3.2 PZTEST.ASM**
|
**LISTING 3.2 PZTEST.ASM**
|
||||||
|
|
||||||
; Program to measure performance of code that takes less than
|
```nasm
|
||||||
; 54 ms to execute. (PZTEST.ASM)
|
; Program to measure performance of code that takes less than
|
||||||
;
|
; 54 ms to execute. (PZTEST.ASM)
|
||||||
; Link with PZTIMER.ASM (Listing 3.1). PZTEST.BAT (Listing 3.4)
|
;
|
||||||
; can be used to assemble and link both files. Code to be
|
; Link with PZTIMER.ASM (Listing 3.1). PZTEST.BAT (Listing 3.4)
|
||||||
; measured must be in the file TESTCODE; Listing 3.3 shows
|
; can be used to assemble and link both files. Code to be
|
||||||
; a sample TESTCODE file.
|
; measured must be in the file TESTCODE; Listing 3.3 shows
|
||||||
;
|
; a sample TESTCODE file.
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
mystack segment para stack ‘STACK'
|
;
|
||||||
|
mystack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
mystack ends
|
mystack ends
|
||||||
;
|
;
|
||||||
Code segment para public ‘CODE'
|
Code segment para public ‘CODE'
|
||||||
assume cs:Code, ds:Code
|
assume cs:Code, ds:Code
|
||||||
extrnZTimerOn:near, ZTimerOff:near, ZTimerReport:near
|
extrnZTimerOn:near, ZTimerOff:near, ZTimerReport:near
|
||||||
Start proc near
|
Start proc near
|
||||||
push cs
|
push cs
|
||||||
pop ds ; set DS to point to the code segment,
|
pop ds ; set DS to point to the code segment,
|
||||||
; so data as well as code can easily
|
; so data as well as code can easily
|
||||||
; be included in TESTCODE
|
; be included in TESTCODE
|
||||||
;
|
;
|
||||||
include TESTCODE ;code to be measured, including
|
include TESTCODE ;code to be measured, including
|
||||||
; calls to ZTimerOn and ZTimerOff
|
; calls to ZTimerOn and ZTimerOff
|
||||||
;
|
;
|
||||||
; Display the results.
|
; Display the results.
|
||||||
;
|
;
|
||||||
call ZTimerReport
|
call ZTimerReport
|
||||||
;
|
;
|
||||||
; Terminate the program.
|
; Terminate the program.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
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,30 +132,32 @@ after the code in Listing 3.3 has been run.
|
||||||
|
|
||||||
**LISTING 3.3 LST3-3.ASM**
|
**LISTING 3.3 LST3-3.ASM**
|
||||||
|
|
||||||
; Test file;
|
```nasm
|
||||||
; Measures the performance of 1,000 loads of AL from
|
; Test file;
|
||||||
; memory. (Use by renaming to TESTCODE, which is
|
; Measures the performance of 1,000 loads of AL from
|
||||||
; included by PZTEST.ASM (Listing 3.2). PZTIME.BAT
|
; memory. (Use by renaming to TESTCODE, which is
|
||||||
; (Listing 3.4) does this, along with all assembly
|
; included by PZTEST.ASM (Listing 3.2). PZTIME.BAT
|
||||||
; and linking.)
|
; (Listing 3.4) does this, along with all assembly
|
||||||
;
|
; and linking.)
|
||||||
jmp Skip ;jump around defined data
|
;
|
||||||
;
|
jmp Skip ;jump around defined data
|
||||||
MemVar db ?
|
;
|
||||||
;
|
MemVar db ?
|
||||||
Skip:
|
;
|
||||||
;
|
Skip:
|
||||||
; Start timing.
|
;
|
||||||
;
|
; Start timing.
|
||||||
|
;
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
;
|
;
|
||||||
rept 1000
|
rept 1000
|
||||||
mov al,[MemVar]
|
mov al,[MemVar]
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
; 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
|
||||||
|
|
|
||||||
132
03-06.md
132
03-06.md
|
|
@ -25,72 +25,76 @@ and "link" with "tlink" in Listing 3.4. The same is true of Listing
|
||||||
|
|
||||||
**LISTING 3.4 PZTIME.BAT**
|
**LISTING 3.4 PZTIME.BAT**
|
||||||
|
|
||||||
echo off
|
```bat
|
||||||
rem
|
echo off
|
||||||
rem *** Listing 3.4 ***
|
rem
|
||||||
rem
|
rem *** Listing 3.4 ***
|
||||||
rem ***************************************************************
|
rem
|
||||||
rem * Batch file PZTIME.BAT, which builds and runs the precision *
|
rem ***************************************************************
|
||||||
rem * Zen timer program PZTEST.EXE to time the code named as the *
|
rem * Batch file PZTIME.BAT, which builds and runs the precision *
|
||||||
rem * command-line parameter. Listing 3.1 must be named *
|
rem * Zen timer program PZTEST.EXE to time the code named as the *
|
||||||
rem * PZTIMER.ASM, and Listing 3.2 must be named PZTEST.ASM. To *
|
rem * command-line parameter. Listing 3.1 must be named *
|
||||||
rem * time the code in LST3-3, you'd type the DOS command: *
|
rem * PZTIMER.ASM, and Listing 3.2 must be named PZTEST.ASM. To *
|
||||||
rem * *
|
rem * time the code in LST3-3, you'd type the DOS command: *
|
||||||
rem * pztime lst3-3 *
|
rem * *
|
||||||
rem * *
|
rem * pztime lst3-3 *
|
||||||
rem * Note that MASM and LINK must be in the current directory or *
|
rem * *
|
||||||
rem * on the current path in order for this batch file to work. *
|
rem * Note that MASM and LINK must be in the current directory or *
|
||||||
rem * *
|
rem * on the current path in order for this batch file to work. *
|
||||||
rem * This batch file can be speeded up by assembling PZTIMER.ASM *
|
rem * *
|
||||||
rem * once, then removing the lines: *
|
rem * This batch file can be speeded up by assembling PZTIMER.ASM *
|
||||||
rem * *
|
rem * once, then removing the lines: *
|
||||||
rem * masm pztimer; *
|
rem * *
|
||||||
rem * if errorlevel 1 goto errorend *
|
rem * masm pztimer; *
|
||||||
rem * *
|
rem * if errorlevel 1 goto errorend *
|
||||||
rem * from this file. *
|
rem * *
|
||||||
rem * *
|
rem * from this file. *
|
||||||
rem * By Michael Abrash *
|
rem * *
|
||||||
rem ***************************************************************
|
rem * By Michael Abrash *
|
||||||
rem
|
rem ***************************************************************
|
||||||
rem Make sure a file to test was specified.
|
rem
|
||||||
rem
|
rem Make sure a file to test was specified.
|
||||||
if not x%1==x goto ckexist
|
rem
|
||||||
echo ***************************************************************
|
if not x%1==x goto ckexist
|
||||||
echo * Please specify a file to test. *
|
echo ***************************************************************
|
||||||
echo ***************************************************************
|
echo * Please specify a file to test. *
|
||||||
goto end
|
echo ***************************************************************
|
||||||
rem
|
goto end
|
||||||
rem Make sure the file exists.
|
rem
|
||||||
rem
|
rem Make sure the file exists.
|
||||||
:ckexist
|
rem
|
||||||
if exist %1 goto docopy
|
:ckexist
|
||||||
echo ***************************************************************
|
if exist %1 goto docopy
|
||||||
echo * The specified file, "%1," doesn't exist. *
|
echo ***************************************************************
|
||||||
echo ***************************************************************
|
echo * The specified file, "%1," doesn't exist. *
|
||||||
goto end
|
echo ***************************************************************
|
||||||
rem
|
goto end
|
||||||
rem copy the file to measure to TESTCODE.
|
rem
|
||||||
rem
|
rem copy the file to measure to TESTCODE.
|
||||||
:docopy
|
rem
|
||||||
copy %1 testcode
|
:docopy
|
||||||
masm pztest;
|
copy %1 testcode
|
||||||
if errorlevel 1 goto errorend
|
masm pztest;
|
||||||
masm pztimer;
|
if errorlevel 1 goto errorend
|
||||||
if errorlevel 1 goto errorend
|
masm pztimer;
|
||||||
link pztest+pztimer;
|
if errorlevel 1 goto errorend
|
||||||
if errorlevel 1 goto errorend
|
link pztest+pztimer;
|
||||||
pztest
|
if errorlevel 1 goto errorend
|
||||||
goto end
|
pztest
|
||||||
:errorend
|
goto end
|
||||||
echo ***************************************************************
|
:errorend
|
||||||
echo * An error occurred while building the precision Zen timer. *
|
echo ***************************************************************
|
||||||
echo ***************************************************************
|
echo * An error occurred while building the precision Zen timer. *
|
||||||
:end
|
echo ***************************************************************
|
||||||
|
: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:
|
||||||
|
|
||||||
pztime LST3-3.ASM
|
```sh
|
||||||
|
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:
|
||||||
|
|
||||||
pztime <filename>
|
```sh
|
||||||
|
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
|
||||||
|
|
|
||||||
682
03-07.md
682
03-07.md
|
|
@ -78,180 +78,181 @@ computers.
|
||||||
|
|
||||||
**LISTING 3.5 LZTIMER.ASM**
|
**LISTING 3.5 LZTIMER.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; The long-period Zen timer. (LZTIMER.ASM)
|
;
|
||||||
; Uses the 8253 timer and the BIOS time-of-day count to time the
|
; The long-period Zen timer. (LZTIMER.ASM)
|
||||||
; performance of code that takes less than an hour to execute.
|
; Uses the 8253 timer and the BIOS time-of-day count to time the
|
||||||
; Because interrupts are left on (in order to allow the timer
|
; performance of code that takes less than an hour to execute.
|
||||||
; interrupt to be recognized), this is less accurate than the
|
; Because interrupts are left on (in order to allow the timer
|
||||||
; precision Zen timer, so it is best used only to time code that takes
|
; interrupt to be recognized), this is less accurate than the
|
||||||
; more than about 54 milliseconds to execute (code that the precision
|
; precision Zen timer, so it is best used only to time code that takes
|
||||||
; Zen timer reports overflow on). Resolution is limited by the
|
; more than about 54 milliseconds to execute (code that the precision
|
||||||
; occurrence of timer interrupts.
|
; Zen timer reports overflow on). Resolution is limited by the
|
||||||
;
|
; occurrence of timer interrupts.
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
; Externally callable routines:
|
;
|
||||||
;
|
; Externally callable routines:
|
||||||
; ZTimerOn: Saves the BIOS time of day count and starts the
|
;
|
||||||
; long-period Zen timer.
|
; ZTimerOn: Saves the BIOS time of day count and starts the
|
||||||
;
|
; long-period Zen timer.
|
||||||
; ZTimerOff: Stops the long-period Zen timer and saves the timer
|
;
|
||||||
; count and the BIOS time-of-day count.
|
; ZTimerOff: Stops the long-period Zen timer and saves the timer
|
||||||
;
|
; count and the BIOS time-of-day count.
|
||||||
; ZTimerReport: Prints the time that passed between starting and
|
;
|
||||||
; stopping the timer.
|
; ZTimerReport: Prints the time that passed between starting and
|
||||||
;
|
; stopping the timer.
|
||||||
; Note: If either more than an hour passes or midnight falls between
|
;
|
||||||
; calls to ZTimerOn and ZTimerOff, an error is reported. For
|
; Note: If either more than an hour passes or midnight falls between
|
||||||
; timing code that takes more than a few minutes to execute,
|
; calls to ZTimerOn and ZTimerOff, an error is reported. For
|
||||||
; either the DOS TIME command in a batch file before and after
|
; timing code that takes more than a few minutes to execute,
|
||||||
; execution of the code to time or the use of the DOS
|
; either the DOS TIME command in a batch file before and after
|
||||||
; time-of-day function in place of the long-period Zen timer is
|
; execution of the code to time or the use of the DOS
|
||||||
; more than adequate.
|
; time-of-day function in place of the long-period Zen timer is
|
||||||
;
|
; more than adequate.
|
||||||
; Note: The PS/2 version is assembled by setting the symbol PS2 to 1.
|
;
|
||||||
; PS2 must be set to 1 on PS/2 computers because the PS/2's
|
; Note: The PS/2 version is assembled by setting the symbol PS2 to 1.
|
||||||
; timers are not compatible with an undocumented timer-stopping
|
; PS2 must be set to 1 on PS/2 computers because the PS/2's
|
||||||
; feature of the 8253; the alternative timing approach that
|
; timers are not compatible with an undocumented timer-stopping
|
||||||
; must be used on PS/2 computers leaves a short window
|
; feature of the 8253; the alternative timing approach that
|
||||||
; during which the timer 0 count and the BIOS timer count may
|
; must be used on PS/2 computers leaves a short window
|
||||||
; not be synchronized. You should also set the PS2 symbol to
|
; during which the timer 0 count and the BIOS timer count may
|
||||||
; 1 if you're getting erratic or obviously incorrect results.
|
; not be synchronized. You should also set the PS2 symbol to
|
||||||
;
|
; 1 if you're getting erratic or obviously incorrect results.
|
||||||
; Note: When PS2 is 0, the code relies on an undocumented 8253
|
;
|
||||||
; feature to get more reliable readings. It is possible that
|
; Note: When PS2 is 0, the code relies on an undocumented 8253
|
||||||
; the 8253 (or whatever chip is emulating the 8253) may be put
|
; feature to get more reliable readings. It is possible that
|
||||||
; into an undefined or incorrect state when this feature is
|
; the 8253 (or whatever chip is emulating the 8253) may be put
|
||||||
; used.
|
; into an undefined or incorrect state when this feature is
|
||||||
;
|
; used.
|
||||||
; ******************************************************************
|
;
|
||||||
; * If your computer displays any hint of erratic behavior *
|
; ******************************************************************
|
||||||
; * after the long-period Zen timer is used, such as the floppy*
|
; * If your computer displays any hint of erratic behavior *
|
||||||
; * drive failing to operate properly, reboot the system, set *
|
; * after the long-period Zen timer is used, such as the floppy*
|
||||||
; * PS2 to 1 and leave it that way! *
|
; * drive failing to operate properly, reboot the system, set *
|
||||||
; ******************************************************************
|
; * PS2 to 1 and leave it that way! *
|
||||||
;
|
; ******************************************************************
|
||||||
; Note: Each block of code being timed should ideally be run several
|
;
|
||||||
; times, with at least two similar readings required to
|
; Note: Each block of code being timed should ideally be run several
|
||||||
; establish a true measurement, in order to eliminate any
|
; times, with at least two similar readings required to
|
||||||
; variability caused by interrupts.
|
; establish a true measurement, in order to eliminate any
|
||||||
;
|
; variability caused by interrupts.
|
||||||
; Note: Interrupts must not be disabled for more than 54 ms at a
|
;
|
||||||
; stretch during the timing interval. Because interrupts
|
; Note: Interrupts must not be disabled for more than 54 ms at a
|
||||||
; are enabled, keys, mice, and other devices that generate
|
; stretch during the timing interval. Because interrupts
|
||||||
; interrupts should not be used during the timing interval.
|
; are enabled, keys, mice, and other devices that generate
|
||||||
;
|
; interrupts should not be used during the timing interval.
|
||||||
; Note: Any extra code running off the timer interrupt (such as
|
;
|
||||||
; some memory-resident utilities) will increase the time
|
; Note: Any extra code running off the timer interrupt (such as
|
||||||
; measured by the Zen timer.
|
; some memory-resident utilities) will increase the time
|
||||||
;
|
; measured by the Zen timer.
|
||||||
; Note: These routines can introduce inaccuracies of up to a few
|
;
|
||||||
; tenths of a second into the system clock count for each
|
; Note: These routines can introduce inaccuracies of up to a few
|
||||||
; code section timed. Consequently, it's a good idea to
|
; tenths of a second into the system clock count for each
|
||||||
; reboot at the conclusion of timing sessions. (The
|
; code section timed. Consequently, it's a good idea to
|
||||||
; battery-backed clock, if any, is not affected by the Zen
|
; reboot at the conclusion of timing sessions. (The
|
||||||
; timer.)
|
; battery-backed clock, if any, is not affected by the Zen
|
||||||
;
|
; timer.)
|
||||||
; All registers and all flags are preserved by all routines.
|
;
|
||||||
;
|
; All registers and all flags are preserved by all routines.
|
||||||
|
;
|
||||||
|
|
||||||
Code segment word public ‘CODE'
|
Code segment word public ‘CODE'
|
||||||
assume cs: Code, ds:nothing
|
assume cs: Code, ds:nothing
|
||||||
public ZTimerOn, ZTimerOff, ZTimerReport
|
public ZTimerOn, ZTimerOff, ZTimerReport
|
||||||
|
|
||||||
;
|
;
|
||||||
; Set PS2 to 0 to assemble for use on a fully 8253-compatible
|
; Set PS2 to 0 to assemble for use on a fully 8253-compatible
|
||||||
; system; when PS2 is 0, the readings are more reliable if the
|
; system; when PS2 is 0, the readings are more reliable if the
|
||||||
; computer supports the undocumented timer-stopping feature,
|
; computer supports the undocumented timer-stopping feature,
|
||||||
; but may be badly off if that feature is not supported. In
|
; but may be badly off if that feature is not supported. In
|
||||||
; fact, timer-stopping may interfere with your computer's
|
; fact, timer-stopping may interfere with your computer's
|
||||||
; overall operation by putting the 8253 into an undefined or
|
; overall operation by putting the 8253 into an undefined or
|
||||||
; incorrect state. Use with caution!!!
|
; incorrect state. Use with caution!!!
|
||||||
;
|
;
|
||||||
; Set PS2 to 1 to assemble for use on non-8253-compatible
|
; Set PS2 to 1 to assemble for use on non-8253-compatible
|
||||||
; systems, including PS/2 computers; when PS2 is 1, readings
|
; systems, including PS/2 computers; when PS2 is 1, readings
|
||||||
; may occasionally be off by 54 ms, but the code will work
|
; may occasionally be off by 54 ms, but the code will work
|
||||||
; properly on all systems.
|
; properly on all systems.
|
||||||
;
|
;
|
||||||
; A setting of 1 is safer and will work on more systems,
|
; A setting of 1 is safer and will work on more systems,
|
||||||
; while a setting of 0 produces more reliable results in systems
|
; while a setting of 0 produces more reliable results in systems
|
||||||
; which support the undocumented timer-stopping feature of the
|
; which support the undocumented timer-stopping feature of the
|
||||||
; 8253. The choice is yours.
|
; 8253. The choice is yours.
|
||||||
;
|
;
|
||||||
PS2 equ1
|
PS2 equ1
|
||||||
;
|
;
|
||||||
; Base address of the 8253 timer chip.
|
; Base address of the 8253 timer chip.
|
||||||
;
|
;
|
||||||
BASE_8253 equ40h
|
BASE_8253 equ40h
|
||||||
;
|
;
|
||||||
; The address of the timer 0 count registers in the 8253.
|
; The address of the timer 0 count registers in the 8253.
|
||||||
;
|
;
|
||||||
TIMER_0_8253 equBASE_8253 + 0
|
TIMER_0_8253 equBASE_8253 + 0
|
||||||
;
|
;
|
||||||
; The address of the mode register in the 8253.
|
; The address of the mode register in the 8253.
|
||||||
;
|
;
|
||||||
MODE_8253 equBASE_8253 + 3
|
MODE_8253 equBASE_8253 + 3
|
||||||
;
|
;
|
||||||
; The address of the BIOS timer count variable in the BIOS
|
; The address of the BIOS timer count variable in the BIOS
|
||||||
; data segment.
|
; data segment.
|
||||||
;
|
;
|
||||||
TIMER_COUNT equ46ch
|
TIMER_COUNT equ46ch
|
||||||
;
|
;
|
||||||
; Macro to emulate a POPF instruction in order to fix the bug in some
|
; Macro to emulate a POPF instruction in order to fix the bug in some
|
||||||
; 80286 chips which allows interrupts to occur during a POPF even when
|
; 80286 chips which allows interrupts to occur during a POPF even when
|
||||||
; interrupts remain disabled.
|
; interrupts remain disabled.
|
||||||
;
|
;
|
||||||
MPOPF macro
|
MPOPF macro
|
||||||
local p1, p2
|
local p1, p2
|
||||||
jmp short p2
|
jmp short p2
|
||||||
p1: iret ;jump to pushed address & pop flags
|
p1: iret ;jump to pushed address & pop flags
|
||||||
p2: pushcs ;construct far return address to
|
p2: pushcs ;construct far return address to
|
||||||
call p1 ; the next instruction
|
call p1 ; the next instruction
|
||||||
endm
|
endm
|
||||||
|
|
||||||
;
|
;
|
||||||
; Macro to delay briefly to ensure that enough time has elapsed
|
; Macro to delay briefly to ensure that enough time has elapsed
|
||||||
; between successive I/O accesses so that the device being accessed
|
; between successive I/O accesses so that the device being accessed
|
||||||
; can respond to both accesses even on a very fast PC.
|
; can respond to both accesses even on a very fast PC.
|
||||||
;
|
;
|
||||||
DELAY macro
|
DELAY macro
|
||||||
jmp $+2
|
jmp $+2
|
||||||
jmp $+2
|
jmp $+2
|
||||||
jmp $+2
|
jmp $+2
|
||||||
endm
|
endm
|
||||||
|
|
||||||
StartBIOSCountLow dw ? ;BIOS count low word at the
|
StartBIOSCountLow dw ? ;BIOS count low word at the
|
||||||
; start of the timing period
|
; start of the timing period
|
||||||
StartBIOSCountHigh dw ? ;BIOS count high word at the
|
StartBIOSCountHigh dw ? ;BIOS count high word at the
|
||||||
; start of the timing period
|
; start of the timing period
|
||||||
EndBIOSCountLow dw ? ;BIOS count low word at the
|
EndBIOSCountLow dw ? ;BIOS count low word at the
|
||||||
; end of the timing period
|
; end of the timing period
|
||||||
EndBIOSCountHigh dw ? ;BIOS count high word at the
|
EndBIOSCountHigh dw ? ;BIOS count high word at the
|
||||||
; end of the timing period
|
; end of the timing period
|
||||||
EndTimedCount dw ? ;timer 0 count at the end of
|
EndTimedCount dw ? ;timer 0 count at the end of
|
||||||
; the timing period
|
; the timing period
|
||||||
ReferenceCount dw ? ;number of counts required to
|
ReferenceCount dw ? ;number of counts required to
|
||||||
; execute timer overhead code
|
; execute timer overhead code
|
||||||
;
|
;
|
||||||
; String printed to report results.
|
; String printed to report results.
|
||||||
;
|
;
|
||||||
OutputStr labelbyte
|
OutputStr labelbyte
|
||||||
db 0dh, 0ah, ‘Timed count: ‘
|
db 0dh, 0ah, ‘Timed count: ‘
|
||||||
TimedCountStr db10 dup (?)
|
TimedCountStr db10 dup (?)
|
||||||
db' microseconds', 0dh, 0ah
|
db' microseconds', 0dh, 0ah
|
||||||
db ‘$'
|
db ‘$'
|
||||||
;
|
;
|
||||||
; Temporary storage for timed count as it's divided down by powers
|
; Temporary storage for timed count as it's divided down by powers
|
||||||
; of ten when converting from doubleword binary to ASCII.
|
; of ten when converting from doubleword binary to ASCII.
|
||||||
;
|
;
|
||||||
CurrentCountLow dw ?
|
CurrentCountLow dw ?
|
||||||
CurrentCountHigh dw ?
|
CurrentCountHigh dw ?
|
||||||
;
|
;
|
||||||
; Powers of ten table used to perform division by 10 when doing
|
; Powers of ten table used to perform division by 10 when doing
|
||||||
; doubleword conversion from binary to ASCII.
|
; doubleword conversion from binary to ASCII.
|
||||||
;
|
;
|
||||||
PowersOfTenlabelword
|
PowersOfTenlabelword
|
||||||
dd 1
|
dd 1
|
||||||
dd 10
|
dd 10
|
||||||
dd 100
|
dd 100
|
||||||
|
|
@ -262,13 +263,13 @@ computers.
|
||||||
dd 10000000
|
dd 10000000
|
||||||
dd 100000000
|
dd 100000000
|
||||||
dd 1000000000
|
dd 1000000000
|
||||||
PowersOfTenEnd labelword
|
PowersOfTenEnd labelword
|
||||||
;
|
;
|
||||||
; String printed to report that the high word of the BIOS count
|
; String printed to report that the high word of the BIOS count
|
||||||
; changed while timing (an hour elapsed or midnight was crossed),
|
; changed while timing (an hour elapsed or midnight was crossed),
|
||||||
; and so the count is invalid and the test needs to be rerun.
|
; and so the count is invalid and the test needs to be rerun.
|
||||||
;
|
;
|
||||||
TurnOverStrlabelbyte
|
TurnOverStrlabelbyte
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘****************************************************'
|
db ‘****************************************************'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
|
|
@ -290,56 +291,56 @@ computers.
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'$'
|
db'$'
|
||||||
|
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
;* Routine called to start timing. *
|
;* Routine called to start timing. *
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
|
|
||||||
ZTimerOn proc near
|
ZTimerOn proc near
|
||||||
|
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
push ax
|
push ax
|
||||||
pus hf
|
pus hf
|
||||||
;
|
;
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
||||||
; linear counting rather than count-by-two counting. Also stops
|
; linear counting rather than count-by-two counting. Also stops
|
||||||
; timer 0 until the timer count is loaded, except on PS/2
|
; timer 0 until the timer count is loaded, except on PS/2
|
||||||
; computers.
|
; computers.
|
||||||
;
|
;
|
||||||
mov al,00110100b ;mode 2
|
mov al,00110100b ;mode 2
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
;
|
;
|
||||||
; Set the timer count to 0, so we know we won't get another
|
; Set the timer count to 0, so we know we won't get another
|
||||||
; timer interrupt right away.
|
; timer interrupt right away.
|
||||||
; Note: this introduces an inaccuracy of up to 54 ms in the system
|
; Note: this introduces an inaccuracy of up to 54 ms in the system
|
||||||
; clock count each time it is executed.
|
; clock count each time it is executed.
|
||||||
;
|
;
|
||||||
DELAY
|
DELAY
|
||||||
subal,al
|
subal,al
|
||||||
outTIMER_0_8253,al ;lsb
|
outTIMER_0_8253,al ;lsb
|
||||||
DELAY
|
DELAY
|
||||||
outTIMER_0_8253,al ;msb
|
outTIMER_0_8253,al ;msb
|
||||||
;
|
;
|
||||||
; In case interrupts are disabled, enable interrupts briefly to allow
|
; In case interrupts are disabled, enable interrupts briefly to allow
|
||||||
; the interrupt generated when switching from mode 3 to mode 2 to be
|
; the interrupt generated when switching from mode 3 to mode 2 to be
|
||||||
; recognized. Interrupts must be enabled for at least 210 ns to allow
|
; recognized. Interrupts must be enabled for at least 210 ns to allow
|
||||||
; time for that interrupt to occur. Here, 10 jumps are used for the
|
; time for that interrupt to occur. Here, 10 jumps are used for the
|
||||||
; delay to ensure that the delay time will be more than long enough
|
; delay to ensure that the delay time will be more than long enough
|
||||||
; even on a very fast PC.
|
; even on a very fast PC.
|
||||||
;
|
;
|
||||||
pushf
|
pushf
|
||||||
sti
|
sti
|
||||||
rept 10
|
rept 10
|
||||||
jmp $+2
|
jmp $+2
|
||||||
endm
|
endm
|
||||||
MPOPF
|
MPOPF
|
||||||
;
|
;
|
||||||
; Store the timing start BIOS count.
|
; Store the timing start BIOS count.
|
||||||
; (Since the timer count was just set to 0, the BIOS count will
|
; (Since the timer count was just set to 0, the BIOS count will
|
||||||
; stay the same for the next 54 ms, so we don't need to disable
|
; stay the same for the next 54 ms, so we don't need to disable
|
||||||
; interrupts in order to avoid getting a half-changed count.)
|
; interrupts in order to avoid getting a half-changed count.)
|
||||||
;
|
;
|
||||||
push ds
|
push ds
|
||||||
subax, ax
|
subax, ax
|
||||||
movds, ax
|
movds, ax
|
||||||
|
|
@ -348,9 +349,9 @@ computers.
|
||||||
movax, ds:[TIMER_COUNT]
|
movax, ds:[TIMER_COUNT]
|
||||||
movcs: [StartBIOSCountLow],ax
|
movcs: [StartBIOSCountLow],ax
|
||||||
pop ds
|
pop ds
|
||||||
;
|
;
|
||||||
; Set the timer count to 0 again to start the timing interval.
|
; Set the timer count to 0 again to start the timing interval.
|
||||||
;
|
;
|
||||||
mov al,00110100b ;set up to load initial
|
mov al,00110100b ;set up to load initial
|
||||||
out MODE_8253,al ; timer count
|
out MODE_8253,al ; timer count
|
||||||
DELAY
|
DELAY
|
||||||
|
|
@ -358,78 +359,78 @@ computers.
|
||||||
out TIMER_0_8253,al; load count lsb
|
out TIMER_0_8253,al; load count lsb
|
||||||
DELAY
|
DELAY
|
||||||
out TIMER_0_8253,al; load count msb
|
out TIMER_0_8253,al; load count msb
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
MPOPF
|
MPOPF
|
||||||
popax
|
popax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerOnendp
|
ZTimerOnendp
|
||||||
|
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
;* Routine called to stop timing and get count. *
|
;* Routine called to stop timing and get count. *
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
|
|
||||||
ZTimerOff procnear
|
ZTimerOff procnear
|
||||||
|
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
pushf
|
pushf
|
||||||
pushax
|
pushax
|
||||||
pushcx
|
pushcx
|
||||||
;
|
;
|
||||||
; In case interrupts are disabled, enable interrupts briefly to allow
|
; In case interrupts are disabled, enable interrupts briefly to allow
|
||||||
; any pending timer interrupt to be handled. Interrupts must be
|
; any pending timer interrupt to be handled. Interrupts must be
|
||||||
; enabled for at least 210 ns to allow time for that interrupt to
|
; enabled for at least 210 ns to allow time for that interrupt to
|
||||||
; occur. Here, 10 jumps are used for the delay to ensure that the
|
; occur. Here, 10 jumps are used for the delay to ensure that the
|
||||||
; delay time will be more than long enough even on a very fast PC.
|
; delay time will be more than long enough even on a very fast PC.
|
||||||
;
|
;
|
||||||
sti
|
sti
|
||||||
rept 10
|
rept 10
|
||||||
jmp $+2
|
jmp $+2
|
||||||
endm
|
endm
|
||||||
|
|
||||||
;
|
;
|
||||||
; Latch the timer count.
|
; Latch the timer count.
|
||||||
;
|
;
|
||||||
|
|
||||||
if PS2
|
if PS2
|
||||||
|
|
||||||
mov al,00000000b
|
mov al,00000000b
|
||||||
out MODE_8253,al ;latch timer 0 count
|
out MODE_8253,al ;latch timer 0 count
|
||||||
;
|
;
|
||||||
; This is where a one-instruction-long window exists on the PS/2.
|
; This is where a one-instruction-long window exists on the PS/2.
|
||||||
; The timer count and the BIOS count can lose synchronization;
|
; The timer count and the BIOS count can lose synchronization;
|
||||||
; since the timer keeps counting after it's latched, it can turn
|
; since the timer keeps counting after it's latched, it can turn
|
||||||
; over right after it's latched and cause the BIOS count to turn
|
; over right after it's latched and cause the BIOS count to turn
|
||||||
; over before interrupts are disabled, leaving us with the timer
|
; over before interrupts are disabled, leaving us with the timer
|
||||||
; count from before the timer turned over coupled with the BIOS
|
; count from before the timer turned over coupled with the BIOS
|
||||||
; count from after the timer turned over. The result is a count
|
; count from after the timer turned over. The result is a count
|
||||||
; that's 54 ms too long.
|
; that's 54 ms too long.
|
||||||
;
|
;
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
;
|
;
|
||||||
; Set timer 0 to mode 2 (divide-by-N), waiting for a 2-byte count
|
; Set timer 0 to mode 2 (divide-by-N), waiting for a 2-byte count
|
||||||
; load, which stops timer 0 until the count is loaded. (Only works
|
; load, which stops timer 0 until the count is loaded. (Only works
|
||||||
; on fully 8253-compatible chips.)
|
; on fully 8253-compatible chips.)
|
||||||
;
|
;
|
||||||
mov al,00110100b; mode 2
|
mov al,00110100b; mode 2
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
DELAY
|
DELAY
|
||||||
mov al,00000000b ;latch timer 0 count
|
mov al,00000000b ;latch timer 0 count
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
cli ;stop the BIOS count
|
cli ;stop the BIOS count
|
||||||
;
|
;
|
||||||
; Read the BIOS count. (Since interrupts are disabled, the BIOS
|
; Read the BIOS count. (Since interrupts are disabled, the BIOS
|
||||||
; count won't change.)
|
; count won't change.)
|
||||||
;
|
;
|
||||||
push ds
|
push ds
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
|
|
@ -438,9 +439,9 @@ computers.
|
||||||
mov ax,ds:[TIMER_COUNT]
|
mov ax,ds:[TIMER_COUNT]
|
||||||
mov cs:[EndBIOSCountLow],ax
|
mov cs:[EndBIOSCountLow],ax
|
||||||
pop ds
|
pop ds
|
||||||
;
|
;
|
||||||
; Read the timer count and save it.
|
; Read the timer count and save it.
|
||||||
;
|
;
|
||||||
in al,TIMER_0_8253 ;lsb
|
in al,TIMER_0_8253 ;lsb
|
||||||
DELAY
|
DELAY
|
||||||
mov ah,al
|
mov ah,al
|
||||||
|
|
@ -450,12 +451,12 @@ computers.
|
||||||
; remaining to elapsed
|
; remaining to elapsed
|
||||||
; count
|
; count
|
||||||
mov cs:[EndTimedCount],ax
|
mov cs:[EndTimedCount],ax
|
||||||
;
|
;
|
||||||
; Restart timer 0, which is still waiting for an initial count
|
; Restart timer 0, which is still waiting for an initial count
|
||||||
; to be loaded.
|
; to be loaded.
|
||||||
;
|
;
|
||||||
|
|
||||||
ife PS2
|
ife PS2
|
||||||
|
|
||||||
DELAY
|
DELAY
|
||||||
mov al,00110100b ;mode 2, waiting to load a
|
mov al,00110100b ;mode 2, waiting to load a
|
||||||
|
|
@ -469,19 +470,19 @@ computers.
|
||||||
out TIMER_0_8253,al ;msb
|
out TIMER_0_8253,al ;msb
|
||||||
DELAY
|
DELAY
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
sti;let the BIOS count continue
|
sti;let the BIOS count continue
|
||||||
;
|
;
|
||||||
; Time a zero-length code fragment, to get a reference for how
|
; Time a zero-length code fragment, to get a reference for how
|
||||||
; much overhead this routine has. Time it 16 times and average it,
|
; much overhead this routine has. Time it 16 times and average it,
|
||||||
; for accuracy, rounding the result.
|
; for accuracy, rounding the result.
|
||||||
;
|
;
|
||||||
mov cs:[ReferenceCount],0
|
mov cs:[ReferenceCount],0
|
||||||
mov cx,16
|
mov cx,16
|
||||||
cli ;interrupts off to allow a
|
cli ;interrupts off to allow a
|
||||||
; precise reference count
|
; precise reference count
|
||||||
RefLoop:
|
RefLoop:
|
||||||
call ReferenceZTimerOn
|
call ReferenceZTimerOn
|
||||||
call ReferenceZTimerOff
|
call ReferenceZTimerOff
|
||||||
loop RefLoop
|
loop RefLoop
|
||||||
|
|
@ -489,67 +490,67 @@ computers.
|
||||||
add cs:[ReferenceCount],8; total + (0.5 * 16)
|
add cs:[ReferenceCount],8; total + (0.5 * 16)
|
||||||
mov cl,4
|
mov cl,4
|
||||||
shr cs:[ReferenceCount],cl;(total) / 16 + 0.5
|
shr cs:[ReferenceCount],cl;(total) / 16 + 0.5
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
popcx
|
popcx
|
||||||
popax
|
popax
|
||||||
MPOPF
|
MPOPF
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerOff endp
|
ZTimerOff endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Called by ZTimerOff to start the timer for overhead measurements.
|
; Called by ZTimerOff to start the timer for overhead measurements.
|
||||||
;
|
;
|
||||||
|
|
||||||
ReferenceZTimerOnprocnear
|
ReferenceZTimerOnprocnear
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
pushax
|
pushax
|
||||||
pushf
|
pushf
|
||||||
;
|
;
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
||||||
; linear counting rather than count-by-two counting.
|
; linear counting rather than count-by-two counting.
|
||||||
;
|
;
|
||||||
mov al,00110100b ;mode 2
|
mov al,00110100b ;mode 2
|
||||||
out MODE_8253,al
|
out MODE_8253,al
|
||||||
;
|
;
|
||||||
; Set the timer count to 0.
|
; Set the timer count to 0.
|
||||||
;
|
;
|
||||||
DELAY
|
DELAY
|
||||||
sub al,al
|
sub al,al
|
||||||
out TIMER_0_8253,al ;lsb
|
out TIMER_0_8253,al ;lsb
|
||||||
DELAY
|
DELAY
|
||||||
out TIMER_0_8253,al ;msb
|
out TIMER_0_8253,al ;msb
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
MPOPF
|
MPOPF
|
||||||
popax
|
popax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ReferenceZTimerOnendp
|
ReferenceZTimerOnendp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Called by ZTimerOff to stop the timer and add the result to
|
; Called by ZTimerOff to stop the timer and add the result to
|
||||||
; ReferenceCount for overhead measurements. Doesn't need to look
|
; ReferenceCount for overhead measurements. Doesn't need to look
|
||||||
; at the BIOS count because timing a zero-length code fragment
|
; at the BIOS count because timing a zero-length code fragment
|
||||||
; isn't going to take anywhere near 54 ms.
|
; isn't going to take anywhere near 54 ms.
|
||||||
;
|
;
|
||||||
|
|
||||||
ReferenceZTimerOff procnear
|
ReferenceZTimerOff procnear
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
pushf
|
pushf
|
||||||
pushax
|
pushax
|
||||||
pushcx
|
pushcx
|
||||||
|
|
||||||
;
|
;
|
||||||
; Match the interrupt-window delay in ZTimerOff.
|
; Match the interrupt-window delay in ZTimerOff.
|
||||||
;
|
;
|
||||||
sti
|
sti
|
||||||
rept10
|
rept10
|
||||||
jmp$+2
|
jmp$+2
|
||||||
|
|
@ -557,9 +558,9 @@ computers.
|
||||||
|
|
||||||
mov al,00000000b
|
mov al,00000000b
|
||||||
out MODE_8253,al ;latch timer
|
out MODE_8253,al ;latch timer
|
||||||
;
|
;
|
||||||
; Read the count and save it.
|
; Read the count and save it.
|
||||||
;
|
;
|
||||||
DELAY
|
DELAY
|
||||||
in al,TIMER_0_8253 ;lsb
|
in al,TIMER_0_8253 ;lsb
|
||||||
DELAY
|
DELAY
|
||||||
|
|
@ -570,21 +571,21 @@ computers.
|
||||||
; remaining to elapsed
|
; remaining to elapsed
|
||||||
; count
|
; count
|
||||||
add cs:[ReferenceCount],ax
|
add cs:[ReferenceCount],ax
|
||||||
;
|
;
|
||||||
; Restore the context and return.
|
; Restore the context and return.
|
||||||
;
|
;
|
||||||
popcx
|
popcx
|
||||||
popax
|
popax
|
||||||
MPOPF
|
MPOPF
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ReferenceZTimerOff endp
|
ReferenceZTimerOff endp
|
||||||
|
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
;* Routine called to report timing results. *
|
;* Routine called to report timing results. *
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
|
|
||||||
ZTimerReportprocnear
|
ZTimerReportprocnear
|
||||||
|
|
||||||
pushf
|
pushf
|
||||||
push ax
|
push ax
|
||||||
|
|
@ -598,10 +599,10 @@ computers.
|
||||||
push cs ;DOS functions require that DS point
|
push cs ;DOS functions require that DS point
|
||||||
pop ds ; to text to be displayed on the screen
|
pop ds ; to text to be displayed on the screen
|
||||||
assume ds :Code
|
assume ds :Code
|
||||||
;
|
;
|
||||||
; See if midnight or more than an hour passed during timing. If so,
|
; See if midnight or more than an hour passed during timing. If so,
|
||||||
; notify the user.
|
; notify the user.
|
||||||
;
|
;
|
||||||
mov ax,[StartBIOSCountHigh]
|
mov ax,[StartBIOSCountHigh]
|
||||||
cmp ax,[EndBIOSCountHigh]
|
cmp ax,[EndBIOSCountHigh]
|
||||||
jz CalcBIOSTime ;hour count didn't change,
|
jz CalcBIOSTime ;hour count didn't change,
|
||||||
|
|
@ -618,21 +619,21 @@ computers.
|
||||||
; the total time wasn't more
|
; the total time wasn't more
|
||||||
; than an hour
|
; than an hour
|
||||||
|
|
||||||
;
|
;
|
||||||
; Over an hour elapsed or midnight passed during timing, which
|
; Over an hour elapsed or midnight passed during timing, which
|
||||||
; renders the results invalid. Notify the user. This misses the
|
; renders the results invalid. Notify the user. This misses the
|
||||||
; case where a multiple of 24 hours has passed, but we'll rely
|
; case where a multiple of 24 hours has passed, but we'll rely
|
||||||
; on the perspicacity of the user to detect that case.
|
; on the perspicacity of the user to detect that case.
|
||||||
;
|
;
|
||||||
TestTooLong:
|
TestTooLong:
|
||||||
mov ah,9
|
mov ah,9
|
||||||
mov dx,offset TurnOverStr
|
mov dx,offset TurnOverStr
|
||||||
int 21h
|
int 21h
|
||||||
jmp short ZTimerReportDone
|
jmp short ZTimerReportDone
|
||||||
;
|
;
|
||||||
; Convert the BIOS time to microseconds.
|
; Convert the BIOS time to microseconds.
|
||||||
;
|
;
|
||||||
CalcBIOSTime:
|
CalcBIOSTime:
|
||||||
mov ax,[EndBIOSCountLow]
|
mov ax,[EndBIOSCountLow]
|
||||||
sub ax,[StartBIOSCountLow]
|
sub ax,[StartBIOSCountLow]
|
||||||
mov dx,54925 ;number of microseconds each
|
mov dx,54925 ;number of microseconds each
|
||||||
|
|
@ -640,23 +641,23 @@ computers.
|
||||||
mul dx
|
mul dx
|
||||||
mov bx,ax ;set aside BIOS count in
|
mov bx,ax ;set aside BIOS count in
|
||||||
mov cx,dx ; microseconds
|
mov cx,dx ; microseconds
|
||||||
;
|
;
|
||||||
; Convert timer count to microseconds.
|
; Convert timer count to microseconds.
|
||||||
;
|
;
|
||||||
mov ax,[EndTimedCount]
|
mov ax,[EndTimedCount]
|
||||||
mov si,8381
|
mov si,8381
|
||||||
mul si
|
mul si
|
||||||
mov si,10000
|
mov si,10000
|
||||||
div si ;* .8381 = * 8381 / 10000
|
div si ;* .8381 = * 8381 / 10000
|
||||||
;
|
;
|
||||||
; Add timer and BIOS counts together to get an overall time in
|
; Add timer and BIOS counts together to get an overall time in
|
||||||
; microseconds.
|
; microseconds.
|
||||||
;
|
;
|
||||||
add bx,ax
|
add bx,ax
|
||||||
adc cx,0
|
adc cx,0
|
||||||
;
|
;
|
||||||
; Subtract the timer overhead and save the result.
|
; Subtract the timer overhead and save the result.
|
||||||
;
|
;
|
||||||
mov ax,[ReferenceCount]
|
mov ax,[ReferenceCount]
|
||||||
mov si,8381 ;convert the reference count
|
mov si,8381 ;convert the reference count
|
||||||
mul si ; to microseconds
|
mul si ; to microseconds
|
||||||
|
|
@ -666,15 +667,15 @@ computers.
|
||||||
sbb cx,0
|
sbb cx,0
|
||||||
mov [CurrentCountLow],bx
|
mov [CurrentCountLow],bx
|
||||||
mov [CurrentCountHigh],cx
|
mov [CurrentCountHigh],cx
|
||||||
;
|
;
|
||||||
; Convert the result to an ASCII string by trial subtractions of
|
; Convert the result to an ASCII string by trial subtractions of
|
||||||
; powers of 10.
|
; powers of 10.
|
||||||
;
|
;
|
||||||
mov di,offset PowersOfTenEnd - offset PowersOfTen - 4
|
mov di,offset PowersOfTenEnd - offset PowersOfTen - 4
|
||||||
mov si,offset TimedCountStr
|
mov si,offset TimedCountStr
|
||||||
CTSNextDigit:
|
CTSNextDigit:
|
||||||
mov bl,'0'
|
mov bl,'0'
|
||||||
CTSLoop:
|
CTSLoop:
|
||||||
mov ax,[CurrentCountLow]
|
mov ax,[CurrentCountLow]
|
||||||
mov dx,[CurrentCountHigh]
|
mov dx,[CurrentCountHigh]
|
||||||
sub ax,PowersOfTen[di]
|
sub ax,PowersOfTen[di]
|
||||||
|
|
@ -684,20 +685,20 @@ computers.
|
||||||
mov [CurrentCountLow],ax
|
mov [CurrentCountLow],ax
|
||||||
mov [CurrentCountHigh],dx
|
mov [CurrentCountHigh],dx
|
||||||
jmp CTSLoop
|
jmp CTSLoop
|
||||||
CTSNextPowerDown:
|
CTSNextPowerDown:
|
||||||
mov [si],bl
|
mov [si],bl
|
||||||
inc si
|
inc si
|
||||||
sub di,4
|
sub di,4
|
||||||
jns CTSNextDigit
|
jns CTSNextDigit
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
; Print the results.
|
; Print the results.
|
||||||
;
|
;
|
||||||
mov ah,9
|
mov ah,9
|
||||||
mov dx,offset OutputStr
|
mov dx,offset OutputStr
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
ZTimerReportDone:
|
ZTimerReportDone:
|
||||||
pop ds
|
pop ds
|
||||||
pop di
|
pop di
|
||||||
pop si
|
pop si
|
||||||
|
|
@ -708,7 +709,8 @@ computers.
|
||||||
MPOPF
|
MPOPF
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerReport endp
|
ZTimerReport endp
|
||||||
|
|
||||||
Code ends
|
Code ends
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
62
03-08.md
62
03-08.md
|
|
@ -70,36 +70,37 @@ timing.
|
||||||
|
|
||||||
**LISTING 3.6 LZTEST.ASM**
|
**LISTING 3.6 LZTEST.ASM**
|
||||||
|
|
||||||
; Program to measure performance of code that takes longer than
|
```nasm
|
||||||
; 54 ms to execute. (LZTEST.ASM)
|
; Program to measure performance of code that takes longer than
|
||||||
;
|
; 54 ms to execute. (LZTEST.ASM)
|
||||||
; Link with LZTIMER.ASM (Listing 3.5). LZTIME.BAT (Listing 3.7)
|
;
|
||||||
; can be used to assemble and link both files. Code to be
|
; Link with LZTIMER.ASM (Listing 3.5). LZTIME.BAT (Listing 3.7)
|
||||||
; measured must be in the file TESTCODE; Listing 3.8 shows
|
; can be used to assemble and link both files. Code to be
|
||||||
; a sample file (LST3-8.ASM) which should be named TESTCODE.
|
; measured must be in the file TESTCODE; Listing 3.8 shows
|
||||||
;
|
; a sample file (LST3-8.ASM) which should be named TESTCODE.
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
mystack segment para stack ‘STACK'
|
;
|
||||||
|
mystack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
mystack ends
|
mystack ends
|
||||||
;
|
;
|
||||||
Code segment para public ‘CODE'
|
Code segment para public ‘CODE'
|
||||||
assume cs:Code, ds:Code
|
assume cs:Code, ds:Code
|
||||||
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
|
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
|
||||||
Startproc near
|
Startproc near
|
||||||
push cs
|
push cs
|
||||||
pop ds ;point DS to the code segment,
|
pop ds ;point DS to the code segment,
|
||||||
; so data as well as code can easily
|
; so data as well as code can easily
|
||||||
; be included in TESTCODE
|
; be included in TESTCODE
|
||||||
;
|
;
|
||||||
; Delay for 6-7 seconds, to let the Enter keystroke that started the
|
; Delay for 6-7 seconds, to let the Enter keystroke that started the
|
||||||
; program come back up.
|
; program come back up.
|
||||||
;
|
;
|
||||||
mov ah,2ch
|
mov ah,2ch
|
||||||
int 21h ;get the current time
|
int 21h ;get the current time
|
||||||
mov bh,dh ;set the current time aside
|
mov bh,dh ;set the current time aside
|
||||||
DelayLoop:
|
DelayLoop:
|
||||||
mov ah,2ch
|
mov ah,2ch
|
||||||
push bx ;preserve start time
|
push bx ;preserve start time
|
||||||
int 21h ;get time
|
int 21h ;get time
|
||||||
|
|
@ -109,25 +110,26 @@ timing.
|
||||||
jnb CheckDelayTime ;no
|
jnb CheckDelayTime ;no
|
||||||
add dh,60 ;yes, a minute must have turned over,
|
add dh,60 ;yes, a minute must have turned over,
|
||||||
; so add one minute
|
; so add one minute
|
||||||
CheckDelayTime:
|
CheckDelayTime:
|
||||||
sub dh,bh ;get time that's passed
|
sub dh,bh ;get time that's passed
|
||||||
cmp dh,7 ;has it been more than 6 seconds yet?
|
cmp dh,7 ;has it been more than 6 seconds yet?
|
||||||
jb DelayLoop ;not yet
|
jb DelayLoop ;not yet
|
||||||
;
|
;
|
||||||
include TESTCODE ;code to be measured, including calls
|
include TESTCODE ;code to be measured, including calls
|
||||||
; to ZTimerOn and ZTimerOff
|
; to ZTimerOn and ZTimerOff
|
||||||
;
|
;
|
||||||
; Display the results.
|
; Display the results.
|
||||||
;
|
;
|
||||||
call ZTimerReport
|
call ZTimerReport
|
||||||
;
|
;
|
||||||
; Terminate the program.
|
; Terminate the program.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
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
|
||||||
|
|
|
||||||
188
03-09.md
188
03-09.md
|
|
@ -12,67 +12,69 @@ pages: 067-070
|
||||||
|
|
||||||
**LISTING 3.7 LZTIME.BAT**
|
**LISTING 3.7 LZTIME.BAT**
|
||||||
|
|
||||||
echo off
|
```bat
|
||||||
rem
|
echo off
|
||||||
rem *** Listing 3.7 ***
|
rem
|
||||||
rem
|
rem *** Listing 3.7 ***
|
||||||
rem ***************************************************************
|
rem
|
||||||
rem * Batch file LZTIME.BAT, which builds and runs the *
|
rem ***************************************************************
|
||||||
rem * long-period Zen timer program LZTEST.EXE to time the code *
|
rem * Batch file LZTIME.BAT, which builds and runs the *
|
||||||
rem * named as the command-line parameter. Listing 3.5 must be *
|
rem * long-period Zen timer program LZTEST.EXE to time the code *
|
||||||
rem * named LZTIMER.ASM, and Listing 3.6 must be named *
|
rem * named as the command-line parameter. Listing 3.5 must be *
|
||||||
rem * LZTEST.ASM. To time the code in LST3-8, you'd type the *
|
rem * named LZTIMER.ASM, and Listing 3.6 must be named *
|
||||||
rem * DOS command: *
|
rem * LZTEST.ASM. To time the code in LST3-8, you'd type the *
|
||||||
rem * *
|
rem * DOS command: *
|
||||||
rem * lztime lst3-8 *
|
rem * *
|
||||||
rem * *
|
rem * lztime lst3-8 *
|
||||||
rem * Note that MASM and LINK must be in the current directory or *
|
rem * *
|
||||||
rem * on the current path in order for this batch file to work. *
|
rem * Note that MASM and LINK must be in the current directory or *
|
||||||
rem * *
|
rem * on the current path in order for this batch file to work. *
|
||||||
rem * This batch file can be speeded up by assembling LZTIMER.ASM *
|
rem * *
|
||||||
rem * once, then removing the lines: *
|
rem * This batch file can be speeded up by assembling LZTIMER.ASM *
|
||||||
rem * *
|
rem * once, then removing the lines: *
|
||||||
rem * masm lztimer; *
|
rem * *
|
||||||
rem * if errorlevel 1 goto errorend *
|
rem * masm lztimer; *
|
||||||
rem * *
|
rem * if errorlevel 1 goto errorend *
|
||||||
rem * from this file. *
|
rem * *
|
||||||
rem * *
|
rem * from this file. *
|
||||||
rem * By Michael Abrash *
|
rem * *
|
||||||
rem ***************************************************************
|
rem * By Michael Abrash *
|
||||||
rem
|
rem ***************************************************************
|
||||||
rem Make sure a file to test was specified.
|
rem
|
||||||
rem
|
rem Make sure a file to test was specified.
|
||||||
if not x%1==x goto ckexist
|
rem
|
||||||
echo ***************************************************************
|
if not x%1==x goto ckexist
|
||||||
echo * Please specify a file to test. *
|
echo ***************************************************************
|
||||||
echo ***************************************************************
|
echo * Please specify a file to test. *
|
||||||
goto end
|
echo ***************************************************************
|
||||||
rem
|
goto end
|
||||||
rem Make sure the file exists.
|
rem
|
||||||
rem
|
rem Make sure the file exists.
|
||||||
:ckexist
|
rem
|
||||||
if exist %1 goto docopy
|
:ckexist
|
||||||
echo ***************************************************************
|
if exist %1 goto docopy
|
||||||
echo * The specified file, "%1," doesn't exist. *
|
echo ***************************************************************
|
||||||
echo ***************************************************************
|
echo * The specified file, "%1," doesn't exist. *
|
||||||
goto end
|
echo ***************************************************************
|
||||||
rem
|
goto end
|
||||||
rem copy the file to measure to TESTCODE.
|
rem
|
||||||
:docopy
|
rem copy the file to measure to TESTCODE.
|
||||||
copy %1 testcode
|
:docopy
|
||||||
masm lztest;
|
copy %1 testcode
|
||||||
if errorlevel 1 goto errorend
|
masm lztest;
|
||||||
masm lztimer;
|
if errorlevel 1 goto errorend
|
||||||
if errorlevel 1 goto errorend
|
masm lztimer;
|
||||||
link lztest+lztimer;
|
if errorlevel 1 goto errorend
|
||||||
if errorlevel 1 goto errorend
|
link lztest+lztimer;
|
||||||
lztest
|
if errorlevel 1 goto errorend
|
||||||
goto end
|
lztest
|
||||||
:errorend
|
goto end
|
||||||
echo ***************************************************************
|
:errorend
|
||||||
echo * An error occurred while building the long-period Zen timer. *
|
echo ***************************************************************
|
||||||
echo ***************************************************************
|
echo * An error occurred while building the long-period Zen timer. *
|
||||||
:end
|
echo ***************************************************************
|
||||||
|
: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,38 +83,42 @@ 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
|
;
|
||||||
; memory. (Use by renaming to TESTCODE, which is
|
; Measures the performance of 20,000 loads of AL from
|
||||||
; included by LZTEST.ASM (Listing 3.6). LZTIME.BAT
|
; memory. (Use by renaming to TESTCODE, which is
|
||||||
; (Listing 3.7) does this, along with all assembly
|
; included by LZTEST.ASM (Listing 3.6). LZTIME.BAT
|
||||||
; and linking.)
|
; (Listing 3.7) does this, along with all assembly
|
||||||
;
|
; and linking.)
|
||||||
; Note: takes about ten minutes to assemble on a slow PC if
|
;
|
||||||
;you are using MASM
|
; Note: takes about ten minutes to assemble on a slow PC if
|
||||||
;
|
;you are using MASM
|
||||||
jmpSkip;jump around defined data
|
;
|
||||||
;
|
jmpSkip;jump around defined data
|
||||||
MemVardb?
|
;
|
||||||
;
|
MemVardb?
|
||||||
Skip:
|
;
|
||||||
;
|
Skip:
|
||||||
; Start timing.
|
;
|
||||||
;
|
; Start timing.
|
||||||
callZTimerOn
|
;
|
||||||
;
|
callZTimerOn
|
||||||
rept20000
|
;
|
||||||
moval,[MemVar]
|
rept20000
|
||||||
endm
|
moval,[MemVar]
|
||||||
;
|
endm
|
||||||
; 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)
|
||||||
|
|
||||||
lztime lst3-8.asm
|
```sh
|
||||||
|
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
|
||||||
|
|
||||||
extern "C" ZTimerOn(void);
|
```c
|
||||||
|
extern "C" ZTimerOn(void);
|
||||||
|
```
|
||||||
|
|
|
||||||
20
03-10.md
20
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:
|
||||||
|
|
||||||
ZTimerOn():
|
```c
|
||||||
for (i=0, x=0; i<100; i++)
|
ZTimerOn():
|
||||||
|
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
|
||||||
|
|
||||||
call far ptr ReferenceZTimerOn
|
```nasm
|
||||||
|
call far ptr ReferenceZTimerOn
|
||||||
|
```
|
||||||
|
|
||||||
with
|
with
|
||||||
|
|
||||||
push cs
|
```nasm
|
||||||
call near ptr ReferenceZTimerOn
|
push cs
|
||||||
|
call near ptr ReferenceZTimerOn
|
||||||
|
```
|
||||||
|
|
||||||
(and likewise for **ReferenceZTimerOff** ), which works because
|
(and likewise for **ReferenceZTimerOff** ), which works because
|
||||||
**ReferenceZTimerOn** is in the same segment as the calling code. This
|
**ReferenceZTimerOn** is in the same segment as the calling code. This
|
||||||
|
|
|
||||||
52
04-02.md
52
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,
|
||||||
|
|
||||||
mov ax,word ptr [MemVar]
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov al,byte ptr [MemVar]
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
add word ptr [MemVar],ax
|
```nasm
|
||||||
|
add word ptr [MemVar],ax
|
||||||
|
```
|
||||||
|
|
||||||
takes about 8 cycles longer to execute than:
|
takes about 8 cycles longer to execute than:
|
||||||
|
|
||||||
add byte ptr [MemVar],al
|
```nasm
|
||||||
|
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,35 +122,39 @@ in all.
|
||||||
|
|
||||||
**LISTING 4.1 LST4-1.ASM**
|
**LISTING 4.1 LST4-1.ASM**
|
||||||
|
|
||||||
; Measures the performance of a loop which uses a
|
```nasm
|
||||||
; byte-sized memory variable as the loop counter.
|
; Measures the performance of a loop which uses a
|
||||||
;
|
; byte-sized memory variable as the loop counter.
|
||||||
|
;
|
||||||
jmp Skip
|
jmp Skip
|
||||||
;
|
;
|
||||||
Counter db 100
|
Counter db 100
|
||||||
;
|
;
|
||||||
Skip:
|
Skip:
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
LoopTop:
|
LoopTop:
|
||||||
dec [Counter]
|
dec [Counter]
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.2 LST4-2.ASM**
|
**LISTING 4.2 LST4-2.ASM**
|
||||||
|
|
||||||
; Measures the performance of a loop which uses a
|
```nasm
|
||||||
; word-sized memory variable as the loop counter.
|
; Measures the performance of a loop which uses a
|
||||||
;
|
; word-sized memory variable as the loop counter.
|
||||||
|
;
|
||||||
jmp Skip
|
jmp Skip
|
||||||
;
|
;
|
||||||
Counter dw 100
|
Counter dw 100
|
||||||
;
|
;
|
||||||
Skip:
|
Skip:
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
LoopTop:
|
LoopTop:
|
||||||
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
|
||||||
|
|
|
||||||
42
04-03.md
42
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,
|
||||||
|
|
||||||
mov dl,byte ptr [MemVar]
|
```nasm
|
||||||
mov dh,byte ptr [MemVar+1]
|
mov dl,byte ptr [MemVar]
|
||||||
|
mov dh,byte ptr [MemVar+1]
|
||||||
|
```
|
||||||
|
|
||||||
versus:
|
versus:
|
||||||
|
|
||||||
mov dx,word ptr [MemVar]
|
```nasm
|
||||||
|
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,25 +52,29 @@ the second byte of each word.
|
||||||
|
|
||||||
**LISTING 4.3 LST4-3.ASM**
|
**LISTING 4.3 LST4-3.ASM**
|
||||||
|
|
||||||
; Measures the performance of reading 1,000 words
|
```nasm
|
||||||
; from memory with 1,000 word-sized accesses.
|
; Measures the performance of reading 1,000 words
|
||||||
;
|
; from memory with 1,000 word-sized accesses.
|
||||||
|
;
|
||||||
sub si,si
|
sub si,si
|
||||||
mov cx,1000
|
mov cx,1000
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rep lodsw
|
rep lodsw
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.4 LST4-4.ASM**
|
**LISTING 4.4 LST4-4.ASM**
|
||||||
|
|
||||||
; Measures the performance of reading 1000 words
|
```nasm
|
||||||
; from memory with 2,000 byte-sized accesses.
|
; Measures the performance of reading 1000 words
|
||||||
;
|
; from memory with 2,000 byte-sized accesses.
|
||||||
|
;
|
||||||
sub si,si
|
sub si,si
|
||||||
mov cx,2000
|
mov cx,2000
|
||||||
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
|
||||||
|
|
||||||
mov [bx],ax
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
shr ax,1
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
24
04-04.md
24
04-04.md
|
|
@ -104,24 +104,27 @@ that the "true" execution time of **SHR** is 8.64 cycles.
|
||||||
|
|
||||||
**LISTING 4.5 LST4-5.ASM**
|
**LISTING 4.5 LST4-5.ASM**
|
||||||
|
|
||||||
; Measures the performance of 1,000 SHR instructions
|
```nasm
|
||||||
; in a row. Since SHR executes in 2 cycles but is
|
; Measures the performance of 1,000 SHR instructions
|
||||||
; 2 bytes long, the prefetch queue is always empty,
|
; in a row. Since SHR executes in 2 cycles but is
|
||||||
; and prefetching time determines the overall
|
; 2 bytes long, the prefetch queue is always empty,
|
||||||
; performance of the code.
|
; and prefetching time determines the overall
|
||||||
;
|
; performance of the code.
|
||||||
|
;
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
shr ax,1
|
shr ax,1
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.6 LST4-6.ASM**
|
**LISTING 4.6 LST4-6.ASM**
|
||||||
|
|
||||||
; Measures the performance of 1,000 MUL/SHR instruction
|
```nasm
|
||||||
; pairs in a row. The lengthy execution time of MUL
|
; Measures the performance of 1,000 MUL/SHR instruction
|
||||||
; should keep the prefetch queue from ever emptying.
|
; pairs in a row. The lengthy execution time of MUL
|
||||||
;
|
; should keep the prefetch queue from ever emptying.
|
||||||
|
;
|
||||||
mov cx,1000
|
mov cx,1000
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
20
04-05.md
20
04-05.md
|
|
@ -43,29 +43,33 @@ Listing 4.6.*](images/04-04.jpg)
|
||||||
|
|
||||||
**LISTING 4.7 LST4-7.ASM**
|
**LISTING 4.7 LST4-7.ASM**
|
||||||
|
|
||||||
; Measures the performance of repeated MOV AL,0 instructions,
|
```nasm
|
||||||
; which take 4 cycles each according to Intel's official
|
; Measures the performance of repeated MOV AL,0 instructions,
|
||||||
; specifications.
|
; which take 4 cycles each according to Intel's official
|
||||||
;
|
; specifications.
|
||||||
|
;
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
mov al,0
|
mov al,0
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 4.8 LST4-8.ASM**
|
**LISTING 4.8 LST4-8.ASM**
|
||||||
|
|
||||||
; Measures the performance of repeated SUB AL,AL instructions,
|
```nasm
|
||||||
; which take 3 cycles each according to Intel's official
|
; Measures the performance of repeated SUB AL,AL instructions,
|
||||||
; specifications.
|
; which take 3 cycles each according to Intel's official
|
||||||
;
|
; specifications.
|
||||||
|
;
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
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
|
||||||
|
|
|
||||||
22
04-07.md
22
04-07.md
|
|
@ -28,17 +28,19 @@ request a memory access from the Bus Interface Unit.)
|
||||||
|
|
||||||
**LISTING 4.9 LST4-9.ASM**
|
**LISTING 4.9 LST4-9.ASM**
|
||||||
|
|
||||||
; Measures the performance of repeated MUL instructions,
|
```nasm
|
||||||
; which allow the prefetch queue to be full at all times,
|
; Measures the performance of repeated MUL instructions,
|
||||||
; to demonstrate a case in which DRAM refresh has no impact
|
; which allow the prefetch queue to be full at all times,
|
||||||
; on code performance.
|
; to demonstrate a case in which DRAM refresh has no impact
|
||||||
;
|
; on code performance.
|
||||||
|
;
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
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,15 +58,17 @@ to fetch the instruction bytes.
|
||||||
|
|
||||||
**LISTING 4.10 LST4-10.ASM**
|
**LISTING 4.10 LST4-10.ASM**
|
||||||
|
|
||||||
; Measures the performance of repeated SHR instructions,
|
```nasm
|
||||||
; which empty the prefetch queue, to demonstrate the
|
; Measures the performance of repeated SHR instructions,
|
||||||
; worst-case impact of DRAM refresh on code performance.
|
; which empty the prefetch queue, to demonstrate the
|
||||||
;
|
; worst-case impact of DRAM refresh on code performance.
|
||||||
|
;
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
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
|
||||||
|
|
|
||||||
20
04-09.md
20
04-09.md
|
|
@ -86,15 +86,16 @@ long as we had assumed, but a long time nonetheless.
|
||||||
|
|
||||||
**LISTING 4.11 LST4-11.ASM**
|
**LISTING 4.11 LST4-11.ASM**
|
||||||
|
|
||||||
; Times speed of memory access to Enhanced Graphics
|
```nasm
|
||||||
; Adapter graphics mode display memory at A000:0000.
|
; Times speed of memory access to Enhanced Graphics
|
||||||
;
|
; Adapter graphics mode display memory at A000:0000.
|
||||||
|
;
|
||||||
mov ax,0010h
|
mov ax,0010h
|
||||||
int 10h; select hi-res EGA graphics
|
int 10h; select hi-res EGA graphics
|
||||||
; mode 10 hex (AH=0 selects
|
; mode 10 hex (AH=0 selects
|
||||||
; BIOS set mode function,
|
; BIOS set mode function,
|
||||||
; with AL=mode to select)
|
; with AL=mode to select)
|
||||||
;
|
;
|
||||||
mov ax,0a000h
|
mov ax,0a000h
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov es,ax ;move to & from same segment
|
mov es,ax ;move to & from same segment
|
||||||
|
|
@ -111,9 +112,10 @@ long as we had assumed, but a long time nonetheless.
|
||||||
; is just to measure memory access
|
; is just to measure memory access
|
||||||
; times
|
; times
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
;
|
;
|
||||||
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,9 +127,10 @@ cycle-eater can *more than double* the execution time of 8088 code!
|
||||||
|
|
||||||
**LISTING 4.12 LST4-12.ASM**
|
**LISTING 4.12 LST4-12.ASM**
|
||||||
|
|
||||||
; Times speed of memory access to normal system
|
```nasm
|
||||||
; memory.
|
; Times speed of memory access to normal system
|
||||||
;
|
; memory.
|
||||||
|
;
|
||||||
mov ax,ds
|
mov ax,ds
|
||||||
mov es,ax ;move to & from same segment
|
mov es,ax ;move to & from same segment
|
||||||
sub si,si ;move to & from same offset
|
sub si,si ;move to & from same offset
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
28
05-03.md
28
05-03.md
|
|
@ -12,30 +12,31 @@ pages: 118-121
|
||||||
|
|
||||||
**LISTING 5.1 SEARCH.C**
|
**LISTING 5.1 SEARCH.C**
|
||||||
|
|
||||||
/* Program to search the file specified by the first command-line
|
```c
|
||||||
|
/* 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
|
||||||
* of size BLOCK_SIZE. */
|
* of size BLOCK_SIZE. */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <alloc.h> /* alloc.h for Borland compilers,
|
#include <alloc.h> /* alloc.h for Borland compilers,
|
||||||
malloc.h for Microsoft compilers */
|
malloc.h for Microsoft compilers */
|
||||||
|
|
||||||
#define BLOCK_SIZE 0x4000 /* we'll process the file in 16K blocks */
|
#define BLOCK_SIZE 0x4000 /* we'll process the file in 16K blocks */
|
||||||
|
|
||||||
/* Searches the specified number of sequences in the specified
|
/* Searches the specified number of sequences in the specified
|
||||||
buffer for matches to SearchString of SearchStringLength. Note
|
buffer for matches to SearchString of SearchStringLength. Note
|
||||||
that the calling code should already have shortened SearchLength
|
that the calling code should already have shortened SearchLength
|
||||||
if necessary to compensate for the distance from the end of the
|
if necessary to compensate for the distance from the end of the
|
||||||
buffer to the last possible start of a matching sequence in the
|
buffer to the last possible start of a matching sequence in the
|
||||||
buffer.
|
buffer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int SearchForString(unsigned char *Buffer, int SearchLength,
|
int SearchForString(unsigned char *Buffer, int SearchLength,
|
||||||
unsigned char *SearchString, int SearchStringLength)
|
unsigned char *SearchString, int SearchStringLength)
|
||||||
{
|
{
|
||||||
unsigned char *PotentialMatch;
|
unsigned char *PotentialMatch;
|
||||||
|
|
||||||
/* Search so long as there are potential-match locations
|
/* Search so long as there are potential-match locations
|
||||||
|
|
@ -66,9 +67,9 @@ pages: 118-121
|
||||||
}
|
}
|
||||||
|
|
||||||
return(0); /* No match found */
|
return(0); /* No match found */
|
||||||
}
|
}
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
int Done; /* Indicates whether search is done */
|
int Done; /* Indicates whether search is done */
|
||||||
int Handle; /* Handle of file being searched */
|
int Handle; /* Handle of file being searched */
|
||||||
int WorkingLength; /* Length of current block */
|
int WorkingLength; /* Length of current block */
|
||||||
|
|
@ -176,4 +177,5 @@ 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 */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
10
06-01.md
10
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:
|
||||||
|
|
||||||
add bx,si
|
```nasm
|
||||||
mov al,[bx]
|
add bx,si
|
||||||
|
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:
|
||||||
|
|
||||||
mov al,[bx+si]
|
```nasm
|
||||||
|
mov al,[bx+si]
|
||||||
|
```
|
||||||
|
|
|
||||||
52
06-02.md
52
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:
|
||||||
|
|
||||||
mov ax,bx
|
```nasm
|
||||||
add ax,di
|
mov ax,bx
|
||||||
add ax,2
|
add ax,di
|
||||||
|
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:
|
||||||
|
|
||||||
lea ax,[bx+di+2]
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov di,si
|
```nasm
|
||||||
add di,2
|
mov di,si
|
||||||
|
add di,2
|
||||||
|
```
|
||||||
|
|
||||||
or:
|
or:
|
||||||
|
|
||||||
lea di,[si+2]
|
```nasm
|
||||||
|
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,
|
||||||
|
|
||||||
lea edi,TableBase[ecx+edx*4]
|
```nasm
|
||||||
|
lea edi,TableBase[ecx+edx*4]
|
||||||
|
```
|
||||||
|
|
||||||
replaces all this
|
replaces all this
|
||||||
|
|
||||||
mov edi,edx
|
```nasm
|
||||||
shl edi,2
|
mov edi,edx
|
||||||
add edi,ecx
|
shl edi,2
|
||||||
add edi,offset TableBase
|
add edi,ecx
|
||||||
|
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:
|
||||||
|
|
||||||
lea ebx,[ebx+ebx*4]
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov edx,ebx
|
```nasm
|
||||||
shl ebx,2
|
mov edx,ebx
|
||||||
add ebx,edx
|
shl ebx,2
|
||||||
|
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.
|
||||||
|
|
|
||||||
12
07-02.md
12
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:
|
||||||
|
|
||||||
and cx,ofh ;Isolate the desired field
|
```nasm
|
||||||
jcxz SkipLoop ;If field is 0, don't bother
|
and cx,ofh ;Isolate the desired field
|
||||||
|
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
|
||||||
|
|
||||||
and cx,0fh ;Isolate the desired field
|
```nasm
|
||||||
jz SkipLoop ;If field is 0, don't bother
|
and cx,0fh ;Isolate the desired field
|
||||||
|
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.
|
||||||
|
|
|
||||||
72
07-03.md
72
07-03.md
|
|
@ -12,34 +12,35 @@ pages: 141-143
|
||||||
|
|
||||||
**LISTING 7.1 L7-1.ASM**
|
**LISTING 7.1 L7-1.ASM**
|
||||||
|
|
||||||
; Program to illustrate searching through a buffer of a specified
|
```nasm
|
||||||
; length until either a specified byte or a zero byte is
|
; Program to illustrate searching through a buffer of a specified
|
||||||
; encountered.
|
; length until either a specified byte or a zero byte is
|
||||||
; A standard loop terminated with LOOP is used.
|
; encountered.
|
||||||
|
; A standard loop terminated with LOOP is used.
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.stack 100h
|
.stack 100h
|
||||||
.data
|
.data
|
||||||
; Sample string to search through.
|
; Sample string to search through.
|
||||||
SampleString labelbyte
|
SampleString labelbyte
|
||||||
db ‘This is a sample string of a long enough length '
|
db ‘This is a sample string of a long enough length '
|
||||||
db ‘so that raw searching speed can outweigh any '
|
db ‘so that raw searching speed can outweigh any '
|
||||||
db ‘extra set-up time that may be required.',0
|
db ‘extra set-up time that may be required.',0
|
||||||
SAMPLE_STRING_LENGTH equ $-SampleString
|
SAMPLE_STRING_LENGTH equ $-SampleString
|
||||||
|
|
||||||
; User prompt.
|
; User prompt.
|
||||||
Prompt db ‘Enter character to search for:$'
|
Prompt db ‘Enter character to search for:$'
|
||||||
|
|
||||||
; Result status messages.
|
; Result status messages.
|
||||||
ByteFoundMsg db 0dh,0ah
|
ByteFoundMsg db 0dh,0ah
|
||||||
db ‘Specified byte found.',0dh,0ah,‘$'
|
db ‘Specified byte found.',0dh,0ah,‘$'
|
||||||
ZeroByteFoundMsg db 0dh, 0ah
|
ZeroByteFoundMsg db 0dh, 0ah
|
||||||
db ‘Zero byte encountered.',0dh,0ah,‘$'
|
db ‘Zero byte encountered.',0dh,0ah,‘$'
|
||||||
NoByteFoundMsg db 0dh,0ah
|
NoByteFoundMsg db 0dh,0ah
|
||||||
db ‘Buffer exhausted with no match.', 0dh, 0ah, ‘$'
|
db ‘Buffer exhausted with no match.', 0dh, 0ah, ‘$'
|
||||||
|
|
||||||
.code
|
.code
|
||||||
Startprocnear
|
Startprocnear
|
||||||
mov ax,@data ;point to standard data segment
|
mov ax,@data ;point to standard data segment
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov dx,offset Prompt
|
mov dx,offset Prompt
|
||||||
|
|
@ -60,31 +61,31 @@ pages: 141-143
|
||||||
;assume we didn't find a zero byte
|
;assume we didn't find a zero byte
|
||||||
jcxz PrintStatus ;we didn't find a zero byte
|
jcxz PrintStatus ;we didn't find a zero byte
|
||||||
mov dx,offset ZeroByteFoundMsg ;we found a zero byte
|
mov dx,offset ZeroByteFoundMsg ;we found a zero byte
|
||||||
PrintStatus:
|
PrintStatus:
|
||||||
mov ah,9 ;DOS print string function
|
mov ah,9 ;DOS print string function
|
||||||
int 21h ;report status
|
int 21h ;report status
|
||||||
mov ah,4ch ;return to DOS
|
mov ah,4ch ;return to DOS
|
||||||
int 21h
|
int 21h
|
||||||
Startendp
|
Startendp
|
||||||
|
|
||||||
; Function to search a buffer of a specified length until either a
|
; Function to search a buffer of a specified length until either a
|
||||||
; specified byte or a zero byte is encountered.
|
; specified byte or a zero byte is encountered.
|
||||||
; Input:
|
; Input:
|
||||||
; AH = character to search for
|
; AH = character to search for
|
||||||
; CX = maximum length to be searched (must be > 0)
|
; CX = maximum length to be searched (must be > 0)
|
||||||
; DS:SI = pointer to buffer to be searched
|
; DS:SI = pointer to buffer to be searched
|
||||||
; Output:
|
; Output:
|
||||||
; CX = 0 if and only if we ran out of bytes without finding
|
; CX = 0 if and only if we ran out of bytes without finding
|
||||||
; either the desired byte or a zero byte
|
; either the desired byte or a zero byte
|
||||||
; DS:SI = pointer to searched-for byte if found, otherwise byte
|
; DS:SI = pointer to searched-for byte if found, otherwise byte
|
||||||
; after zero byte if found, otherwise byte after last
|
; after zero byte if found, otherwise byte after last
|
||||||
; byte checked if neither searched-for byte nor zero
|
; byte checked if neither searched-for byte nor zero
|
||||||
; byte is found
|
; byte is found
|
||||||
; Carry Flag = set if searched-for byte found, reset otherwise
|
; Carry Flag = set if searched-for byte found, reset otherwise
|
||||||
|
|
||||||
SearchMaxLengthprocnear
|
SearchMaxLengthprocnear
|
||||||
cld
|
cld
|
||||||
SearchMaxLengthLoop:
|
SearchMaxLengthLoop:
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
cmp al,ah ;is this the byte we want?
|
cmp al,ah ;is this the byte we want?
|
||||||
jz ByteFound ;yes, we're done with success
|
jz ByteFound ;yes, we're done with success
|
||||||
|
|
@ -92,16 +93,17 @@ pages: 141-143
|
||||||
jz ByteNotFound ;yes, we're done with failure
|
jz ByteNotFound ;yes, we're done with failure
|
||||||
loop SearchMaxLengthLoop ;it's neither, so check the next
|
loop SearchMaxLengthLoop ;it's neither, so check the next
|
||||||
;byte, if any
|
;byte, if any
|
||||||
ByteNotFound:
|
ByteNotFound:
|
||||||
clc ;return "not found" status
|
clc ;return "not found" status
|
||||||
ret
|
ret
|
||||||
ByteFound:
|
ByteFound:
|
||||||
dec si ;point back to the location at which
|
dec si ;point back to the location at which
|
||||||
;we found the searched-for byte
|
;we found the searched-for byte
|
||||||
stc ;return "found" status
|
stc ;return "found" status
|
||||||
ret
|
ret
|
||||||
SearchMaxLengthendp
|
SearchMaxLengthendp
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
||||||
### Unrolling Loops {#Heading7}
|
### Unrolling Loops {#Heading7}
|
||||||
|
|
||||||
|
|
|
||||||
86
07-04.md
86
07-04.md
|
|
@ -12,41 +12,42 @@ pages: 143-145
|
||||||
|
|
||||||
**LISTING 7.2 L7-2.ASM**
|
**LISTING 7.2 L7-2.ASM**
|
||||||
|
|
||||||
; Program to illustrate searching through a buffer of a specified
|
```nasm
|
||||||
; length until a specified zero byte is encountered.
|
; Program to illustrate searching through a buffer of a specified
|
||||||
; A loop unrolled four times and terminated with LOOP is used.
|
; length until a specified zero byte is encountered.
|
||||||
|
; A loop unrolled four times and terminated with LOOP is used.
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.stack 100h
|
.stack 100h
|
||||||
.data
|
.data
|
||||||
; Sample string to search through.
|
; Sample string to search through.
|
||||||
SampleStringlabelbyte
|
SampleStringlabelbyte
|
||||||
db ‘This is a sample string of a long enough length '
|
db ‘This is a sample string of a long enough length '
|
||||||
db ‘so that raw searching speed can outweigh any '
|
db ‘so that raw searching speed can outweigh any '
|
||||||
db ‘extra set-up time that may be required.',0
|
db ‘extra set-up time that may be required.',0
|
||||||
SAMPLE_STRING_LENGTH equ $-SampleString
|
SAMPLE_STRING_LENGTH equ $-SampleString
|
||||||
|
|
||||||
; User prompt.
|
; User prompt.
|
||||||
Prompt db ‘Enter character to search for:$'
|
Prompt db ‘Enter character to search for:$'
|
||||||
|
|
||||||
; Result status messages.
|
; Result status messages.
|
||||||
ByteFoundMsg db 0dh,0ah
|
ByteFoundMsg db 0dh,0ah
|
||||||
db ‘Specified byte found.',0dh,0ah,‘$'
|
db ‘Specified byte found.',0dh,0ah,‘$'
|
||||||
ZeroByteFoundMsg db 0dh,0ah
|
ZeroByteFoundMsg db 0dh,0ah
|
||||||
db ‘Zero byte encountered.', 0dh, 0ah, ‘$'
|
db ‘Zero byte encountered.', 0dh, 0ah, ‘$'
|
||||||
NoByteFoundMsg db 0dh,0ah
|
NoByteFoundMsg db 0dh,0ah
|
||||||
db ‘Buffer exhausted with no match.', 0dh, 0ah, ‘$'
|
db ‘Buffer exhausted with no match.', 0dh, 0ah, ‘$'
|
||||||
|
|
||||||
; Table of initial, possibly partial loop entry points for
|
; Table of initial, possibly partial loop entry points for
|
||||||
; SearchMaxLength.
|
; SearchMaxLength.
|
||||||
SearchMaxLengthEntryTable labelword
|
SearchMaxLengthEntryTable labelword
|
||||||
dw SearchMaxLengthEntry4
|
dw SearchMaxLengthEntry4
|
||||||
dw SearchMaxLengthEntry1
|
dw SearchMaxLengthEntry1
|
||||||
dw SearchMaxLengthEntry2
|
dw SearchMaxLengthEntry2
|
||||||
dw SearchMaxLengthEntry3
|
dw SearchMaxLengthEntry3
|
||||||
|
|
||||||
.code
|
.code
|
||||||
Start proc near
|
Start proc near
|
||||||
mov ax,@data ;point to standard data segment
|
mov ax,@data ;point to standard data segment
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov dx,offset Prompt
|
mov dx,offset Prompt
|
||||||
|
|
@ -67,30 +68,30 @@ pages: 143-145
|
||||||
;assume we didn't find a zero byte
|
;assume we didn't find a zero byte
|
||||||
jcxz PrintStatus ;we didn't find a zero byte
|
jcxz PrintStatus ;we didn't find a zero byte
|
||||||
mov dx,offset ZeroByteFoundMsg ;we found a zero byte
|
mov dx,offset ZeroByteFoundMsg ;we found a zero byte
|
||||||
PrintStatus:
|
PrintStatus:
|
||||||
mov ah,9 ;DOS print string function
|
mov ah,9 ;DOS print string function
|
||||||
int 21h ;report status
|
int 21h ;report status
|
||||||
|
|
||||||
mov ah,4ch ;return to DOS
|
mov ah,4ch ;return to DOS
|
||||||
int 21h
|
int 21h
|
||||||
Startendp
|
Startendp
|
||||||
|
|
||||||
; Function to search a buffer of a specified length until either a
|
; Function to search a buffer of a specified length until either a
|
||||||
; specified byte or a zero byte is encountered.
|
; specified byte or a zero byte is encountered.
|
||||||
; Input:
|
; Input:
|
||||||
; AH = character to search for
|
; AH = character to search for
|
||||||
; CX = maximum length to be searched (must be > 0)
|
; CX = maximum length to be searched (must be > 0)
|
||||||
; DS:SI = pointer to buffer to be searched
|
; DS:SI = pointer to buffer to be searched
|
||||||
; Output:
|
; Output:
|
||||||
; CX = 0 if and only if we ran out of bytes without finding
|
; CX = 0 if and only if we ran out of bytes without finding
|
||||||
; either the desired byte or a zero byte
|
; either the desired byte or a zero byte
|
||||||
; DS:SI = pointer to searched-for byte if found, otherwise byte
|
; DS:SI = pointer to searched-for byte if found, otherwise byte
|
||||||
; after zero byte if found, otherwise byte after last
|
; after zero byte if found, otherwise byte after last
|
||||||
; byte checked if neither searched-for byte nor zero
|
; byte checked if neither searched-for byte nor zero
|
||||||
; byte is found
|
; byte is found
|
||||||
; Carry Flag = set if searched-for byte found, reset otherwise
|
; Carry Flag = set if searched-for byte found, reset otherwise
|
||||||
|
|
||||||
SearchMaxLength proc near
|
SearchMaxLength proc near
|
||||||
cld
|
cld
|
||||||
mov bx,cx
|
mov bx,cx
|
||||||
add cx,3 ;calculate the maximum # of passes
|
add cx,3 ;calculate the maximum # of passes
|
||||||
|
|
@ -103,26 +104,26 @@ pages: 143-145
|
||||||
jmp SearchMaxLengthEntryTable[bx]
|
jmp SearchMaxLengthEntryTable[bx]
|
||||||
;branch into the unrolled loop to do
|
;branch into the unrolled loop to do
|
||||||
;the first, possibly partial loop
|
;the first, possibly partial loop
|
||||||
SearchMaxLengthLoop:
|
SearchMaxLengthLoop:
|
||||||
SearchMaxLengthEntry4:
|
SearchMaxLengthEntry4:
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
cmp al,ah ;is this the byte we want?
|
cmp al,ah ;is this the byte we want?
|
||||||
jz ByteFound ;yes, we're done with success
|
jz ByteFound ;yes, we're done with success
|
||||||
and al,al ;is this the terminating 0 byte?
|
and al,al ;is this the terminating 0 byte?
|
||||||
jz ByteNotFound ;yes, we're done with failure
|
jz ByteNotFound ;yes, we're done with failure
|
||||||
SearchMaxLengthEntry3:
|
SearchMaxLengthEntry3:
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
cmp al,ah ;is this the byte we want?
|
cmp al,ah ;is this the byte we want?
|
||||||
jz ByteFound ;yes, we're done with success
|
jz ByteFound ;yes, we're done with success
|
||||||
and al,al ;is this the terminating 0 byte?
|
and al,al ;is this the terminating 0 byte?
|
||||||
jz ByteNotFound ;yes, we're done with failure
|
jz ByteNotFound ;yes, we're done with failure
|
||||||
SearchMaxLengthEntry2:
|
SearchMaxLengthEntry2:
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
cmp al,ah ;is this the byte we want?
|
cmp al,ah ;is this the byte we want?
|
||||||
jz ByteFound ;yes, we're done with success
|
jz ByteFound ;yes, we're done with success
|
||||||
and al,al ;is this the terminating 0 byte?
|
and al,al ;is this the terminating 0 byte?
|
||||||
jz ByteNotFound ;yes, we're done with failure
|
jz ByteNotFound ;yes, we're done with failure
|
||||||
SearchMaxLengthEntry1:
|
SearchMaxLengthEntry1:
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
cmp al,ah ;is this the byte we want?
|
cmp al,ah ;is this the byte we want?
|
||||||
jz ByteFound ;yes, we're done with success
|
jz ByteFound ;yes, we're done with success
|
||||||
|
|
@ -130,16 +131,17 @@ pages: 143-145
|
||||||
jz ByteNotFound ;yes, we're done with failure
|
jz ByteNotFound ;yes, we're done with failure
|
||||||
loop SearchMaxLengthLoop ;it's neither, so check the next
|
loop SearchMaxLengthLoop ;it's neither, so check the next
|
||||||
; four bytes, if any
|
; four bytes, if any
|
||||||
ByteNotFound:
|
ByteNotFound:
|
||||||
clc ;return "not found" status
|
clc ;return "not found" status
|
||||||
ret
|
ret
|
||||||
ByteFound:
|
ByteFound:
|
||||||
dec si ;point back to the location at which
|
dec si ;point back to the location at which
|
||||||
; we found the searched-for byte
|
; we found the searched-for byte
|
||||||
stc ;return "found" status
|
stc ;return "found" status
|
||||||
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
|
||||||
|
|
|
||||||
44
07-05.md
44
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:
|
||||||
|
|
||||||
MOV BX,1
|
```nasm
|
||||||
SHL BX,CL
|
MOV BX,1
|
||||||
OR AX,BX
|
SHL BX,CL
|
||||||
|
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,15 +33,17 @@ 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
|
||||||
:
|
:
|
||||||
ShiftTable LABEL WORD
|
ShiftTable LABEL WORD
|
||||||
BIT_PATTERN=0001H
|
BIT_PATTERN=0001H
|
||||||
REPT 16
|
REPT 16
|
||||||
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,14 +59,16 @@ 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
|
||||||
BIT_PATTERN=0001H
|
BIT_PATTERN=0001H
|
||||||
REPT 32
|
REPT 32
|
||||||
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,8 +125,9 @@ 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
|
||||||
ADC [DI],AX;add with Carry to dest operand word
|
ADC [DI],AX;add with Carry to dest operand word
|
||||||
INC SI ;point to next source operand word
|
INC SI ;point to next source operand word
|
||||||
|
|
@ -128,14 +135,16 @@ 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
|
||||||
ADC [DI],AX ;add with carry to dest operand word
|
ADC [DI],AX ;add with carry to dest operand word
|
||||||
LAHF ;set aside the carry flag
|
LAHF ;set aside the carry flag
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
INC AX
|
```nasm
|
||||||
ADC DX,0
|
INC AX
|
||||||
|
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:
|
||||||
|
|
||||||
ADD AX,1
|
```nasm
|
||||||
ADC DX,0
|
ADD AX,1
|
||||||
|
ADC DX,0
|
||||||
|
```
|
||||||
|
|
||||||
As always, pay attention!
|
As always, pay attention!
|
||||||
6
08-02.md
6
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:
|
||||||
|
|
||||||
repnz scasw
|
```nasm
|
||||||
jz Match
|
repnz scasw
|
||||||
|
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.
|
||||||
|
|
|
||||||
72
08-03.md
72
08-03.md
|
|
@ -12,38 +12,39 @@ pages: 156-160
|
||||||
|
|
||||||
**LISTING 8.1 L8-1.C**
|
**LISTING 8.1 L8-1.C**
|
||||||
|
|
||||||
/* Program to search an array spanning a linked list of variable-
|
```c
|
||||||
|
/* 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
|
||||||
the variable-sized blocks may contain any number of data entries,
|
the variable-sized blocks may contain any number of data entries,
|
||||||
stored as an array of structures within the block. */
|
stored as an array of structures within the block. */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef __TURBOC__
|
#ifdef __TURBOC__
|
||||||
#include <alloc.h>
|
#include <alloc.h>
|
||||||
#else
|
#else
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void main(void);
|
void main(void);
|
||||||
void exit(int);
|
void exit(int);
|
||||||
unsigned int FindIDAverage(unsigned int, struct BlockHeader *);
|
unsigned int FindIDAverage(unsigned int, struct BlockHeader *);
|
||||||
/* 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
|
||||||
if this is the last block in the
|
if this is the last block in the
|
||||||
linked list */
|
linked list */
|
||||||
unsigned int BlockCount; /* The number of DataElement entries
|
unsigned int BlockCount; /* The number of DataElement entries
|
||||||
in this variable-sized block */
|
in this variable-sized block */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Structure that contains one element of the array we'll search */
|
/* Structure that contains one element of the array we'll search */
|
||||||
struct DataElement {
|
struct DataElement {
|
||||||
unsigned int ID; /* ID # for array entry */
|
unsigned int ID; /* ID # for array entry */
|
||||||
unsigned int Value; /* Value of array entry */
|
unsigned int Value; /* Value of array entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
int i,j;
|
int i,j;
|
||||||
unsigned int IDToFind;
|
unsigned int IDToFind;
|
||||||
struct BlockHeader *BaseArrayBlockPointer,*WorkingBlockPointer;
|
struct BlockHeader *BaseArrayBlockPointer,*WorkingBlockPointer;
|
||||||
|
|
@ -85,17 +86,17 @@ pages: 156-160
|
||||||
printf("Average of all elements with ID %d: %u\n",
|
printf("Average of all elements with ID %d: %u\n",
|
||||||
IDToFind, FindIDAverage(IDToFind, BaseArrayBlockPointer));
|
IDToFind, FindIDAverage(IDToFind, BaseArrayBlockPointer));
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Searches through the array of DataElement entries spanning the
|
/* Searches through the array of DataElement entries spanning the
|
||||||
linked list of variable-sized blocks, starting with the block
|
linked list of variable-sized blocks, starting with the block
|
||||||
pointed to by BlockPointer, for all entries with IDs matching
|
pointed to by BlockPointer, for all entries with IDs matching
|
||||||
SearchedForID, and returns the average value of those entries. If
|
SearchedForID, and returns the average value of those entries. If
|
||||||
no matches are found, zero is returned */
|
no matches are found, zero is returned */
|
||||||
|
|
||||||
unsigned int FindIDAverage(unsigned int SearchedForID,
|
unsigned int FindIDAverage(unsigned int SearchedForID,
|
||||||
struct BlockHeader *BlockPointer)
|
struct BlockHeader *BlockPointer)
|
||||||
{
|
{
|
||||||
struct DataElement *DataPointer;
|
struct DataElement *DataPointer;
|
||||||
unsigned int IDMatchSum;
|
unsigned int IDMatchSum;
|
||||||
unsigned int IDMatchCount;
|
unsigned int IDMatchCount;
|
||||||
|
|
@ -130,7 +131,8 @@ pages: 156-160
|
||||||
return(0); /* Avoid division by 0 */
|
return(0); /* Avoid division by 0 */
|
||||||
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,10 +156,11 @@ instruction can be used.
|
||||||
|
|
||||||
**LISTING 8.2 L8-2.COD**
|
**LISTING 8.2 L8-2.COD**
|
||||||
|
|
||||||
; Code generated by Microsoft C for inner loop of FindIDAverage.
|
```nasm
|
||||||
;|*** for (WorkingBlockCount=0;
|
; Code generated by Microsoft C for inner loop of FindIDAverage.
|
||||||
;|*** WorkingBlockCount<BlockPointer->BlockCount;
|
;|*** for (WorkingBlockCount=0;
|
||||||
;|*** WorkingBlockCount++, DataPointer++) {
|
;|*** WorkingBlockCount<BlockPointer->BlockCount;
|
||||||
|
;|*** WorkingBlockCount++, DataPointer++) {
|
||||||
mov WORD PTR [bp-6],0 ;WorkingBlockCount
|
mov WORD PTR [bp-6],0 ;WorkingBlockCount
|
||||||
mov bx,WORD PTR [bp+6] ;BlockPointer
|
mov bx,WORD PTR [bp+6] ;BlockPointer
|
||||||
cmp WORD PTR [bx+2],0
|
cmp WORD PTR [bx+2],0
|
||||||
|
|
@ -166,20 +169,21 @@ instruction can be used.
|
||||||
add WORD PTR [bp-6],cx ;WorkingBlockCount
|
add WORD PTR [bp-6],cx ;WorkingBlockCount
|
||||||
mov di,WORD PTR [bp-2] ;IDMatchSum
|
mov di,WORD PTR [bp-2] ;IDMatchSum
|
||||||
mov dx,WORD PTR [bp-4] ;IDMatchCount
|
mov dx,WORD PTR [bp-4] ;IDMatchCount
|
||||||
$L20004:
|
$L20004:
|
||||||
;|*** if (DataPointer->ID == SearchedForID) {
|
;|*** if (DataPointer->ID == SearchedForID) {
|
||||||
mov ax,WORD PTR [si]
|
mov ax,WORD PTR [si]
|
||||||
cmp WORD PTR [bp+4],ax ;SearchedForID
|
cmp WORD PTR [bp+4],ax ;SearchedForID
|
||||||
jne $I265
|
jne $I265
|
||||||
;|*** IDMatchCount++;
|
;|*** IDMatchCount++;
|
||||||
inc dx
|
inc dx
|
||||||
;|*** IDMatchSum += DataPointer->Value;
|
;|*** IDMatchSum += DataPointer->Value;
|
||||||
add di,WORD PTR [si+2]
|
add di,WORD PTR [si+2]
|
||||||
;|*** }
|
;|*** }
|
||||||
;|*** }
|
;|*** }
|
||||||
$I265:
|
$I265:
|
||||||
add si,4
|
add si,4
|
||||||
loop $L20004
|
loop $L20004
|
||||||
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:
|
||||||
|
```
|
||||||
|
|
|
||||||
108
08-04.md
108
08-04.md
|
|
@ -24,19 +24,21 @@ optimization, isn't it?
|
||||||
|
|
||||||
**LISTING 8.3 L8-3.ASM**
|
**LISTING 8.3 L8-3.ASM**
|
||||||
|
|
||||||
; Typically optimized assembly language version of FindIDAverage.
|
```nasm
|
||||||
SearchedForID equ 4 ;Passed parameter offsets in the
|
; Typically optimized assembly language version of FindIDAverage.
|
||||||
BlockPointer equ 6 ; stack frame (skip over pushed BP
|
SearchedForID equ 4 ;Passed parameter offsets in the
|
||||||
; and the return address)
|
BlockPointer equ 6 ; stack frame (skip over pushed BP
|
||||||
NextBlock equ 0 ;Field offsets in struct BlockHeader
|
; and the return address)
|
||||||
BlockCount equ 2
|
NextBlock equ 0 ;Field offsets in struct BlockHeader
|
||||||
BLOCK_HEADER_SIZE equ 4 ;Number of bytes in struct BlockHeader
|
BlockCount equ 2
|
||||||
ID equ 0 ;struct DataElement field offsets
|
BLOCK_HEADER_SIZE equ 4 ;Number of bytes in struct BlockHeader
|
||||||
Value equ 2
|
ID equ 0 ;struct DataElement field offsets
|
||||||
DATA_ELEMENT_SIZE equ 4 ;Number of bytes in struct DataElement
|
Value equ 2
|
||||||
|
DATA_ELEMENT_SIZE equ 4 ;Number of bytes in struct DataElement
|
||||||
.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,7 +49,8 @@ optimization, isn't it?
|
||||||
|
|
||||||
Table: Table 8.1 Execution Times of FindIDAverage.
|
Table: Table 8.1 Execution Times of FindIDAverage.
|
||||||
|
|
||||||
_FindIDAverage proc near
|
```nasm
|
||||||
|
_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
|
||||||
push di ;Preserve C register variables
|
push di ;Preserve C register variables
|
||||||
|
|
@ -56,40 +59,41 @@ Table: Table 8.1 Execution Times of FindIDAverage.
|
||||||
mov bx,dx ;IDMatchCount = 0
|
mov bx,dx ;IDMatchCount = 0
|
||||||
mov si,[bp+BlockPointer] ;Pointer to first block
|
mov si,[bp+BlockPointer] ;Pointer to first block
|
||||||
mov ax,[bp+SearchedForID] ;ID we're looking for
|
mov ax,[bp+SearchedForID] ;ID we're looking for
|
||||||
; Search through all the linked blocks until the last block
|
; Search through all the linked blocks until the last block
|
||||||
; (marked with a NULL pointer to the next block) has been searched.
|
; (marked with a NULL pointer to the next block) has been searched.
|
||||||
BlockLoop:
|
BlockLoop:
|
||||||
; Point to the first DataElement entry within this block.
|
; Point to the first DataElement entry within this block.
|
||||||
lea di,[si+BLOCK_HEADER_SIZE]
|
lea di,[si+BLOCK_HEADER_SIZE]
|
||||||
; Search through all the DataElement entries within this block
|
; Search through all the DataElement entries within this block
|
||||||
; and accumulate data from all that match the desired ID.
|
; and accumulate data from all that match the desired ID.
|
||||||
mov cx,[si+BlockCount]
|
mov cx,[si+BlockCount]
|
||||||
jcxz DoNextBlock ;No data in this block
|
jcxz DoNextBlock ;No data in this block
|
||||||
IntraBlockLoop:
|
IntraBlockLoop:
|
||||||
cmp [di+ID],ax ;Do we have an ID match?
|
cmp [di+ID],ax ;Do we have an ID match?
|
||||||
jnz NoMatch ;No match
|
jnz NoMatch ;No match
|
||||||
inc bx ;We have a match; IDMatchCount++;
|
inc bx ;We have a match; IDMatchCount++;
|
||||||
add dx,[di+Value] ;IDMatchSum += DataPointer->Value;
|
add dx,[di+Value] ;IDMatchSum += DataPointer->Value;
|
||||||
NoMatch:
|
NoMatch:
|
||||||
add di,DATA_ELEMENT_SIZE ;point to the next element
|
add di,DATA_ELEMENT_SIZE ;point to the next element
|
||||||
loop IntraBlockLoop
|
loop IntraBlockLoop
|
||||||
; Point to the next block and continue if that pointer isn't NULL.
|
; Point to the next block and continue if that pointer isn't NULL.
|
||||||
DoNextBlock:
|
DoNextBlock:
|
||||||
mov si,[si+NextBlock] ;Get pointer to the next block
|
mov si,[si+NextBlock] ;Get pointer to the next block
|
||||||
and si,si ;Is it a NULL pointer?
|
and si,si ;Is it a NULL pointer?
|
||||||
jnz BlockLoop ;No, continue
|
jnz BlockLoop ;No, continue
|
||||||
; Calculate the average of all matches.
|
; Calculate the average of all matches.
|
||||||
sub ax,ax ;Assume we found no matches
|
sub ax,ax ;Assume we found no matches
|
||||||
and bx,bx
|
and bx,bx
|
||||||
jz Done ;We didn't find any matches, return 0
|
jz Done ;We didn't find any matches, return 0
|
||||||
xchg ax,dx ;Prepare for division
|
xchg ax,dx ;Prepare for division
|
||||||
div bx ;Return IDMatchSum / IDMatchCount
|
div bx ;Return IDMatchSum / IDMatchCount
|
||||||
Done: pop si ;Restore C register variables
|
Done: pop si ;Restore C register variables
|
||||||
pop di
|
pop di
|
||||||
pop bp ;Restore caller's stack frame
|
pop bp ;Restore caller's stack frame
|
||||||
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,21 +106,22 @@ but not a tremendous return for the optimization effort invested.
|
||||||
|
|
||||||
**LISTING 8.4 L8-4.ASM**
|
**LISTING 8.4 L8-4.ASM**
|
||||||
|
|
||||||
; Heavily optimized assembly language version of FindIDAverage.
|
```nasm
|
||||||
; Features an unrolled loop and more efficient pointer use.
|
; Heavily optimized assembly language version of FindIDAverage.
|
||||||
SearchedForID equ 4 ;Passed parameter offsets in the
|
; Features an unrolled loop and more efficient pointer use.
|
||||||
BlockPointer equ 6 ; stack frame (skip over pushed BP
|
SearchedForID equ 4 ;Passed parameter offsets in the
|
||||||
|
BlockPointer equ 6 ; stack frame (skip over pushed BP
|
||||||
; and the return address)
|
; and the return address)
|
||||||
NextBlock equ 0 ;Field offsets in struct BlockHeader
|
NextBlock equ 0 ;Field offsets in struct BlockHeader
|
||||||
BlockCount equ 2
|
BlockCount equ 2
|
||||||
BLOCK_HEADER_SIZE equ 4 ;Number of bytes in struct BlockHeader
|
BLOCK_HEADER_SIZE equ 4 ;Number of bytes in struct BlockHeader
|
||||||
ID equ 0 ;struct DataElement field offsets
|
ID equ 0 ;struct DataElement field offsets
|
||||||
Value equ 2
|
Value equ 2
|
||||||
DATA_ELEMENT_SIZE equ 4 ;Number of bytes in struct DataElement
|
DATA_ELEMENT_SIZE equ 4 ;Number of bytes in struct DataElement
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindIDAverage
|
public _FindIDAverage
|
||||||
_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
|
||||||
push di ;Preserve C register variables
|
push di ;Preserve C register variables
|
||||||
|
|
@ -128,13 +133,13 @@ but not a tremendous return for the optimization effort invested.
|
||||||
mov bx,dx ;IDMatchCount = 0
|
mov bx,dx ;IDMatchCount = 0
|
||||||
mov si,[bp+BlockPointer] ;Pointer to first block
|
mov si,[bp+BlockPointer] ;Pointer to first block
|
||||||
mov ax,[bp+SearchedForID] ;ID we're looking for
|
mov ax,[bp+SearchedForID] ;ID we're looking for
|
||||||
; Search through all of the linked blocks until the last block
|
; Search through all of the linked blocks until the last block
|
||||||
; (marked with a NULL pointer to the next block) has been searched.
|
; (marked with a NULL pointer to the next block) has been searched.
|
||||||
BlockLoop:
|
BlockLoop:
|
||||||
; Point to the first DataElement entry within this block.
|
; Point to the first DataElement entry within this block.
|
||||||
lea di,[si+BLOCK_HEADER_SIZE]
|
lea di,[si+BLOCK_HEADER_SIZE]
|
||||||
; Search through all the DataElement entries within this block
|
; Search through all the DataElement entries within this block
|
||||||
; and accumulate data from all that match the desired ID.
|
; and accumulate data from all that match the desired ID.
|
||||||
mov cx,[si+BlockCount] ;Number of elements in this block
|
mov cx,[si+BlockCount] ;Number of elements in this block
|
||||||
jcxz DoNextBlock ;Skip this block if it's empty
|
jcxz DoNextBlock ;Skip this block if it's empty
|
||||||
mov bp,cx ;***stack frame no longer available***
|
mov bp,cx ;***stack frame no longer available***
|
||||||
|
|
@ -147,23 +152,23 @@ but not a tremendous return for the optimization effort invested.
|
||||||
jmp cs:[LoopEntryTable+bp] ; the unrolled loop and
|
jmp cs:[LoopEntryTable+bp] ; the unrolled loop and
|
||||||
; vector to that entry point
|
; vector to that entry point
|
||||||
align 2
|
align 2
|
||||||
LoopEntryTable label word
|
LoopEntryTable label word
|
||||||
dw LoopEntry8,LoopEntry1,LoopEntry2,LoopEntry3
|
dw LoopEntry8,LoopEntry1,LoopEntry2,LoopEntry3
|
||||||
dw LoopEntry4,LoopEntry5,LoopEntry6,LoopEntry7
|
dw LoopEntry4,LoopEntry5,LoopEntry6,LoopEntry7
|
||||||
M_IBL macro P1
|
M_IBL macro P1
|
||||||
local NoMatch
|
local NoMatch
|
||||||
LoopEntry&P1&:
|
LoopEntry&P1&:
|
||||||
scasw ;Do we have an ID match?
|
scasw ;Do we have an ID match?
|
||||||
jnz NoMatch ;No match
|
jnz NoMatch ;No match
|
||||||
;We have a match
|
;We have a match
|
||||||
inc bx ;IDMatchCount++;
|
inc bx ;IDMatchCount++;
|
||||||
add dx,[di] ;IDMatchSum += DataPointer->Value;
|
add dx,[di] ;IDMatchSum += DataPointer->Value;
|
||||||
NoMatch:
|
NoMatch:
|
||||||
add di,DATA_ELEMENT_SIZE-2 ;point to the next element
|
add di,DATA_ELEMENT_SIZE-2 ;point to the next element
|
||||||
; (SCASW advanced 2 bytes already)
|
; (SCASW advanced 2 bytes already)
|
||||||
endm
|
endm
|
||||||
align 2
|
align 2
|
||||||
IntraBlockLoop:
|
IntraBlockLoop:
|
||||||
M_IBL 8
|
M_IBL 8
|
||||||
M_IBL 7
|
M_IBL 7
|
||||||
M_IBL 6
|
M_IBL 6
|
||||||
|
|
@ -173,20 +178,21 @@ but not a tremendous return for the optimization effort invested.
|
||||||
M_IBL 2
|
M_IBL 2
|
||||||
M_IBL 1
|
M_IBL 1
|
||||||
loop IntraBlockLoop
|
loop IntraBlockLoop
|
||||||
; Point to the next block and continue if that pointer isn't NULL.
|
; Point to the next block and continue if that pointer isn't NULL.
|
||||||
DoNextBlock:
|
DoNextBlock:
|
||||||
mov si,[si+NextBlock] ;Get pointer to the next block
|
mov si,[si+NextBlock] ;Get pointer to the next block
|
||||||
and si,si ;Is it a NULL pointer?
|
and si,si ;Is it a NULL pointer?
|
||||||
jnz BlockLoop ;No, continue
|
jnz BlockLoop ;No, continue
|
||||||
; Calculate the average of all matches.
|
; Calculate the average of all matches.
|
||||||
sub ax,ax ;Assume we found no matches
|
sub ax,ax ;Assume we found no matches
|
||||||
and bx,bx
|
and bx,bx
|
||||||
jz Done ;We didn't find any matches, return 0
|
jz Done ;We didn't find any matches, return 0
|
||||||
xchg ax,dx ;Prepare for division
|
xchg ax,dx ;Prepare for division
|
||||||
div bx ;Return IDMatchSum / IDMatchCount
|
div bx ;Return IDMatchSum / IDMatchCount
|
||||||
Done: pop si ;Restore C register variables
|
Done: pop si ;Restore C register variables
|
||||||
pop di
|
pop di
|
||||||
pop bp ;Restore caller's stack frame
|
pop bp ;Restore caller's stack frame
|
||||||
ret
|
ret
|
||||||
_FindIDAverage ENDP
|
_FindIDAverage ENDP
|
||||||
end
|
end
|
||||||
|
```
|
||||||
86
08-05.md
86
08-05.md
|
|
@ -20,37 +20,40 @@ merely rearranged.
|
||||||
|
|
||||||
**LISTING 8.5 L8-5.C**
|
**LISTING 8.5 L8-5.C**
|
||||||
|
|
||||||
/* Program to search an array spanning a linked list of variable-
|
```c
|
||||||
|
/* 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
|
||||||
the variable-sized blocks may contain any number of data entries,
|
the variable-sized blocks may contain any number of data entries,
|
||||||
stored in the form of two separate arrays, one for ID numbers and
|
stored in the form of two separate arrays, one for ID numbers and
|
||||||
one for values. */
|
one for values. */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef __TURBOC__
|
#ifdef __TURBOC__
|
||||||
#include <alloc.h>
|
#include <alloc.h>
|
||||||
#else
|
#else
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void main(void);
|
void main(void);
|
||||||
void exit(int);
|
void exit(int);
|
||||||
extern unsigned int FindIDAverage2(unsigned int,
|
extern unsigned int FindIDAverage2(unsigned int,
|
||||||
struct BlockHeader *);
|
struct BlockHeader *);
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
/* Structure that starts each variable-sized block */
|
```c
|
||||||
struct BlockHeader {
|
/* Structure that starts each variable-sized block */
|
||||||
|
struct BlockHeader {
|
||||||
struct BlockHeader *NextBlock; /* Pointer to next block, or NULL
|
struct BlockHeader *NextBlock; /* Pointer to next block, or NULL
|
||||||
if this is the last block in the
|
if this is the last block in the
|
||||||
linked list */
|
linked list */
|
||||||
unsigned int BlockCount; /* The number of DataElement entries
|
unsigned int BlockCount; /* The number of DataElement entries
|
||||||
in this variable-sized block */
|
in this variable-sized block */
|
||||||
};
|
};
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
int i,j;
|
int i,j;
|
||||||
unsigned int IDToFind;
|
unsigned int IDToFind;
|
||||||
struct BlockHeader *BaseArrayBlockPointer,*WorkingBlockPointer;
|
struct BlockHeader *BaseArrayBlockPointer,*WorkingBlockPointer;
|
||||||
|
|
@ -92,26 +95,28 @@ merely rearranged.
|
||||||
printf("Average of all elements with ID %d: %u\n",
|
printf("Average of all elements with ID %d: %u\n",
|
||||||
IDToFind, FindIDAverage2(IDToFind, BaseArrayBlockPointer));
|
IDToFind, FindIDAverage2(IDToFind, BaseArrayBlockPointer));
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 8.6 L8-6.ASM**
|
**LISTING 8.6 L8-6.ASM**
|
||||||
|
|
||||||
; Alternative optimized assembly language version of FindIDAverage
|
```asm
|
||||||
; requires data organized as two arrays within each block rather
|
; Alternative optimized assembly language version of FindIDAverage
|
||||||
; than as an array of two-value element structures. This allows the
|
; requires data organized as two arrays within each block rather
|
||||||
; use of REP SCASW for ID searching.
|
; than as an array of two-value element structures. This allows the
|
||||||
|
; use of REP SCASW for ID searching.
|
||||||
|
|
||||||
SearchedForIDequ4 ;Passed parameter offsets in the
|
SearchedForIDequ4 ;Passed parameter offsets in the
|
||||||
BlockPointerequ6 ; stack frame (skip over pushed BP
|
BlockPointerequ6 ; stack frame (skip over pushed BP
|
||||||
; and the return address)
|
; and the return address)
|
||||||
NextBlockequ0 ;Field offsets in struct BlockHeader
|
NextBlockequ0 ;Field offsets in struct BlockHeader
|
||||||
BlockCountequ2
|
BlockCountequ2
|
||||||
BLOCK_HEADER_SIZEequ4 ;Number of bytes in struct BlockHeader
|
BLOCK_HEADER_SIZEequ4 ;Number of bytes in struct BlockHeader
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindIDAverage2
|
public _FindIDAverage2
|
||||||
_FindIDAverage2 proc near
|
_FindIDAverage2 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
|
||||||
push di ;Preserve C register variables
|
push di ;Preserve C register variables
|
||||||
|
|
@ -124,45 +129,46 @@ merely rearranged.
|
||||||
sub dx,dx ;IDMatchSum = 0
|
sub dx,dx ;IDMatchSum = 0
|
||||||
mov bp,dx ;IDMatchCount = 0
|
mov bp,dx ;IDMatchCount = 0
|
||||||
;***stack frame no longer available***
|
;***stack frame no longer available***
|
||||||
; Search through all the linked blocks until the last block
|
; Search through all the linked blocks until the last block
|
||||||
; (marked with a NULL pointer to the next block) has been searched.
|
; (marked with a NULL pointer to the next block) has been searched.
|
||||||
BlockLoop:
|
BlockLoop:
|
||||||
; Search through all the DataElement entries within this block
|
; Search through all the DataElement entries within this block
|
||||||
; and accumulate data from all that match the desired ID.
|
; and accumulate data from all that match the desired ID.
|
||||||
mov cx,[si+BlockCount]
|
mov cx,[si+BlockCount]
|
||||||
jcxz DoNextBlock;Skip this block if there's no data
|
jcxz DoNextBlock;Skip this block if there's no data
|
||||||
; to search through
|
; to search through
|
||||||
mov bx,cx ;We'll use BX to point to the
|
mov bx,cx ;We'll use BX to point to the
|
||||||
shl bx,1 ; corresponding value entry in the
|
shl bx,1 ; corresponding value entry in the
|
||||||
; case of an ID match (BX is the
|
; case of an ID match (BX is the
|
||||||
; length in bytes of the ID array)
|
; length in bytes of the ID array)
|
||||||
; Point to the first DataElement entry within this block.
|
; Point to the first DataElement entry within this block.
|
||||||
lea di,[si+BLOCK_HEADER_SIZE]
|
lea di,[si+BLOCK_HEADER_SIZE]
|
||||||
IntraBlockLoop:
|
IntraBlockLoop:
|
||||||
repnz scasw ;Search for the ID
|
repnz scasw ;Search for the ID
|
||||||
jnz DoNextBlock ;No match, the block is done
|
jnz DoNextBlock ;No match, the block is done
|
||||||
inc bp ;We have a match; IDMatchCount++;
|
inc bp ;We have a match; IDMatchCount++;
|
||||||
add dx,[di+bx-2];IDMatchSum += DataPointer->Value;
|
add dx,[di+bx-2];IDMatchSum += DataPointer->Value;
|
||||||
; (SCASW has advanced DI 2 bytes)
|
; (SCASW has advanced DI 2 bytes)
|
||||||
and cx,cx ;Is there more data to search through?
|
and cx,cx ;Is there more data to search through?
|
||||||
jnz IntraBlockLoop ;yes
|
jnz IntraBlockLoop ;yes
|
||||||
; Point to the next block and continue if that pointer isn't NULL.
|
; Point to the next block and continue if that pointer isn't NULL.
|
||||||
DoNextBlock:
|
DoNextBlock:
|
||||||
mov si,[si+NextBlock] ;Get pointer to the next block
|
mov si,[si+NextBlock] ;Get pointer to the next block
|
||||||
and si,si ;Is it a NULL pointer?
|
and si,si ;Is it a NULL pointer?
|
||||||
jnz BlockLoop ;No, continue
|
jnz BlockLoop ;No, continue
|
||||||
; Calculate the average of all matches.
|
; Calculate the average of all matches.
|
||||||
sub ax,ax ;Assume we found no matches
|
sub ax,ax ;Assume we found no matches
|
||||||
and bp,bp
|
and bp,bp
|
||||||
jz Done ;We didn't find any matches, return 0
|
jz Done ;We didn't find any matches, return 0
|
||||||
xchg ax,dx ;Prepare for division
|
xchg ax,dx ;Prepare for division
|
||||||
div bp ;Return IDMatchSum / IDMatchCount
|
div bp ;Return IDMatchSum / IDMatchCount
|
||||||
Done: pop si ;Restore C register variables
|
Done: pop si ;Restore C register variables
|
||||||
pop di
|
pop di
|
||||||
pop bp ;Restore caller's stack frame
|
pop bp ;Restore caller's stack frame
|
||||||
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
|
||||||
|
|
|
||||||
44
09-01.md
44
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
|
||||||
|
|
||||||
ADD EAX,EBX
|
```nasm
|
||||||
ADC EDX,ECX
|
ADD EAX,EBX
|
||||||
|
ADC EDX,ECX
|
||||||
|
```
|
||||||
|
|
||||||
could *not* be replaced
|
could *not* be replaced
|
||||||
|
|
||||||
LEA EAX,[EAX+EBX]
|
```nasm
|
||||||
ADC EDX,ECX
|
LEA EAX,[EAX+EBX]
|
||||||
|
ADC EDX,ECX
|
||||||
|
```
|
||||||
|
|
||||||
because **LEA** doesn't affect the Carry flag.
|
because **LEA** doesn't affect the Carry flag.
|
||||||
|
|
||||||
|
|
@ -103,16 +107,18 @@ 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
|
||||||
ADDLOOP:
|
ADDLOOP:
|
||||||
|
|
||||||
MOV EAX,[ESI] ;get the next element of one array
|
MOV EAX,[ESI] ;get the next element of one array
|
||||||
ADC [EDI],EAX ;add it to the other array, with carry
|
ADC [EDI],EAX ;add it to the other array, with carry
|
||||||
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:
|
||||||
|
|
||||||
CWD
|
```nasm
|
||||||
XOR AX,DX
|
CWD
|
||||||
SUB AX,DX
|
XOR 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:
|
||||||
|
|
||||||
SHR CX,1 ;word count
|
```nasm
|
||||||
REP MOVSW ;copy as many words as possible
|
SHR CX,1 ;word count
|
||||||
ADC CX,CX ;CX=1 if copy length was odd,
|
REP MOVSW ;copy as many words as possible
|
||||||
|
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:
|
||||||
|
```
|
||||||
|
|
|
||||||
40
09-02.md
40
09-02.md
|
|
@ -27,34 +27,42 @@ 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):
|
||||||
|
|
||||||
SHL AX,1 ;*2
|
```nasm
|
||||||
SH LAX,1 ;*4
|
SHL AX,1 ;*2
|
||||||
SH LAX,1 ;*8
|
SH LAX,1 ;*4
|
||||||
SH LAX,1 ;*16
|
SH LAX,1 ;*8
|
||||||
MO VBX,AX
|
SH LAX,1 ;*16
|
||||||
SH LAX,1 ;*32
|
MO VBX,AX
|
||||||
SH LAX,1 ;*64
|
SH LAX,1 ;*32
|
||||||
ADD AX,BX ;*80
|
SH LAX,1 ;*64
|
||||||
|
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
|
||||||
|
|
||||||
LEA EAX,[EAX*2] ;*2
|
```nasm
|
||||||
LEA EAX,[EAX*8] ;*16
|
LEA EAX,[EAX*2] ;*2
|
||||||
LEA EAX,[EAX+EAX*4] ;*80
|
LEA EAX,[EAX*8] ;*16
|
||||||
|
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
|
||||||
|
|
||||||
MOV EAX,MultiplesOf80Table[EAX*4]
|
```nasm
|
||||||
|
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:
|
||||||
|
|
||||||
SH LAX,4 ;*16
|
```nasm
|
||||||
MOV BX,AX
|
SH LAX,4 ;*16
|
||||||
SHL AX,2 ;*64
|
MOV BX,AX
|
||||||
ADD AX,BX ;*80
|
SHL AX,2 ;*64
|
||||||
|
ADD AX,BX ;*80
|
||||||
|
```
|
||||||
|
|
||||||
#### Speeding Up Multiplication {#Heading5}
|
#### Speeding Up Multiplication {#Heading5}
|
||||||
|
|
||||||
|
|
|
||||||
64
09-03.md
64
09-03.md
|
|
@ -40,33 +40,34 @@ using **REPZ CMPS** to check scanning matches.
|
||||||
|
|
||||||
**LISTING 9.1 L9-1.ASM**
|
**LISTING 9.1 L9-1.ASM**
|
||||||
|
|
||||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to sca"n
|
```nasm
|
||||||
; the buffer for locations that match the first character of the
|
; Searches a text buffer for a text string. Uses REPNZ SCASB to sca"n
|
||||||
; searched-for string, then uses REPZ CMPS to check fully only those
|
; the buffer for locations that match the first character of the
|
||||||
; locations that REPNZ SCASB has identified as potential matches.
|
; searched-for string, then uses REPZ CMPS to check fully only those
|
||||||
;
|
; locations that REPNZ SCASB has identified as potential matches.
|
||||||
; Adapted from Zen of Assembly Language, by Michael Abrash
|
;
|
||||||
;
|
; Adapted from Zen of Assembly Language, by Michael Abrash
|
||||||
; C small model-callable as:
|
;
|
||||||
; unsigned char * FindString(unsigned char * Buffer,
|
; C small model-callable as:
|
||||||
; unsigned int BufferLength, unsigned char * SearchString,
|
; unsigned char * FindString(unsigned char * Buffer,
|
||||||
; unsigned int SearchStringLength);
|
; unsigned int BufferLength, unsigned char * SearchString,
|
||||||
;
|
; unsigned int SearchStringLength);
|
||||||
; Returns a pointer to the first match for SearchString in Buffer,or
|
;
|
||||||
; a NULL pointer if no match is found. Buffer should not start at
|
; Returns a pointer to the first match for SearchString in Buffer,or
|
||||||
; offset 0 in the data segment to avoid confusing a match at 0 with
|
; a NULL pointer if no match is found. Buffer should not start at
|
||||||
; no match found.
|
; offset 0 in the data segment to avoid confusing a match at 0 with
|
||||||
Parmsstruc
|
; no match found.
|
||||||
|
Parmsstruc
|
||||||
dw 2 dup(?) ;pushed BP/return address
|
dw 2 dup(?) ;pushed BP/return address
|
||||||
Buffer dw ? ;pointer to buffer to search
|
Buffer dw ? ;pointer to buffer to search
|
||||||
BufferLength dw ? ;length of buffer to search
|
BufferLength dw ? ;length of buffer to search
|
||||||
SearchString dw ? ;pointer to string for which to search
|
SearchString dw ? ;pointer to string for which to search
|
||||||
SearchStringLength dw ? ;length of string for which to search
|
SearchStringLength dw ? ;length of string for which to search
|
||||||
Parmsends
|
Parmsends
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindString
|
public _FindString
|
||||||
_FindStringprocnear
|
_FindStringprocnear
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;point to our stack frame
|
mov bp,sp ;point to our stack frame
|
||||||
push si ;preserve caller's register variables
|
push si ;preserve caller's register variables
|
||||||
|
|
@ -90,7 +91,7 @@ using **REPZ CMPS** to check scanning matches.
|
||||||
mov bp,si ;set aside pointer to the second search byte
|
mov bp,si ;set aside pointer to the second search byte
|
||||||
dec bx ;don't need to compare the first byte of the
|
dec bx ;don't need to compare the first byte of the
|
||||||
; string with CMPS; we'll do it with SCAS
|
; string with CMPS; we'll do it with SCAS
|
||||||
FindStringLoop:
|
FindStringLoop:
|
||||||
mov cx,dx ;put remaining buffer search length in CX
|
mov cx,dx ;put remaining buffer search length in CX
|
||||||
repnz scasb ;scan for the first byte of the string
|
repnz scasb ;scan for the first byte of the string
|
||||||
jnz FindStringNotFound ;not found, so there's no match
|
jnz FindStringNotFound ;not found, so there's no match
|
||||||
|
|
@ -106,28 +107,29 @@ using **REPZ CMPS** to check scanning matches.
|
||||||
cmpsb ;compare the odd byte
|
cmpsb ;compare the odd byte
|
||||||
jnz FindStringNoMatch ;odd byte doesn't match, so we
|
jnz FindStringNoMatch ;odd byte doesn't match, so we
|
||||||
; haven't found the search string here
|
; haven't found the search string here
|
||||||
FindStringWord:
|
FindStringWord:
|
||||||
jcxz FindStringFound ;test whether we've already checked
|
jcxz FindStringFound ;test whether we've already checked
|
||||||
; the whole string; if so, this is a match
|
; the whole string; if so, this is a match
|
||||||
; bytes long; if so, we've found a match
|
; bytes long; if so, we've found a match
|
||||||
repz cmpsw ;check the rest of the string a word at a time
|
repz cmpsw ;check the rest of the string a word at a time
|
||||||
jz FindStringFound ;it's a match
|
jz FindStringFound ;it's a match
|
||||||
FindStringNoMatch:
|
FindStringNoMatch:
|
||||||
pop di ;get back pointer to the next byte to scan
|
pop di ;get back pointer to the next byte to scan
|
||||||
and dx,dx ;is there anything left to check?
|
and dx,dx ;is there anything left to check?
|
||||||
jnz FindStringLoop ;yes-check next byte
|
jnz FindStringLoop ;yes-check next byte
|
||||||
FindStringNotFound:
|
FindStringNotFound:
|
||||||
sub ax,ax ;return a NULL pointer indicating that the
|
sub ax,ax ;return a NULL pointer indicating that the
|
||||||
jmp FindStringDone ; string was not found
|
jmp FindStringDone ; string was not found
|
||||||
FindStringFound:
|
FindStringFound:
|
||||||
pop ax ;point to the buffer location at which the
|
pop ax ;point to the buffer location at which the
|
||||||
dec ax ; string was found (earlier we pushed the
|
dec ax ; string was found (earlier we pushed the
|
||||||
; address of the byte after the start of the
|
; address of the byte after the start of the
|
||||||
; potential match)
|
; potential match)
|
||||||
FindStringDone:
|
FindStringDone:
|
||||||
pop di ;restore caller's register variables
|
pop di ;restore caller's register variables
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
ret
|
ret
|
||||||
_FindStringendp
|
_FindStringendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
90
09-04.md
90
09-04.md
|
|
@ -12,34 +12,35 @@ pages: 178-180
|
||||||
|
|
||||||
**LISTING 9.2 L9-2.ASM**
|
**LISTING 9.2 L9-2.ASM**
|
||||||
|
|
||||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
```nasm
|
||||||
; the buffer for locations that match a specified character of the
|
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
||||||
; searched-for string, then uses REPZ CMPS to check fully only those
|
; the buffer for locations that match a specified character of the
|
||||||
; locations that REPNZ SCASB has identified as potential matches.
|
; searched-for string, then uses REPZ CMPS to check fully only those
|
||||||
;
|
; locations that REPNZ SCASB has identified as potential matches.
|
||||||
; C small model-callable as:
|
;
|
||||||
; unsigned char * FindString(unsigned char * Buffer,
|
; C small model-callable as:
|
||||||
; unsigned int BufferLength, unsigned char * SearchString,
|
; unsigned char * FindString(unsigned char * Buffer,
|
||||||
; unsigned int SearchStringLength,
|
; unsigned int BufferLength, unsigned char * SearchString,
|
||||||
; unsigned int ScanCharOffset);
|
; unsigned int SearchStringLength,
|
||||||
;
|
; unsigned int ScanCharOffset);
|
||||||
; Returns a pointer to the first match for SearchString in Buffer,or
|
;
|
||||||
; a NULL pointer if no match is found. Buffer should not start at
|
; Returns a pointer to the first match for SearchString in Buffer,or
|
||||||
; offset 0 in the data segment to avoid confusing a match at 0 with
|
; a NULL pointer if no match is found. Buffer should not start at
|
||||||
; no match found.
|
; offset 0 in the data segment to avoid confusing a match at 0 with
|
||||||
Parms struc
|
; no match found.
|
||||||
|
Parms struc
|
||||||
dw 2 dup(?) ;pushed BP/return address
|
dw 2 dup(?) ;pushed BP/return address
|
||||||
Buffer dw ? ;pointer to buffer to search
|
Buffer dw ? ;pointer to buffer to search
|
||||||
BufferLength dw ? ;length of buffer to search
|
BufferLength dw ? ;length of buffer to search
|
||||||
SearchString dw ? ;pointer to string for which to search
|
SearchString dw ? ;pointer to string for which to search
|
||||||
SearchStringLength dw ? ;length of string for which to search
|
SearchStringLength dw ? ;length of string for which to search
|
||||||
ScanCharOffset dw ? ;offset in string of character for
|
ScanCharOffset dw ? ;offset in string of character for
|
||||||
; which to scan
|
; which to scan
|
||||||
Parmsends
|
Parmsends
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindString
|
public _FindString
|
||||||
_FindStringprocnear
|
_FindStringprocnear
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;point to our stack frame
|
mov bp,sp ;point to our stack frame
|
||||||
push si ;preserve caller's register variables
|
push si ;preserve caller's register variables
|
||||||
|
|
@ -67,7 +68,7 @@ pages: 178-180
|
||||||
; potential full match after a scan match,
|
; potential full match after a scan match,
|
||||||
; accounting for the 1-byte overrun of
|
; accounting for the 1-byte overrun of
|
||||||
; REPNZ SCASB
|
; REPNZ SCASB
|
||||||
FindStringLoop:
|
FindStringLoop:
|
||||||
mov cx,dx ;put remaining buffer search length in CX
|
mov cx,dx ;put remaining buffer search length in CX
|
||||||
repnz scasb ;scan for the scan byte
|
repnz scasb ;scan for the scan byte
|
||||||
jnz FindStringNotFound ;not found, so there's no match
|
jnz FindStringNotFound ;not found, so there's no match
|
||||||
|
|
@ -85,46 +86,48 @@ pages: 178-180
|
||||||
cmpsb ;compare the odd byte
|
cmpsb ;compare the odd byte
|
||||||
jnz FindStringNoMatch ;odd byte doesn't match, so we
|
jnz FindStringNoMatch ;odd byte doesn't match, so we
|
||||||
; haven't found the search string here
|
; haven't found the search string here
|
||||||
FindStringWord:
|
FindStringWord:
|
||||||
jcxz FindStringFound ;if the string is only 1 byte long,
|
jcxz FindStringFound ;if the string is only 1 byte long,
|
||||||
; we've found a match
|
; we've found a match
|
||||||
repz cmpsw ;check the rest of the string a word at a time
|
repz cmpsw ;check the rest of the string a word at a time
|
||||||
jz FindStringFound ;it's a match
|
jz FindStringFound ;it's a match
|
||||||
FindStringNoMatch:
|
FindStringNoMatch:
|
||||||
pop di ;get back pointer to the next byte to scan
|
pop di ;get back pointer to the next byte to scan
|
||||||
and dx,dx ;is there anything left to check?
|
and dx,dx ;is there anything left to check?
|
||||||
jnz FindStringLoop ;yes-check next byte
|
jnz FindStringLoop ;yes-check next byte
|
||||||
FindStringNotFound:
|
FindStringNotFound:
|
||||||
sub ax,ax ;return a NULL pointer indicating that the
|
sub ax,ax ;return a NULL pointer indicating that the
|
||||||
jmp FindStringDone ; string was not found
|
jmp FindStringDone ; string was not found
|
||||||
FindStringFound:
|
FindStringFound:
|
||||||
pop ax ;point to the buffer location at which the
|
pop ax ;point to the buffer location at which the
|
||||||
sub ax,bx ; string was found (earlier we pushed the
|
sub ax,bx ; string was found (earlier we pushed the
|
||||||
; address of the byte after the scan match)
|
; address of the byte after the scan match)
|
||||||
FindStringDone:
|
FindStringDone:
|
||||||
pop di ;restore caller's register variables
|
pop di ;restore caller's register variables
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
ret
|
ret
|
||||||
_FindStringendp
|
_FindStringendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 9.3 L9-3.C**
|
**LISTING 9.3 L9-3.C**
|
||||||
|
|
||||||
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
```c
|
||||||
#include <stdio.h>
|
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
||||||
#include <string.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define DISPLAY_LENGTH 40
|
#define DISPLAY_LENGTH 40
|
||||||
extern unsigned char * FindString(unsigned char *, unsigned int,
|
extern unsigned char * FindString(unsigned char *, unsigned int,
|
||||||
unsigned char *, unsigned int, unsigned int);
|
unsigned char *, unsigned int, unsigned int);
|
||||||
void main(void);
|
void main(void);
|
||||||
static unsigned char TestBuffer[] = "When, in the course of human \
|
static unsigned char TestBuffer[] = "When, in the course of human \
|
||||||
events, it becomes necessary for one people to dissolve the \
|
events, it becomes necessary for one people to dissolve the \
|
||||||
political bands which have connected them with another, and to \
|
political bands which have connected them with another, and to \
|
||||||
assume among the powers of the earth the separate and equal station \
|
assume among the powers of the earth the separate and equal station \
|
||||||
to which the laws of nature and of nature's God entitle them...";
|
to which the laws of nature and of nature's God entitle them...";
|
||||||
void main() {
|
void main() {
|
||||||
static unsigned char TestString[] = "equal";
|
static unsigned char TestString[] = "equal";
|
||||||
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
||||||
unsigned char *MatchPtr;
|
unsigned char *MatchPtr;
|
||||||
|
|
@ -142,4 +145,5 @@ pages: 178-180
|
||||||
TestString, DISPLAY_LENGTH,
|
TestString, DISPLAY_LENGTH,
|
||||||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
19
09-05.md
19
09-05.md
|
|
@ -43,19 +43,19 @@ 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[] );
|
||||||
;
|
;
|
||||||
; Courtesy of David Stafford.
|
; Courtesy of David Stafford.
|
||||||
;--------------------------------------------------------------------------
|
;--------------------------------------------------------------------------
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _sort
|
public _sort
|
||||||
|
|
||||||
top: mov dx,[bx] ;swap two adjacent integers
|
top: mov dx,[bx] ;swap two adjacent integers
|
||||||
xchg dx,[bx+2]
|
xchg dx,[bx+2]
|
||||||
xchg dx,[bx]
|
xchg dx,[bx]
|
||||||
cmp dx,[bx] ;did we put them in the right order?
|
cmp dx,[bx] ;did we put them in the right order?
|
||||||
|
|
@ -63,7 +63,7 @@ one.
|
||||||
inc bx ;go to next integer
|
inc bx ;go to next integer
|
||||||
inc bx
|
inc bx
|
||||||
loop top
|
loop top
|
||||||
_sort: pop dx ;get return address (entry point)
|
_sort: pop dx ;get return address (entry point)
|
||||||
pop cx ;get count
|
pop cx ;get count
|
||||||
pop bx ;get pointer
|
pop bx ;get pointer
|
||||||
push bx ;restore pointer
|
push bx ;restore pointer
|
||||||
|
|
@ -75,6 +75,7 @@ one.
|
||||||
ret
|
ret
|
||||||
|
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
#### Full 32-Bit Division {#Heading8}
|
#### Full 32-Bit Division {#Heading8}
|
||||||
|
|
||||||
|
|
|
||||||
50
09-06.md
50
09-06.md
|
|
@ -12,36 +12,37 @@ pages: 182-185
|
||||||
|
|
||||||
**LISTING 9.5 L9-5.ASM**
|
**LISTING 9.5 L9-5.ASM**
|
||||||
|
|
||||||
; Divides an arbitrarily long unsigned dividend by a 16-bit unsigned
|
```nasm
|
||||||
; divisor. C near-callable as:
|
; Divides an arbitrarily long unsigned dividend by a 16-bit unsigned
|
||||||
; unsigned int Div(unsigned int * Dividend,
|
; divisor. C near-callable as:
|
||||||
; int DividendLength, unsigned int Divisor,
|
; unsigned int Div(unsigned int * Dividend,
|
||||||
; unsigned int * Quotient);
|
; int DividendLength, unsigned int Divisor,
|
||||||
;
|
; unsigned int * Quotient);
|
||||||
; Returns the remainder of the division.
|
;
|
||||||
;
|
; Returns the remainder of the division.
|
||||||
; Tested with TASM 2.
|
;
|
||||||
|
; Tested with TASM 2.
|
||||||
|
|
||||||
parms struc
|
parms struc
|
||||||
dw 2 dup (?) ;pushed BP & return address
|
dw 2 dup (?) ;pushed BP & return address
|
||||||
Dividend dw ? ;pointer to value to divide, stored in Intel
|
Dividend dw ? ;pointer to value to divide, stored in Intel
|
||||||
; order, with lsb at lowest address, msb at
|
; order, with lsb at lowest address, msb at
|
||||||
; highest. Must be composed of an integral
|
; highest. Must be composed of an integral
|
||||||
; number of words
|
; number of words
|
||||||
DividendLength dw ? ;# of bytes in Dividend. Must be a multiple
|
DividendLength dw ? ;# of bytes in Dividend. Must be a multiple
|
||||||
; of 2
|
; of 2
|
||||||
Divisor dw ? ;value by which to divide. Must not be zero,
|
Divisor dw ? ;value by which to divide. Must not be zero,
|
||||||
; or a Divide By Zero interrupt will occur
|
; or a Divide By Zero interrupt will occur
|
||||||
Quotient dw ? ;pointer to buffer in which to store the
|
Quotient dw ? ;pointer to buffer in which to store the
|
||||||
; result of the division, in Intel order.
|
; result of the division, in Intel order.
|
||||||
; The quotient returned is of the same
|
; The quotient returned is of the same
|
||||||
; length as the dividend
|
; length as the dividend
|
||||||
parmsends
|
parmsends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _Div
|
public _Div
|
||||||
_Divprocnear
|
_Divprocnear
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;point to our stack frame
|
mov bp,sp ;point to our stack frame
|
||||||
push si ;preserve caller's register variables
|
push si ;preserve caller's register variables
|
||||||
|
|
@ -63,7 +64,7 @@ pages: 182-185
|
||||||
inc cx ;# of words to process
|
inc cx ;# of words to process
|
||||||
sub dx,dx ;convert initial divisor word to a 32-bit
|
sub dx,dx ;convert initial divisor word to a 32-bit
|
||||||
;value for DIV
|
;value for DIV
|
||||||
DivLoop:
|
DivLoop:
|
||||||
lod sw ;get next most significant word of divisor
|
lod sw ;get next most significant word of divisor
|
||||||
div bx
|
div bx
|
||||||
sto sw ;save this word of the quotient
|
sto sw ;save this word of the quotient
|
||||||
|
|
@ -76,27 +77,30 @@ pages: 182-185
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
ret
|
ret
|
||||||
_Divendp
|
_Divendp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 9.6 L9-6.C**
|
**LISTING 9.6 L9-6.C**
|
||||||
|
|
||||||
/* Sample use of Div function to perform division when the result
|
```c
|
||||||
|
/* Sample use of Div function to perform division when the result
|
||||||
doesn't fit in 16 bits */
|
doesn't fit in 16 bits */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
extern unsigned int Div(unsigned int * Dividend,
|
extern unsigned int Div(unsigned int * Dividend,
|
||||||
int DividendLength, unsigned int Divisor,
|
int DividendLength, unsigned int Divisor,
|
||||||
unsigned int * Quotient);
|
unsigned int * Quotient);
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
unsigned long m, i = 0x20000001;
|
unsigned long m, i = 0x20000001;
|
||||||
unsigned int k, j = 0x10;
|
unsigned int k, j = 0x10;
|
||||||
|
|
||||||
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}
|
||||||
|
|
||||||
|
|
|
||||||
42
09-07.md
42
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:
|
||||||
|
|
||||||
Ptr dd ?
|
```nasm
|
||||||
|
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,22 +89,24 @@ solution; if you have one, pass it along.
|
||||||
|
|
||||||
**LISTING 9.7 L9-7.ASM**
|
**LISTING 9.7 L9-7.ASM**
|
||||||
|
|
||||||
; Program to perform a direct far jump to address 1000:5.
|
```nasm
|
||||||
; *** Do not run this program! It's just an example of how ***
|
; Program to perform a direct far jump to address 1000:5.
|
||||||
; *** to build a direct far jump to an absolute address ***
|
; *** Do not run this program! It's just an example of how ***
|
||||||
;
|
; *** to build a direct far jump to an absolute address ***
|
||||||
; Tested with TASM 2 and MASM 5.
|
;
|
||||||
|
; Tested with TASM 2 and MASM 5.
|
||||||
|
|
||||||
FarSeg segment at 01000h
|
FarSeg segment at 01000h
|
||||||
org 5
|
org 5
|
||||||
FarLabel label far
|
FarLabel label far
|
||||||
FarSeg ends
|
FarSeg ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
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,
|
||||||
|
|
||||||
sub eax,eax
|
```nasm
|
||||||
inc eax
|
sub eax,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
|
||||||
|
|
||||||
mov eax,1
|
```nasm
|
||||||
|
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,
|
||||||
|
|
||||||
or ebx,-1
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
move bx,-1
|
```nasm
|
||||||
|
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
|
||||||
|
|
|
||||||
16
10-02.md
16
10-02.md
|
|
@ -64,13 +64,14 @@ default optimization was used. All times measured with the Zen timer
|
||||||
|
|
||||||
**LISTING 10.1 L10-1.C**
|
**LISTING 10.1 L10-1.C**
|
||||||
|
|
||||||
/* Finds and returns the greatest common divisor of two positive
|
```c
|
||||||
|
/* 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
|
||||||
both integers evenly is found. All C code tested with Microsoft
|
both integers evenly is found. All C code tested with Microsoft
|
||||||
and Borland compilers.*/
|
and Borland compilers.*/
|
||||||
|
|
||||||
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
||||||
unsigned int temp, trial_divisor;
|
unsigned int temp, trial_divisor;
|
||||||
/* Swap if necessary to make sure that int1 >= int2 */
|
/* Swap if necessary to make sure that int1 >= int2 */
|
||||||
if (int1 < int2) {
|
if (int1 < int2) {
|
||||||
|
|
@ -85,7 +86,8 @@ default optimization was used. All times measured with the Zen timer
|
||||||
((int2 % trial_divisor) != 0); trial_divisor—)
|
((int2 % trial_divisor) != 0); trial_divisor—)
|
||||||
;
|
;
|
||||||
return(trial_divisor);
|
return(trial_divisor);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Wasted Breakthroughs {#Heading5}
|
#### Wasted Breakthroughs {#Heading5}
|
||||||
|
|
||||||
|
|
@ -99,14 +101,15 @@ in Listing 10.2.
|
||||||
|
|
||||||
**LISTING 10.2 L10-2.C**
|
**LISTING 10.2 L10-2.C**
|
||||||
|
|
||||||
/* Finds and returns the greatest common divisor of two positive
|
```c
|
||||||
|
/* 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
|
||||||
that's the gcd), or the larger integer becomes the smaller of
|
that's the gcd), or the larger integer becomes the smaller of
|
||||||
the two, in which case the two integers swap roles and the
|
the two, in which case the two integers swap roles and the
|
||||||
subtraction process continues. */
|
subtraction process continues. */
|
||||||
|
|
||||||
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
/* If the two integers are the same, that's the gcd and we're
|
/* If the two integers are the same, that's the gcd and we're
|
||||||
done */
|
done */
|
||||||
|
|
@ -127,4 +130,5 @@ in Listing 10.2.
|
||||||
} while (int1 > int2);
|
} while (int1 > int2);
|
||||||
/* 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));
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
24
10-03.md
24
10-03.md
|
|
@ -45,16 +45,17 @@ 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**
|
||||||
|
|
||||||
/* Finds and returns the greatest common divisor of two integers.
|
```c
|
||||||
|
/* 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,
|
||||||
otherwise the smaller integer becomes the larger integer, the
|
otherwise the smaller integer becomes the larger integer, the
|
||||||
remainder becomes the smaller integer, and the process is
|
remainder becomes the smaller integer, and the process is
|
||||||
repeated. */
|
repeated. */
|
||||||
|
|
||||||
static unsigned int gcd_recurs(unsigned int, unsigned int);
|
static unsigned int gcd_recurs(unsigned int, unsigned int);
|
||||||
|
|
||||||
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
/* If the two integers are the same, that's the GCD and we're
|
/* If the two integers are the same, that's the GCD and we're
|
||||||
done */
|
done */
|
||||||
|
|
@ -71,11 +72,11 @@ Figure 10.3. Listing 10.3 is an implementation of Euclid's algorithm.
|
||||||
/* Now call the recursive form of the function, which assumes
|
/* Now call the recursive form of the function, which assumes
|
||||||
that the first parameter is the larger of the two */
|
that the first parameter is the larger of the two */
|
||||||
return(gcd_recurs(int1, int2));
|
return(gcd_recurs(int1, int2));
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int gcd_recurs(unsigned int larger_int,
|
static unsigned int gcd_recurs(unsigned int larger_int,
|
||||||
unsigned int smaller_int)
|
unsigned int smaller_int)
|
||||||
{
|
{
|
||||||
int temp;
|
int temp;
|
||||||
|
|
||||||
/* If the remainder of larger_int divided by smaller_int is 0,
|
/* If the remainder of larger_int divided by smaller_int is 0,
|
||||||
|
|
@ -87,7 +88,8 @@ Figure 10.3. Listing 10.3 is an implementation of Euclid's algorithm.
|
||||||
smaller integer, and call this function recursively to
|
smaller integer, and call this function recursively to
|
||||||
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,14 +131,15 @@ recursive operations that Listing 10.3 does.
|
||||||
|
|
||||||
**LISTING 10.4 L10-4.C**
|
**LISTING 10.4 L10-4.C**
|
||||||
|
|
||||||
/* Finds and returns the greatest common divisor of two integers.
|
```c
|
||||||
|
/* 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,
|
||||||
otherwise the smaller integer becomes the larger integer, the
|
otherwise the smaller integer becomes the larger integer, the
|
||||||
remainder becomes the smaller integer, and the process is
|
remainder becomes the smaller integer, and the process is
|
||||||
repeated. Avoids code recursion. */
|
repeated. Avoids code recursion. */
|
||||||
|
|
||||||
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
unsigned int gcd(unsigned int int1, unsigned int int2) {
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
|
|
||||||
/* Swap if necessary to make sure that int1 >= int2 */
|
/* Swap if necessary to make sure that int1 >= int2 */
|
||||||
|
|
@ -160,7 +163,8 @@ recursive operations that Listing 10.3 does.
|
||||||
int1 = int2;
|
int1 = int2;
|
||||||
int2 = temp;
|
int2 = temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Patient Optimization {#Heading7}
|
#### Patient Optimization {#Heading7}
|
||||||
|
|
||||||
|
|
|
||||||
60
10-04.md
60
10-04.md
|
|
@ -12,48 +12,49 @@ pages: 200-203
|
||||||
|
|
||||||
**LISTING 10.5 L10-5.ASM**
|
**LISTING 10.5 L10-5.ASM**
|
||||||
|
|
||||||
; Finds and returns the greatest common divisor of two integers.
|
```nasm
|
||||||
; Uses Euclid's algorithm: divides the larger integer by the
|
; Finds and returns the greatest common divisor of two integers.
|
||||||
; smaller; if the remainder is 0, the smaller integer is the GCD,
|
; Uses Euclid's algorithm: divides the larger integer by the
|
||||||
; otherwise the smaller integer becomes the larger integer, the
|
; smaller; if the remainder is 0, the smaller integer is the GCD,
|
||||||
; remainder becomes the smaller integer, and the process is
|
; otherwise the smaller integer becomes the larger integer, the
|
||||||
; repeated. Avoids code recursion.
|
; remainder becomes the smaller integer, and the process is
|
||||||
;
|
; repeated. Avoids code recursion.
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
; C near-callable as:
|
;
|
||||||
; unsigned int gcd(unsigned int int1, unsigned int int2);
|
; C near-callable as:
|
||||||
|
; unsigned int gcd(unsigned int int1, unsigned int int2);
|
||||||
|
|
||||||
; Parameter structure:
|
; Parameter structure:
|
||||||
parms struc
|
parms struc
|
||||||
dw ? ;pushed BP
|
dw ? ;pushed BP
|
||||||
dw ? ;pushed return address
|
dw ? ;pushed return address
|
||||||
int1 dw ? ;integers for which to find
|
int1 dw ? ;integers for which to find
|
||||||
int2 dw ? ; the GCD
|
int2 dw ? ; the GCD
|
||||||
parms ends
|
parms ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _gcd
|
public _gcd
|
||||||
align 2
|
align 2
|
||||||
_gcd proc near
|
_gcd proc near
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;set up our stack frame
|
mov bp,sp ;set up our stack frame
|
||||||
push si ;preserve caller's register variables
|
push si ;preserve caller's register variables
|
||||||
push di
|
push di
|
||||||
|
|
||||||
;Swap if necessary to make sure that int1 >= int2
|
;Swap if necessary to make sure that int1 >= int2
|
||||||
mov ax,int1[bp]
|
mov ax,int1[bp]
|
||||||
mov bx,int2[bp]
|
mov bx,int2[bp]
|
||||||
cmp ax,bx ;is int1 >= int2?
|
cmp ax,bx ;is int1 >= int2?
|
||||||
jnb IntsSet ;yes, so we're all set
|
jnb IntsSet ;yes, so we're all set
|
||||||
xchg ax,bx ;no, so swap int1 and int2
|
xchg ax,bx ;no, so swap int1 and int2
|
||||||
IntsSet:
|
IntsSet:
|
||||||
|
|
||||||
; Now loop, dividing int1 by int2 and checking the remainder, until
|
; Now loop, dividing int1 by int2 and checking the remainder, until
|
||||||
; the remainder is 0. At each step, if the remainder isn't 0, assign
|
; the remainder is 0. At each step, if the remainder isn't 0, assign
|
||||||
; int2 to int1, and the remainder to int2, then repeat.
|
; int2 to int1, and the remainder to int2, then repeat.
|
||||||
GCDLoop:
|
GCDLoop:
|
||||||
;if the remainder of int1 divided by
|
;if the remainder of int1 divided by
|
||||||
; int2 is 0, then int2 is the gcd
|
; int2 is 0, then int2 is the gcd
|
||||||
sub dx,dx ;prepare int1 in DX:AX for division
|
sub dx,dx ;prepare int1 in DX:AX for division
|
||||||
|
|
@ -66,39 +67,40 @@ pages: 200-203
|
||||||
mov ax,bx ;int1 = int2;
|
mov ax,bx ;int1 = int2;
|
||||||
mov bx,dx ;int2 = remainder from DIV
|
mov bx,dx ;int2 = remainder from DIV
|
||||||
|
|
||||||
;—start of loop unrolling; the above is repeated three times—
|
;—start of loop unrolling; the above is repeated three times—
|
||||||
sub dx,dx ;prepare int1 in DX:AX for division
|
sub dx,dx ;prepare int1 in DX:AX for division
|
||||||
div bx ;int1/int2; remainder is in DX
|
div bx ;int1/int2; remainder is in DX
|
||||||
and dx,dx ;is the remainder zero?
|
and dx,dx ;is the remainder zero?
|
||||||
jz Done ;yes, so int2 (BX) is the gcd
|
jz Done ;yes, so int2 (BX) is the gcd
|
||||||
mov ax,bx ;int1 = int2;
|
mov ax,bx ;int1 = int2;
|
||||||
mov bx,dx ;int2 = remainder from DIV
|
mov bx,dx ;int2 = remainder from DIV
|
||||||
;—
|
;—
|
||||||
sub dx,dx ;prepare int1 in DX:AX for division
|
sub dx,dx ;prepare int1 in DX:AX for division
|
||||||
div bx ;int1/int2; remainder is in DX
|
div bx ;int1/int2; remainder is in DX
|
||||||
and dx,dx ;is the remainder zero?
|
and dx,dx ;is the remainder zero?
|
||||||
jz Done ;yes, so int2 (BX) is the gcd
|
jz Done ;yes, so int2 (BX) is the gcd
|
||||||
mov ax,bx ;int1 = int2;
|
mov ax,bx ;int1 = int2;
|
||||||
mov bx,dx ;int2 = remainder from DIV
|
mov bx,dx ;int2 = remainder from DIV
|
||||||
;—
|
;—
|
||||||
sub dx,dx ;prepare int1 in DX:AX for division
|
sub dx,dx ;prepare int1 in DX:AX for division
|
||||||
div bx ;int1/int2; remainder is in DX
|
div bx ;int1/int2; remainder is in DX
|
||||||
and dx,dx ;is the remainder zero?
|
and dx,dx ;is the remainder zero?
|
||||||
jz Done ;yes, so int2 (BX) is the gcd
|
jz Done ;yes, so int2 (BX) is the gcd
|
||||||
mov ax,bx ;int1 = int2;
|
mov ax,bx ;int1 = int2;
|
||||||
mov bx,dx ;int2 = remainder from DIV
|
mov bx,dx ;int2 = remainder from DIV
|
||||||
;—end of loop unrolling—
|
;—end of loop unrolling—
|
||||||
jmp GCDLoop
|
jmp GCDLoop
|
||||||
|
|
||||||
align2
|
align2
|
||||||
Done:
|
Done:
|
||||||
mov ax,bx ;return the GCD
|
mov ax,bx ;return the GCD
|
||||||
pop di ;restore caller's register variables
|
pop di ;restore caller's register variables
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
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
|
||||||
|
|
|
||||||
12
11-01.md
12
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
|
||||||
|
|
||||||
les ax,dword ptr [LongVar]
|
```nasm
|
||||||
mov dx,es
|
les ax,dword ptr [LongVar]
|
||||||
|
mov dx,es
|
||||||
|
```
|
||||||
|
|
||||||
which loads **LongVar** into DX:AX faster than this:
|
which loads **LongVar** into DX:AX faster than this:
|
||||||
|
|
||||||
mov ax,word ptr [LongVar]
|
```nasm
|
||||||
mov dx,word ptr [LongVar+2]
|
mov ax,word ptr [LongVar]
|
||||||
|
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
|
||||||
|
|
|
||||||
24
11-02.md
24
11-02.md
|
|
@ -126,25 +126,27 @@ shortly.)
|
||||||
|
|
||||||
**LISTING 11.1 L11-1.ASM**
|
**LISTING 11.1 L11-1.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; *** Listing 11.1 ***
|
;
|
||||||
;
|
; *** Listing 11.1 ***
|
||||||
; Measures the performance of an immediate move to
|
;
|
||||||
; memory, in order to demonstrate that the prefetch
|
; Measures the performance of an immediate move to
|
||||||
; queue cycle-eater is alive and well on the AT.
|
; memory, in order to demonstrate that the prefetch
|
||||||
;
|
; queue cycle-eater is alive and well on the AT.
|
||||||
|
;
|
||||||
jmp Skip
|
jmp Skip
|
||||||
;
|
;
|
||||||
even ;always make sure word-sized memory
|
even ;always make sure word-sized memory
|
||||||
; variables are word-aligned!
|
; variables are word-aligned!
|
||||||
WordVar dw 0
|
WordVar dw 0
|
||||||
;
|
;
|
||||||
Skip:
|
Skip:
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
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
|
||||||
|
|
|
||||||
34
11-03.md
34
11-03.md
|
|
@ -101,14 +101,15 @@ refresh.
|
||||||
|
|
||||||
**LISTING 11.2 L11-2.ASM**
|
**LISTING 11.2 L11-2.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; *** Listing 11.2 ***
|
;
|
||||||
;
|
; *** Listing 11.2 ***
|
||||||
; Measures the performance of accesses to word-sized
|
;
|
||||||
; variables that start at odd addresses (are not
|
; Measures the performance of accesses to word-sized
|
||||||
; word-aligned).
|
; variables that start at odd addresses (are not
|
||||||
;
|
; word-aligned).
|
||||||
Skip:
|
;
|
||||||
|
Skip:
|
||||||
push ds
|
push ds
|
||||||
pop es
|
pop es
|
||||||
mov si,1 ;source and destination are the same
|
mov si,1 ;source and destination are the same
|
||||||
|
|
@ -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,13 +130,14 @@ we predicted.
|
||||||
|
|
||||||
**LISTING 11.3 L11-3.ASM**
|
**LISTING 11.3 L11-3.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; *** Listing 11.3 ***
|
;
|
||||||
;
|
; *** Listing 11.3 ***
|
||||||
; Measures the performance of accesses to word-sized
|
;
|
||||||
; variables that start at even addresses (are word-aligned).
|
; Measures the performance of accesses to word-sized
|
||||||
;
|
; variables that start at even addresses (are word-aligned).
|
||||||
Skip:
|
;
|
||||||
|
Skip:
|
||||||
push ds
|
push ds
|
||||||
pop es
|
pop es
|
||||||
sub si,si ;source and destination are the same
|
sub si,si ;source and destination are the same
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
13
11-04.md
13
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
|
||||||
|
|
|
||||||
16
11-06.md
16
11-06.md
|
|
@ -113,15 +113,17 @@ Theory confirmed.
|
||||||
|
|
||||||
**LISTING 11.4 L11-4.ASM**
|
**LISTING 11.4 L11-4.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; *** Listing 11.4 ***
|
;
|
||||||
;
|
; *** Listing 11.4 ***
|
||||||
; Measures the performance of adding an immediate value
|
;
|
||||||
; to a register, for comparison with Listing 11.5, which
|
; Measures the performance of adding an immediate value
|
||||||
; adds an immediate value to a memory variable.
|
; to a register, for comparison with Listing 11.5, which
|
||||||
;
|
; adds an immediate value to a memory variable.
|
||||||
|
;
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
add dx,100h
|
add dx,100h
|
||||||
endm
|
endm
|
||||||
call ZTimerOff
|
call ZTimerOff
|
||||||
|
```
|
||||||
|
|
|
||||||
24
11-07.md
24
11-07.md
|
|
@ -12,25 +12,27 @@ pages: 224-226
|
||||||
|
|
||||||
**LISTING 11.5 L11-5.ASM**
|
**LISTING 11.5 L11-5.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; *** Listing 11.5 ***
|
;
|
||||||
;
|
; *** Listing 11.5 ***
|
||||||
; Measures the performance of adding an immediate value
|
;
|
||||||
; to a memory variable, for comparison with Listing 11.4,
|
; Measures the performance of adding an immediate value
|
||||||
; which adds an immediate value to a register.
|
; to a memory variable, for comparison with Listing 11.4,
|
||||||
;
|
; which adds an immediate value to a register.
|
||||||
|
;
|
||||||
jmp Skip
|
jmp Skip
|
||||||
;
|
;
|
||||||
even ;always make sure word-sized memory
|
even ;always make sure word-sized memory
|
||||||
; variables are word-aligned!
|
; variables are word-aligned!
|
||||||
WordVar dw 0
|
WordVar dw 0
|
||||||
;
|
;
|
||||||
Skip:
|
Skip:
|
||||||
call ZTimerOn
|
call ZTimerOn
|
||||||
rept 1000
|
rept 1000
|
||||||
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
|
||||||
|
|
|
||||||
30
11-08.md
30
11-08.md
|
|
@ -43,22 +43,24 @@ 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
|
||||||
; call, popping the word below the address
|
; call, popping the word below the address
|
||||||
; pushed by CALL into the FLAGS register
|
; pushed by CALL into the FLAGS register
|
||||||
popfskip:
|
popfskip:
|
||||||
call far ptr popfiret
|
call far ptr popfiret
|
||||||
;pushes the segment:offset of the next
|
;pushes the segment:offset of the next
|
||||||
; instruction on the stack just above
|
; instruction on the stack just above
|
||||||
; the flags word, setting things up so
|
; the flags word, setting things up so
|
||||||
; that IRET will branch to the next
|
; that IRET will branch to the next
|
||||||
; instruction and pop the flags
|
; instruction and pop the flags
|
||||||
; When execution reaches the instruction following this comment,
|
; When execution reaches the instruction following this comment,
|
||||||
; 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,26 +70,30 @@ 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:
|
||||||
|
|
||||||
EMULATE_POPF macro
|
```nasm
|
||||||
|
EMULATE_POPF macro
|
||||||
local popfskip, popfiret
|
local popfskip, popfiret
|
||||||
jmp short popfskip
|
jmp short popfskip
|
||||||
popfiret:
|
popfiret:
|
||||||
iret
|
iret
|
||||||
popfskip:
|
popfskip:
|
||||||
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:
|
||||||
|
|
||||||
EMULATE_POPF_TRASH_AX macro
|
```nasm
|
||||||
|
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,13 +102,15 @@ 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
|
||||||
pushcs
|
pushcs
|
||||||
pushoffset $+4
|
pushoffset $+4
|
||||||
iret
|
iret
|
||||||
endm
|
endm
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
8
12-01.md
8
12-01.md
|
|
@ -128,18 +128,22 @@ 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
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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]
|
||||||
add si,2
|
add si,2
|
||||||
dec cx
|
dec cx
|
||||||
jnz LoopTop
|
jnz LoopTop
|
||||||
sub si,bx
|
sub si,bx
|
||||||
|
```
|
||||||
|
|
|
||||||
50
12-02.md
50
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
|
||||||
|
|
||||||
MOV BX,OFFSET MemVar
|
```nasm
|
||||||
MOV AX,[BX]
|
MOV BX,OFFSET MemVar
|
||||||
|
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
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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:
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov bx,offset MemVar
|
```nasm
|
||||||
mov ax,[bx]
|
mov bx,offset MemVar
|
||||||
inc dx
|
mov ax,[bx]
|
||||||
dec cx
|
inc dx
|
||||||
jnz LoopTop
|
dec cx
|
||||||
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
loses two cycles from interrupting the address calculation pipeline,
|
loses two cycles from interrupting the address calculation pipeline,
|
||||||
this code
|
this code
|
||||||
|
|
||||||
mov bx,offset MemVar
|
```nasm
|
||||||
inc dx
|
mov bx,offset MemVar
|
||||||
mov ax,[bx]
|
inc dx
|
||||||
dec cx
|
mov ax,[bx]
|
||||||
jnz LoopTop
|
dec cx
|
||||||
|
jnz LoopTop
|
||||||
|
```
|
||||||
|
|
||||||
loses only one cycle, and this code
|
loses only one cycle, and this code
|
||||||
|
|
||||||
mov bx,offset MemVar
|
```nasm
|
||||||
inc dx
|
mov bx,offset MemVar
|
||||||
dec cx
|
inc dx
|
||||||
mov ax,[bx]
|
dec cx
|
||||||
jnz LoopTop
|
mov ax,[bx]
|
||||||
|
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
|
||||||
|
|
|
||||||
64
12-03.md
64
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
|
:
|
||||||
ret
|
pop ax
|
||||||
pop ax
|
ret
|
||||||
et
|
pop ax
|
||||||
:
|
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
|
||||||
|
|
||||||
pop dx
|
```nasm
|
||||||
pop cx
|
pop dx
|
||||||
pop bx
|
pop cx
|
||||||
pop ax
|
pop bx
|
||||||
|
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
|
||||||
|
|
||||||
add sp,10h
|
```nasm
|
||||||
ret
|
add sp,10h
|
||||||
|
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
|
||||||
|
|
||||||
add sp,10h
|
```nasm
|
||||||
pop ax
|
add sp,10h
|
||||||
|
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
|
||||||
|
|
||||||
mov ah,o
|
```nasm
|
||||||
|
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:
|
||||||
|
|
||||||
mov ah,o
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov bx,offset MemTable
|
```nasm
|
||||||
|
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
|
||||||
|
|
|
||||||
53
12-04.md
53
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
|
||||||
|
|
||||||
mov al,bl
|
```nasm
|
||||||
mov cx,dx
|
mov al,bl
|
||||||
mov si,[di]
|
mov cx,dx
|
||||||
|
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
|
||||||
|
|
||||||
mov bl,al
|
```nasm
|
||||||
mov cl,3
|
mov bl,al
|
||||||
mov bx,[si]
|
mov cl,3
|
||||||
|
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,
|
||||||
|
|
||||||
mov cx,3
|
```nasm
|
||||||
mov dl,al
|
mov cx,3
|
||||||
mov si,[bx]
|
mov dl,al
|
||||||
|
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:
|
||||||
|
|
||||||
mov bx,offset MemVar
|
```nasm
|
||||||
mov cl,al
|
mov bx,offset MemVar
|
||||||
mov ax,[bx]
|
mov cl,al
|
||||||
|
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,12 +104,13 @@ addressing memory.
|
||||||
|
|
||||||
**LISTING 12.1 LST12-1.ASM**
|
**LISTING 12.1 LST12-1.ASM**
|
||||||
|
|
||||||
; Measures the effect of loading a byte register 2 cycles before
|
```nasm
|
||||||
; using a register to address memory.
|
; Measures the effect of loading a byte register 2 cycles before
|
||||||
|
; 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
|
||||||
; it's cached
|
; it's cached
|
||||||
sub bx,bx
|
sub bx,bx
|
||||||
CacheFillLoop:
|
CacheFillLoop:
|
||||||
call ZTimerOn ;start timing
|
call ZTimerOn ;start timing
|
||||||
rept 1000
|
rept 1000
|
||||||
mov dl,cl
|
mov dl,cl
|
||||||
|
|
@ -113,16 +121,18 @@ addressing memory.
|
||||||
dec bp
|
dec bp
|
||||||
jz Done
|
jz Done
|
||||||
jmp CacheFillLoop
|
jmp CacheFillLoop
|
||||||
Done:
|
Done:
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 12.2 LST12-2.ASM**
|
**LISTING 12.2 LST12-2.ASM**
|
||||||
|
|
||||||
; Measures the effect of loading a byte register 1 cycle before
|
```nasm
|
||||||
; using a register to address memory.
|
; Measures the effect of loading a byte register 1 cycle before
|
||||||
|
; 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
|
||||||
; it's cached
|
; it's cached
|
||||||
sub bx,bx
|
sub bx,bx
|
||||||
CacheFillLoop:
|
CacheFillLoop:
|
||||||
call ZTimerOn ;start timing
|
call ZTimerOn ;start timing
|
||||||
rept 1000
|
rept 1000
|
||||||
nop
|
nop
|
||||||
|
|
@ -133,7 +143,8 @@ addressing memory.
|
||||||
dec bp
|
dec bp
|
||||||
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
|
||||||
|
|
|
||||||
16
13-01.md
16
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**
|
||||||
|
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
```nasm
|
||||||
mov bl,[di] ;get the state value for the pair
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
add dx,[bx+8000h] ;increment word and line count
|
mov bl,[di] ;get the state value for the pair
|
||||||
|
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**
|
||||||
|
|
||||||
mov bl,[di] ;get the state value for the pair
|
```nasm
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov bl,[di] ;get the state value for the pair
|
||||||
add dx,[bx+8000h] ;increment word and line count
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
|
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
|
||||||
|
|
|
||||||
38
13-02.md
38
13-02.md
|
|
@ -22,18 +22,22 @@ throughput of two cycles/char."
|
||||||
|
|
||||||
**LISTING 13.3 L13-3.ASM**
|
**LISTING 13.3 L13-3.ASM**
|
||||||
|
|
||||||
mov bl,[di] ;get the state value for the pair
|
```nasm
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov bl,[di] ;get the state value for the pair
|
||||||
mov ax,[bx+8000h] ;increment word and line count
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
add dx,ax ; appropriately for the pair
|
mov ax,[bx+8000h] ;increment word and line count
|
||||||
|
add dx,ax ; appropriately for the pair
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 13.4 L13-4.ASM**
|
**LISTING 13.4 L13-4.ASM**
|
||||||
|
|
||||||
mov bl,[di] ;get the state value for the pair
|
```nasm
|
||||||
mov di,[bp+OFFS] ;get the next pair of characters
|
mov bl,[di] ;get the state value for the pair
|
||||||
add dx,ax ;increment word and line count
|
mov di,[bp+OFFS] ;get the next pair of characters
|
||||||
|
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:
|
||||||
|
|
||||||
lodsd
|
```nasm
|
||||||
bswap
|
lodsd
|
||||||
stosd
|
bswap
|
||||||
|
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,10 +100,11 @@ memory, isn't it?
|
||||||
|
|
||||||
**LISTING 13.5 L13-5.ASM**
|
**LISTING 13.5 L13-5.ASM**
|
||||||
|
|
||||||
mov cx,[initialskip]
|
```nasm
|
||||||
shl ecx,16 ;put skip value in upper half of ECX
|
mov cx,[initialskip]
|
||||||
mov cx,100 ;put loop count in CX
|
shl ecx,16 ;put skip value in upper half of ECX
|
||||||
looptop:
|
mov cx,100 ;put loop count in CX
|
||||||
|
looptop:
|
||||||
:
|
:
|
||||||
ror ecx,16 ;make skip value word accessible in CX
|
ror ecx,16 ;make skip value word accessible in CX
|
||||||
add bx,cx ;skip BX ahead
|
add bx,cx ;skip BX ahead
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
22
13-03.md
22
13-03.md
|
|
@ -21,10 +21,11 @@ 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
|
||||||
looptop:
|
looptop:
|
||||||
:
|
:
|
||||||
bswap ecx ;make skip value word accessible in CX
|
bswap ecx ;make skip value word accessible in CX
|
||||||
add bx,cx ;skip BX ahead
|
add bx,cx ;skip BX ahead
|
||||||
|
|
@ -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,
|
||||||
|
|
||||||
mov ax,[bx]
|
```nasm
|
||||||
push ax
|
mov ax,[bx]
|
||||||
|
push ax
|
||||||
|
```
|
||||||
|
|
||||||
is twice as fast as
|
is twice as fast as
|
||||||
|
|
||||||
push word ptr [bx]
|
```nasm
|
||||||
|
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:
|
||||||
|
|
||||||
mov ax,1
|
```nasm
|
||||||
db 0c1h, 0e0h, 001h
|
mov ax,1
|
||||||
mov dx,ax
|
db 0c1h, 0e0h, 001h
|
||||||
|
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,
|
||||||
|
|
|
||||||
12
13-04.md
12
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,
|
||||||
|
|
||||||
mov al,BaseTable[ecx+edx*4]
|
```nasm
|
||||||
|
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,21 +84,25 @@ 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:
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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]
|
||||||
add ebxX,2
|
add ebxX,2
|
||||||
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
|
||||||
|
|
|
||||||
34
14-04.md
34
14-04.md
|
|
@ -12,19 +12,20 @@ pages: 268-271
|
||||||
|
|
||||||
**LISTING 14.1 L14-1.C**
|
**LISTING 14.1 L14-1.C**
|
||||||
|
|
||||||
/* Searches a buffer for a specified pattern. In case of a mismatch,
|
```c
|
||||||
|
/* 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).
|
||||||
Returns start offset of first match searching forward, or NULL if
|
Returns start offset of first match searching forward, or NULL if
|
||||||
no match is found.
|
no match is found.
|
||||||
Tested with Borland C++ in C mode and the small model. */
|
Tested with Borland C++ in C mode and the small model. */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
unsigned char * FindString(unsigned char * BufferPtr,
|
unsigned char * FindString(unsigned char * BufferPtr,
|
||||||
unsigned int BufferLength, unsigned char * PatternPtr,
|
unsigned int BufferLength, unsigned char * PatternPtr,
|
||||||
unsigned int PatternLength)
|
unsigned int PatternLength)
|
||||||
{
|
{
|
||||||
unsigned char * WorkingPatternPtr, * WorkingBufferPtr;
|
unsigned char * WorkingPatternPtr, * WorkingBufferPtr;
|
||||||
unsigned int CompCount, SkipTable[256], Skip, DistanceMatched;
|
unsigned int CompCount, SkipTable[256], Skip, DistanceMatched;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -96,26 +97,28 @@ pages: 268-271
|
||||||
BufferLength -= Skip;
|
BufferLength -= Skip;
|
||||||
BufferPtr += Skip;
|
BufferPtr += Skip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 14.2 L14-2.C**
|
**LISTING 14.2 L14-2.C**
|
||||||
|
|
||||||
/* Program to exercise buffer-search routines in Listings 14.1 & 14.3.
|
```c
|
||||||
|
/* 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.) */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#define DISPLAY_LENGTH 40
|
#define DISPLAY_LENGTH 40
|
||||||
#define BUFFER_SIZE 0x8000
|
#define BUFFER_SIZE 0x8000
|
||||||
|
|
||||||
extern unsigned char * FindString(unsigned char *, unsigned int,
|
extern unsigned char * FindString(unsigned char *, unsigned int,
|
||||||
unsigned char *, unsigned int);
|
unsigned char *, unsigned int);
|
||||||
void main(void);
|
void main(void);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
||||||
unsigned char Filename[150], Pattern[150], *MatchPtr, *TestBuffer;
|
unsigned char Filename[150], Pattern[150], *MatchPtr, *TestBuffer;
|
||||||
int Handle;
|
int Handle;
|
||||||
|
|
@ -153,7 +156,8 @@ pages: 268-271
|
||||||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
|
||||||
122
14-05.md
122
14-05.md
|
|
@ -12,39 +12,40 @@ pages: 271-274
|
||||||
|
|
||||||
**LISTING 14.3 L14-3.ASM**
|
**LISTING 14.3 L14-3.ASM**
|
||||||
|
|
||||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
```nasm
|
||||||
; uses the value of the mismatched byte to skip across as many
|
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||||
; potential match locations as possible (partial Boyer-Moore).
|
; uses the value of the mismatched byte to skip across as many
|
||||||
; Returns start offset of first match searching forward, or NULL if
|
; potential match locations as possible (partial Boyer-Moore).
|
||||||
; no match is found.
|
; Returns start offset of first match searching forward, or NULL if
|
||||||
; Tested with TASM.
|
; no match is found.
|
||||||
; C near-callable as:
|
; Tested with TASM.
|
||||||
; unsigned char * FindString(unsigned char * BufferPtr,
|
; C near-callable as:
|
||||||
; unsigned int BufferLength, unsigned char * PatternPtr,
|
; unsigned char * FindString(unsigned char * BufferPtr,
|
||||||
; unsigned int PatternLength);
|
; unsigned int BufferLength, unsigned char * PatternPtr,
|
||||||
|
; unsigned int PatternLength);
|
||||||
|
|
||||||
parms struc
|
parms struc
|
||||||
dw 2 dup(?) ;pushed BP & return address
|
dw 2 dup(?) ;pushed BP & return address
|
||||||
BufferPtr dw ? ;pointer to buffer to be searched
|
BufferPtr dw ? ;pointer to buffer to be searched
|
||||||
BufferLength dw ? ;# of bytes in buffer to be searched
|
BufferLength dw ? ;# of bytes in buffer to be searched
|
||||||
PatternPtr dw ? ;pointer to pattern for which to search
|
PatternPtr dw ? ;pointer to pattern for which to search
|
||||||
PatternLength dw ? ;length of pattern for which to search
|
PatternLength dw ? ;length of pattern for which to search
|
||||||
parms ends
|
parms ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindString
|
public _FindString
|
||||||
_FindString proc near
|
_FindString proc near
|
||||||
cld
|
cld
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;point to our stack frame
|
mov bp,sp ;point to our stack frame
|
||||||
push si ;preserve caller's register variables
|
push si ;preserve caller's register variables
|
||||||
push di
|
push di
|
||||||
sub sp,256*2 ;allocate space for SkipTable
|
sub sp,256*2 ;allocate space for SkipTable
|
||||||
; Create the table of distances by which to skip ahead on mismatches
|
; Create the table of distances by which to skip ahead on mismatches
|
||||||
; for every possible byte value. First, initialize all skips to the
|
; for every possible byte value. First, initialize all skips to the
|
||||||
; pattern length; this is the skip distance for bytes that don't
|
; pattern length; this is the skip distance for bytes that don't
|
||||||
; appear in the pattern.
|
; appear in the pattern.
|
||||||
mov ax,[bp+PatternLength]
|
mov ax,[bp+PatternLength]
|
||||||
and ax,ax ;return an instant match if the pattern is
|
and ax,ax ;return an instant match if the pattern is
|
||||||
jz InstantMatch ;0-length
|
jz InstantMatch ;0-length
|
||||||
|
|
@ -55,26 +56,26 @@ pages: 271-274
|
||||||
rep stosw
|
rep stosw
|
||||||
dec ax ;from now on, we only need
|
dec ax ;from now on, we only need
|
||||||
mov [bp+PatternLength],ax ; PatternLength - 1
|
mov [bp+PatternLength],ax ; PatternLength - 1
|
||||||
; Point to last (rightmost) byte of first potential pattern match
|
; Point to last (rightmost) byte of first potential pattern match
|
||||||
; location in buffer.
|
; location in buffer.
|
||||||
add [bp+BufferPtr],ax
|
add [bp+BufferPtr],ax
|
||||||
; Reject if buffer is too small, and set the count of the number of
|
; Reject if buffer is too small, and set the count of the number of
|
||||||
; potential pattern match locations in the buffer.
|
; potential pattern match locations in the buffer.
|
||||||
sub [bp+BufferLength],ax
|
sub [bp+BufferLength],ax
|
||||||
jbe NoMatch
|
jbe NoMatch
|
||||||
; Set the skip values for the bytes that do appear in the pattern to
|
; Set the skip values for the bytes that do appear in the pattern to
|
||||||
; the distance from the byte location to the end of the pattern.
|
; the distance from the byte location to the end of the pattern.
|
||||||
; When there are multiple instances of the same byte, the rightmost
|
; When there are multiple instances of the same byte, the rightmost
|
||||||
; instance's skip value is used. Note that the rightmost byte of the
|
; instance's skip value is used. Note that the rightmost byte of the
|
||||||
; pattern isn't entered in the skip table; if we get that value for
|
; pattern isn't entered in the skip table; if we get that value for
|
||||||
; a mismatch, we know for sure that the right end of the pattern has
|
; a mismatch, we know for sure that the right end of the pattern has
|
||||||
; already passed the mismatch location, so this is not a relevant byte
|
; already passed the mismatch location, so this is not a relevant byte
|
||||||
; for skipping purposes.
|
; for skipping purposes.
|
||||||
mov si,[bp+PatternPtr] ;point to start of pattern
|
mov si,[bp+PatternPtr] ;point to start of pattern
|
||||||
and ax,ax ;are there any skips to set?
|
and ax,ax ;are there any skips to set?
|
||||||
jz SetSkipDone ;no
|
jz SetSkipDone ;no
|
||||||
mov di,sp ;point to SkipBuffer
|
mov di,sp ;point to SkipBuffer
|
||||||
SetSkipLoop:
|
SetSkipLoop:
|
||||||
sub bx,bx ;prepare for word addressing off byte value
|
sub bx,bx ;prepare for word addressing off byte value
|
||||||
mov bl,[si] ;get the next pattern byte
|
mov bl,[si] ;get the next pattern byte
|
||||||
inc si ;advance the pattern pointer
|
inc si ;advance the pattern pointer
|
||||||
|
|
@ -83,18 +84,18 @@ pages: 271-274
|
||||||
; the mismatch value in the buffer
|
; the mismatch value in the buffer
|
||||||
dec ax
|
dec ax
|
||||||
jnz SetSkipLoop
|
jnz SetSkipLoop
|
||||||
SetSkipDone:
|
SetSkipDone:
|
||||||
mov dl,[si] ;DL=rightmost pattern byte from now on
|
mov dl,[si] ;DL=rightmost pattern byte from now on
|
||||||
dec si ;point to next-to-rightmost byte of pattern
|
dec si ;point to next-to-rightmost byte of pattern
|
||||||
mov [bp+PatternPtr],si ; from now on
|
mov [bp+PatternPtr],si ; from now on
|
||||||
; Search the buffer.
|
; Search the buffer.
|
||||||
std ;for backward REPZ CMPSB
|
std ;for backward REPZ CMPSB
|
||||||
mov di,[bp+BufferPtr] ;point to first search location
|
mov di,[bp+BufferPtr] ;point to first search location
|
||||||
mov cx,[bp+BufferLength] ;# of match locations to check
|
mov cx,[bp+BufferLength] ;# of match locations to check
|
||||||
SearchLoop:
|
SearchLoop:
|
||||||
mov si,sp ;point SI to SkipTable
|
mov si,sp ;point SI to SkipTable
|
||||||
; Skip through until there's a match for the rightmost pattern byte.
|
; Skip through until there's a match for the rightmost pattern byte.
|
||||||
QuickSearchLoop:
|
QuickSearchLoop:
|
||||||
mov bl,[di] ;rightmost buffer byte at this location
|
mov bl,[di] ;rightmost buffer byte at this location
|
||||||
cmp dl,bl ;does it match the rightmost pattern byte?
|
cmp dl,bl ;does it match the rightmost pattern byte?
|
||||||
jz FullCompare ;yes, so keep going
|
jz FullCompare ;yes, so keep going
|
||||||
|
|
@ -106,15 +107,15 @@ pages: 271-274
|
||||||
sub cx,ax ;BufferLength -= Skip;
|
sub cx,ax ;BufferLength -= Skip;
|
||||||
ja QuickSearchLoop ;continue if any buffer left
|
ja QuickSearchLoop ;continue if any buffer left
|
||||||
jmp short NoMatch
|
jmp short NoMatch
|
||||||
; Return a pointer to the start of the buffer (for 0-length pattern).
|
; Return a pointer to the start of the buffer (for 0-length pattern).
|
||||||
align 2
|
align 2
|
||||||
InstantMatch:
|
InstantMatch:
|
||||||
mov ax,[bp+BufferPtr]
|
mov ax,[bp+BufferPtr]
|
||||||
jmp short Done
|
jmp short Done
|
||||||
; Compare the pattern and the buffer location, searching from high
|
; Compare the pattern and the buffer location, searching from high
|
||||||
; memory toward low (right to left).
|
; memory toward low (right to left).
|
||||||
align 2
|
align 2
|
||||||
FullCompare:
|
FullCompare:
|
||||||
mov [bp+BufferPtr],di ;save the current state of
|
mov [bp+BufferPtr],di ;save the current state of
|
||||||
mov [bp+BufferLength],cx ; the search
|
mov [bp+BufferLength],cx ; the search
|
||||||
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
||||||
|
|
@ -123,17 +124,17 @@ pages: 271-274
|
||||||
dec di ; of buffer location and pattern
|
dec di ; of buffer location and pattern
|
||||||
repz cmpsb ;compare the rest of the pattern
|
repz cmpsb ;compare the rest of the pattern
|
||||||
jz Match ;that's it; we've found a match
|
jz Match ;that's it; we've found a match
|
||||||
; It's a mismatch; let's see what we can learn from it.
|
; It's a mismatch; let's see what we can learn from it.
|
||||||
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
||||||
; point to mismatch location in buffer
|
; point to mismatch location in buffer
|
||||||
; # of bytes that did match.
|
; # of bytes that did match.
|
||||||
mov si,[bp+BufferPtr]
|
mov si,[bp+BufferPtr]
|
||||||
sub si,di
|
sub si,di
|
||||||
; If, based on the mismatch character, we can't even skip ahead as far
|
; If, based on the mismatch character, we can't even skip ahead as far
|
||||||
; as where we started this particular comparison, then just advance by
|
; as where we started this particular comparison, then just advance by
|
||||||
; 1 to the next potential match; otherwise, skip ahead from this
|
; 1 to the next potential match; otherwise, skip ahead from this
|
||||||
; comparison location by the skip distance for the mismatch character,
|
; comparison location by the skip distance for the mismatch character,
|
||||||
; less the distance covered by the partial match.
|
; less the distance covered by the partial match.
|
||||||
sub bx,bx ;prepare for word addressing off byte value
|
sub bx,bx ;prepare for word addressing off byte value
|
||||||
mov bl,[di] ;get the value of the mismatch byte in buffer
|
mov bl,[di] ;get the value of the mismatch byte in buffer
|
||||||
add bx,bx ;prepare for word look-up
|
add bx,bx ;prepare for word look-up
|
||||||
|
|
@ -145,30 +146,31 @@ pages: 271-274
|
||||||
jna MoveAhead ;no, go with the default advance of 1
|
jna MoveAhead ;no, go with the default advance of 1
|
||||||
mov ax,cx ;yes; this is the distance to skip ahead from
|
mov ax,cx ;yes; this is the distance to skip ahead from
|
||||||
; the last potential match location checked
|
; the last potential match location checked
|
||||||
MoveAhead:
|
MoveAhead:
|
||||||
; Skip ahead and perform the next comparison, if there's any buffer
|
; Skip ahead and perform the next comparison, if there's any buffer
|
||||||
; left to check.
|
; left to check.
|
||||||
mov di,[bp+BufferPtr]
|
mov di,[bp+BufferPtr]
|
||||||
add di,ax ;BufferPtr += Skip;
|
add di,ax ;BufferPtr += Skip;
|
||||||
mov cx,[bp+BufferLength]
|
mov cx,[bp+BufferLength]
|
||||||
sub cx,ax ;BufferLength -= Skip;
|
sub cx,ax ;BufferLength -= Skip;
|
||||||
ja SearchLoop ;continue if any buffer left
|
ja SearchLoop ;continue if any buffer left
|
||||||
; Return a NULL pointer for no match.
|
; Return a NULL pointer for no match.
|
||||||
align 2
|
align 2
|
||||||
NoMatch:
|
NoMatch:
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
jmp short Done
|
jmp short Done
|
||||||
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
||||||
align 2
|
align 2
|
||||||
Match:
|
Match:
|
||||||
mov ax,[bp+BufferPtr]
|
mov ax,[bp+BufferPtr]
|
||||||
sub ax,[bp+PatternLength]
|
sub ax,[bp+PatternLength]
|
||||||
Done:
|
Done:
|
||||||
cld ;restore default direction flag
|
cld ;restore default direction flag
|
||||||
add sp,256*2 ;deallocate space for SkipTable
|
add sp,256*2 ;deallocate space for SkipTable
|
||||||
pop di ;restore caller's register variables
|
pop di ;restore caller's register variables
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
ret
|
ret
|
||||||
_FindString endp
|
_FindString endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
|
||||||
110
14-06.md
110
14-06.md
|
|
@ -35,46 +35,47 @@ about 60 percent faster than Listing 14.3.
|
||||||
|
|
||||||
**LISTING 14.4 L14-4.ASM**
|
**LISTING 14.4 L14-4.ASM**
|
||||||
|
|
||||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
```nasm
|
||||||
; uses the value of the mismatched byte to skip across as many
|
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||||
; potential match locations as possible (partial Boyer-Moore).
|
; uses the value of the mismatched byte to skip across as many
|
||||||
; Returns start offset of first match searching forward, or NULL if
|
; potential match locations as possible (partial Boyer-Moore).
|
||||||
; no match is found.
|
; Returns start offset of first match searching forward, or NULL if
|
||||||
; Requires that the pattern be no longer than 255 bytes, and that
|
; no match is found.
|
||||||
; there be a match for the pattern somewhere in the buffer (ie., a
|
; Requires that the pattern be no longer than 255 bytes, and that
|
||||||
; copy of the pattern should be placed as a sentinel at the end of
|
; there be a match for the pattern somewhere in the buffer (ie., a
|
||||||
; the buffer if the pattern isn't already known to be in the buffer).
|
; copy of the pattern should be placed as a sentinel at the end of
|
||||||
; Tested with TASM.
|
; the buffer if the pattern isn't already known to be in the buffer).
|
||||||
; C near-callable as:
|
; Tested with TASM.
|
||||||
; unsigned char * FindString(unsigned char * BufferPtr,
|
; C near-callable as:
|
||||||
; unsigned int BufferLength, unsigned char * PatternPtr,
|
; unsigned char * FindString(unsigned char * BufferPtr,
|
||||||
; unsigned int PatternLength);
|
; unsigned int BufferLength, unsigned char * PatternPtr,
|
||||||
|
; unsigned int PatternLength);
|
||||||
|
|
||||||
parms struc
|
parms struc
|
||||||
dw 2 dup(?) ;pushed BP & return address
|
dw 2 dup(?) ;pushed BP & return address
|
||||||
BufferPtr dw ? ;pointer to buffer to be searched
|
BufferPtr dw ? ;pointer to buffer to be searched
|
||||||
BufferLength dw ? ;# of bytes in buffer to be searched
|
BufferLength dw ? ;# of bytes in buffer to be searched
|
||||||
; (not used, actually)
|
; (not used, actually)
|
||||||
PatternPtr dw ? ;pointer to pattern for which to search
|
PatternPtr dw ? ;pointer to pattern for which to search
|
||||||
; (pattern *MUST* exist in the buffer)
|
; (pattern *MUST* exist in the buffer)
|
||||||
PatternLength dw ? ;length of pattern for which to search (must
|
PatternLength dw ? ;length of pattern for which to search (must
|
||||||
; be <= 255)
|
; be <= 255)
|
||||||
parms ends
|
parms ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _FindString
|
public _FindString
|
||||||
_FindString proc near
|
_FindString proc near
|
||||||
cld
|
cld
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;point to our stack frame
|
mov bp,sp ;point to our stack frame
|
||||||
push si ;preserve caller's register variables
|
push si ;preserve caller's register variables
|
||||||
push di
|
push di
|
||||||
sub sp,256 ;allocate space for SkipTable
|
sub sp,256 ;allocate space for SkipTable
|
||||||
; Create the table of distances by which to skip ahead on mismatches
|
; Create the table of distances by which to skip ahead on mismatches
|
||||||
; for every possible byte value. First, initialize all skips to the
|
; for every possible byte value. First, initialize all skips to the
|
||||||
; pattern length; this is the skip distance for bytes that don't
|
; pattern length; this is the skip distance for bytes that don't
|
||||||
; appear in the pattern.
|
; appear in the pattern.
|
||||||
mov di,ds
|
mov di,ds
|
||||||
mov es,di ;ES=DS=SS
|
mov es,di ;ES=DS=SS
|
||||||
mov di,sp ;point to SkipBuffer
|
mov di,sp ;point to SkipBuffer
|
||||||
|
|
@ -87,36 +88,36 @@ about 60 percent faster than Listing 14.3.
|
||||||
mov ax,[bp+PatternLength]
|
mov ax,[bp+PatternLength]
|
||||||
dec ax ;from now on, we only need
|
dec ax ;from now on, we only need
|
||||||
mov [bp+PatternLength],ax ; PatternLength - 1
|
mov [bp+PatternLength],ax ; PatternLength - 1
|
||||||
; Point to rightmost byte of first potential pattern match location
|
; Point to rightmost byte of first potential pattern match location
|
||||||
; in buffer.
|
; in buffer.
|
||||||
add [bp+BufferPtr],ax
|
add [bp+BufferPtr],ax
|
||||||
; Set the skip values for the bytes that do appear in the pattern to
|
; Set the skip values for the bytes that do appear in the pattern to
|
||||||
; the distance from the byte location to the end of the pattern.
|
; the distance from the byte location to the end of the pattern.
|
||||||
mov si,[bp+PatternPtr] ;point to start of pattern
|
mov si,[bp+PatternPtr] ;point to start of pattern
|
||||||
and ax,ax ;are there any skips to set?
|
and ax,ax ;are there any skips to set?
|
||||||
jz SetSkipDone ;no
|
jz SetSkipDone ;no
|
||||||
mov di,sp ;point to SkipBuffer
|
mov di,sp ;point to SkipBuffer
|
||||||
sub bx,bx ;prepare for word addressing off byte value
|
sub bx,bx ;prepare for word addressing off byte value
|
||||||
SetSkipLoop:
|
SetSkipLoop:
|
||||||
mov bl,[si] ;get the next pattern byte
|
mov bl,[si] ;get the next pattern byte
|
||||||
inc si ;advance the pattern pointer
|
inc si ;advance the pattern pointer
|
||||||
mov [di+bx],al ;set the skip value when this byte value is
|
mov [di+bx],al ;set the skip value when this byte value is
|
||||||
;the mismatch value in the buffer
|
;the mismatch value in the buffer
|
||||||
dec ax
|
dec ax
|
||||||
jnz SetSkipLoop
|
jnz SetSkipLoop
|
||||||
SetSkipDone:
|
SetSkipDone:
|
||||||
mov dl,[si] ;DL=rightmost pattern byte from now on
|
mov dl,[si] ;DL=rightmost pattern byte from now on
|
||||||
dec si ;point to next-to-rightmost byte of pattern
|
dec si ;point to next-to-rightmost byte of pattern
|
||||||
mov [bp+PatternPtr],si ; from now on
|
mov [bp+PatternPtr],si ; from now on
|
||||||
; Search the buffer.
|
; Search the buffer.
|
||||||
std ;for backward REPZ CMPSB
|
std ;for backward REPZ CMPSB
|
||||||
mov di,[bp+BufferPtr] ;point to the first search location
|
mov di,[bp+BufferPtr] ;point to the first search location
|
||||||
mov bx,sp ;point to SkipTable for XLAT
|
mov bx,sp ;point to SkipTable for XLAT
|
||||||
SearchLoop:
|
SearchLoop:
|
||||||
sub ah,ah ;used to convert AL to a word
|
sub ah,ah ;used to convert AL to a word
|
||||||
; Skip through until there's a match for the first pattern byte.
|
; Skip through until there's a match for the first pattern byte.
|
||||||
QuickSearchLoop:
|
QuickSearchLoop:
|
||||||
; See if we have a match at the first buffer location.
|
; See if we have a match at the first buffer location.
|
||||||
REPT 8 ;unroll loop 8 times to reduce branching
|
REPT 8 ;unroll loop 8 times to reduce branching
|
||||||
mov al,[di] ;next buffer byte
|
mov al,[di] ;next buffer byte
|
||||||
cmp dl,al ;does it match the pattern?
|
cmp dl,al ;does it match the pattern?
|
||||||
|
|
@ -125,15 +126,15 @@ about 60 percent faster than Listing 14.3.
|
||||||
add di,ax ;BufferPtr += Skip;
|
add di,ax ;BufferPtr += Skip;
|
||||||
ENDM
|
ENDM
|
||||||
jmp QuickSearchLoop
|
jmp QuickSearchLoop
|
||||||
; Return a pointer to the start of the buffer (for 0-length pattern).
|
; Return a pointer to the start of the buffer (for 0-length pattern).
|
||||||
align 2
|
align 2
|
||||||
InstantMatch:
|
InstantMatch:
|
||||||
mov ax,[bp+BufferPtr]
|
mov ax,[bp+BufferPtr]
|
||||||
jmp short Done
|
jmp short Done
|
||||||
; Compare the pattern and the buffer location, searching from high
|
; Compare the pattern and the buffer location, searching from high
|
||||||
; memory toward low (right to left).
|
; memory toward low (right to left).
|
||||||
align 2
|
align 2
|
||||||
FullCompare:
|
FullCompare:
|
||||||
mov [bp+BufferPtr],di ;save the current buffer location
|
mov [bp+BufferPtr],di ;save the current buffer location
|
||||||
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
||||||
jcxz Match ;done if there was only one character
|
jcxz Match ;done if there was only one character
|
||||||
|
|
@ -141,17 +142,17 @@ about 60 percent faster than Listing 14.3.
|
||||||
; points to next-to-rightmost source byte)
|
; points to next-to-rightmost source byte)
|
||||||
repz cmpsb ;compare the rest of the pattern
|
repz cmpsb ;compare the rest of the pattern
|
||||||
jz Match ;that's it; we've found a match
|
jz Match ;that's it; we've found a match
|
||||||
; It's a mismatch; let's see what we can learn from it.
|
; It's a mismatch; let's see what we can learn from it.
|
||||||
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
||||||
; point to mismatch location in buffer
|
; point to mismatch location in buffer
|
||||||
; # of bytes that did match.
|
; # of bytes that did match.
|
||||||
mov si,[bp+BufferPtr]
|
mov si,[bp+BufferPtr]
|
||||||
sub si,di
|
sub si,di
|
||||||
; If, based on the mismatch character, we can't even skip ahead as far
|
; If, based on the mismatch character, we can't even skip ahead as far
|
||||||
; as where we started this particular comparison, then just advance by
|
; as where we started this particular comparison, then just advance by
|
||||||
; 1 to the next potential match; otherwise, skip ahead from this
|
; 1 to the next potential match; otherwise, skip ahead from this
|
||||||
; comparison location by the skip distance for the mismatch character,
|
; comparison location by the skip distance for the mismatch character,
|
||||||
; less the distance covered by the partial match.
|
; less the distance covered by the partial match.
|
||||||
mov al,[di] ;get the value of the mismatch byte in buffer
|
mov al,[di] ;get the value of the mismatch byte in buffer
|
||||||
xlat ;get the skip value for this mismatch
|
xlat ;get the skip value for this mismatch
|
||||||
mov cx,1 ;assume we'll just advance to the next
|
mov cx,1 ;assume we'll just advance to the next
|
||||||
|
|
@ -160,27 +161,28 @@ about 60 percent faster than Listing 14.3.
|
||||||
jna MoveAhead ;no, go with the default advance of 1
|
jna MoveAhead ;no, go with the default advance of 1
|
||||||
mov cx,ax ;yes, this is the distance to skip ahead from
|
mov cx,ax ;yes, this is the distance to skip ahead from
|
||||||
;the last potential match location checked
|
;the last potential match location checked
|
||||||
MoveAhead:
|
MoveAhead:
|
||||||
; Skip ahead and perform the next comparison.
|
; Skip ahead and perform the next comparison.
|
||||||
mov di,[bp+BufferPtr]
|
mov di,[bp+BufferPtr]
|
||||||
add di,cx ;BufferPtr += Skip;
|
add di,cx ;BufferPtr += Skip;
|
||||||
mov si,[bp+PatternPtr] ;point to the next-to-rightmost
|
mov si,[bp+PatternPtr] ;point to the next-to-rightmost
|
||||||
; pattern byte
|
; pattern byte
|
||||||
jmp SearchLoop
|
jmp SearchLoop
|
||||||
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
||||||
align 2
|
align 2
|
||||||
Match:
|
Match:
|
||||||
mov ax,[bp+BufferPtr]
|
mov ax,[bp+BufferPtr]
|
||||||
sub ax,[bp+PatternLength]
|
sub ax,[bp+PatternLength]
|
||||||
Done:
|
Done:
|
||||||
cld ;restore default direction flag
|
cld ;restore default direction flag
|
||||||
add sp,256 ;deallocate space for SkipTable
|
add sp,256 ;deallocate space for SkipTable
|
||||||
pop di ;restore caller's register variables
|
pop di ;restore caller's register variables
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
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
|
||||||
|
|
|
||||||
58
15-02.md
58
15-02.md
|
|
@ -12,47 +12,52 @@ pages: 284-287
|
||||||
|
|
||||||
**LISTING 15.1 L15-1.C**
|
**LISTING 15.1 L15-1.C**
|
||||||
|
|
||||||
/* Deletes the node in a linked list that follows the indicated node.
|
```c
|
||||||
|
/* 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
|
||||||
that was passed in. */
|
that was passed in. */
|
||||||
|
|
||||||
#include "llist.h"
|
#include "llist.h"
|
||||||
struct LinkNode *DeleteNodeAfter(struct LinkNode *NodeToDeleteAfter)
|
struct LinkNode *DeleteNodeAfter(struct LinkNode *NodeToDeleteAfter)
|
||||||
{
|
{
|
||||||
NodeToDeleteAfter->NextNode =
|
NodeToDeleteAfter->NextNode =
|
||||||
NodeToDeleteAfter->NextNode->NextNode;
|
NodeToDeleteAfter->NextNode->NextNode;
|
||||||
return(NodeToDeleteAfter);
|
return(NodeToDeleteAfter);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 15.2 LLIST.H**
|
**LISTING 15.2 LLIST.H**
|
||||||
|
|
||||||
/* Linked list header file. */
|
```c
|
||||||
#define MAX_TEXT_LENGTH 100 /* longest allowed Text field */
|
/* Linked list header file. */
|
||||||
#define SENTINEL 32767 /* largest possible Value field */
|
#define MAX_TEXT_LENGTH 100 /* longest allowed Text field */
|
||||||
|
#define SENTINEL 32767 /* largest possible Value field */
|
||||||
|
|
||||||
struct LinkNode {
|
struct LinkNode {
|
||||||
struct LinkNode *NextNode;
|
struct LinkNode *NextNode;
|
||||||
int Value;
|
int Value;
|
||||||
char Text[MAX_TEXT_LENGTH+1];
|
char Text[MAX_TEXT_LENGTH+1];
|
||||||
/* Any number of additional data fields may by present */
|
/* Any number of additional data fields may by present */
|
||||||
};
|
};
|
||||||
struct LinkNode *DeleteNodeAfter(struct LinkNode *);
|
struct LinkNode *DeleteNodeAfter(struct LinkNode *);
|
||||||
struct LinkNode *FindNodeBeforeValue(struct LinkNode *, int);
|
struct LinkNode *FindNodeBeforeValue(struct LinkNode *, int);
|
||||||
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**
|
||||||
|
|
||||||
/* Deletes the node in the specified linked list that follows the
|
```c
|
||||||
|
/* 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
|
||||||
pointer, special handling is performed. */
|
pointer, special handling is performed. */
|
||||||
#include "llist.h"
|
#include "llist.h"
|
||||||
struct LinkNode *DeleteNodeAfter(struct LinkNode **HeadOfListPtr,
|
struct LinkNode *DeleteNodeAfter(struct LinkNode **HeadOfListPtr,
|
||||||
struct LinkNode *NodeToDeleteAfter)
|
struct LinkNode *NodeToDeleteAfter)
|
||||||
{
|
{
|
||||||
/* Handle specially if the node to delete after is actually the
|
/* Handle specially if the node to delete after is actually the
|
||||||
head of the list (delete the first element in the list) */
|
head of the list (delete the first element in the list) */
|
||||||
if (NodeToDeleteAfter == (struct LinkNode *)HeadOfListPtr) {
|
if (NodeToDeleteAfter == (struct LinkNode *)HeadOfListPtr) {
|
||||||
|
|
@ -62,7 +67,8 @@ pages: 284-287
|
||||||
NodeToDeleteAfter->NextNode->NextNode;
|
NodeToDeleteAfter->NextNode->NextNode;
|
||||||
}
|
}
|
||||||
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,16 +129,17 @@ 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**
|
||||||
|
|
||||||
/* Finds the first node in a linked list with a value field greater
|
```c
|
||||||
|
/* 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
|
||||||
NULL pointer if no such value was found. Assumes the list is
|
NULL pointer if no such value was found. Assumes the list is
|
||||||
terminated with a tail node pointing to itself as the next node. */
|
terminated with a tail node pointing to itself as the next node. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "llist.h"
|
#include "llist.h"
|
||||||
struct LinkNode *FindNodeBeforeValueNotLess(
|
struct LinkNode *FindNodeBeforeValueNotLess(
|
||||||
struct LinkNode *HeadOfListNode, int SearchValue)
|
struct LinkNode *HeadOfListNode, int SearchValue)
|
||||||
{
|
{
|
||||||
struct LinkNode *NodePtr = HeadOfListNode;
|
struct LinkNode *NodePtr = HeadOfListNode;
|
||||||
|
|
||||||
while ( (NodePtr->NextNode->NextNode != NodePtr->NextNode) &&
|
while ( (NodePtr->NextNode->NextNode != NodePtr->NextNode) &&
|
||||||
|
|
@ -144,7 +151,8 @@ value has to perform two tests in the inner loop, as shown in Listing
|
||||||
else
|
else
|
||||||
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
|
||||||
|
|
|
||||||
52
15-03.md
52
15-03.md
|
|
@ -12,18 +12,19 @@ pages: 287-290
|
||||||
|
|
||||||
**LISTING 15.5 L15-5.C**
|
**LISTING 15.5 L15-5.C**
|
||||||
|
|
||||||
/* Finds the first node in a value-sorted linked list that
|
```c
|
||||||
|
/* 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
|
||||||
insertion and deletion), or a NULL pointer if no such value was
|
insertion and deletion), or a NULL pointer if no such value was
|
||||||
found. Assumes the list is terminated with a sentinel tail node
|
found. Assumes the list is terminated with a sentinel tail node
|
||||||
containing the largest possible Value field setting and pointing
|
containing the largest possible Value field setting and pointing
|
||||||
to itself as the next node. */
|
to itself as the next node. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "llist.h"
|
#include "llist.h"
|
||||||
struct LinkNode *FindNodeBeforeValueNotLess(
|
struct LinkNode *FindNodeBeforeValueNotLess(
|
||||||
struct LinkNode *HeadOfListNode, int SearchValue)
|
struct LinkNode *HeadOfListNode, int SearchValue)
|
||||||
{
|
{
|
||||||
struct LinkNode *NodePtr = HeadOfListNode;
|
struct LinkNode *NodePtr = HeadOfListNode;
|
||||||
while (NodePtr->NextNode->Value < SearchValue)
|
while (NodePtr->NextNode->Value < SearchValue)
|
||||||
NodePtr = NodePtr->NextNode;
|
NodePtr = NodePtr->NextNode;
|
||||||
|
|
@ -32,7 +33,8 @@ pages: 287-290
|
||||||
else
|
else
|
||||||
return(NodePtr); /* success; return pointer to node preceding
|
return(NodePtr); /* success; return pointer to node preceding
|
||||||
node that was >= */
|
node that was >= */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -80,20 +82,21 @@ before you write a single line of code.
|
||||||
|
|
||||||
**LISTING 15.6 L15-6.C**
|
**LISTING 15.6 L15-6.C**
|
||||||
|
|
||||||
/* Suite of functions for maintaining a linked list sorted by
|
```c
|
||||||
|
/* 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.
|
||||||
The dummy node is a sentinel, containing the largest possible
|
The dummy node is a sentinel, containing the largest possible
|
||||||
Value field setting. Tested with Borland C++ in C mode. */
|
Value field setting. Tested with Borland C++ in C mode. */
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "llist.h"
|
#include "llist.h"
|
||||||
/* Initializes an empty linked list of LinkNode structures,
|
/* Initializes an empty linked list of LinkNode structures,
|
||||||
consisting of a single head/tail/sentinel node, and returns a
|
consisting of a single head/tail/sentinel node, and returns a
|
||||||
pointer to the list. Returns NULL for failure. */
|
pointer to the list. Returns NULL for failure. */
|
||||||
struct LinkNode *InitLinkedList()
|
struct LinkNode *InitLinkedList()
|
||||||
{
|
{
|
||||||
struct LinkNode *Sentinel;
|
struct LinkNode *Sentinel;
|
||||||
|
|
||||||
if ((Sentinel = malloc(sizeof(struct LinkNode))) == NULL)
|
if ((Sentinel = malloc(sizeof(struct LinkNode))) == NULL)
|
||||||
|
|
@ -102,17 +105,17 @@ before you write a single line of code.
|
||||||
Sentinel->Value = SENTINEL;
|
Sentinel->Value = SENTINEL;
|
||||||
strcpy(Sentinel->Text, "*** sentinel ***");
|
strcpy(Sentinel->Text, "*** sentinel ***");
|
||||||
return(Sentinel);
|
return(Sentinel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finds the first node in a value-sorted linked list with a value
|
/* Finds the first node in a value-sorted linked list with a value
|
||||||
field equal to a key value, and returns a pointer to the node
|
field 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
|
||||||
NULL pointer if no value was found. Assumes list is terminated
|
NULL pointer if no value was found. Assumes list is terminated
|
||||||
with a sentinel node containing the largest possible value. */
|
with a sentinel node containing the largest possible value. */
|
||||||
|
|
||||||
struct LinkNode *FindNodeBeforeValue(struct LinkNode *HeadOfListNode,
|
struct LinkNode *FindNodeBeforeValue(struct LinkNode *HeadOfListNode,
|
||||||
int SearchValue)
|
int SearchValue)
|
||||||
{
|
{
|
||||||
struct LinkNode *NodePtr = HeadOfListNode;
|
struct LinkNode *NodePtr = HeadOfListNode;
|
||||||
|
|
||||||
while (NodePtr->NextNode->Value < SearchValue)
|
while (NodePtr->NextNode->Value < SearchValue)
|
||||||
|
|
@ -129,14 +132,14 @@ before you write a single line of code.
|
||||||
} else {
|
} else {
|
||||||
return(NULL); /* No match; return failure status */
|
return(NULL); /* No match; return failure status */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inserts the specified node into a value-sorted linked list, such
|
/* Inserts the specified node into a value-sorted linked list, such
|
||||||
that value-sorting is maintained. Returns a pointer to the node
|
that value-sorting is maintained. Returns a pointer to the node
|
||||||
after which the new node is inserted. */
|
after which the new node is inserted. */
|
||||||
struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
|
struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
|
||||||
struct LinkNode *NodeToInsert)
|
struct LinkNode *NodeToInsert)
|
||||||
{
|
{
|
||||||
struct LinkNode *NodePtr = HeadOfListNode;
|
struct LinkNode *NodePtr = HeadOfListNode;
|
||||||
int SearchValue = NodeToInsert->Value;
|
int SearchValue = NodeToInsert->Value;
|
||||||
while (NodePtr->NextNode->Value < SearchValue)
|
while (NodePtr->NextNode->Value < SearchValue)
|
||||||
|
|
@ -144,4 +147,5 @@ before you write a single line of code.
|
||||||
NodeToInsert->NextNode = NodePtr->NextNode;
|
NodeToInsert->NextNode = NodePtr->NextNode;
|
||||||
NodePtr->NextNode = NodeToInsert;
|
NodePtr->NextNode = NodeToInsert;
|
||||||
return(NodePtr);
|
return(NodePtr);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
98
15-04.md
98
15-04.md
|
|
@ -12,37 +12,38 @@ pages: 290-293
|
||||||
|
|
||||||
**LISTING 15.7 L15-7.ASM**
|
**LISTING 15.7 L15-7.ASM**
|
||||||
|
|
||||||
; C near-callable assembly function for inserting a new node in a
|
```nasm
|
||||||
; linked list sorted by ascending order of the Value field. The list
|
; C near-callable assembly function for inserting a new node in a
|
||||||
; is circular; that is, it has a dummy node as both the head and the
|
; linked list sorted by ascending order of the Value field. The list
|
||||||
; tail of the list. The dummy node is a sentinel, containing the
|
; is circular; that is, it has a dummy node as both the head and the
|
||||||
; largest possible Value field setting. Tested with TASM.
|
; tail of the list. The dummy node is a sentinel, containing the
|
||||||
MAX_TEXT_LENGTH equ 100 ;longest allowed Text field
|
; largest possible Value field setting. Tested with TASM.
|
||||||
SENTINEL equ 32767 ;largest possible Value field
|
MAX_TEXT_LENGTH equ 100 ;longest allowed Text field
|
||||||
LinkNode struc
|
SENTINEL equ 32767 ;largest possible Value field
|
||||||
NextNode dw ?
|
LinkNode struc
|
||||||
Value dw ?
|
NextNode dw ?
|
||||||
Text db MAX_TEXT_LENGTH+1 dup(?)
|
Value dw ?
|
||||||
;*** Any number of additional data fields may by present ***
|
Text db MAX_TEXT_LENGTH+1 dup(?)
|
||||||
LinkNode ends
|
;*** Any number of additional data fields may by present ***
|
||||||
|
LinkNode ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
|
|
||||||
; Inserts the specified node into a ascending-value-sorted linked
|
; Inserts the specified node into a ascending-value-sorted linked
|
||||||
; list, such that value-sorting is maintained. Returns a pointer to
|
; list, such that value-sorting is maintained. Returns a pointer to
|
||||||
; the node after which the new node is inserted.
|
; the node after which the new node is inserted.
|
||||||
; C near-callable as:
|
; C near-callable as:
|
||||||
; struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
|
; struct LinkNode *InsertNodeSorted(struct LinkNode *HeadOfListNode,
|
||||||
; struct LinkNode *NodeToInsert)
|
; struct LinkNode *NodeToInsert)
|
||||||
parms struc
|
parms struc
|
||||||
dw 2 dup (?) ;pushed return address & BP
|
dw 2 dup (?) ;pushed return address & BP
|
||||||
HeadOfListNode dw ? ;pointer to head node of list
|
HeadOfListNode dw ? ;pointer to head node of list
|
||||||
NodeToInsert dw ? ;pointer to node to insert
|
NodeToInsert dw ? ;pointer to node to insert
|
||||||
parms ends
|
parms ends
|
||||||
|
|
||||||
public _InsertNodeSorted
|
public _InsertNodeSorted
|
||||||
_InsertNodeSorted proc near
|
_InsertNodeSorted proc near
|
||||||
push bp
|
push bp
|
||||||
mov bp,sp ;point to stack frame
|
mov bp,sp ;point to stack frame
|
||||||
push si ;preserve register vars
|
push si ;preserve register vars
|
||||||
|
|
@ -51,7 +52,7 @@ pages: 290-293
|
||||||
mov ax,[si].Value ;search value
|
mov ax,[si].Value ;search value
|
||||||
mov di,[bp].HeadOfListNode ;point to linked list in
|
mov di,[bp].HeadOfListNode ;point to linked list in
|
||||||
; which to insert
|
; which to insert
|
||||||
SearchLoop:
|
SearchLoop:
|
||||||
mov bx,di ;advance to the next node
|
mov bx,di ;advance to the next node
|
||||||
mov di,[bx].NextNode ;point to following node
|
mov di,[bx].NextNode ;point to following node
|
||||||
cmp [di].Value,ax ;is the following node's
|
cmp [di].Value,ax ;is the following node's
|
||||||
|
|
@ -69,21 +70,23 @@ pages: 290-293
|
||||||
pop si
|
pop si
|
||||||
pop bp
|
pop bp
|
||||||
ret
|
ret
|
||||||
_InsertNodeSorted endp
|
_InsertNodeSorted endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 15.8 L15-8.C**
|
**LISTING 15.8 L15-8.C**
|
||||||
|
|
||||||
/* Sample linked list program. Tested with Borland C++. */
|
```c
|
||||||
#include <stdlib.h>
|
/* Sample linked list program. Tested with Borland C++. */
|
||||||
#include <stdio.h>
|
#include <stdlib.h>
|
||||||
#include <conio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <conio.h>
|
||||||
#include <string.h>
|
#include <ctype.h>
|
||||||
#include "llist.h"
|
#include <string.h>
|
||||||
|
#include "llist.h"
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{ int Done = 0, Char, TempValue;
|
{ int Done = 0, Char, TempValue;
|
||||||
struct LinkNode *TempPtr, *ListPtr, *TempPtr2;
|
struct LinkNode *TempPtr, *ListPtr, *TempPtr2;
|
||||||
char TempBuffer[MAX_TEXT_LENGTH+3];
|
char TempBuffer[MAX_TEXT_LENGTH+3];
|
||||||
|
|
||||||
|
|
@ -154,7 +157,8 @@ pages: 290-293
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Hi/Lo in 24 Bytes {#Heading6}
|
### Hi/Lo in 24 Bytes {#Heading6}
|
||||||
|
|
||||||
|
|
@ -182,33 +186,35 @@ reads closely enough.
|
||||||
|
|
||||||
**LISTING 15.9 L15-9.ASM**
|
**LISTING 15.9 L15-9.ASM**
|
||||||
|
|
||||||
; Find the greatest or smallest unsigned int.
|
```nasm
|
||||||
; C callable (small model); 24 bytes.
|
; Find the greatest or smallest unsigned int.
|
||||||
; By David Stafford.
|
; C callable (small model); 24 bytes.
|
||||||
; unsigned hi( int num, unsigned a[] );
|
; By David Stafford.
|
||||||
; unsigned lo( int num, unsigned a[] );
|
; unsigned hi( int num, unsigned a[] );
|
||||||
|
; unsigned lo( int num, unsigned a[] );
|
||||||
|
|
||||||
public _hi, _lo
|
public _hi, _lo
|
||||||
|
|
||||||
_hi: db 0b9h ;mov cx,immediate
|
_hi: db 0b9h ;mov cx,immediate
|
||||||
_lo: xor cx,cx
|
_lo: xor cx,cx
|
||||||
pop ax ;get return address
|
pop ax ;get return address
|
||||||
pop dx ;get count
|
pop dx ;get count
|
||||||
pop bx ;get pointer
|
pop bx ;get pointer
|
||||||
push bx ;restore pointer
|
push bx ;restore pointer
|
||||||
push dx ;restore count
|
push dx ;restore count
|
||||||
push ax ;restore return address
|
push ax ;restore return address
|
||||||
save: mov ax,[bx]
|
save: mov ax,[bx]
|
||||||
top: cmp ax,[bx]
|
top: cmp ax,[bx]
|
||||||
jcxz around
|
jcxz around
|
||||||
cmc
|
cmc
|
||||||
around: ja save
|
around: ja save
|
||||||
inc bx
|
inc bx
|
||||||
inc bx
|
inc bx
|
||||||
dec dx
|
dec dx
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
68
16-02.md
68
16-02.md
|
|
@ -22,21 +22,22 @@ generates.
|
||||||
|
|
||||||
**LISTING 16.2 L16-2.C**
|
**LISTING 16.2 L16-2.C**
|
||||||
|
|
||||||
/* Word-counting program incorporating assembly language. Tested
|
```c
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys\stat.h>
|
#include <sys\stat.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
|
||||||
#define BUFFER_SIZE 0x8000 /* largest chunk of file worked
|
#define BUFFER_SIZE 0x8000 /* largest chunk of file worked
|
||||||
with at any one time */
|
with at any one time */
|
||||||
int main(int, char **);
|
int main(int, char **);
|
||||||
void ScanBuffer(char *, unsigned int, char *, unsigned long *);
|
void ScanBuffer(char *, unsigned int, char *, unsigned long *);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
int Handle;
|
int Handle;
|
||||||
unsigned int BlockSize;
|
unsigned int BlockSize;
|
||||||
long FileSize;
|
long FileSize;
|
||||||
|
|
@ -79,33 +80,35 @@ 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**
|
||||||
|
|
||||||
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
```nasm
|
||||||
; length BufferLength, counting words and updating WordCount as
|
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
||||||
; appropriate. BufferLength must be > 0. *CharFlag and *WordCount
|
; length BufferLength, counting words and updating WordCount as
|
||||||
; should equal 0 on the first call. Tested with TASM.
|
; appropriate. BufferLength must be > 0. *CharFlag and *WordCount
|
||||||
; C near-callable as:
|
; should equal 0 on the first call. Tested with TASM.
|
||||||
; void ScanBuffer(char *Buffer, unsigned int BufferLength,
|
; C near-callable as:
|
||||||
; char *CharFlag, unsigned long *WordCount);
|
; void ScanBuffer(char *Buffer, unsigned int BufferLength,
|
||||||
|
; char *CharFlag, unsigned long *WordCount);
|
||||||
|
|
||||||
parms struc
|
parms struc
|
||||||
dw 2 dup(?) ;pushed return address & BP
|
dw 2 dup(?) ;pushed return address & BP
|
||||||
Buffer dw ? ;buffer to scan
|
Buffer dw ? ;buffer to scan
|
||||||
BufferLength dw ? ;length of buffer to scan
|
BufferLength dw ? ;length of buffer to scan
|
||||||
CharFlag dw ? ;pointer to flag for state of last
|
CharFlag dw ? ;pointer to flag for state of last
|
||||||
; char processed on entry (0 on
|
; char processed on entry (0 on
|
||||||
; initial call). Updated on exit
|
; initial call). Updated on exit
|
||||||
WordCount dw ? ;pointer to 32-bit count of words
|
WordCount dw ? ;pointer to 32-bit count of words
|
||||||
; found (0 on initial call)
|
; found (0 on initial call)
|
||||||
parms ends
|
parms ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.code
|
.code
|
||||||
public _ScanBuffer
|
public _ScanBuffer
|
||||||
_ScanBuffer proc near
|
_ScanBuffer proc near
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;set up local stack frame
|
mov bp,sp ;set up local stack frame
|
||||||
push si ;preserve caller's register vars
|
push si ;preserve caller's register vars
|
||||||
|
|
@ -118,7 +121,7 @@ generates.
|
||||||
mov bx,[bp+CharFlag]
|
mov bx,[bp+CharFlag]
|
||||||
mov bl,[bx] ;get current CharFlag
|
mov bl,[bx] ;get current CharFlag
|
||||||
mov di,[bp+BufferLength];get # of bytes to scan
|
mov di,[bp+BufferLength];get # of bytes to scan
|
||||||
ScanLoop:
|
ScanLoop:
|
||||||
mov bh,bl ;PredCharFlag = CharFlag;
|
mov bh,bl ;PredCharFlag = CharFlag;
|
||||||
lodsb ;Ch = *BufferPtr++ & 0x7F;
|
lodsb ;Ch = *BufferPtr++ & 0x7F;
|
||||||
and al,7fh ;strip high bit for word processors
|
and al,7fh ;strip high bit for word processors
|
||||||
|
|
@ -128,17 +131,17 @@ generates.
|
||||||
jb CheckAZ
|
jb CheckAZ
|
||||||
cmp al,‘z'
|
cmp al,‘z'
|
||||||
jna IsAChar
|
jna IsAChar
|
||||||
CheckAZ:
|
CheckAZ:
|
||||||
cmp al,‘A' ;it is a char if between A and Z
|
cmp al,‘A' ;it is a char if between A and Z
|
||||||
jb Check09
|
jb Check09
|
||||||
cmp al,‘Z'
|
cmp al,‘Z'
|
||||||
jna IsAChar
|
jna IsAChar
|
||||||
Check09:
|
Check09:
|
||||||
cmp al,‘0' ;it is a char if between 0 and 9
|
cmp al,‘0' ;it is a char if between 0 and 9
|
||||||
jb CheckApostrophe
|
jb CheckApostrophe
|
||||||
cmp al,‘9'
|
cmp al,‘9'
|
||||||
jna IsAChar
|
jna IsAChar
|
||||||
CheckApostrophe:
|
CheckApostrophe:
|
||||||
cmp al,27h ;it is a char if an apostrophe
|
cmp al,27h ;it is a char if an apostrophe
|
||||||
jz IsAChar
|
jz IsAChar
|
||||||
sub bl,bl ;not a char; CharFlag = 0;
|
sub bl,bl ;not a char; CharFlag = 0;
|
||||||
|
|
@ -146,8 +149,8 @@ generates.
|
||||||
jz ScanLoopBottom ;if ((!CharFlag) && PredCharFlag) {
|
jz ScanLoopBottom ;if ((!CharFlag) && PredCharFlag) {
|
||||||
add cx,1 ; (WordCount)++;
|
add cx,1 ; (WordCount)++;
|
||||||
adc dx,0 ;}
|
adc dx,0 ;}
|
||||||
IsAChar:
|
IsAChar:
|
||||||
ScanLoopBottom:
|
ScanLoopBottom:
|
||||||
dec di ;} while (—BufferLength);
|
dec di ;} while (—BufferLength);
|
||||||
jnz ScanLoop
|
jnz ScanLoop
|
||||||
|
|
||||||
|
|
@ -161,8 +164,9 @@ generates.
|
||||||
pop si
|
pop si
|
||||||
pop bp ;restore caller's stack frame
|
pop bp ;restore caller's stack frame
|
||||||
ret
|
ret
|
||||||
_ScanBuffer endp
|
_ScanBuffer endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
#### Which Way to Go from Here? {#Heading4}
|
#### Which Way to Go from Here? {#Heading4}
|
||||||
|
|
||||||
|
|
|
||||||
51
16-03.md
51
16-03.md
|
|
@ -12,32 +12,33 @@ pages: 303-305
|
||||||
|
|
||||||
**LISTING 16.4 L16-4.ASM**
|
**LISTING 16.4 L16-4.ASM**
|
||||||
|
|
||||||
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
```nasm
|
||||||
; length BufferLength, counting words and updating WordCount as
|
; Assembly subroutine for Listing 16.2. Scans through Buffer, of
|
||||||
; appropriate, using a lookup table-based approach. BufferLength
|
; length BufferLength, counting words and updating WordCount as
|
||||||
; must be > 0. *CharFlag and *WordCount should equal 0 on the
|
; appropriate, using a lookup table-based approach. BufferLength
|
||||||
; first call. Tested with TASM.
|
; must be > 0. *CharFlag and *WordCount should equal 0 on the
|
||||||
; C near-callable as:
|
; first call. Tested with TASM.
|
||||||
; void ScanBuffer(char *Buffer, unsigned int BufferLength,
|
; C near-callable as:
|
||||||
; char *CharFlag, unsigned long *WordCount);
|
; void ScanBuffer(char *Buffer, unsigned int BufferLength,
|
||||||
|
; char *CharFlag, unsigned long *WordCount);
|
||||||
|
|
||||||
parms struc
|
parms struc
|
||||||
dw 2 dup(?) ;pushed return address & BP
|
dw 2 dup(?) ;pushed return address & BP
|
||||||
Buffer dw ? ;buffer to scan
|
Buffer dw ? ;buffer to scan
|
||||||
BufferLength dw ? ;length of buffer to scan
|
BufferLength dw ? ;length of buffer to scan
|
||||||
CharFlag dw ? ;pointer to flag for state of last
|
CharFlag dw ? ;pointer to flag for state of last
|
||||||
;char processed on entry (0 on
|
;char processed on entry (0 on
|
||||||
;initial call). Updated on exit
|
;initial call). Updated on exit
|
||||||
WordCount dw ? ;pointer to 32-bit count of words
|
WordCount dw ? ;pointer to 32-bit count of words
|
||||||
; found (0 on initial call)
|
; found (0 on initial call)
|
||||||
parms ends
|
parms ends
|
||||||
|
|
||||||
.model small
|
.model small
|
||||||
.data
|
.data
|
||||||
; Table of char/not statuses for byte values 0-255 (128-255 are
|
; Table of char/not statuses for byte values 0-255 (128-255 are
|
||||||
; duplicates of 0-127 to effectively mask off bit 7, which some
|
; duplicates of 0-127 to effectively mask off bit 7, which some
|
||||||
; word processors set as an internal flag).
|
; word processors set as an internal flag).
|
||||||
CharStatusTable label byte
|
CharStatusTable label byte
|
||||||
REPT 2
|
REPT 2
|
||||||
db 39 dup(0)
|
db 39 dup(0)
|
||||||
db 1 ;apostrophe
|
db 1 ;apostrophe
|
||||||
|
|
@ -52,7 +53,7 @@ pages: 303-305
|
||||||
|
|
||||||
.code
|
.code
|
||||||
public _ScanBuffer
|
public _ScanBuffer
|
||||||
_ScanBuffer proc near
|
_ScanBuffer proc near
|
||||||
push bp ;preserve caller's stack frame
|
push bp ;preserve caller's stack frame
|
||||||
mov bp,sp ;set up local stack frame
|
mov bp,sp ;set up local stack frame
|
||||||
push si ;preserve caller's register vars
|
push si ;preserve caller's register vars
|
||||||
|
|
@ -66,7 +67,7 @@ pages: 303-305
|
||||||
mov al,[bx] ;get current CharFlag
|
mov al,[bx] ;get current CharFlag
|
||||||
mov cx,[bp+BufferLength] ;get # of bytes to scan
|
mov cx,[bp+BufferLength] ;get # of bytes to scan
|
||||||
mov bx,offset CharStatusTable
|
mov bx,offset CharStatusTable
|
||||||
ScanLoop:
|
ScanLoop:
|
||||||
and al,al ;ZF=0 if last byte was a char,
|
and al,al ;ZF=0 if last byte was a char,
|
||||||
; ZF=1 if not
|
; ZF=1 if not
|
||||||
lodsb ;get the next byte
|
lodsb ;get the next byte
|
||||||
|
|
@ -78,10 +79,10 @@ pages: 303-305
|
||||||
and al,al ;last byte was a character; is the
|
and al,al ;last byte was a character; is the
|
||||||
; current byte a character?
|
; current byte a character?
|
||||||
jz CountWord ;no, so count a word
|
jz CountWord ;no, so count a word
|
||||||
ScanLoopBottom:
|
ScanLoopBottom:
|
||||||
dec cx ;count down buffer length
|
dec cx ;count down buffer length
|
||||||
jnz ScanLoop
|
jnz ScanLoop
|
||||||
Done:
|
Done:
|
||||||
mov si,[bp+CharFlag]
|
mov si,[bp+CharFlag]
|
||||||
mov [si],al ;set new CharFlag
|
mov [si],al ;set new CharFlag
|
||||||
mov bx,[bp+WordCount]
|
mov bx,[bp+WordCount]
|
||||||
|
|
@ -94,15 +95,15 @@ pages: 303-305
|
||||||
ret
|
ret
|
||||||
|
|
||||||
align 2
|
align 2
|
||||||
CountWord:
|
CountWord:
|
||||||
add di,1 ;increment the word count
|
add di,1 ;increment the word count
|
||||||
adc dx,0
|
adc dx,0
|
||||||
dec cx ;count down buffer length
|
dec cx ;count down buffer length
|
||||||
jnz ScanLoop
|
jnz ScanLoop
|
||||||
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
|
||||||
|
|
|
||||||
6
16-04.md
6
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
|
||||||
|
|
|
||||||
54
16-05.md
54
16-05.md
|
|
@ -36,40 +36,41 @@ 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
|
||||||
|
|
||||||
|
|
||||||
COMMENT $
|
COMMENT $
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
——————
|
——————
|
||||||
The idea is to go through the buffer fetching each letter-pair (words
|
The idea is to go through the buffer fetching each letter-pair (words
|
||||||
rather than bytes). The carry flag indicates whether we are
|
rather than bytes). The carry flag indicates whether we are
|
||||||
currently in a (text) word or not. The letter-pair fetched from the
|
currently in a (text) word or not. The letter-pair fetched from the
|
||||||
buffer is converted to a 16-bit address by shifting it left one bit
|
buffer is converted to a 16-bit address by shifting it left one bit
|
||||||
(losing the high bit of the second character) and putting the carry
|
(losing the high bit of the second character) and putting the carry
|
||||||
flag in the low bit. The high bit of the count register is set to
|
flag in the low bit. The high bit of the count register is set to
|
||||||
1. Then the count register is added to the byte found at the given
|
1. Then the count register is added to the byte found at the given
|
||||||
address in a large (64K, naturally) table. The byte at the given
|
address in a large (64K, naturally) table. The byte at the given
|
||||||
address will contain a 1 in the high bit if the last character of the
|
address will contain a 1 in the high bit if the last character of the
|
||||||
letter-pair is a word-letter (alphanumeric or apostrophe). This will
|
letter-pair is a word-letter (alphanumeric or apostrophe). This will
|
||||||
set the carry flag since the high bit of the count register is also a
|
set the carry flag since the high bit of the count register is also a
|
||||||
1. The low bit of the byte found at the given address will be one if
|
1. The low bit of the byte found at the given address will be one if
|
||||||
the second character of the previous letter-pair was a word-letter
|
the second character of the previous letter-pair was a word-letter
|
||||||
and the first character of this letter-pair is not a word-letter. It
|
and the first character of this letter-pair is not a word-letter. It
|
||||||
will also be 1 if the first character of this letter-pair is a
|
will also be 1 if the first character of this letter-pair is a
|
||||||
word-letter but the second character is not. This process is
|
word-letter but the second character is not. This process is
|
||||||
repeated. Finally, the carry flag is saved to indicate the final
|
repeated. Finally, the carry flag is saved to indicate the final
|
||||||
in-a-word/not-in-a-word status. The count register is masked to
|
in-a-word/not-in-a-word status. The count register is masked to
|
||||||
remove the high bit and the count of words remains in the count
|
remove the high bit and the count of words remains in the count
|
||||||
register.
|
register.
|
||||||
Sound complicated? You're right! But it's fast!
|
Sound complicated? You're right! But it's fast!
|
||||||
|
|
||||||
The beauty of this method is that no jumps are required, the
|
The beauty of this method is that no jumps are required, the
|
||||||
operations are fast, it requires only one table and the process can
|
operations are fast, it requires only one table and the process can
|
||||||
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
||||||
jumping.
|
jumping.
|
||||||
|
|
||||||
COMMEND $
|
COMMEND $
|
||||||
.model small
|
.model small
|
||||||
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
||||||
64
16-07.md
64
16-07.md
|
|
@ -12,26 +12,27 @@ pages: 313-316
|
||||||
|
|
||||||
**Listing 16.6 OPT2.ASM**
|
**Listing 16.6 OPT2.ASM**
|
||||||
|
|
||||||
;
|
```nasm
|
||||||
; Opt2 Final optimization word count
|
;
|
||||||
; Written by Michael Abrash
|
; Opt2 Final optimization word count
|
||||||
; Modified by Willem Clements
|
; Written by Michael Abrash
|
||||||
; C/ Moncayo 5, Laurel de la Reina
|
; Modified by Willem Clements
|
||||||
; 18140 La Zubia
|
; C/ Moncayo 5, Laurel de la Reina
|
||||||
; Granada, Spain
|
; 18140 La Zubia
|
||||||
; Tel 34-58-890398
|
; Granada, Spain
|
||||||
; Fax 34-58-224102
|
; Tel 34-58-890398
|
||||||
;
|
; Fax 34-58-224102
|
||||||
parms struc
|
;
|
||||||
|
parms struc
|
||||||
dw 2 dup(?)
|
dw 2 dup(?)
|
||||||
buffer dw ?
|
buffer dw ?
|
||||||
bufferlength dw ?
|
bufferlength dw ?
|
||||||
charflag dw ?
|
charflag dw ?
|
||||||
wordcount dw ?
|
wordcount dw ?
|
||||||
parms ends
|
parms ends
|
||||||
.model small
|
.model small
|
||||||
.data
|
.data
|
||||||
charstatustable label byte
|
charstatustable label byte
|
||||||
rept 2
|
rept 2
|
||||||
db 39 dup(0)
|
db 39 dup(0)
|
||||||
db 1
|
db 1
|
||||||
|
|
@ -45,7 +46,7 @@ pages: 313-316
|
||||||
endm
|
endm
|
||||||
.code
|
.code
|
||||||
public _ScanBuffer
|
public _ScanBuffer
|
||||||
_ScanBuffer proc near
|
_ScanBuffer proc near
|
||||||
push bp
|
push bp
|
||||||
mov bp,sp
|
mov bp,sp
|
||||||
push si
|
push si
|
||||||
|
|
@ -61,18 +62,18 @@ pages: 313-316
|
||||||
cmp al,01h ; check if last one is char
|
cmp al,01h ; check if last one is char
|
||||||
jne scanloop4 ; if not so, search for char
|
jne scanloop4 ; if not so, search for char
|
||||||
jmp scanloop1 ; if so, search for zero
|
jmp scanloop1 ; if so, search for zero
|
||||||
oddentry: xchg al,ah ; last one in ah
|
oddentry: xchg al,ah ; last one in ah
|
||||||
lodsb ; get first byte
|
lodsb ; get first byte
|
||||||
inc cx
|
inc cx
|
||||||
cmp ah,01h ; check if last one was char
|
cmp ah,01h ; check if last one was char
|
||||||
jne scanloop5 ; if not so, search for char
|
jne scanloop5 ; if not so, search for char
|
||||||
jmp scanloop2 ; if so, search for zero
|
jmp scanloop2 ; if so, search for zero
|
||||||
;
|
;
|
||||||
; locate the end of a word
|
; locate the end of a word
|
||||||
scanloop1: lodsw ; get two chars
|
scanloop1: lodsw ; get two chars
|
||||||
xlat ; translate first
|
xlat ; translate first
|
||||||
xchg al,ah ; first in ah
|
xchg al,ah ; first in ah
|
||||||
scanloop2: xlat ; translate second
|
scanloop2: xlat ; translate second
|
||||||
dec cx ; count down
|
dec cx ; count down
|
||||||
jz done1 ; no more bytes left
|
jz done1 ; no more bytes left
|
||||||
cmp ax,0101h ; check if two chars
|
cmp ax,0101h ; check if two chars
|
||||||
|
|
@ -80,12 +81,12 @@ pages: 313-316
|
||||||
inc di ; increase wordcount
|
inc di ; increase wordcount
|
||||||
cmp al,01h ; check if new word started
|
cmp al,01h ; check if new word started
|
||||||
je scanloop1 ; locate end of word
|
je scanloop1 ; locate end of word
|
||||||
;
|
;
|
||||||
; locate the begin of a word
|
; locate the begin of a word
|
||||||
scanloop4: lodsw ; get two chars
|
scanloop4: lodsw ; get two chars
|
||||||
xlat ; translate first
|
xlat ; translate first
|
||||||
xchg al,ah ; first in ah
|
xchg al,ah ; first in ah
|
||||||
scanloop5: xlat ; translate second
|
scanloop5: xlat ; translate second
|
||||||
dec cx ; count down
|
dec cx ; count down
|
||||||
jz done2 ; no more bytes left
|
jz done2 ; no more bytes left
|
||||||
cmp ax,0 ; check if word started
|
cmp ax,0 ; check if word started
|
||||||
|
|
@ -94,14 +95,14 @@ pages: 313-316
|
||||||
je scanloop1 ; if not, locate end of word
|
je scanloop1 ; if not, locate end of word
|
||||||
inc di ; increase wordcount
|
inc di ; increase wordcount
|
||||||
jmp scanloop4 ; locate begin of next word
|
jmp scanloop4 ; locate begin of next word
|
||||||
done1: cmp ax,0101h ; check if end-of-word
|
done1: cmp ax,0101h ; check if end-of-word
|
||||||
je done ; if not, we have finished
|
je done ; if not, we have finished
|
||||||
inc di ; increase wordcount
|
inc di ; increase wordcount
|
||||||
jmp done
|
jmp done
|
||||||
done2: cmp ax,0100h ; check for one-letter word
|
done2: cmp ax,0100h ; check for one-letter word
|
||||||
jne done ; if not, we have finished
|
jne done ; if not, we have finished
|
||||||
inc di ; increase wordcount
|
inc di ; increase wordcount
|
||||||
done: mov si,[bp+charflag]
|
done: mov si,[bp+charflag]
|
||||||
mov [si],al
|
mov [si],al
|
||||||
mov bx,[bp+wordcount]
|
mov bx,[bp+wordcount]
|
||||||
mov ax,[bx]
|
mov ax,[bx]
|
||||||
|
|
@ -114,8 +115,9 @@ pages: 313-316
|
||||||
pop si
|
pop si
|
||||||
pop bp
|
pop bp
|
||||||
ret
|
ret
|
||||||
_ScanBuffer endp
|
_ScanBuffer endp
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Level 2: A New Perspective {#Heading11}
|
### Level 2: A New Perspective {#Heading11}
|
||||||
|
|
||||||
|
|
|
||||||
22
16-08.md
22
16-08.md
|
|
@ -12,7 +12,8 @@ pages: 316-319
|
||||||
|
|
||||||
**Listing 16.7 L16-7.ASM**
|
**Listing 16.7 L16-7.ASM**
|
||||||
|
|
||||||
ScanLoop:
|
```nasm
|
||||||
|
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
|
||||||
xor dl,al ;see if there's a new char/not status
|
xor dl,al ;see if there's a new 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,17 +67,18 @@ report a "location counter overflow" warning; ignore it.)
|
||||||
|
|
||||||
**LISTING 16.8 MAKETAB.C**
|
**LISTING 16.8 MAKETAB.C**
|
||||||
|
|
||||||
// MAKETAB.C — Build QSCAN3.INC for QSCAN3.ASM
|
```c
|
||||||
|
// MAKETAB.C — Build QSCAN3.INC for QSCAN3.ASM
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#define ChType( c ) (((c) & 0x7f) == ‘\'' || isalnum((c) & 0x7f))
|
#define ChType( c ) (((c) & 0x7f) == ‘\'' || isalnum((c) & 0x7f))
|
||||||
|
|
||||||
int NoCarry[ 4 ] = { 0, 0x80, 1, 0x80 };
|
int NoCarry[ 4 ] = { 0, 0x80, 1, 0x80 };
|
||||||
int Carry[ 4 ] = { 1, 0x81, 1, 0x80 };
|
int Carry[ 4 ] = { 1, 0x81, 1, 0x80 };
|
||||||
|
|
||||||
void main( void )
|
void main( void )
|
||||||
{
|
{
|
||||||
int ahChar, alChar, i;
|
int ahChar, alChar, i;
|
||||||
FILE *t = fopen( "QSCAN3.INC", "wt" );
|
FILE *t = fopen( "QSCAN3.INC", "wt" );
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
182
17-02.md
182
17-02.md
|
|
@ -12,33 +12,34 @@ pages: 325-329
|
||||||
|
|
||||||
**LISTING 17.1 L17-1.CPP**
|
**LISTING 17.1 L17-1.CPP**
|
||||||
|
|
||||||
/* C++ Game of Life implementation for any mode for which mode set
|
```cpp
|
||||||
|
/* 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. */
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <iostream.h>
|
#include <iostream.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <dos.h>
|
#include <dos.h>
|
||||||
#include <bios.h>
|
#include <bios.h>
|
||||||
#include <mem.h>
|
#include <mem.h>
|
||||||
|
|
||||||
#define ON_COLOR 15 // on-cell pixel color
|
#define ON_COLOR 15 // on-cell pixel color
|
||||||
#define OFF_COLOR 0 // off-cell pixel color
|
#define OFF_COLOR 0 // off-cell pixel color
|
||||||
#define MSG_LINE 10 // row for text messages
|
#define MSG_LINE 10 // row for text messages
|
||||||
#define GENERATION_LINE 12 // row for generation # display
|
#define GENERATION_LINE 12 // row for generation # display
|
||||||
#define LIMIT_18_HZ 1 // set 1 for maximum frame rate = 18Hz
|
#define LIMIT_18_HZ 1 // set 1 for maximum frame rate = 18Hz
|
||||||
#define WRAP_EDGES 1 // set to 0 to disable wrapping around
|
#define WRAP_EDGES 1 // set to 0 to disable wrapping around
|
||||||
// at cell map edges
|
// at cell map edges
|
||||||
class cellmap {
|
class cellmap {
|
||||||
private:
|
private:
|
||||||
unsigned char *cells;
|
unsigned char *cells;
|
||||||
unsigned int width;
|
unsigned int width;
|
||||||
unsigned int width_in_bytes;
|
unsigned int width_in_bytes;
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
unsigned int length_in_bytes;
|
unsigned int length_in_bytes;
|
||||||
public:
|
public:
|
||||||
cellmap(unsigned int h, unsigned int v);
|
cellmap(unsigned int h, unsigned int v);
|
||||||
~cellmap(void);
|
~cellmap(void);
|
||||||
void copy_cells(cellmap &sourcemap);
|
void copy_cells(cellmap &sourcemap);
|
||||||
|
|
@ -46,24 +47,24 @@ pages: 325-329
|
||||||
void clear_cell(unsigned int x, unsigned int y);
|
void clear_cell(unsigned int x, unsigned int y);
|
||||||
int cell_state(int x, int y);
|
int cell_state(int x, int y);
|
||||||
void next_generation(cellmap& dest_map);
|
void next_generation(cellmap& dest_map);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void enter_display_mode(void);
|
extern void enter_display_mode(void);
|
||||||
extern void exit_display_mode(void);
|
extern void exit_display_mode(void);
|
||||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
extern void draw_pixel(unsigned int X, unsigned int Y,
|
||||||
unsigned int Color);
|
unsigned int Color);
|
||||||
extern void show_text(int x, int y, char *text);
|
extern void show_text(int x, int y, char *text);
|
||||||
|
|
||||||
/* Controls the size of the cell map. Must be within the capabilities
|
/* Controls the size of the cell map. Must be within the capabilities
|
||||||
of the display mode, and must be limited to leave room for text
|
of the display mode, and must be limited to leave room for text
|
||||||
display at right. */
|
display at right. */
|
||||||
unsigned int cellmap_width = 96;
|
unsigned int cellmap_width = 96;
|
||||||
unsigned int cellmap_height = 96;
|
unsigned int cellmap_height = 96;
|
||||||
/* Width & height in pixels of each cell as displayed on screen. */
|
/* Width & height in pixels of each cell as displayed on screen. */
|
||||||
unsigned int magnifier = 2;
|
unsigned int magnifier = 2;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
unsigned int init_length, x, y, seed;
|
unsigned int init_length, x, y, seed;
|
||||||
unsigned long generation = 0;
|
unsigned long generation = 0;
|
||||||
char gen_text[80];
|
char gen_text[80];
|
||||||
|
|
@ -102,85 +103,85 @@ pages: 325-329
|
||||||
current_map.next_generation(next_map);
|
current_map.next_generation(next_map);
|
||||||
// Make current_map current again
|
// Make current_map current again
|
||||||
current_map.copy_cells(next_map);
|
current_map.copy_cells(next_map);
|
||||||
#if LIMIT_18_HZ
|
#if LIMIT_18_HZ
|
||||||
// Limit to a maximum of 18.2 frames per second,for visibility
|
// Limit to a maximum of 18.2 frames per second,for visibility
|
||||||
do {
|
do {
|
||||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||||
} while (start_bios_time == bios_time);
|
} while (start_bios_time == bios_time);
|
||||||
start_bios_time = bios_time;
|
start_bios_time = bios_time;
|
||||||
#endif
|
#endif
|
||||||
} while (!kbhit());
|
} while (!kbhit());
|
||||||
getch(); // clear keypress
|
getch(); // clear keypress
|
||||||
exit_display_mode();
|
exit_display_mode();
|
||||||
cout << "Total generations: " << generation << "\nSeed: " <<
|
cout << "Total generations: " << generation << "\nSeed: " <<
|
||||||
seed << "\n";
|
seed << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cellmap constructor. */
|
/* cellmap constructor. */
|
||||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||||
{
|
{
|
||||||
width = w;
|
width = w;
|
||||||
width_in_bytes = (w + 7) / 8;
|
width_in_bytes = (w + 7) / 8;
|
||||||
height = h;
|
height = h;
|
||||||
length_in_bytes = width_in_bytes * h;
|
length_in_bytes = width_in_bytes * h;
|
||||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
cells = new unsigned char[length_in_bytes]; // cell storage
|
||||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cellmap destructor. */
|
/* cellmap destructor. */
|
||||||
cellmap::~cellmap(void)
|
cellmap::~cellmap(void)
|
||||||
{
|
{
|
||||||
delete[] cells;
|
delete[] cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copies one cellmap's cells to another cellmap. Both cellmaps are
|
/* Copies one cellmap's cells to another cellmap. Both cellmaps are
|
||||||
assumed to be the same size. */
|
assumed to be the same size. */
|
||||||
void cellmap::copy_cells(cellmap &sourcemap)
|
void cellmap::copy_cells(cellmap &sourcemap)
|
||||||
{
|
{
|
||||||
memcpy(cells, sourcemap.cells, length_in_bytes);
|
memcpy(cells, sourcemap.cells, length_in_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns cell on. */
|
/* Turns cell on. */
|
||||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr =
|
unsigned char *cell_ptr =
|
||||||
cells + (y * width_in_bytes) + (x / 8);
|
cells + (y * width_in_bytes) + (x / 8);
|
||||||
|
|
||||||
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns cell off. */
|
/* Turns cell off. */
|
||||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr =
|
unsigned char *cell_ptr =
|
||||||
cells + (y * width_in_bytes) + (x / 8);
|
cells + (y * width_in_bytes) + (x / 8);
|
||||||
|
|
||||||
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns cell state (1=on or 0=off), optionally wrapping at the
|
/* Returns cell state (1=on or 0=off), optionally wrapping at the
|
||||||
borders around to the opposite edge. */
|
borders around to the opposite edge. */
|
||||||
int cellmap::cell_state(int x, int y)
|
int cellmap::cell_state(int x, int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr;
|
unsigned char *cell_ptr;
|
||||||
|
|
||||||
#if WRAP_EDGES
|
#if WRAP_EDGES
|
||||||
while (x < 0) x += width; // wrap, if necessary
|
while (x < 0) x += width; // wrap, if necessary
|
||||||
while (x >= width) x -= width;
|
while (x >= width) x -= width;
|
||||||
while (y < 0) y += height;
|
while (y < 0) y += height;
|
||||||
while (y >= height) y -= height;
|
while (y >= height) y -= height;
|
||||||
#else
|
#else
|
||||||
if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
|
if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
|
||||||
return 0; // return 0 for off edges if no wrapping
|
return 0; // return 0 for off edges if no wrapping
|
||||||
#endif
|
#endif
|
||||||
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
|
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
|
||||||
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculates the next generation of a cellmap and stores it in
|
/* Calculates the next generation of a cellmap and stores it in
|
||||||
next_map. */
|
next_map. */
|
||||||
void cellmap::next_generation(cellmap& next_map)
|
void cellmap::next_generation(cellmap& next_map)
|
||||||
{
|
{
|
||||||
unsigned int x, y, neighbor_count;
|
unsigned int x, y, neighbor_count;
|
||||||
|
|
||||||
for (y=0; y<height; y++) {
|
for (y=0; y<height; y++) {
|
||||||
|
|
@ -205,27 +206,29 @@ pages: 325-329
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 17.2 L17-2.CPP**
|
**LISTING 17.2 L17-2.CPP**
|
||||||
|
|
||||||
/* VGA mode 13h functions for Game of Life.
|
```cpp
|
||||||
|
/* VGA mode 13h functions for Game of Life.
|
||||||
Tested with Borland C++. */
|
Tested with Borland C++. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <dos.h>
|
#include <dos.h>
|
||||||
|
|
||||||
#define TEXT_X_OFFSET 27
|
#define TEXT_X_OFFSET 27
|
||||||
#define SCREEN_WIDTH_IN_BYTES 320
|
#define SCREEN_WIDTH_IN_BYTES 320
|
||||||
|
|
||||||
/* Width & height in pixels of each cell. */
|
/* Width & height in pixels of each cell. */
|
||||||
extern unsigned int magnifier;
|
extern unsigned int magnifier;
|
||||||
|
|
||||||
/* Mode 13h draw pixel function. Pixels are of width & height
|
/* Mode 13h draw pixel function. Pixels are of width & height
|
||||||
specified by magnifier. */
|
specified by magnifier. */
|
||||||
void draw_pixel(unsigned int x, unsigned int y, unsigned int color)
|
void draw_pixel(unsigned int x, unsigned int y, unsigned int color)
|
||||||
{
|
{
|
||||||
#define SCREEN_SEGMENT 0xA000
|
#define SCREEN_SEGMENT 0xA000
|
||||||
unsigned char far *screen_ptr;
|
unsigned char far *screen_ptr;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
|
@ -238,30 +241,31 @@ pages: 325-329
|
||||||
}
|
}
|
||||||
screen_ptr += SCREEN_WIDTH_IN_BYTES;
|
screen_ptr += SCREEN_WIDTH_IN_BYTES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mode 13h mode-set function. */
|
/* Mode 13h mode-set function. */
|
||||||
void enter_display_mode()
|
void enter_display_mode()
|
||||||
{
|
{
|
||||||
union REGS regset;
|
union REGS regset;
|
||||||
|
|
||||||
regset.x.ax = 0x0013;
|
regset.x.ax = 0x0013;
|
||||||
int86(0x10, ®set, ®set);
|
int86(0x10, ®set, ®set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Text mode mode-set function. */
|
/* Text mode mode-set function. */
|
||||||
void exit_display_mode()
|
void exit_display_mode()
|
||||||
{
|
{
|
||||||
union REGS regset;
|
union REGS regset;
|
||||||
|
|
||||||
regset.x.ax = 0x0003;
|
regset.x.ax = 0x0003;
|
||||||
int86(0x10, ®set, ®set);
|
int86(0x10, ®set, ®set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Text display function. Offsets text to non-graphics area of
|
/* Text display function. Offsets text to non-graphics area of
|
||||||
screen. */
|
screen. */
|
||||||
void show_text(int x, int y, char *text)
|
void show_text(int x, int y, char *text)
|
||||||
{
|
{
|
||||||
gotoxy(TEXT_X_OFFSET + x, y);
|
gotoxy(TEXT_X_OFFSET + x, y);
|
||||||
puts(text);
|
puts(text);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
82
17-04.md
82
17-04.md
|
|
@ -48,19 +48,20 @@ improvement.
|
||||||
|
|
||||||
**LISTING 17.3 L17-3.CPP**
|
**LISTING 17.3 L17-3.CPP**
|
||||||
|
|
||||||
/* cellmap class definition, constructor, copy_cells(), set_cell(),
|
```cpp
|
||||||
|
/* 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.
|
||||||
Otherwise, the same as Listing 17.1 */
|
Otherwise, the same as Listing 17.1 */
|
||||||
|
|
||||||
class cellmap {
|
class cellmap {
|
||||||
private:
|
private:
|
||||||
unsigned char *cells;
|
unsigned char *cells;
|
||||||
unsigned int width;
|
unsigned int width;
|
||||||
unsigned int width_in_bytes;
|
unsigned int width_in_bytes;
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
unsigned int length_in_bytes;
|
unsigned int length_in_bytes;
|
||||||
public:
|
public:
|
||||||
cellmap(unsigned int h, unsigned int v);
|
cellmap(unsigned int h, unsigned int v);
|
||||||
~cellmap(void);
|
~cellmap(void);
|
||||||
void copy_cells(cellmap &sourcemap);
|
void copy_cells(cellmap &sourcemap);
|
||||||
|
|
@ -69,12 +70,12 @@ improvement.
|
||||||
int cell_state(int x, int y);
|
int cell_state(int x, int y);
|
||||||
int count_neighbors(int x, int y);
|
int count_neighbors(int x, int y);
|
||||||
void next_generation(cellmap& dest_map);
|
void next_generation(cellmap& dest_map);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* cellmap constructor. Pads around cell storage area with 1 extra
|
/* cellmap constructor. Pads around cell storage area with 1 extra
|
||||||
byte, used for handling edge wrapping. */
|
byte, used for handling edge wrapping. */
|
||||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||||
{
|
{
|
||||||
width = w;
|
width = w;
|
||||||
width_in_bytes = ((w + 7) / 8) + 2; // pad each side with
|
width_in_bytes = ((w + 7) / 8) + 2; // pad each side with
|
||||||
// 1 extra byte
|
// 1 extra byte
|
||||||
|
|
@ -83,71 +84,71 @@ improvement.
|
||||||
// with 1 extra byte
|
// with 1 extra byte
|
||||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
cells = new unsigned char[length_in_bytes]; // cell storage
|
||||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copies one cellmap's cells to another cellmap. If wrapping is
|
/* Copies one cellmap's cells to another cellmap. If wrapping is
|
||||||
enabled, copies edge (wrap) bytes into opposite padding bytes in
|
enabled, copies edge (wrap) bytes into opposite padding bytes in
|
||||||
source first, so that the padding bytes off each edge have the
|
source first, so that the padding bytes off each edge have the
|
||||||
same values as would be found by wrapping around to the opposite
|
same values as would be found by wrapping around to the opposite
|
||||||
edge. Both cellmaps are assumed to be the same size. */
|
edge. Both cellmaps are assumed to be the same size. */
|
||||||
void cellmap::copy_cells(cellmap &sourcemap)
|
void cellmap::copy_cells(cellmap &sourcemap)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr;
|
unsigned char *cell_ptr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
#if WRAP_EDGES
|
#if WRAP_EDGES
|
||||||
// Copy left and right edges into padding bytes on right and left
|
// Copy left and right edges into padding bytes on right and left
|
||||||
cell_ptr = sourcemap.cells + width_in_bytes;
|
cell_ptr = sourcemap.cells + width_in_bytes;
|
||||||
for (i=0; i<height; i++) {
|
for (i=0; i<height; i++) {
|
||||||
*cell_ptr = *(cell_ptr + width_in_bytes - 2);
|
*cell_ptr = *(cell_ptr + width_in_bytes - 2);
|
||||||
*(cell_ptr + width_in_bytes - 1) = *(cell_ptr + 1);
|
*(cell_ptr + width_in_bytes - 1) = *(cell_ptr + 1);
|
||||||
cell_ptr += width_in_bytes;
|
cell_ptr += width_in_bytes;
|
||||||
}
|
}
|
||||||
// Copy top and bottom edges into padding bytes on bottom and top
|
// Copy top and bottom edges into padding bytes on bottom and top
|
||||||
memcpy(sourcemap.cells, sourcemap.cells + length_in_bytes -
|
memcpy(sourcemap.cells, sourcemap.cells + length_in_bytes -
|
||||||
(width_in_bytes * 2), width_in_bytes);
|
(width_in_bytes * 2), width_in_bytes);
|
||||||
memcpy(sourcemap.cells + length_in_bytes - width_in_bytes,
|
memcpy(sourcemap.cells + length_in_bytes - width_in_bytes,
|
||||||
sourcemap.cells + width_in_bytes, width_in_bytes);
|
sourcemap.cells + width_in_bytes, width_in_bytes);
|
||||||
#endif
|
#endif
|
||||||
// Copy all cells to the destination
|
// Copy all cells to the destination
|
||||||
memcpy(cells, sourcemap.cells, length_in_bytes);
|
memcpy(cells, sourcemap.cells, length_in_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns cell on. x and y are offset by 1 byte down and to the right, to compensate for the
|
/* Turns cell on. x and y are offset by 1 byte down and to the right, to compensate for the
|
||||||
padding bytes around the cellmap. */
|
padding bytes around the cellmap. */
|
||||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr =
|
unsigned char *cell_ptr =
|
||||||
cells + ((y + 1) * width_in_bytes) + ((x / 8) + 1);
|
cells + ((y + 1) * width_in_bytes) + ((x / 8) + 1);
|
||||||
|
|
||||||
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns cell off. x and y are offset by 1 byte down and to the right,
|
/* Turns cell off. x and y are offset by 1 byte down and to the right,
|
||||||
to compensate for the padding bytes around the cell map. */
|
to compensate for the padding bytes around the cell map. */
|
||||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr =
|
unsigned char *cell_ptr =
|
||||||
cells + ((y + 1) * width_in_bytes) + ((x / 8) + 1);
|
cells + ((y + 1) * width_in_bytes) + ((x / 8) + 1);
|
||||||
|
|
||||||
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns cell state (1=on or 0=off). x and y are offset by 1 byte
|
/* Returns cell state (1=on or 0=off). x and y are offset by 1 byte
|
||||||
down and to the right, to
|
down and to the right, to
|
||||||
compensate for the padding bytes around
|
compensate for the padding bytes around
|
||||||
the cell map. */
|
the cell map. */
|
||||||
int cellmap::cell_state(int x, int y)
|
int cellmap::cell_state(int x, int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr =
|
unsigned char *cell_ptr =
|
||||||
cells + ((y + 1) * width_in_bytes) + ((x / 8) + 1);
|
cells + ((y + 1) * width_in_bytes) + ((x / 8) + 1);
|
||||||
|
|
||||||
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Counts the number of neighboring on-cells for specified cell. */
|
/* Counts the number of neighboring on-cells for specified cell. */
|
||||||
int cellmap::count_neighbors(int x, int y)
|
int cellmap::count_neighbors(int x, int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr, mask;
|
unsigned char *cell_ptr, mask;
|
||||||
unsigned int neighbor_count;
|
unsigned int neighbor_count;
|
||||||
|
|
||||||
|
|
@ -184,12 +185,12 @@ improvement.
|
||||||
if ((*(cell_ptr + (width_in_bytes * 2)) & mask)) neighbor_count++;
|
if ((*(cell_ptr + (width_in_bytes * 2)) & mask)) neighbor_count++;
|
||||||
|
|
||||||
return neighbor_count;
|
return neighbor_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculates the next generation of current_map and stores it in
|
/* Calculates the next generation of current_map and stores it in
|
||||||
next_map. */
|
next_map. */
|
||||||
void cellmap::next_generation(cellmap& next_map)
|
void cellmap::next_generation(cellmap& next_map)
|
||||||
{
|
{
|
||||||
unsigned int x, y, neighbor_count;
|
unsigned int x, y, neighbor_count;
|
||||||
|
|
||||||
for (y=0; y<height; y++) {
|
for (y=0; y<height; y++) {
|
||||||
|
|
@ -208,4 +209,5 @@ improvement.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
14
17-05.md
14
17-05.md
|
|
@ -51,14 +51,15 @@ Listing 17.3.
|
||||||
|
|
||||||
**LISTING 17.4 L17-4.CPP**
|
**LISTING 17.4 L17-4.CPP**
|
||||||
|
|
||||||
/* next_generation(), implemented using fast, all-in-one hard-wired
|
```cpp
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
/* Calculates the next generation of current_map and stores it in
|
/* Calculates the next generation of current_map and stores it in
|
||||||
next_map. */
|
next_map. */
|
||||||
void cellmap::next_generation(cellmap& next_map)
|
void cellmap::next_generation(cellmap& next_map)
|
||||||
{
|
{
|
||||||
unsigned int x, y, neighbor_count;
|
unsigned int x, y, neighbor_count;
|
||||||
unsigned int width_in_bytesX2 = width_in_bytes << 1;
|
unsigned int width_in_bytesX2 = width_in_bytes << 1;
|
||||||
unsigned char *cell_ptr, *current_cell_ptr, mask, current_mask;
|
unsigned char *cell_ptr, *current_cell_ptr, mask, current_mask;
|
||||||
|
|
@ -83,7 +84,7 @@ Listing 17.3.
|
||||||
if ((*(cell_ptr + width_in_bytes) & mask)) neighbor_count++;
|
if ((*(cell_ptr + width_in_bytes) & mask)) neighbor_count++;
|
||||||
// Count lower left neighbor
|
// Count lower left neighbor
|
||||||
if ((*(cell_ptr + width_in_bytesX2) & mask))
|
if ((*(cell_ptr + width_in_bytesX2) & mask))
|
||||||
neighbor_count++;
|
neighbor_count++;
|
||||||
// Point to upper neighbor
|
// Point to upper neighbor
|
||||||
if ((mask >>= 1) == 0) {
|
if ((mask >>= 1) == 0) {
|
||||||
mask = 0x80;
|
mask = 0x80;
|
||||||
|
|
@ -130,7 +131,8 @@ 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
|
||||||
|
|
|
||||||
128
17-07.md
128
17-07.md
|
|
@ -12,36 +12,37 @@ pages: 340-345
|
||||||
|
|
||||||
**LISTING 17.5 L17-5.CPP**
|
**LISTING 17.5 L17-5.CPP**
|
||||||
|
|
||||||
/* C++ Game of Life implementation for any mode for which mode set
|
```cpp
|
||||||
|
/* 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;
|
||||||
this allows very fast next-state determination. Edges always wrap
|
this allows very fast next-state determination. Edges always wrap
|
||||||
in this implementation.
|
in this implementation.
|
||||||
Tested with Borland C++. To run, link with Listing 17.2
|
Tested with Borland C++. To run, link with Listing 17.2
|
||||||
in the large model. */
|
in the large model. */
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <iostream.h>
|
#include <iostream.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <dos.h>
|
#include <dos.h>
|
||||||
#include <bios.h>
|
#include <bios.h>
|
||||||
#include <mem.h>
|
#include <mem.h>
|
||||||
|
|
||||||
#define ON_COLOR 15 // on-cell pixel color
|
#define ON_COLOR 15 // on-cell pixel color
|
||||||
#define OFF_COLOR 0 // off-cell pixel color
|
#define OFF_COLOR 0 // off-cell pixel color
|
||||||
#define MSG_LINE 10 // row for text messages
|
#define MSG_LINE 10 // row for text messages
|
||||||
#define GENERATION_LINE 12 // row for generation # display
|
#define GENERATION_LINE 12 // row for generation # display
|
||||||
#define LIMIT_18_HZ 0 // set 1 to to maximum frame rate = 18Hz
|
#define LIMIT_18_HZ 0 // set 1 to to maximum frame rate = 18Hz
|
||||||
|
|
||||||
class cellmap {
|
class cellmap {
|
||||||
private:
|
private:
|
||||||
unsigned char *cells;
|
unsigned char *cells;
|
||||||
unsigned char *temp_cells;
|
unsigned char *temp_cells;
|
||||||
unsigned int width;
|
unsigned int width;
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
unsigned int length_in_bytes;
|
unsigned int length_in_bytes;
|
||||||
public:
|
public:
|
||||||
cellmap(unsigned int h, unsigned int v);
|
cellmap(unsigned int h, unsigned int v);
|
||||||
~cellmap(void);
|
~cellmap(void);
|
||||||
void set_cell(unsigned int x, unsigned int y);
|
void set_cell(unsigned int x, unsigned int y);
|
||||||
|
|
@ -50,28 +51,28 @@ pages: 340-345
|
||||||
int count_neighbors(int x, int y);
|
int count_neighbors(int x, int y);
|
||||||
void next_generation(void);
|
void next_generation(void);
|
||||||
void init(void);
|
void init(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void enter_display_mode(void);
|
extern void enter_display_mode(void);
|
||||||
extern void exit_display_mode(void);
|
extern void exit_display_mode(void);
|
||||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
extern void draw_pixel(unsigned int X, unsigned int Y,
|
||||||
unsigned int Color);
|
unsigned int Color);
|
||||||
extern void show_text(int x, int y, char *text);
|
extern void show_text(int x, int y, char *text);
|
||||||
|
|
||||||
/* Controls the size of the cell map. Must be within the capabilities
|
/* Controls the size of the cell map. Must be within the capabilities
|
||||||
of the display mode, and must be limited to leave room for text
|
of the display mode, and must be limited to leave room for text
|
||||||
display at right. */
|
display at right. */
|
||||||
unsigned int cellmap_width = 96;
|
unsigned int cellmap_width = 96;
|
||||||
unsigned int cellmap_height = 96;
|
unsigned int cellmap_height = 96;
|
||||||
|
|
||||||
/* Width & height in pixels of each cell. */
|
/* Width & height in pixels of each cell. */
|
||||||
unsigned int magnifier = 2;
|
unsigned int magnifier = 2;
|
||||||
|
|
||||||
/* Randomizing seed */
|
/* Randomizing seed */
|
||||||
unsigned int seed;
|
unsigned int seed;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
unsigned long generation = 0;
|
unsigned long generation = 0;
|
||||||
char gen_text[80];
|
char gen_text[80];
|
||||||
long bios_time, start_bios_time;
|
long bios_time, start_bios_time;
|
||||||
|
|
@ -92,24 +93,24 @@ pages: 340-345
|
||||||
show_text(1, GENERATION_LINE, gen_text);
|
show_text(1, GENERATION_LINE, gen_text);
|
||||||
// Recalculate and draw the next generation
|
// Recalculate and draw the next generation
|
||||||
current_map.next_generation();
|
current_map.next_generation();
|
||||||
#if LIMIT_18_HZ
|
#if LIMIT_18_HZ
|
||||||
// Limit to a maximum of 18.2 frames per second, for visibility
|
// Limit to a maximum of 18.2 frames per second, for visibility
|
||||||
do {
|
do {
|
||||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||||
} while (start_bios_time == bios_time);
|
} while (start_bios_time == bios_time);
|
||||||
start_bios_time = bios_time;
|
start_bios_time = bios_time;
|
||||||
#endif
|
#endif
|
||||||
} while (!kbhit());
|
} while (!kbhit());
|
||||||
|
|
||||||
getch(); // clear keypress
|
getch(); // clear keypress
|
||||||
exit_display_mode();
|
exit_display_mode();
|
||||||
cout << "Total generations: " << generation << "\nSeed: " <<
|
cout << "Total generations: " << generation << "\nSeed: " <<
|
||||||
seed << "\n";
|
seed << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cellmap constructor. */
|
/* cellmap constructor. */
|
||||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||||
{
|
{
|
||||||
width = w;
|
width = w;
|
||||||
height = h;
|
height = h;
|
||||||
length_in_bytes = w * h;
|
length_in_bytes = w * h;
|
||||||
|
|
@ -120,19 +121,19 @@ pages: 340-345
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cellmap destructor. */
|
/* cellmap destructor. */
|
||||||
cellmap::~cellmap(void)
|
cellmap::~cellmap(void)
|
||||||
{
|
{
|
||||||
delete[] cells;
|
delete[] cells;
|
||||||
delete[] temp_cells;
|
delete[] temp_cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns an off-cell on, incrementing the on-neighbor count for the
|
/* Turns an off-cell on, incrementing the on-neighbor count for the
|
||||||
eight neighboring cells. */
|
eight neighboring cells. */
|
||||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||||
{
|
{
|
||||||
unsigned int w = width, h = height;
|
unsigned int w = width, h = height;
|
||||||
int xoleft, xoright, yoabove, yobelow;
|
int xoleft, xoright, yoabove, yobelow;
|
||||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
unsigned char *cell_ptr = cells + (y * w) + x;
|
||||||
|
|
@ -165,12 +166,12 @@ pages: 340-345
|
||||||
*(cell_ptr + yobelow + xoleft) += 2;
|
*(cell_ptr + yobelow + xoleft) += 2;
|
||||||
*(cell_ptr + yobelow) += 2;
|
*(cell_ptr + yobelow) += 2;
|
||||||
*(cell_ptr + yobelow + xoright) += 2;
|
*(cell_ptr + yobelow + xoright) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns an on-cell off, decrementing the on-neighbor count for the
|
/* Turns an on-cell off, decrementing the on-neighbor count for the
|
||||||
eight neighboring cells. */
|
eight neighboring cells. */
|
||||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||||
{
|
{
|
||||||
unsigned int w = width, h = height;
|
unsigned int w = width, h = height;
|
||||||
int xoleft, xoright, yoabove, yobelow;
|
int xoleft, xoright, yoabove, yobelow;
|
||||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
unsigned char *cell_ptr = cells + (y * w) + x;
|
||||||
|
|
@ -203,20 +204,20 @@ pages: 340-345
|
||||||
*(cell_ptr + yobelow + xoleft) -= 2;
|
*(cell_ptr + yobelow + xoleft) -= 2;
|
||||||
*(cell_ptr + yobelow) -= 2;
|
*(cell_ptr + yobelow) -= 2;
|
||||||
*(cell_ptr + yobelow + xoright) -= 2;
|
*(cell_ptr + yobelow + xoright) -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns cell state (1=on or 0=off). */
|
/* Returns cell state (1=on or 0=off). */
|
||||||
int cellmap::cell_state(int x, int y)
|
int cellmap::cell_state(int x, int y)
|
||||||
{
|
{
|
||||||
unsigned char *cell_ptr;
|
unsigned char *cell_ptr;
|
||||||
|
|
||||||
cell_ptr = cells + (y * width) + x;
|
cell_ptr = cells + (y * width) + x;
|
||||||
return *cell_ptr & 0x01;
|
return *cell_ptr & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculates and displays the next generation of current_map */
|
/* Calculates and displays the next generation of current_map */
|
||||||
void cellmap::next_generation()
|
void cellmap::next_generation()
|
||||||
{
|
{
|
||||||
unsigned int x, y, count;
|
unsigned int x, y, count;
|
||||||
unsigned int h = height, w = width;
|
unsigned int h = height, w = width;
|
||||||
unsigned char *cell_ptr, *row_cell_ptr;
|
unsigned char *cell_ptr, *row_cell_ptr;
|
||||||
|
|
@ -257,13 +258,13 @@ pages: 340-345
|
||||||
// Advance to the next cell
|
// Advance to the next cell
|
||||||
cell_ptr++; // advance to the next cell byte
|
cell_ptr++; // advance to the next cell byte
|
||||||
} while (++x < w);
|
} while (++x < w);
|
||||||
RowDone:
|
RowDone:
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Randomly initializes the cellmap to about 50% on-pixels. */
|
/* Randomly initializes the cellmap to about 50% on-pixels. */
|
||||||
void cellmap::init()
|
void cellmap::init()
|
||||||
{
|
{
|
||||||
unsigned int x, y, init_length;
|
unsigned int x, y, init_length;
|
||||||
|
|
||||||
// Get the seed; seed randomly if 0 entered
|
// Get the seed; seed randomly if 0 entered
|
||||||
|
|
@ -284,4 +285,5 @@ pages: 340-345
|
||||||
set_cell(x, y);
|
set_cell(x, y);
|
||||||
}
|
}
|
||||||
} while (—init_length);
|
} while (—init_length);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
54
18-03.md
54
18-03.md
|
|
@ -12,30 +12,33 @@ pages: 352-361
|
||||||
|
|
||||||
**LISTING 18.1 BUILD.BAT**
|
**LISTING 18.1 BUILD.BAT**
|
||||||
|
|
||||||
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 lcomp.c
|
```bat
|
||||||
lcomp > qlife.asm
|
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 lcomp.c
|
||||||
tasmx /mx /kh30000 qlife
|
lcomp > qlife.asm
|
||||||
bcc -v -D%1=%2;%2=%3;%3=%4;%4=%5;%5=%6;%6=%7;%7=%8;%8 qlife.obj main.c video.c
|
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
|
||||||
|
```
|
||||||
|
|
||||||
**LISTING 18.2 LCOMP.C**
|
**LISTING 18.2 LCOMP.C**
|
||||||
|
|
||||||
// LCOMP.C
|
```c
|
||||||
//
|
// LCOMP.C
|
||||||
// Life compiler, ver 1.3
|
//
|
||||||
//
|
// Life compiler, ver 1.3
|
||||||
// David Stafford
|
//
|
||||||
//
|
// David Stafford
|
||||||
|
//
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "life.h"
|
#include "life.h"
|
||||||
|
|
||||||
#define LIST_LIMIT (46 * 138) // when we need to use es:
|
#define LIST_LIMIT (46 * 138) // when we need to use es:
|
||||||
|
|
||||||
int Old, New, Edge, Label;
|
int Old, New, Edge, Label;
|
||||||
char Buf[ 20 ];
|
char Buf[ 20 ];
|
||||||
|
|
||||||
void Next1( void )
|
void Next1( void )
|
||||||
{
|
{
|
||||||
char *Seg = "";
|
char *Seg = "";
|
||||||
|
|
||||||
|
|
@ -48,7 +51,7 @@ pages: 352-361
|
||||||
printf( "jmp dx\n" );
|
printf( "jmp dx\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Next2( void )
|
void Next2( void )
|
||||||
{
|
{
|
||||||
printf( "mov bp,es:[si]\n" );
|
printf( "mov bp,es:[si]\n" );
|
||||||
printf( "add si,2\n" );
|
printf( "add si,2\n" );
|
||||||
|
|
@ -57,7 +60,7 @@ pages: 352-361
|
||||||
printf( "jmp dx\n" );
|
printf( "jmp dx\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildMaps( void )
|
void BuildMaps( void )
|
||||||
{
|
{
|
||||||
unsigned short i, j, Size, x = 0, y, N1, N2, N3, C1, C2, C3;
|
unsigned short i, j, Size, x = 0, y, N1, N2, N3, C1, C2, C3;
|
||||||
|
|
||||||
|
|
@ -165,7 +168,7 @@ pages: 352-361
|
||||||
printf( "ends\n\n" );
|
printf( "ends\n\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetUpAndDown( void )
|
void GetUpAndDown( void )
|
||||||
{
|
{
|
||||||
printf( "mov ax,[bp+_RowColMap-_CellMap]\n" );
|
printf( "mov ax,[bp+_RowColMap-_CellMap]\n" );
|
||||||
printf( "or ah,ah\n" );
|
printf( "or ah,ah\n" );
|
||||||
|
|
@ -179,7 +182,7 @@ pages: 352-361
|
||||||
printf( "D%d:\n", Label );
|
printf( "D%d:\n", Label );
|
||||||
}
|
}
|
||||||
|
|
||||||
void FirstPass( void )
|
void FirstPass( void )
|
||||||
{
|
{
|
||||||
char *Op;
|
char *Op;
|
||||||
unsigned short UpDown = 0;
|
unsigned short UpDown = 0;
|
||||||
|
|
@ -334,7 +337,7 @@ pages: 352-361
|
||||||
Next1();
|
Next1();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Test( char *Offset, char *Str )
|
void Test( char *Offset, char *Str )
|
||||||
{
|
{
|
||||||
printf( "mov bx,[bp+%s]\n", Offset );
|
printf( "mov bx,[bp+%s]\n", Offset );
|
||||||
printf( "cmp bh,[bx]\n" );
|
printf( "cmp bh,[bx]\n" );
|
||||||
|
|
@ -342,7 +345,7 @@ pages: 352-361
|
||||||
printf( "%s%d:\n", Str, Label );
|
printf( "%s%d:\n", Str, Label );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fix( char *Offset, char *Str, int JumpBack )
|
void Fix( char *Offset, char *Str, int JumpBack )
|
||||||
{
|
{
|
||||||
printf( "FIX_%s%d:\n", Str, Label );
|
printf( "FIX_%s%d:\n", Str, Label );
|
||||||
printf( "mov bh,[bx]\n" );
|
printf( "mov bh,[bx]\n" );
|
||||||
|
|
@ -356,7 +359,7 @@ pages: 352-361
|
||||||
if( JumpBack ) printf( "jmp short %s%d\n", Str, Label );
|
if( JumpBack ) printf( "jmp short %s%d\n", Str, Label );
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecondPass( void )
|
void SecondPass( void )
|
||||||
{
|
{
|
||||||
printf( "org 0%02x00h\n",
|
printf( "org 0%02x00h\n",
|
||||||
(Edge << 7) + (New << 4) + (Old << 1) + 1 );
|
(Edge << 7) + (New << 4) + (Old << 1) + 1 );
|
||||||
|
|
@ -459,7 +462,7 @@ pages: 352-361
|
||||||
Next2();
|
Next2();
|
||||||
}
|
}
|
||||||
|
|
||||||
void main( void )
|
void main( void )
|
||||||
{
|
{
|
||||||
char *Seg = "ds";
|
char *Seg = "ds";
|
||||||
|
|
||||||
|
|
@ -512,3 +515,4 @@ pages: 352-361
|
||||||
|
|
||||||
printf( "LIFE ends\nend\n" );
|
printf( "LIFE ends\nend\n" );
|
||||||
}
|
}
|
||||||
|
```
|
||||||
106
18-04.md
106
18-04.md
|
|
@ -12,24 +12,25 @@ pages: 361-365
|
||||||
|
|
||||||
**LISTING 18.3 MAIN.C**
|
**LISTING 18.3 MAIN.C**
|
||||||
|
|
||||||
// MAIN.C
|
```c
|
||||||
//
|
// MAIN.C
|
||||||
// David Stafford
|
//
|
||||||
//
|
// David Stafford
|
||||||
|
//
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <bios.h>
|
#include <bios.h>
|
||||||
#include "life.h"
|
#include "life.h"
|
||||||
|
|
||||||
// functions in VIDEO.C
|
// functions in VIDEO.C
|
||||||
void enter_display_mode( void );
|
void enter_display_mode( void );
|
||||||
void exit_display_mode( void );
|
void exit_display_mode( void );
|
||||||
void show_text( int x, int y, char *text );
|
void show_text( int x, int y, char *text );
|
||||||
|
|
||||||
void InitCellmap( void )
|
void InitCellmap( void )
|
||||||
{
|
{
|
||||||
unsigned int i, j, t, x, y, init;
|
unsigned int i, j, t, x, y, init;
|
||||||
|
|
||||||
|
|
@ -52,7 +53,7 @@ pages: 361-365
|
||||||
NextGen(); // Set cell states, prime the pump.
|
NextGen(); // Set cell states, prime the pump.
|
||||||
}
|
}
|
||||||
|
|
||||||
void main( void )
|
void main( void )
|
||||||
{
|
{
|
||||||
unsigned long generation = 0;
|
unsigned long generation = 0;
|
||||||
char gen_text[ 80 ];
|
char gen_text[ 80 ];
|
||||||
|
|
@ -102,65 +103,70 @@ 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**
|
||||||
|
|
||||||
/* VGA mode 13h functions for Game of Life.
|
```c
|
||||||
|
/* VGA mode 13h functions for Game of Life.
|
||||||
Tested with Borland C++. */
|
Tested with Borland C++. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <dos.h>
|
#include <dos.h>
|
||||||
|
|
||||||
#define TEXT_X_OFFSET 28
|
#define TEXT_X_OFFSET 28
|
||||||
#define SCREEN_WIDTH_IN_BYTES 320
|
#define SCREEN_WIDTH_IN_BYTES 320
|
||||||
|
|
||||||
#define SCREEN_SEGMENT 0xA000
|
#define SCREEN_SEGMENT 0xA000
|
||||||
|
|
||||||
/* Mode 13h mode-set function. */
|
/* Mode 13h mode-set function. */
|
||||||
void enter_display_mode()
|
void enter_display_mode()
|
||||||
{
|
{
|
||||||
union REGS regset;
|
union REGS regset;
|
||||||
|
|
||||||
regset.x.ax = 0x0013;
|
regset.x.ax = 0x0013;
|
||||||
int86(0x10, ®set, ®set);
|
int86(0x10, ®set, ®set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Text mode mode-set function. */
|
/* Text mode mode-set function. */
|
||||||
void exit_display_mode()
|
void exit_display_mode()
|
||||||
{
|
{
|
||||||
union REGS regset;
|
union REGS regset;
|
||||||
|
|
||||||
regset.x.ax = 0x0003;
|
regset.x.ax = 0x0003;
|
||||||
int86(0x10, ®set, ®set);
|
int86(0x10, ®set, ®set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Text display function. Offsets text to non-graphics area of
|
/* Text display function. Offsets text to non-graphics area of
|
||||||
screen. */
|
screen. */
|
||||||
void show_text(int x, int y, char *text)
|
void show_text(int x, int y, char *text)
|
||||||
{
|
{
|
||||||
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**
|
||||||
|
|
||||||
void far NextGen( void );
|
```c
|
||||||
|
void far NextGen( void );
|
||||||
|
|
||||||
extern unsigned short CellMap[];
|
extern unsigned short CellMap[];
|
||||||
extern unsigned short far ChangeList1[];
|
extern unsigned short far ChangeList1[];
|
||||||
|
|
||||||
#define LEFT (-2)
|
#define LEFT (-2)
|
||||||
#define RIGHT (+2)
|
#define RIGHT (+2)
|
||||||
#define UP (WIDTH * LEFT)
|
#define UP (WIDTH * LEFT)
|
||||||
#define DOWN (WIDTH * RIGHT)
|
#define DOWN (WIDTH * RIGHT)
|
||||||
#define UPPERLEFT (UP + LEFT)
|
#define UPPERLEFT (UP + LEFT)
|
||||||
#define UPPERRIGHT (UP + RIGHT)
|
#define UPPERRIGHT (UP + RIGHT)
|
||||||
#define LOWERLEFT (DOWN + LEFT)
|
#define LOWERLEFT (DOWN + LEFT)
|
||||||
#define LOWERRIGHT (DOWN + RIGHT)
|
#define LOWERRIGHT (DOWN + RIGHT)
|
||||||
#define WRAPLEFT (RIGHT * (WIDTH - 1))
|
#define WRAPLEFT (RIGHT * (WIDTH - 1))
|
||||||
#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}
|
||||||
|
|
||||||
|
|
|
||||||
8
18-05.md
8
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:
|
||||||
|
|
||||||
mov dh,[bp+1]
|
```nasm
|
||||||
or dh,1
|
mov dh,[bp+1]
|
||||||
jmp dx
|
or dh,1
|
||||||
|
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
|
||||||
|
|
|
||||||
20
19-02.md
20
19-02.md
|
|
@ -102,13 +102,15 @@ 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
|
||||||
|
|
||||||
add edx,4 ;U-pipe cycle 1
|
```nasm
|
||||||
mov ecx,[ebx] ;V-pipe cycle 1
|
add edx,4 ;U-pipe cycle 1
|
||||||
add ebx,4 ;U-pipe cycle 2
|
mov ecx,[ebx] ;V-pipe cycle 1
|
||||||
mov [edx],ecx ;V-pipe cycle 3
|
add ebx,4 ;U-pipe cycle 2
|
||||||
|
mov [edx],ecx ;V-pipe cycle 3
|
||||||
; 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
|
||||||
|
|
||||||
mov ecx,[ebx] ;U-pipe cycle 1
|
```nasm
|
||||||
add ebx,4 ;V-pipe cycle 1
|
mov ecx,[ebx] ;U-pipe cycle 1
|
||||||
mov [edx+4],ecx ;U-pipe cycle 2
|
add ebx,4 ;V-pipe cycle 1
|
||||||
add edx,4 ;V-pipe cycle 2
|
mov [edx+4],ecx ;U-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
|
||||||
|
|
|
||||||
6
19-03.md
6
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
|
||||||
|
|
||||||
mov ebx,3
|
```nasm
|
||||||
|
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
|
||||||
|
|
|
||||||
66
20-02.md
66
20-02.md
|
|
@ -40,43 +40,45 @@ 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.
|
||||||
|
|
||||||
MOV reg,reg (1 cycle)
|
```nasm
|
||||||
|
MOV reg,reg (1 cycle)
|
||||||
mem,reg (1 cycle)
|
mem,reg (1 cycle)
|
||||||
reg,mem (1 cycle)
|
reg,mem (1 cycle)
|
||||||
reg,immediate (1 cycle)
|
reg,immediate (1 cycle)
|
||||||
mem,immediate (1 cycle)†
|
mem,immediate (1 cycle)†
|
||||||
|
|
||||||
AND/OR/XOR/ADD/SUB reg,reg (1 cycle)
|
AND/OR/XOR/ADD/SUB reg,reg (1 cycle)
|
||||||
mem,reg (3 cycles)
|
mem,reg (3 cycles)
|
||||||
reg,mem (2 cycles)
|
reg,mem (2 cycles)
|
||||||
reg,immediate (1 cycle)
|
reg,immediate (1 cycle)
|
||||||
mem,immediate (3 cycles)†
|
mem,immediate (3 cycles)†
|
||||||
|
|
||||||
INC/DEC reg (1 cycle)
|
INC/DEC reg (1 cycle)
|
||||||
mem (3 cycles)
|
mem (3 cycles)
|
||||||
|
|
||||||
CMP reg,reg (1 cycle)
|
CMP reg,reg (1 cycle)
|
||||||
mem,reg (2 cycles)
|
mem,reg (2 cycles)
|
||||||
reg,mem (2 cycles)
|
reg,mem (2 cycles)
|
||||||
reg,immediate (1 cycle)
|
reg,immediate (1 cycle)
|
||||||
mem,immediate (2 cycles)†
|
mem,immediate (2 cycles)†
|
||||||
|
|
||||||
TEST reg,reg (1 cycle)
|
TEST reg,reg (1 cycle)
|
||||||
EAX,immediate (1 cycle)
|
EAX,immediate (1 cycle)
|
||||||
|
|
||||||
PUSH/POP reg (1 cycle)
|
PUSH/POP reg (1 cycle)
|
||||||
immediate (1 cycle)
|
immediate (1 cycle)
|
||||||
|
|
||||||
LEA reg,mem (1 cycle)
|
LEA reg,mem (1 cycle)
|
||||||
|
|
||||||
JCC near (1 cycle if predicted correctly;
|
JCC near (1 cycle if predicted correctly;
|
||||||
5 cycles otherwise in V-pipe,
|
5 cycles otherwise in V-pipe,
|
||||||
4 cycles otherwise in U-pipe)
|
4 cycles otherwise in U-pipe)
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
**Table 20.1 Instructions that can execute in the V-pipe.**
|
**Table 20.1 Instructions that can execute in the V-pipe.**
|
||||||
|
|
||||||
|
|
@ -100,40 +102,42 @@ 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.
|
||||||
|
|
||||||
MOV reg,reg (1 cycle)
|
```nasm
|
||||||
|
MOV reg,reg (1 cycle)
|
||||||
mem,reg (1 cycle)
|
mem,reg (1 cycle)
|
||||||
reg,mem (1 cycle)
|
reg,mem (1 cycle)
|
||||||
reg,immediate (1 cycle)
|
reg,immediate (1 cycle)
|
||||||
mem,immediate (1 cycle)†
|
mem,immediate (1 cycle)†
|
||||||
|
|
||||||
AND/OR/XOR/ADD/SUB/ADC/SBB reg,reg (1 cycle)
|
AND/OR/XOR/ADD/SUB/ADC/SBB reg,reg (1 cycle)
|
||||||
mem,reg (3 cycles)
|
mem,reg (3 cycles)
|
||||||
reg,mem (2 cycles)
|
reg,mem (2 cycles)
|
||||||
reg,immediate (1 cycle)
|
reg,immediate (1 cycle)
|
||||||
mem,immediate (3 cycles)†
|
mem,immediate (3 cycles)†
|
||||||
|
|
||||||
INC/DEC reg (1 cycle)
|
INC/DEC reg (1 cycle)
|
||||||
mem (3 cycles)
|
mem (3 cycles)
|
||||||
|
|
||||||
CMP reg,reg (1 cycle)
|
CMP reg,reg (1 cycle)
|
||||||
mem,reg (2 cycles)
|
mem,reg (2 cycles)
|
||||||
reg,mem (2 cycles)
|
reg,mem (2 cycles)
|
||||||
reg,immediate (1 cycle)
|
reg,immediate (1 cycle)
|
||||||
mem,immediate (2 cycles)†
|
mem,immediate (2 cycles)†
|
||||||
|
|
||||||
TEST reg,reg (1 cycle)
|
TEST reg,reg (1 cycle)
|
||||||
EAX,immediate (1 cycle)
|
EAX,immediate (1 cycle)
|
||||||
|
|
||||||
PUSH/POP reg (1 cycle)
|
PUSH/POP reg (1 cycle)
|
||||||
immediate (1 cycle)
|
immediate (1 cycle)
|
||||||
|
|
||||||
LEA reg,mem (1 cycle)
|
LEA reg,mem (1 cycle)
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
push dword ptr [esi]
|
```nasm
|
||||||
|
push dword ptr [esi]
|
||||||
|
```
|
||||||
|
|
||||||
is one byte smaller than this sequence:
|
is one byte smaller than this sequence:
|
||||||
|
|
||||||
mov eax,[esi]
|
```nasm
|
||||||
push eax
|
mov eax,[esi]
|
||||||
|
push eax
|
||||||
|
```
|
||||||
|
|
||||||

|
cycle.*](images/20-03.jpg)
|
||||||
|
|
||||||
A more telling example is the following
|
A more telling example is the following
|
||||||
|
|
||||||
add [MemVar],eax
|
```nasm
|
||||||
|
add [MemVar],eax
|
||||||
|
```
|
||||||
|
|
||||||
versus the equivalent:
|
versus the equivalent:
|
||||||
|
|
||||||
mov edx,[MemVar]
|
```nasm
|
||||||
add edx,eax
|
mov edx,[MemVar]
|
||||||
mov [MemVar],edx
|
add edx,eax
|
||||||
|
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
|
||||||
|
|
|
||||||
24
20-03.md
24
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
|
||||||
|
|
||||||
mov bl,[esi]
|
```nasm
|
||||||
mov bh,[esi+1]
|
mov bl,[esi]
|
||||||
|
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:
|
||||||
|
|
||||||
mov bl,[esi]
|
```nasm
|
||||||
mov edi,edx
|
mov bl,[esi]
|
||||||
mov bh,[esi+1]
|
mov edi,edx
|
||||||
|
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
|
||||||
|
|
||||||
mov bx,[esi]
|
```nasm
|
||||||
|
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:
|
||||||
|
|
||||||
add esi,[SourceSkip] ;U-pipe cycles 1 and 2
|
```nasm
|
||||||
add edi,[DestinationSkip] ;V-pipe cycles 1 and 2
|
add esi,[SourceSkip] ;U-pipe cycles 1 and 2
|
||||||
|
add edi,[DestinationSkip] ;V-pipe cycles 1 and 2
|
||||||
|
```
|
||||||
|
|
|
||||||
12
20-04.md
12
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
|
||||||
|
|
||||||
and [ebx],al
|
```nasm
|
||||||
|
and [ebx],al
|
||||||
|
```
|
||||||
|
|
||||||
into
|
into
|
||||||
|
|
||||||
mov dl,[ebx]
|
```nasm
|
||||||
and dl,al
|
mov dl,[ebx]
|
||||||
mov [ebx],dl
|
and dl,al
|
||||||
|
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,
|
||||||
|
|
|
||||||
18
21-01.md
18
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
|
||||||
|
|
||||||
mov [ebx],eax
|
```nasm
|
||||||
|
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,14 +103,16 @@ 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:
|
||||||
|
|
||||||
push ebx ;U-pipe cycle 1
|
```nasm
|
||||||
mov ebx,[Ptr] ;V-pipe cycle 1
|
push ebx ;U-pipe cycle 1
|
||||||
and ebx,ebx ;U-pipe cycle 2
|
mov ebx,[Ptr] ;V-pipe cycle 1
|
||||||
jz short IsNull ;V-pipe cycle 2
|
and ebx,ebx ;U-pipe cycle 2
|
||||||
mov eax,[ebx] ;U-pipe cycle 3 AGI stall
|
jz short IsNull ;V-pipe cycle 2
|
||||||
mov edx,[ebp-8] ;V-pipe cycle 3 lockstep idle
|
mov eax,[ebx] ;U-pipe cycle 3 AGI stall
|
||||||
|
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
|
||||||
|
|
|
||||||
70
21-02.md
70
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
|
||||||
|
|
||||||
sub esp,100h
|
```nasm
|
||||||
|
sub esp,100h
|
||||||
|
```
|
||||||
|
|
||||||
for example, or with the popular
|
for example, or with the popular
|
||||||
|
|
||||||
mov esp,ebp
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
moveax,[esp+20h]
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov dword ptr [ebp+16],1
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
sub eax,eax ;U-pipe 1
|
```nasm
|
||||||
|
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:
|
||||||
|
|
||||||
mov [MemVar1],0 ;U-pipe 1
|
```nasm
|
||||||
mov [MemVar2],0 ;U-pipe 2
|
mov [MemVar1],0 ;U-pipe 1
|
||||||
|
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
|
||||||
|
|
||||||
inc eax ;U-pipe cycle 1
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
sub eax,eax ;U-pipe cycle 1
|
```nasm
|
||||||
|
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
|
||||||
|
|
||||||
mov eax,edx ;U-pipe cycle 1
|
```nasm
|
||||||
sub edx,edxX ;V-pipe cycle 1
|
mov eax,edx ;U-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:
|
||||||
|
|
||||||
mov eax,[MemVar] ;U-pipe cycle 1
|
```nasm
|
||||||
push esi ;V-pipe cycle 1
|
mov eax,[MemVar] ;U-pipe cycle 1
|
||||||
push eax ;U-pipe cycle 2
|
push esi ;V-pipe cycle 1
|
||||||
push edi ;V-pipe cycle 2
|
push eax ;U-pipe cycle 2
|
||||||
push ebx ;U-pipe cycle 3
|
push edi ;V-pipe cycle 2
|
||||||
call FooTilde ;V-pipe cycle 3
|
push ebx ;U-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:
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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,7 +181,8 @@ 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:
|
||||||
|
|
||||||
LoopTop:
|
```nasm
|
||||||
|
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
|
||||||
add esi,4 ;U-pipe cycle 2
|
add esi,4 ;U-pipe cycle 2
|
||||||
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
||||||
28
21-03.md
28
21-03.md
|
|
@ -40,22 +40,23 @@ right?
|
||||||
|
|
||||||
**LISTING 21.1 L21-1.ASM**
|
**LISTING 21.1 L21-1.ASM**
|
||||||
|
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
```nasm
|
||||||
; starting at ESI, of length ECX words.
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; Returns checksum in AX.
|
; starting at ESI, of length ECX words.
|
||||||
; ECX and ESI destroyed.
|
; Returns checksum in AX.
|
||||||
; All cycle counts assume 32-bit protected mode.
|
; ECX and ESI destroyed.
|
||||||
; Assumes buffer length > 0.
|
; All cycle counts assume 32-bit protected mode.
|
||||||
; Note that timing indicates that the pipe sequence and
|
; Assumes buffer length > 0.
|
||||||
; cycle counts shown (based on documented execution rules)
|
; Note that timing indicates that the pipe sequence and
|
||||||
; differ from the actual execution sequence and cycle counts;
|
; cycle counts shown (based on documented execution rules)
|
||||||
; this loop has been measured to execute in 5 cycles; apparently,
|
; differ from the actual execution sequence and cycle counts;
|
||||||
; the 1st half of ADD somehow pairs with the prefix byte, or the
|
; this loop has been measured to execute in 5 cycles; apparently,
|
||||||
; refix byte gets executed ahead of time.
|
; the 1st half of ADD somehow pairs with the prefix byte, or the
|
||||||
|
; refix byte gets executed ahead of time.
|
||||||
|
|
||||||
sub ax,ax ;initialize the checksum
|
sub ax,ax ;initialize the checksum
|
||||||
|
|
||||||
ckloop:
|
ckloop:
|
||||||
add ax,[esi] ;cycle 1 U-pipe prefix byte
|
add ax,[esi] ;cycle 1 U-pipe prefix byte
|
||||||
;cycle 1 V-pipe idle (no pairing w/prefix)
|
;cycle 1 V-pipe idle (no pairing w/prefix)
|
||||||
;cycle 2 U-pipe 1st half of ADD
|
;cycle 2 U-pipe 1st half of ADD
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
40
21-04.md
40
21-04.md
|
|
@ -27,12 +27,13 @@ same cache data bank, as discussed in the last chapter).
|
||||||
|
|
||||||
**LISTING 21.2 L21-2.ASM**
|
**LISTING 21.2 L21-2.ASM**
|
||||||
|
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
```nasm
|
||||||
; starting at ESI, of length ECX words.
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; Returns checksum in AX.
|
; starting at ESI, of length ECX words.
|
||||||
; High word of EAX, DX, ECX and ESI destroyed.
|
; Returns checksum in AX.
|
||||||
; All cycle counts assume 32-bit protected mode.
|
; High word of EAX, DX, ECX and ESI destroyed.
|
||||||
; Assumes buffer length > 0.
|
; All cycle counts assume 32-bit protected mode.
|
||||||
|
; Assumes buffer length > 0.
|
||||||
|
|
||||||
sub eax,eax ;initialize the checksum
|
sub eax,eax ;initialize the checksum
|
||||||
mov dx,[esi] ;first word to checksum
|
mov dx,[esi] ;first word to checksum
|
||||||
|
|
@ -40,7 +41,7 @@ same cache data bank, as discussed in the last chapter).
|
||||||
jz short ckloopend ;only 1 checksum to do
|
jz short ckloopend ;only 1 checksum to do
|
||||||
add esi,2 ;point to the next word to checksum
|
add esi,2 ;point to the next word to checksum
|
||||||
|
|
||||||
ckloop:
|
ckloop:
|
||||||
add al,dl ;cycle 1 U-pipe
|
add al,dl ;cycle 1 U-pipe
|
||||||
mov dl,[esi] ;cycle 1 V-pipe
|
mov dl,[esi] ;cycle 1 V-pipe
|
||||||
adc ah,dh ;cycle 2 U-pipe
|
adc ah,dh ;cycle 2 U-pipe
|
||||||
|
|
@ -50,9 +51,10 @@ same cache data bank, as discussed in the last chapter).
|
||||||
dec ecx ;cycle 4 U-pipe
|
dec ecx ;cycle 4 U-pipe
|
||||||
jnz ckloop ;cycle 4 V-pipe
|
jnz ckloop ;cycle 4 V-pipe
|
||||||
|
|
||||||
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,12 +70,13 @@ placement of **ADD ESI,4** to avoid an AGI.
|
||||||
|
|
||||||
**LISTING 21.3 L21-3.ASM**
|
**LISTING 21.3 L21-3.ASM**
|
||||||
|
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
```nasm
|
||||||
; starting at ESI, of length ECX words.
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; Returns checksum in AX.
|
; starting at ESI, of length ECX words.
|
||||||
; High word of EAX, BX, EDX, ECX and ESI destroyed.
|
; Returns checksum in AX.
|
||||||
; All cycle counts assume 32-bit protected mode.
|
; High word of EAX, BX, EDX, ECX and ESI destroyed.
|
||||||
; Assumes buffer length > 0.
|
; All cycle counts assume 32-bit protected mode.
|
||||||
|
; Assumes buffer length > 0.
|
||||||
|
|
||||||
sub eax,eax ;initialize the checksum
|
sub eax,eax ;initialize the checksum
|
||||||
sub edx,edx ;prepare for later ORing
|
sub edx,edx ;prepare for later ORing
|
||||||
|
|
@ -82,13 +85,13 @@ placement of **ADD ESI,4** to avoid an AGI.
|
||||||
mov ax,[esi] ;do the odd word
|
mov ax,[esi] ;do the odd word
|
||||||
jz short ckloopdone ;no more words to checksum
|
jz short ckloopdone ;no more words to checksum
|
||||||
add esi,2 ;point to the next word
|
add esi,2 ;point to the next word
|
||||||
ckloopsetup:
|
ckloopsetup:
|
||||||
mov dx,[esi] ;load most of 1st word to
|
mov dx,[esi] ;load most of 1st word to
|
||||||
mov bl,[esi+2] ; checksum (last byte loaded in loop)
|
mov bl,[esi+2] ; checksum (last byte loaded in loop)
|
||||||
dec ecx ;any more dwords to checksum?
|
dec ecx ;any more dwords to checksum?
|
||||||
jz short ckloopend ;no
|
jz short ckloopend ;no
|
||||||
|
|
||||||
ckloop:
|
ckloop:
|
||||||
mov bh,[esi+3] ;cycle 1 U-pipe
|
mov bh,[esi+3] ;cycle 1 U-pipe
|
||||||
add esi,4 ;cycle 1 V-pipe
|
add esi,4 ;cycle 1 V-pipe
|
||||||
shl ebx,16 ;cycle 2 U-pipe
|
shl ebx,16 ;cycle 2 U-pipe
|
||||||
|
|
@ -103,7 +106,7 @@ placement of **ADD ESI,4** to avoid an AGI.
|
||||||
dec ecx ;cycle 6 U-pipe
|
dec ecx ;cycle 6 U-pipe
|
||||||
jnz ckloop ;cycle 6 V-pipe
|
jnz ckloop ;cycle 6 V-pipe
|
||||||
|
|
||||||
ckloopend:
|
ckloopend:
|
||||||
mov bh,[esi+3] ;checksum the last dword
|
mov bh,[esi+3] ;checksum the last dword
|
||||||
add ax,dx
|
add ax,dx
|
||||||
adc ax,bx
|
adc ax,bx
|
||||||
|
|
@ -113,7 +116,8 @@ placement of **ADD ESI,4** to avoid an AGI.
|
||||||
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
|
||||||
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
|
||||||
|
|
|
||||||
44
21-05.md
44
21-05.md
|
|
@ -12,13 +12,14 @@ pages: 409-411
|
||||||
|
|
||||||
**LISTING 21.4 L21-4.ASM**
|
**LISTING 21.4 L21-4.ASM**
|
||||||
|
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
```nasm
|
||||||
; starting at ESI, of length ECX words.
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; Returns checksum in AX.
|
; starting at ESI, of length ECX words.
|
||||||
; High word of EAX, ECX, EDX, and ESI destroyed.
|
; Returns checksum in AX.
|
||||||
; All cycle counts assume 32-bit protected mode.
|
; High word of EAX, ECX, EDX, and ESI destroyed.
|
||||||
; Assumes buffer starts on a dword boundary, is a dword multiple
|
; All cycle counts assume 32-bit protected mode.
|
||||||
; in length, and length > 0.
|
; Assumes buffer starts on a dword boundary, is a dword multiple
|
||||||
|
; in length, and length > 0.
|
||||||
|
|
||||||
sub eax,eax ;initialize the checksum
|
sub eax,eax ;initialize the checksum
|
||||||
shr ecx,1 ;we'll do two words per loop
|
shr ecx,1 ;we'll do two words per loop
|
||||||
|
|
@ -27,7 +28,7 @@ pages: 409-411
|
||||||
dec ecx ;we'll do 1 checksum outside the loop
|
dec ecx ;we'll do 1 checksum outside the loop
|
||||||
jz short ckloopend ;only 1 checksum to do
|
jz short ckloopend ;only 1 checksum to do
|
||||||
|
|
||||||
ckloop:
|
ckloop:
|
||||||
add eax,edx ;cycle 1 U-pipe
|
add eax,edx ;cycle 1 U-pipe
|
||||||
mov edx,[esi] ;cycle 1 V-pipe
|
mov edx,[esi] ;cycle 1 V-pipe
|
||||||
adc eax,0 ;cycle 2 U-pipe
|
adc eax,0 ;cycle 2 U-pipe
|
||||||
|
|
@ -35,13 +36,14 @@ pages: 409-411
|
||||||
dec ecx ;cycle 3 U-pipe
|
dec ecx ;cycle 3 U-pipe
|
||||||
jnz ckloop ;cycle 3 V-pipe
|
jnz ckloop ;cycle 3 V-pipe
|
||||||
|
|
||||||
ckloopend:
|
ckloopend:
|
||||||
add eax,edx ;checksum the last dword
|
add eax,edx ;checksum the last dword
|
||||||
adc eax,0
|
adc eax,0
|
||||||
mov edx,eax ;compress the 32-bit checksum
|
mov edx,eax ;compress the 32-bit checksum
|
||||||
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,13 +55,14 @@ more registers.
|
||||||
|
|
||||||
**LISTING 21.5 L21-5.ASM**
|
**LISTING 21.5 L21-5.ASM**
|
||||||
|
|
||||||
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
```nasm
|
||||||
; starting at ESI, of length ECX words.
|
; Calculates TCP/IP (16-bit carry-wrapping) checksum for buffer
|
||||||
; Returns checksum in AX.
|
; starting at ESI, of length ECX words.
|
||||||
; High word of EAX, EBX, ECX, EDX, and ESI destroyed.
|
; Returns checksum in AX.
|
||||||
; All cycle counts assume 32-bit protected mode.
|
; High word of EAX, EBX, ECX, EDX, and ESI destroyed.
|
||||||
; Assumes buffer starts on a dword boundary, is a dword multiple
|
; All cycle counts assume 32-bit protected mode.
|
||||||
; in length, and length > 0.
|
; Assumes buffer starts on a dword boundary, is a dword multiple
|
||||||
|
; in length, and length > 0.
|
||||||
|
|
||||||
sub eax,eax ;initialize the checksum
|
sub eax,eax ;initialize the checksum
|
||||||
shr ecx,2 ;we'll do two dwords per loop
|
shr ecx,2 ;we'll do two dwords per loop
|
||||||
|
|
@ -67,14 +70,14 @@ more registers.
|
||||||
mov eax,[esi] ;checksum the odd dword
|
mov eax,[esi] ;checksum the odd dword
|
||||||
jz short ckloopdone ;no, done
|
jz short ckloopdone ;no, done
|
||||||
add esi,4 ;point to the next dword
|
add esi,4 ;point to the next dword
|
||||||
noodddword:
|
noodddword:
|
||||||
mov edx,[esi] ;preload the first dword
|
mov edx,[esi] ;preload the first dword
|
||||||
mov ebx,[esi+4] ;preload the second dword
|
mov ebx,[esi+4] ;preload the second dword
|
||||||
dec ecx ;we'll do 1 checksum outside the loop
|
dec ecx ;we'll do 1 checksum outside the loop
|
||||||
jz short ckloopend ;only 1 checksum to do
|
jz short ckloopend ;only 1 checksum to do
|
||||||
add esi,8 ;point to the next dword
|
add esi,8 ;point to the next dword
|
||||||
|
|
||||||
ckloop:
|
ckloop:
|
||||||
add eax,edx ;cycle 1 U-pipe
|
add eax,edx ;cycle 1 U-pipe
|
||||||
mov edx,[esi] ;cycle 1 V-pipe
|
mov edx,[esi] ;cycle 1 V-pipe
|
||||||
adc eax,ebx ;cycle 2 U-pipe
|
adc eax,ebx ;cycle 2 U-pipe
|
||||||
|
|
@ -84,15 +87,16 @@ more registers.
|
||||||
dec ecx ;cycle 4 U-pipe
|
dec ecx ;cycle 4 U-pipe
|
||||||
jnz ckloop ;cycle 4 V-pipe
|
jnz ckloop ;cycle 4 V-pipe
|
||||||
|
|
||||||
ckloopend:
|
ckloopend:
|
||||||
add eax,edx ;checksum the last two dwords
|
add eax,edx ;checksum the last two dwords
|
||||||
adc eax,ebx
|
adc eax,ebx
|
||||||
adc eax,0
|
adc eax,0
|
||||||
ckloopdone:
|
ckloopdone:
|
||||||
mov edx,eax ;compress the 32-bit checksum
|
mov edx,eax ;compress the 32-bit checksum
|
||||||
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
|
||||||
|
|
|
||||||
32
22-01.md
32
22-01.md
|
|
@ -55,25 +55,26 @@ are mine.
|
||||||
|
|
||||||
**LISTING 22.1 L22-1.ASM**
|
**LISTING 22.1 L22-1.ASM**
|
||||||
|
|
||||||
OnStack struc ;data that's stored on the stack after PUSH BP
|
```nasm
|
||||||
OldBP dw ? ;caller's BP
|
OnStack struc ;data that's stored on the stack after PUSH BP
|
||||||
RetAddr dw ? ;return address
|
OldBP dw ? ;caller's BP
|
||||||
Filler dw ? ;character to fill the buffer with
|
RetAddr dw ? ;return address
|
||||||
Attrib dw ? ;attribute to fill the buffer with
|
Filler dw ? ;character to fill the buffer with
|
||||||
BufSize dw ? ;number of character/attribute pairs to fill
|
Attrib dw ? ;attribute to fill the buffer with
|
||||||
BufOfs dw ? ;buffer offset
|
BufSize dw ? ;number of character/attribute pairs to fill
|
||||||
BufSeg dw ? ;buffer segment
|
BufOfs dw ? ;buffer offset
|
||||||
EndMrk db ? ;marker for the end of the stack frame
|
BufSeg dw ? ;buffer segment
|
||||||
OnStack ends
|
EndMrk db ? ;marker for the end of the stack frame
|
||||||
;
|
OnStack ends
|
||||||
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
|
||||||
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
||||||
jne Start ; pointer is passed
|
jne Start ; pointer is passed
|
||||||
cmp word ptr [bp].BufOfs,0
|
cmp word ptr [bp].BufOfs,0
|
||||||
je Bye
|
je Bye
|
||||||
Start: cld ;make STOSW count up
|
Start: cld ;make STOSW count up
|
||||||
mov ax,[bp].Attrib ;load AX with attribute parameter
|
mov ax,[bp].Attrib ;load AX with attribute parameter
|
||||||
and ax,0ff00h ;prepare for merging with fill char
|
and ax,0ff00h ;prepare for merging with fill char
|
||||||
mov bx,[bp].Filler ;load BX with fill char
|
mov bx,[bp].Filler ;load BX with fill char
|
||||||
|
|
@ -85,10 +86,11 @@ are mine.
|
||||||
mov es,bx
|
mov es,bx
|
||||||
mov cx,[bp].BufSize ;load CX with buffer size
|
mov cx,[bp].BufSize ;load CX with buffer size
|
||||||
rep stosw ;fill the buffer
|
rep stosw ;fill the buffer
|
||||||
Bye:mov sp,bp ;restore original stack pointer
|
Bye:mov sp,bp ;restore original stack pointer
|
||||||
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
|
||||||
|
|
|
||||||
20
22-02.md
20
22-02.md
|
|
@ -12,14 +12,15 @@ pages: 417-418
|
||||||
|
|
||||||
**LISTING 22.2 L22-2.ASM**
|
**LISTING 22.2 L22-2.ASM**
|
||||||
|
|
||||||
ClearS proc near
|
```nasm
|
||||||
|
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
|
||||||
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
||||||
jne Start ; pointer is passed
|
jne Start ; pointer is passed
|
||||||
cmp word ptr [bp].BufOfs,0
|
cmp word ptr [bp].BufOfs,0
|
||||||
je Bye
|
je Bye
|
||||||
Start: cld ;make STOSW count up
|
Start: cld ;make STOSW count up
|
||||||
mov ax,[bp].Attrib ;load AX with attribute parameter
|
mov ax,[bp].Attrib ;load AX with attribute parameter
|
||||||
and ax,0ff00h ;prepare for merging with fill char
|
and ax,0ff00h ;prepare for merging with fill char
|
||||||
mov bx,[bp].Filler ;load BX with fill char
|
mov bx,[bp].Filler ;load BX with fill char
|
||||||
|
|
@ -29,10 +30,11 @@ pages: 417-418
|
||||||
mov es,[bp].BufSeg ;load ES with target buffer segment
|
mov es,[bp].BufSeg ;load ES with target buffer segment
|
||||||
mov cx,[bp].BufSize ;load CX with buffer size
|
mov cx,[bp].BufSize ;load CX with buffer size
|
||||||
rep stosw ;fill the buffer
|
rep stosw ;fill the buffer
|
||||||
Bye:
|
Bye:
|
||||||
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,14 +49,15 @@ loading ES and DI as shown in Listing 22.3.
|
||||||
|
|
||||||
**LISTING 22.3 L22-3.ASM**
|
**LISTING 22.3 L22-3.ASM**
|
||||||
|
|
||||||
ClearS proc near
|
```nasm
|
||||||
|
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
|
||||||
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
||||||
jne Start ; pointer is passed
|
jne Start ; pointer is passed
|
||||||
cmp word ptr [bp].BufOfs,0
|
cmp word ptr [bp].BufOfs,0
|
||||||
je Bye
|
je Bye
|
||||||
Start: cld ;make STOSW count up
|
Start: cld ;make STOSW count up
|
||||||
mov ax,[bp].Attrib ;load AX with attribute parameter
|
mov ax,[bp].Attrib ;load AX with attribute parameter
|
||||||
and ax,0ff00h ;prepare for merging with fill char
|
and ax,0ff00h ;prepare for merging with fill char
|
||||||
mov bx,[bp].Filler ;load BX with fill char
|
mov bx,[bp].Filler ;load BX with fill char
|
||||||
|
|
@ -64,10 +67,11 @@ loading ES and DI as shown in Listing 22.3.
|
||||||
;segment:offset
|
;segment:offset
|
||||||
mov cx,[bp].BufSize ;load CX with buffer size
|
mov cx,[bp].BufSize ;load CX with buffer size
|
||||||
rep stosw ;fill the buffer
|
rep stosw ;fill the buffer
|
||||||
Bye:
|
Bye:
|
||||||
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.
|
||||||
|
|
|
||||||
28
22-03.md
28
22-03.md
|
|
@ -12,23 +12,25 @@ pages: 419-420
|
||||||
|
|
||||||
**LISTING 22.5 L22-5.ASM**
|
**LISTING 22.5 L22-5.ASM**
|
||||||
|
|
||||||
ClearS proc near
|
```nasm
|
||||||
|
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
|
||||||
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
cmp word ptr [bp].BufSeg,0 ;skip the fill if a null
|
||||||
jne Start ; pointer is passed
|
jne Start ; pointer is passed
|
||||||
cmp word ptr [bp].BufOfs,0
|
cmp word ptr [bp].BufOfs,0
|
||||||
je Bye
|
je Bye
|
||||||
Start: cld ;make STOSW count up
|
Start: cld ;make STOSW count up
|
||||||
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
|
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
|
||||||
mov al,byte ptr [bp].Filler ;load AL with fill char
|
mov al,byte ptr [bp].Filler ;load AL with fill char
|
||||||
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer segment:offset
|
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer segment:offset
|
||||||
mov cx,[bp].BufSize ;load CX with buffer size
|
mov cx,[bp].BufSize ;load CX with buffer size
|
||||||
rep stosw ;fill the buffer
|
rep stosw ;fill the buffer
|
||||||
Bye:
|
Bye:
|
||||||
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,22 +46,24 @@ shown in Listing 22.6.
|
||||||
|
|
||||||
**LISTING 22.6 L22-6.ASM**
|
**LISTING 22.6 L22-6.ASM**
|
||||||
|
|
||||||
ClearS proc near
|
```nasm
|
||||||
|
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
|
||||||
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer;segment:offset
|
les di,dword ptr [bp].BufOfs ;load ES:DI with target buffer;segment:offset
|
||||||
mov ax,es ;put segment where we can test it
|
mov ax,es ;put segment where we can test it
|
||||||
or ax,di ;is it a null pointer?
|
or ax,di ;is it a null pointer?
|
||||||
je Bye ;yes, so we're done
|
je Bye ;yes, so we're done
|
||||||
Start: cld ;make STOSW count up
|
Start: cld ;make STOSW count up
|
||||||
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
|
mov ah,byte ptr [bp].Attrib[1];load AH with attribute
|
||||||
mov al,byte ptr [bp].Filler ;load AL with fill char
|
mov al,byte ptr [bp].Filler ;load AL with fill char
|
||||||
mov cx,[bp].BufSize ;load CX with buffer size
|
mov cx,[bp].BufSize ;load CX with buffer size
|
||||||
rep stosw ;fill the buffer
|
rep stosw ;fill the buffer
|
||||||
Bye:
|
Bye:
|
||||||
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,7 +107,8 @@ 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**
|
||||||
|
|
||||||
ClearS procnear
|
```nasm
|
||||||
|
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
|
||||||
pop bx ;get the attribute
|
pop bx ;get the attribute
|
||||||
|
|
@ -116,9 +121,10 @@ With that problem dealt with, Listing 22.7 shows the Zenned version of
|
||||||
je Bye ;yes, so we're done
|
je Bye ;yes, so we're done
|
||||||
cld ;make STOSW count up
|
cld ;make STOSW count up
|
||||||
rep stosw ;do the string store
|
rep stosw ;do the string store
|
||||||
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
|
||||||
|
|
|
||||||
474
23-03.md
474
23-03.md
|
|
@ -72,61 +72,62 @@ seen the features in action.
|
||||||
|
|
||||||
**LISTING 23.1 L23-1.ASM**
|
**LISTING 23.1 L23-1.ASM**
|
||||||
|
|
||||||
; Sample VGA program.
|
```nasm
|
||||||
; Animates four balls bouncing around a playfield by using
|
; Sample VGA program.
|
||||||
; page flipping. Playfield is panned smoothly both horizontally
|
; Animates four balls bouncing around a playfield by using
|
||||||
; and vertically.
|
; page flipping. Playfield is panned smoothly both horizontally
|
||||||
; By Michael Abrash.
|
; and vertically.
|
||||||
;
|
; By Michael Abrash.
|
||||||
stack segment para stack 'STACK'
|
;
|
||||||
|
stack segment para stack 'STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
MEDRES_VIDEO_MODE equ 0 ;define for 640x350 video mode
|
MEDRES_VIDEO_MODE equ 0 ;define for 640x350 video mode
|
||||||
; comment out for 640x200 mode
|
; comment out for 640x200 mode
|
||||||
VIDEO_SEGMENT equ 0a000h ;display memory segment for
|
VIDEO_SEGMENT equ 0a000h ;display memory segment for
|
||||||
; true VGA graphics modes
|
; true VGA graphics modes
|
||||||
LOGICAL_SCREEN_WIDTH equ 672/8 ;width in bytes and height in scan
|
LOGICAL_SCREEN_WIDTH equ 672/8 ;width in bytes and height in scan
|
||||||
LOGICAL_SCREEN_HEIGHT equ 384 ; lines of the virtual screen
|
LOGICAL_SCREEN_HEIGHT equ 384 ; lines of the virtual screen
|
||||||
; we'll work with
|
; we'll work with
|
||||||
PAGE0 equ 0 ;flag for page 0 when page flipping
|
PAGE0 equ 0 ;flag for page 0 when page flipping
|
||||||
PAGE1 equ 1 ;flag for page 1 when page flipping
|
PAGE1 equ 1 ;flag for page 1 when page flipping
|
||||||
PAGE0_OFFSET equ 0 ;start offset of page 0 in VGA memory
|
PAGE0_OFFSET equ 0 ;start offset of page 0 in VGA memory
|
||||||
PAGE1_OFFSET equ LOGICAL_SCREEN_WIDTH * LOGICAL_SCREEN_HEIGHT
|
PAGE1_OFFSET equ LOGICAL_SCREEN_WIDTH * LOGICAL_SCREEN_HEIGHT
|
||||||
;start offset of page 1 (both pages
|
;start offset of page 1 (both pages
|
||||||
; are 672x384 virtual screens)
|
; are 672x384 virtual screens)
|
||||||
BALL_WIDTH equ 24/8 ;width of ball in display memory bytes
|
BALL_WIDTH equ 24/8 ;width of ball in display memory bytes
|
||||||
BALL_HEIGHT equ 24 ;height of ball in scan lines
|
BALL_HEIGHT equ 24 ;height of ball in scan lines
|
||||||
BLANK_OFFSET equ PAGE1_OFFSET * 2 ;start of blank image
|
BLANK_OFFSET equ PAGE1_OFFSET * 2 ;start of blank image
|
||||||
; in VGA memory
|
; in VGA memory
|
||||||
BALL_OFFSET equ BLANK_OFFSET + (BALL_WIDTH * BALL_HEIGHT)
|
BALL_OFFSET equ BLANK_OFFSET + (BALL_WIDTH * BALL_HEIGHT)
|
||||||
;start offset of ball image in VGA memory
|
;start offset of ball image in VGA memory
|
||||||
NUM_BALLS equ 4 ;number of balls to animate
|
NUM_BALLS equ 4 ;number of balls to animate
|
||||||
;
|
;
|
||||||
; VGA register equates.
|
; VGA register equates.
|
||||||
;
|
;
|
||||||
SC_INDEX equ 3c4h ;SC index register
|
SC_INDEX equ 3c4h ;SC index register
|
||||||
MAP_MASK equ 2 ;SC map mask register
|
MAP_MASK equ 2 ;SC map mask register
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_MODE equ 5 ;GC mode register
|
GC_MODE equ 5 ;GC mode register
|
||||||
CRTC_INDEX equ 03d4h ;CRTC index register
|
CRTC_INDEX equ 03d4h ;CRTC index register
|
||||||
START_ADDRESS_HIGH equ 0ch ;CRTC start address high byte
|
START_ADDRESS_HIGH equ 0ch ;CRTC start address high byte
|
||||||
START_ADDRESS_LOW equ 0dh ;CRTC start address low byte
|
START_ADDRESS_LOW equ 0dh ;CRTC start address low byte
|
||||||
CRTC_OFFSET equ 13h ;CRTC offset register
|
CRTC_OFFSET equ 13h ;CRTC offset register
|
||||||
INPUT_STATUS_1 equ 03dah ;VGA status register
|
INPUT_STATUS_1 equ 03dah ;VGA status register
|
||||||
VSYNC_MASK equ 08h ;vertical sync bit in status register 1
|
VSYNC_MASK equ 08h ;vertical sync bit in status register 1
|
||||||
DE_MASK equ 01h ;display enable bit in status register 1
|
DE_MASK equ 01h ;display enable bit in status register 1
|
||||||
AC_INDEX equ 03c0h ;AC index register
|
AC_INDEX equ 03c0h ;AC index register
|
||||||
HPELPAN equ 20h OR 13h ;AC horizontal pel panning register
|
HPELPAN equ 20h OR 13h ;AC horizontal pel panning register
|
||||||
; (bit 7 is high to keep palette RAM
|
; (bit 7 is high to keep palette RAM
|
||||||
; addressing on)
|
; addressing on)
|
||||||
dseg segment para common 'DATA'
|
dseg segment para common 'DATA'
|
||||||
CurrentPage db PAGE1 ;page to draw to
|
CurrentPage db PAGE1 ;page to draw to
|
||||||
CurrentPageOffset dw PAGE1_OFFSET
|
CurrentPageOffset dw PAGE1_OFFSET
|
||||||
;
|
;
|
||||||
; Four plane's worth of multicolored ball image.
|
; Four plane's worth of multicolored ball image.
|
||||||
;
|
;
|
||||||
BallPlane0Image label byte ;blue plane image
|
BallPlane0Image label byte ;blue plane image
|
||||||
db 000h, 03ch, 000h, 001h, 0ffh, 080h
|
db 000h, 03ch, 000h, 001h, 0ffh, 080h
|
||||||
db 007h, 0ffh, 0e0h, 00fh, 0ffh, 0f0h
|
db 007h, 0ffh, 0e0h, 00fh, 0ffh, 0f0h
|
||||||
db 4 * 3 dup(000h)
|
db 4 * 3 dup(000h)
|
||||||
|
|
@ -136,7 +137,7 @@ seen the features in action.
|
||||||
db 07fh, 0ffh, 0feh, 03fh, 0ffh, 0fch
|
db 07fh, 0ffh, 0feh, 03fh, 0ffh, 0fch
|
||||||
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
|
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
|
||||||
db 4 * 3 dup(000h)
|
db 4 * 3 dup(000h)
|
||||||
BallPlane1Image label byte ;green plane image
|
BallPlane1Image label byte ;green plane image
|
||||||
db 4 * 3 dup(000h)
|
db 4 * 3 dup(000h)
|
||||||
db 01fh, 0ffh, 0f8h, 03fh, 0ffh, 0fch
|
db 01fh, 0ffh, 0f8h, 03fh, 0ffh, 0fch
|
||||||
db 03fh, 0ffh, 0fch, 07fh, 0ffh, 0feh
|
db 03fh, 0ffh, 0fch, 07fh, 0ffh, 0feh
|
||||||
|
|
@ -145,7 +146,7 @@ seen the features in action.
|
||||||
db 8 * 3 dup(000h)
|
db 8 * 3 dup(000h)
|
||||||
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
|
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
|
||||||
db 001h, 0ffh, 080h, 000h, 03ch, 000h
|
db 001h, 0ffh, 080h, 000h, 03ch, 000h
|
||||||
BallPlane2Image label byte ;red plane image
|
BallPlane2Image label byte ;red plane image
|
||||||
db 12 * 3 dup(000h)
|
db 12 * 3 dup(000h)
|
||||||
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
|
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
|
||||||
db 0ffh, 0ffh, 0ffh, 07fh, 0ffh, 0feh
|
db 0ffh, 0ffh, 0ffh, 07fh, 0ffh, 0feh
|
||||||
|
|
@ -153,7 +154,7 @@ seen the features in action.
|
||||||
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
|
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
|
||||||
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
|
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
|
||||||
db 001h, 0ffh, 080h, 000h, 03ch, 000h
|
db 001h, 0ffh, 080h, 000h, 03ch, 000h
|
||||||
BallPlane3Image label byte ;intensity on for all planes,
|
BallPlane3Image label byte ;intensity on for all planes,
|
||||||
; to produce high-intensity colors
|
; to produce high-intensity colors
|
||||||
db 000h, 03ch, 000h, 001h, 0ffh, 080h
|
db 000h, 03ch, 000h, 001h, 0ffh, 080h
|
||||||
db 007h, 0ffh, 0e0h, 00fh, 0ffh, 0f0h
|
db 007h, 0ffh, 0e0h, 00fh, 0ffh, 0f0h
|
||||||
|
|
@ -167,91 +168,91 @@ seen the features in action.
|
||||||
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
|
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
|
||||||
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
|
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
|
||||||
db 001h, 0ffh, 080h, 000h, 03ch, 000h
|
db 001h, 0ffh, 080h, 000h, 03ch, 000h
|
||||||
;
|
;
|
||||||
BallX dw 15, 50, 40, 70 ;array of ball x coords
|
BallX dw 15, 50, 40, 70 ;array of ball x coords
|
||||||
BallY dw 40, 200, 110, 300 ;array of ball y coords
|
BallY dw 40, 200, 110, 300 ;array of ball y coords
|
||||||
LastBallX dw 15, 50, 40, 70 ;previous ball x coords
|
LastBallX dw 15, 50, 40, 70 ;previous ball x coords
|
||||||
LastBallY dw 40, 100, 160, 30 ;previous ball y coords
|
LastBallY dw 40, 100, 160, 30 ;previous ball y coords
|
||||||
BallXInc dw 1, 1, 1, 1 ;x move factors for ball
|
BallXInc dw 1, 1, 1, 1 ;x move factors for ball
|
||||||
BallYInc dw 8, 8, 8, 8 ;y move factors for ball
|
BallYInc dw 8, 8, 8, 8 ;y move factors for ball
|
||||||
BallRep dw 1, 1, 1, 1 ;# times to keep moving
|
BallRep dw 1, 1, 1, 1 ;# times to keep moving
|
||||||
; ball according to current
|
; ball according to current
|
||||||
; increments
|
; increments
|
||||||
BallControl dw Ball0Control, Ball1Control ;pointers to current
|
BallControl dw Ball0Control, Ball1Control ;pointers to current
|
||||||
dw Ball2Control, Ball3Control ; locations in ball
|
dw Ball2Control, Ball3Control ; locations in ball
|
||||||
; control strings
|
; control strings
|
||||||
BallControlString dw Ball0Control, Ball1Control ;pointers to
|
BallControlString dw Ball0Control, Ball1Control ;pointers to
|
||||||
dw Ball2Control, Ball3Control ; start of ball
|
dw Ball2Control, Ball3Control ; start of ball
|
||||||
; control strings
|
; control strings
|
||||||
;
|
;
|
||||||
; Ball control strings.
|
; Ball control strings.
|
||||||
;
|
;
|
||||||
Ball0Control label word
|
Ball0Control label word
|
||||||
dw 10, 1, 4, 10, -1, 4, 10, -1, -4, 10, 1, -4, 0
|
dw 10, 1, 4, 10, -1, 4, 10, -1, -4, 10, 1, -4, 0
|
||||||
Ball1Control label word
|
Ball1Control label word
|
||||||
dw 12, -1, 1, 28, -1, -1, 12, 1, -1, 28, 1, 1, 0
|
dw 12, -1, 1, 28, -1, -1, 12, 1, -1, 28, 1, 1, 0
|
||||||
Ball2Control label word
|
Ball2Control label word
|
||||||
dw 20, 0, -1, 40, 0, 1, 20, 0, -1, 0
|
dw 20, 0, -1, 40, 0, 1, 20, 0, -1, 0
|
||||||
Ball3Control label word
|
Ball3Control label word
|
||||||
dw 8, 1, 0, 52, -1, 0, 44, 1, 0, 0
|
dw 8, 1, 0, 52, -1, 0, 44, 1, 0, 0
|
||||||
;
|
;
|
||||||
; Panning control string.
|
; Panning control string.
|
||||||
;
|
;
|
||||||
ifdef MEDRES_VIDEO_MODE
|
ifdef MEDRES_VIDEO_MODE
|
||||||
PanningControlString dw 32, 1, 0, 34, 0, 1, 32, -1, 0, 34, 0, -1, 0
|
PanningControlString dw 32, 1, 0, 34, 0, 1, 32, -1, 0, 34, 0, -1, 0
|
||||||
else
|
else
|
||||||
PanningControlString dw 32, 1, 0, 184, 0, 1, 32, -1, 0, 184, 0, -1, 0
|
PanningControlString dw 32, 1, 0, 184, 0, 1, 32, -1, 0, 184, 0, -1, 0
|
||||||
endif
|
endif
|
||||||
PanningControl dw PanningControlString ;pointer to current location
|
PanningControl dw PanningControlString ;pointer to current location
|
||||||
; in panning control string
|
; in panning control string
|
||||||
PanningRep dw 1 ;# times to pan according to current
|
PanningRep dw 1 ;# times to pan according to current
|
||||||
; panning increments
|
; panning increments
|
||||||
PanningXInc dw 1 ;x panning factor
|
PanningXInc dw 1 ;x panning factor
|
||||||
PanningYInc dw 0 ;y panning factor
|
PanningYInc dw 0 ;y panning factor
|
||||||
HPan db 0 ;horizontal pel panning setting
|
HPan db 0 ;horizontal pel panning setting
|
||||||
PanningStartOffset dw 0 ;start offset adjustment to produce vertical
|
PanningStartOffset dw 0 ;start offset adjustment to produce vertical
|
||||||
; panning & coarse horizontal panning
|
; panning & coarse horizontal panning
|
||||||
dseg ends
|
dseg ends
|
||||||
;
|
;
|
||||||
; Macro to set indexed register P2 of chip with index register
|
; Macro to set indexed register P2 of chip with index register
|
||||||
; at P1 to AL.
|
; at P1 to AL.
|
||||||
;
|
;
|
||||||
SETREG macro P1, P2
|
SETREG macro P1, P2
|
||||||
mov dx,P1
|
mov dx,P1
|
||||||
mov ah,al
|
mov ah,al
|
||||||
mov al,P2
|
mov al,P2
|
||||||
out dx,ax
|
out dx,ax
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
cseg segment para public 'CODE'
|
cseg segment para public 'CODE'
|
||||||
assume cs:cseg, ds:dseg
|
assume cs:cseg, ds:dseg
|
||||||
start proc near
|
start proc near
|
||||||
mov ax,dseg
|
mov ax,dseg
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
;
|
;
|
||||||
; Select graphics mode.
|
; Select graphics mode.
|
||||||
;
|
;
|
||||||
ifdef MEDRES_VIDEO_MODE
|
ifdef MEDRES_VIDEO_MODE
|
||||||
mov ax,010h
|
mov ax,010h
|
||||||
else
|
else
|
||||||
mov ax,0eh
|
mov ax,0eh
|
||||||
endif
|
endif
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; ES always points to VGA memory.
|
; ES always points to VGA memory.
|
||||||
;
|
;
|
||||||
mov ax,VIDEO_SEGMENT
|
mov ax,VIDEO_SEGMENT
|
||||||
mov es,ax
|
mov es,ax
|
||||||
;
|
;
|
||||||
; Draw border around playfield in both pages.
|
; Draw border around playfield in both pages.
|
||||||
;
|
;
|
||||||
mov di,PAGE0_OFFSET
|
mov di,PAGE0_OFFSET
|
||||||
call DrawBorder ;page 0 border
|
call DrawBorder ;page 0 border
|
||||||
mov di,PAGE1_OFFSET
|
mov di,PAGE1_OFFSET
|
||||||
call DrawBorder ;page 1 border
|
call DrawBorder ;page 1 border
|
||||||
;
|
;
|
||||||
; Draw all four plane's worth of the ball to undisplayed VGA memory.
|
; Draw all four plane's worth of the ball to undisplayed VGA memory.
|
||||||
;
|
;
|
||||||
mov al,01h ;enable plane 0
|
mov al,01h ;enable plane 0
|
||||||
SETREG SC_INDEX, MAP_MASK
|
SETREG SC_INDEX, MAP_MASK
|
||||||
mov si,offset BallPlane0Image
|
mov si,offset BallPlane0Image
|
||||||
|
|
@ -276,18 +277,18 @@ seen the features in action.
|
||||||
mov di,BALL_OFFSET
|
mov di,BALL_OFFSET
|
||||||
mov cx,BALL_WIDTH * BALL_HEIGHT
|
mov cx,BALL_WIDTH * BALL_HEIGHT
|
||||||
rep movsb
|
rep movsb
|
||||||
;
|
;
|
||||||
; Draw a blank image the size of the ball to undisplayed VGA memory.
|
; Draw a blank image the size of the ball to undisplayed VGA memory.
|
||||||
;
|
;
|
||||||
mov al,0fh ;enable all memory planes, since the
|
mov al,0fh ;enable all memory planes, since the
|
||||||
SETREG SC_INDEX, MAP_MASK ; blank has to erase all planes
|
SETREG SC_INDEX, MAP_MASK ; blank has to erase all planes
|
||||||
mov di,BLANK_OFFSET
|
mov di,BLANK_OFFSET
|
||||||
mov cx,BALL_WIDTH * BALL_HEIGHT
|
mov cx,BALL_WIDTH * BALL_HEIGHT
|
||||||
sub al,al
|
sub al,al
|
||||||
rep stosb
|
rep stosb
|
||||||
;
|
;
|
||||||
; Set VGA to write mode 1, for block copying ball and blank images.
|
; Set VGA to write mode 1, for block copying ball and blank images.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_MODE
|
mov al,GC_MODE
|
||||||
out dx,al ;point GC Index to GC Mode register
|
out dx,al ;point GC Index to GC Mode register
|
||||||
|
|
@ -298,35 +299,35 @@ seen the features in action.
|
||||||
or al,1 ;set the write mode field to 1
|
or al,1 ;set the write mode field to 1
|
||||||
jmp $+2 ;delay to let bus settle
|
jmp $+2 ;delay to let bus settle
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Set VGA offset register in words to define logical screen width.
|
; Set VGA offset register in words to define logical screen width.
|
||||||
;
|
;
|
||||||
mov al,LOGICAL_SCREEN_WIDTH / 2
|
mov al,LOGICAL_SCREEN_WIDTH / 2
|
||||||
SETREG CRTC_INDEX, CRTC_OFFSET
|
SETREG CRTC_INDEX, CRTC_OFFSET
|
||||||
;
|
;
|
||||||
; Move the balls by erasing each ball, moving it, and
|
; Move the balls by erasing each ball, moving it, and
|
||||||
; redrawing it, then switching pages when they're all moved.
|
; redrawing it, then switching pages when they're all moved.
|
||||||
;
|
;
|
||||||
BallAnimationLoop:
|
BallAnimationLoop:
|
||||||
mov bx,( NUM_BALLS * 2 ) - 2
|
mov bx,( NUM_BALLS * 2 ) - 2
|
||||||
EachBallLoop:
|
EachBallLoop:
|
||||||
;
|
;
|
||||||
; Erase old image of ball in this page (at location from one more earlier).
|
; Erase old image of ball in this page (at location from one more earlier).
|
||||||
;
|
;
|
||||||
mov si,BLANK_OFFSET ;point to blank image
|
mov si,BLANK_OFFSET ;point to blank image
|
||||||
mov cx,[LastBallX+bx]
|
mov cx,[LastBallX+bx]
|
||||||
mov dx,[LastBallY+bx]
|
mov dx,[LastBallY+bx]
|
||||||
call DrawBall
|
call DrawBall
|
||||||
;
|
;
|
||||||
; Set new last ball location.
|
; Set new last ball location.
|
||||||
;
|
;
|
||||||
mov ax,[BallX+bx]
|
mov ax,[BallX+bx]
|
||||||
mov [LastballX+bx],ax
|
mov [LastballX+bx],ax
|
||||||
mov ax,[BallY+bx]
|
mov ax,[BallY+bx]
|
||||||
mov [LastballY+bx],ax
|
mov [LastballY+bx],ax
|
||||||
;
|
;
|
||||||
; Change the ball movement values if it's time to do so.
|
; Change the ball movement values if it's time to do so.
|
||||||
;
|
;
|
||||||
dec [BallRep+bx] ;has current repeat factor run out?
|
dec [BallRep+bx] ;has current repeat factor run out?
|
||||||
jnz MoveBall
|
jnz MoveBall
|
||||||
mov si,[BallControl+bx] ;it's time to change movement values
|
mov si,[BallControl+bx] ;it's time to change movement values
|
||||||
|
|
@ -336,48 +337,48 @@ seen the features in action.
|
||||||
jnz SetNewMove
|
jnz SetNewMove
|
||||||
mov si,[BallControlString+bx] ;reset control string
|
mov si,[BallControlString+bx] ;reset control string
|
||||||
lodsw ;get new repeat factor
|
lodsw ;get new repeat factor
|
||||||
SetNewMove:
|
SetNewMove:
|
||||||
mov [BallRep+bx],ax ;set new movement repeat factor
|
mov [BallRep+bx],ax ;set new movement repeat factor
|
||||||
lodsw ;set new x movement increment
|
lodsw ;set new x movement increment
|
||||||
mov [BallXInc+bx],ax
|
mov [BallXInc+bx],ax
|
||||||
lodsw ;set new y movement increment
|
lodsw ;set new y movement increment
|
||||||
mov [BallYInc+bx],ax
|
mov [BallYInc+bx],ax
|
||||||
mov [BallControl+bx],si ;save new control string pointer
|
mov [BallControl+bx],si ;save new control string pointer
|
||||||
;
|
;
|
||||||
; Move the ball.
|
; Move the ball.
|
||||||
;
|
;
|
||||||
MoveBall:
|
MoveBall:
|
||||||
mov ax,[BallXInc+bx]
|
mov ax,[BallXInc+bx]
|
||||||
add [BallX+bx],ax ;move in x direction
|
add [BallX+bx],ax ;move in x direction
|
||||||
mov ax,[BallYInc+bx]
|
mov ax,[BallYInc+bx]
|
||||||
add [BallY+bx],ax ;move in y direction
|
add [BallY+bx],ax ;move in y direction
|
||||||
;
|
;
|
||||||
; Draw ball at new location.
|
; Draw ball at new location.
|
||||||
;
|
;
|
||||||
mov si,BALL_OFFSET ;point to ball's image
|
mov si,BALL_OFFSET ;point to ball's image
|
||||||
mov cx,[BallX+bx]
|
mov cx,[BallX+bx]
|
||||||
mov dx,[BallY+bx]
|
mov dx,[BallY+bx]
|
||||||
call DrawBall
|
call DrawBall
|
||||||
;
|
;
|
||||||
dec bx
|
dec bx
|
||||||
dec bx
|
dec bx
|
||||||
jns EachBallLoop
|
jns EachBallLoop
|
||||||
|
|
||||||
;
|
;
|
||||||
; Set up the next panning state (but don't program it into the
|
; Set up the next panning state (but don't program it into the
|
||||||
; VGA yet).
|
; VGA yet).
|
||||||
;
|
;
|
||||||
call AdjustPanning
|
call AdjustPanning
|
||||||
|
|
||||||
;
|
;
|
||||||
; Wait for display enable (pixel data being displayed) so we know
|
; Wait for display enable (pixel data being displayed) so we know
|
||||||
; we're nowhere near vertical sync, where the start address gets
|
; we're nowhere near vertical sync, where the start address gets
|
||||||
; latched and used.
|
; latched and used.
|
||||||
;
|
;
|
||||||
call WaitDisplayEnable
|
call WaitDisplayEnable
|
||||||
;
|
;
|
||||||
; Flip to the new page by changing the start address.
|
; Flip to the new page by changing the start address.
|
||||||
;
|
;
|
||||||
mov ax,[CurrentPageOffset]
|
mov ax,[CurrentPageOffset]
|
||||||
add ax,[PanningStartOffset]
|
add ax,[PanningStartOffset]
|
||||||
push ax
|
push ax
|
||||||
|
|
@ -386,14 +387,14 @@ seen the features in action.
|
||||||
pop ax
|
pop ax
|
||||||
mov al,ah
|
mov al,ah
|
||||||
SETREG CRTC_INDEX, START_ADDRESS_HIGH
|
SETREG CRTC_INDEX, START_ADDRESS_HIGH
|
||||||
;
|
;
|
||||||
; Wait for vertical sync so the new start address has a chance
|
; Wait for vertical sync so the new start address has a chance
|
||||||
; to take effect.
|
; to take effect.
|
||||||
;
|
;
|
||||||
call WaitVSync
|
call WaitVSync
|
||||||
;
|
;
|
||||||
; Set horizontal panning now, just as new start address takes effect.
|
; Set horizontal panning now, just as new start address takes effect.
|
||||||
;
|
;
|
||||||
mov al,[HPan]
|
mov al,[HPan]
|
||||||
mov dx,INPUT_STATUS_1
|
mov dx,INPUT_STATUS_1
|
||||||
in al,dx ;reset AC addressing to index reg
|
in al,dx ;reset AC addressing to index reg
|
||||||
|
|
@ -402,43 +403,43 @@ seen the features in action.
|
||||||
out dx,al ;set AC index to pel pan reg
|
out dx,al ;set AC index to pel pan reg
|
||||||
mov al,[HPan]
|
mov al,[HPan]
|
||||||
out dx,al ;set new pel panning
|
out dx,al ;set new pel panning
|
||||||
;
|
;
|
||||||
; Flip the page to draw to to the undisplayed page.
|
; Flip the page to draw to to the undisplayed page.
|
||||||
;
|
;
|
||||||
xor [CurrentPage],1
|
xor [CurrentPage],1
|
||||||
jnz IsPage1
|
jnz IsPage1
|
||||||
mov [CurrentPageOffset],PAGE0_OFFSET
|
mov [CurrentPageOffset],PAGE0_OFFSET
|
||||||
jmp short EndFlipPage
|
jmp short EndFlipPage
|
||||||
IsPage1:
|
IsPage1:
|
||||||
mov [CurrentPageOffset],PAGE1_OFFSET
|
mov [CurrentPageOffset],PAGE1_OFFSET
|
||||||
EndFlipPage:
|
EndFlipPage:
|
||||||
;
|
;
|
||||||
; Exit if a key's been hit.
|
; Exit if a key's been hit.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 16h
|
int 16h
|
||||||
jnz Done
|
jnz Done
|
||||||
jmp BallAnimationLoop
|
jmp BallAnimationLoop
|
||||||
;
|
;
|
||||||
; Finished, clear key, reset screen mode and exit.
|
; Finished, clear key, reset screen mode and exit.
|
||||||
;
|
;
|
||||||
Done:
|
Done:
|
||||||
mov ah,0 ;clear key
|
mov ah,0 ;clear key
|
||||||
int 16h
|
int 16h
|
||||||
;
|
;
|
||||||
mov ax,3 ;reset to text mode
|
mov ax,3 ;reset to text mode
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
mov ah,4ch ;exit to DOS
|
mov ah,4ch ;exit to DOS
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
start endp
|
start endp
|
||||||
;
|
;
|
||||||
; Routine to draw a ball-sized image to all planes, copying from
|
; Routine to draw a ball-sized image to all planes, copying from
|
||||||
; offset SI in VGA memory to offset CX,DX (x,y) in VGA memory in
|
; offset SI in VGA memory to offset CX,DX (x,y) in VGA memory in
|
||||||
; the current page.
|
; the current page.
|
||||||
;
|
;
|
||||||
DrawBall proc near
|
DrawBall proc near
|
||||||
mov ax,LOGICAL_SCREEN_WIDTH
|
mov ax,LOGICAL_SCREEN_WIDTH
|
||||||
mul dx ;offset of start of top image scan line
|
mul dx ;offset of start of top image scan line
|
||||||
add ax,cx ;offset of upper left of image
|
add ax,cx ;offset of upper left of image
|
||||||
|
|
@ -448,7 +449,7 @@ seen the features in action.
|
||||||
push ds
|
push ds
|
||||||
push es
|
push es
|
||||||
pop ds ;move from VGA memory to VGA memory
|
pop ds ;move from VGA memory to VGA memory
|
||||||
DrawBallLoop:
|
DrawBallLoop:
|
||||||
push di
|
push di
|
||||||
mov cx,BALL_WIDTH
|
mov cx,BALL_WIDTH
|
||||||
rep movsb ;draw a scan line of image
|
rep movsb ;draw a scan line of image
|
||||||
|
|
@ -458,40 +459,40 @@ seen the features in action.
|
||||||
jnz DrawBallLoop
|
jnz DrawBallLoop
|
||||||
pop ds
|
pop ds
|
||||||
ret
|
ret
|
||||||
DrawBall endp
|
DrawBall endp
|
||||||
;
|
;
|
||||||
; Wait for the leading edge of vertical sync pulse.
|
; Wait for the leading edge of vertical sync pulse.
|
||||||
;
|
;
|
||||||
WaitVSync proc near
|
WaitVSync proc near
|
||||||
mov dx,INPUT_STATUS_1
|
mov dx,INPUT_STATUS_1
|
||||||
WaitNotVSyncLoop:
|
WaitNotVSyncLoop:
|
||||||
in al,dx
|
in al,dx
|
||||||
and al,VSYNC_MASK
|
and al,VSYNC_MASK
|
||||||
jnz WaitNotVSyncLoop
|
jnz WaitNotVSyncLoop
|
||||||
WaitVSyncLoop:
|
WaitVSyncLoop:
|
||||||
in al,dx
|
in al,dx
|
||||||
and al,VSYNC_MASK
|
and al,VSYNC_MASK
|
||||||
jz WaitVSyncLoop
|
jz WaitVSyncLoop
|
||||||
ret
|
ret
|
||||||
WaitVSync endp
|
WaitVSync endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Wait for display enable to happen (pixels to be scanned to
|
; Wait for display enable to happen (pixels to be scanned to
|
||||||
; the screen, indicating we're in the middle of displaying a frame).
|
; the screen, indicating we're in the middle of displaying a frame).
|
||||||
;
|
;
|
||||||
WaitDisplayEnable proc near
|
WaitDisplayEnable proc near
|
||||||
mov dx,INPUT_STATUS_1
|
mov dx,INPUT_STATUS_1
|
||||||
WaitDELoop:
|
WaitDELoop:
|
||||||
in al,dx
|
in al,dx
|
||||||
and al,DE_MASK
|
and al,DE_MASK
|
||||||
jnz WaitDELoop
|
jnz WaitDELoop
|
||||||
ret
|
ret
|
||||||
WaitDisplayEnable endp
|
WaitDisplayEnable endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Perform horizontal/vertical panning.
|
; Perform horizontal/vertical panning.
|
||||||
;
|
;
|
||||||
AdjustPanning proc near
|
AdjustPanning proc near
|
||||||
dec [PanningRep] ;time to get new panning values?
|
dec [PanningRep] ;time to get new panning values?
|
||||||
jnz DoPan
|
jnz DoPan
|
||||||
mov si,[PanningControl] ;point to current location in
|
mov si,[PanningControl] ;point to current location in
|
||||||
|
|
@ -501,7 +502,7 @@ seen the features in action.
|
||||||
jnz SetnewPanValues
|
jnz SetnewPanValues
|
||||||
mov si,offset PanningControlString ;reset to start of string
|
mov si,offset PanningControlString ;reset to start of string
|
||||||
lodsw ;get panning repeat factor
|
lodsw ;get panning repeat factor
|
||||||
SetNewPanValues:
|
SetNewPanValues:
|
||||||
mov [PanningRep],ax ;set new panning repeat value
|
mov [PanningRep],ax ;set new panning repeat value
|
||||||
lodsw
|
lodsw
|
||||||
mov [PanningXInc],ax ;horizontal panning value
|
mov [PanningXInc],ax ;horizontal panning value
|
||||||
|
|
@ -509,10 +510,10 @@ seen the features in action.
|
||||||
mov [PanningYInc],ax ;vertical panning value
|
mov [PanningYInc],ax ;vertical panning value
|
||||||
mov [PanningControl],si ;save current location in panning
|
mov [PanningControl],si ;save current location in panning
|
||||||
; control string
|
; control string
|
||||||
;
|
;
|
||||||
; Pan according to panning values.
|
; Pan according to panning values.
|
||||||
;
|
;
|
||||||
DoPan:
|
DoPan:
|
||||||
mov ax,[PanningXInc] ;horizontal panning
|
mov ax,[PanningXInc] ;horizontal panning
|
||||||
and ax,ax
|
and ax,ax
|
||||||
js PanLeft ;negative means pan left
|
js PanLeft ;negative means pan left
|
||||||
|
|
@ -524,15 +525,15 @@ seen the features in action.
|
||||||
sub al,al ; and a start offset that's one
|
sub al,al ; and a start offset that's one
|
||||||
inc [PanningStartOffset] ; higher
|
inc [PanningStartOffset] ; higher
|
||||||
jmp short SetHPan
|
jmp short SetHPan
|
||||||
PanLeft:
|
PanLeft:
|
||||||
mov al,[HPan]
|
mov al,[HPan]
|
||||||
dec al ;pan left; if pel pan reaches -1,
|
dec al ;pan left; if pel pan reaches -1,
|
||||||
jns SetHPan ; it's time to move to the next
|
jns SetHPan ; it's time to move to the next
|
||||||
mov al,7 ; byte with a pel pan of 7 and a
|
mov al,7 ; byte with a pel pan of 7 and a
|
||||||
dec [PanningStartOffset] ; start offset that's one lower
|
dec [PanningStartOffset] ; start offset that's one lower
|
||||||
SetHPan:
|
SetHPan:
|
||||||
mov [HPan],al ;save new pel pan value
|
mov [HPan],al ;save new pel pan value
|
||||||
CheckVerticalPan:
|
CheckVerticalPan:
|
||||||
mov ax,[PanningYInc] ;vertical panning
|
mov ax,[PanningYInc] ;vertical panning
|
||||||
and ax,ax
|
and ax,ax
|
||||||
js PanUp ;negative means pan up
|
js PanUp ;negative means pan up
|
||||||
|
|
@ -541,22 +542,22 @@ seen the features in action.
|
||||||
;pan down by advancing the start
|
;pan down by advancing the start
|
||||||
; address by a scan line
|
; address by a scan line
|
||||||
jmp short EndPan
|
jmp short EndPan
|
||||||
PanUp:
|
PanUp:
|
||||||
sub [PanningStartOffset],LOGICAL_SCREEN_WIDTH
|
sub [PanningStartOffset],LOGICAL_SCREEN_WIDTH
|
||||||
;pan up by retarding the start
|
;pan up by retarding the start
|
||||||
; address by a scan line
|
; address by a scan line
|
||||||
EndPan:
|
EndPan:
|
||||||
ret
|
ret
|
||||||
;
|
;
|
||||||
; Draw textured border around playfield that starts at DI.
|
; Draw textured border around playfield that starts at DI.
|
||||||
;
|
;
|
||||||
DrawBorder proc near
|
DrawBorder proc near
|
||||||
;
|
;
|
||||||
; Draw the left border.
|
; Draw the left border.
|
||||||
;
|
;
|
||||||
push di
|
push di
|
||||||
mov cx,LOGICAL_SCREEN_HEIGHT / 16
|
mov cx,LOGICAL_SCREEN_HEIGHT / 16
|
||||||
DrawLeftBorderLoop:
|
DrawLeftBorderLoop:
|
||||||
mov al,0ch ;select red color for block
|
mov al,0ch ;select red color for block
|
||||||
call DrawBorderBlock
|
call DrawBorderBlock
|
||||||
add di,LOGICAL_SCREEN_WIDTH * 8
|
add di,LOGICAL_SCREEN_WIDTH * 8
|
||||||
|
|
@ -565,13 +566,13 @@ seen the features in action.
|
||||||
add di,LOGICAL_SCREEN_WIDTH * 8
|
add di,LOGICAL_SCREEN_WIDTH * 8
|
||||||
loop DrawLeftBorderLoop
|
loop DrawLeftBorderLoop
|
||||||
pop di
|
pop di
|
||||||
;
|
;
|
||||||
; Draw the right border.
|
; Draw the right border.
|
||||||
;
|
;
|
||||||
push di
|
push di
|
||||||
add di,LOGICAL_SCREEN_WIDTH - 1
|
add di,LOGICAL_SCREEN_WIDTH - 1
|
||||||
mov cx,LOGICAL_SCREEN_HEIGHT / 16
|
mov cx,LOGICAL_SCREEN_HEIGHT / 16
|
||||||
DrawRightBorderLoop:
|
DrawRightBorderLoop:
|
||||||
mov al,0eh ;select yellow color for block
|
mov al,0eh ;select yellow color for block
|
||||||
call DrawBorderBlock
|
call DrawBorderBlock
|
||||||
add di,LOGICAL_SCREEN_WIDTH * 8
|
add di,LOGICAL_SCREEN_WIDTH * 8
|
||||||
|
|
@ -580,12 +581,12 @@ seen the features in action.
|
||||||
add di,LOGICAL_SCREEN_WIDTH * 8
|
add di,LOGICAL_SCREEN_WIDTH * 8
|
||||||
loop DrawRightBorderLoop
|
loop DrawRightBorderLoop
|
||||||
pop di
|
pop di
|
||||||
;
|
;
|
||||||
; Draw the top border.
|
; Draw the top border.
|
||||||
;
|
;
|
||||||
push di
|
push di
|
||||||
mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2
|
mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2
|
||||||
DrawTopBorderLoop:
|
DrawTopBorderLoop:
|
||||||
inc di
|
inc di
|
||||||
mov al,0eh ;select yellow color for block
|
mov al,0eh ;select yellow color for block
|
||||||
call DrawBorderBlock
|
call DrawBorderBlock
|
||||||
|
|
@ -594,12 +595,12 @@ seen the features in action.
|
||||||
call DrawBorderBlock
|
call DrawBorderBlock
|
||||||
loop DrawTopBorderLoop
|
loop DrawTopBorderLoop
|
||||||
pop di
|
pop di
|
||||||
;
|
;
|
||||||
; Draw the bottom border.
|
; Draw the bottom border.
|
||||||
;
|
;
|
||||||
add di,(LOGICAL_SCREEN_HEIGHT - 8) * LOGICAL_SCREEN_WIDTH
|
add di,(LOGICAL_SCREEN_HEIGHT - 8) * LOGICAL_SCREEN_WIDTH
|
||||||
mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2
|
mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2
|
||||||
DrawBottomBorderLoop:
|
DrawBottomBorderLoop:
|
||||||
inc di
|
inc di
|
||||||
mov al,0ch ;select red color for block
|
mov al,0ch ;select red color for block
|
||||||
call DrawBorderBlock
|
call DrawBorderBlock
|
||||||
|
|
@ -608,12 +609,12 @@ seen the features in action.
|
||||||
call DrawBorderBlock
|
call DrawBorderBlock
|
||||||
loop DrawBottomBorderLoop
|
loop DrawBottomBorderLoop
|
||||||
ret
|
ret
|
||||||
DrawBorder endp
|
DrawBorder endp
|
||||||
;
|
;
|
||||||
; Draws an 8x8 border block in color in AL at location DI.
|
; Draws an 8x8 border block in color in AL at location DI.
|
||||||
; DI preserved.
|
; DI preserved.
|
||||||
;
|
;
|
||||||
DrawBorderBlock proc near
|
DrawBorderBlock proc near
|
||||||
push di
|
push di
|
||||||
SETREG SC_INDEX, MAP_MASK
|
SETREG SC_INDEX, MAP_MASK
|
||||||
mov al,0ffh
|
mov al,0ffh
|
||||||
|
|
@ -623,7 +624,8 @@ seen the features in action.
|
||||||
endm
|
endm
|
||||||
pop di
|
pop di
|
||||||
ret
|
ret
|
||||||
DrawBorderBlock endp
|
DrawBorderBlock endp
|
||||||
AdjustPanning endp
|
AdjustPanning endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
248
24-02.md
248
24-02.md
|
|
@ -12,70 +12,71 @@ pages: 453-458
|
||||||
|
|
||||||
**LISTING 24.1 L24-1.ASM**
|
**LISTING 24.1 L24-1.ASM**
|
||||||
|
|
||||||
; Program to illustrate operation of ALUs and latches of the VGA's
|
```nasm
|
||||||
; Graphics Controller. Draws a variety of patterns against
|
; Program to illustrate operation of ALUs and latches of the VGA's
|
||||||
; a horizontally striped background, using each of the 4 available
|
; Graphics Controller. Draws a variety of patterns against
|
||||||
; logical functions (data unmodified, AND, OR, XOR) in turn to combine
|
; a horizontally striped background, using each of the 4 available
|
||||||
; the images with the background.
|
; logical functions (data unmodified, AND, OR, XOR) in turn to combine
|
||||||
; By Michael Abrash.
|
; the images with the background.
|
||||||
;
|
; By Michael Abrash.
|
||||||
stack segment para stack ‘STACK'
|
;
|
||||||
|
stack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
||||||
SCREEN_HEIGHT equ 350
|
SCREEN_HEIGHT equ 350
|
||||||
SCREEN_WIDTH_IN_BYTES equ 80
|
SCREEN_WIDTH_IN_BYTES equ 80
|
||||||
DEMO_AREA_HEIGHT equ 336 ;# of scan lines in area
|
DEMO_AREA_HEIGHT equ 336 ;# of scan lines in area
|
||||||
; logical function operation
|
; logical function operation
|
||||||
; is demonstrated in
|
; is demonstrated in
|
||||||
DEMO_AREA_WIDTH_IN_BYTES equ 40 ;width in bytes of area
|
DEMO_AREA_WIDTH_IN_BYTES equ 40 ;width in bytes of area
|
||||||
; logical function operation
|
; logical function operation
|
||||||
; is demonstrated in
|
; is demonstrated in
|
||||||
VERTICAL_BOX_WIDTH_IN_BYTES equ 10 ;width in bytes of the box used to
|
VERTICAL_BOX_WIDTH_IN_BYTES equ 10 ;width in bytes of the box used to
|
||||||
; demonstrate each logical function
|
; demonstrate each logical function
|
||||||
;
|
;
|
||||||
; VGA register equates.
|
; VGA register equates.
|
||||||
;
|
;
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_ROTATE equ 3 ;GC data rotate/logical function
|
GC_ROTATE equ 3 ;GC data rotate/logical function
|
||||||
; register index
|
; register index
|
||||||
GC_MODE equ 5 ;GC mode register index
|
GC_MODE equ 5 ;GC mode register index
|
||||||
;
|
;
|
||||||
dseg segment para common ‘DATA'
|
dseg segment para common ‘DATA'
|
||||||
;
|
;
|
||||||
; String used to label logical functions.
|
; String used to label logical functions.
|
||||||
;
|
;
|
||||||
LabelString label byte
|
LabelString label byte
|
||||||
db ‘UNMODIFIED AND OR XOR '
|
db ‘UNMODIFIED AND OR XOR '
|
||||||
LABEL_STRING_LENGTH equ $-LabelString
|
LABEL_STRING_LENGTH equ $-LabelString
|
||||||
;
|
;
|
||||||
; Strings used to label fill patterns.
|
; Strings used to label fill patterns.
|
||||||
;
|
;
|
||||||
FillPatternFF db ‘Fill Pattern: 0FFh'
|
FillPatternFF db ‘Fill Pattern: 0FFh'
|
||||||
FILL_PATTERN_FF_LENGTH equ $ - FillPatternFF
|
FILL_PATTERN_FF_LENGTH equ $ - FillPatternFF
|
||||||
FillPattern00 db ‘Fill Pattern: 000h'
|
FillPattern00 db ‘Fill Pattern: 000h'
|
||||||
FILL_PATTERN_00_LENGTH equ $ - FillPattern00
|
FILL_PATTERN_00_LENGTH equ $ - FillPattern00
|
||||||
FillPatternVert db ‘Fill Pattern: Vertical Bar'
|
FillPatternVert db ‘Fill Pattern: Vertical Bar'
|
||||||
FILL_PATTERN_VERT_LENGTH equ $ - FillPatternVert
|
FILL_PATTERN_VERT_LENGTH equ $ - FillPatternVert
|
||||||
FillPatternHorz db ‘Fill Pattern: Horizontal Bar'
|
FillPatternHorz db ‘Fill Pattern: Horizontal Bar'
|
||||||
FILL_PATTERN_HORZ_LENGTH equ $ - FillPatternHorz
|
FILL_PATTERN_HORZ_LENGTH equ $ - FillPatternHorz
|
||||||
;
|
;
|
||||||
dseg ends
|
dseg ends
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of GC chip to SETTING.
|
; Macro to set indexed register INDEX of GC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETGC macro INDEX, SETTING
|
SETGC macro INDEX, SETTING
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov ax,(SETTING SHL 8) OR INDEX
|
mov ax,(SETTING SHL 8) OR INDEX
|
||||||
out dx,ax
|
out dx,ax
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
; Macro to call BIOS write string function to display text string
|
; Macro to call BIOS write string function to display text string
|
||||||
; TEXT_STRING, of length TEXT_LENGTH, at location ROW,COLUMN.
|
; TEXT_STRING, of length TEXT_LENGTH, at location ROW,COLUMN.
|
||||||
;
|
;
|
||||||
TEXT_UP macro TEXT_STRING, TEXT_LENGTH, ROW, COLUMN
|
TEXT_UP macro TEXT_STRING, TEXT_LENGTH, ROW, COLUMN
|
||||||
mov ah,13h ;BIOS write string function
|
mov ah,13h ;BIOS write string function
|
||||||
mov bp,offset TEXT_STRING ;ES:BP points to string
|
mov bp,offset TEXT_STRING ;ES:BP points to string
|
||||||
mov cx,TEXT_LENGTH
|
mov cx,TEXT_LENGTH
|
||||||
|
|
@ -84,25 +85,25 @@ pages: 453-458
|
||||||
mov bl,7 ;text attribute is white (light gray)
|
mov bl,7 ;text attribute is white (light gray)
|
||||||
int 10h
|
int 10h
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE'
|
cseg segment para public ‘CODE'
|
||||||
assume cs:cseg, ds:dseg
|
assume cs:cseg, ds:dseg
|
||||||
start proc near
|
start proc near
|
||||||
mov ax,dseg
|
mov ax,dseg
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
;
|
;
|
||||||
; Select 640x350 graphics mode.
|
; Select 640x350 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,010h
|
mov ax,010h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; ES points to VGA memory.
|
; ES points to VGA memory.
|
||||||
;
|
;
|
||||||
mov ax,VGA_VIDEO_SEGMENT
|
mov ax,VGA_VIDEO_SEGMENT
|
||||||
mov es,ax
|
mov es,ax
|
||||||
;
|
;
|
||||||
; Draw background of horizontal bars.
|
; Draw background of horizontal bars.
|
||||||
;
|
;
|
||||||
mov dx,SCREEN_HEIGHT/4
|
mov dx,SCREEN_HEIGHT/4
|
||||||
;# of bars to draw (each 4 pixels high)
|
;# of bars to draw (each 4 pixels high)
|
||||||
sub di,di ;start at offset 0 in display memory
|
sub di,di ;start at offset 0 in display memory
|
||||||
|
|
@ -110,7 +111,7 @@ pages: 453-458
|
||||||
mov bx,DEMO_AREA_WIDTH_IN_BYTES / 2 ;length of each bar
|
mov bx,DEMO_AREA_WIDTH_IN_BYTES / 2 ;length of each bar
|
||||||
mov si,SCREEN_WIDTH_IN_BYTES - DEMO_AREA_WIDTH_IN_BYTES
|
mov si,SCREEN_WIDTH_IN_BYTES - DEMO_AREA_WIDTH_IN_BYTES
|
||||||
mov bp,(SCREEN_WIDTH_IN_BYTES * 3) - DEMO_AREA_WIDTH_IN_BYTES
|
mov bp,(SCREEN_WIDTH_IN_BYTES * 3) - DEMO_AREA_WIDTH_IN_BYTES
|
||||||
BackgroundLoop:
|
BackgroundLoop:
|
||||||
mov cx,bx ;length of bar
|
mov cx,bx ;length of bar
|
||||||
rep stosw ;draw top half of bar
|
rep stosw ;draw top half of bar
|
||||||
add di,si ;point to start of bottom half of bar
|
add di,si ;point to start of bottom half of bar
|
||||||
|
|
@ -119,89 +120,89 @@ pages: 453-458
|
||||||
add di,bp ;point to start of top of next bar
|
add di,bp ;point to start of top of next bar
|
||||||
dec dx
|
dec dx
|
||||||
jnz BackgroundLoop
|
jnz BackgroundLoop
|
||||||
;
|
;
|
||||||
; Draw vertical boxes filled with a variety of fill patterns
|
; Draw vertical boxes filled with a variety of fill patterns
|
||||||
; using each of the 4 logical functions in turn.
|
; using each of the 4 logical functions in turn.
|
||||||
;
|
;
|
||||||
SETGC GC_ROTATE, 0 ;select data unmodified
|
SETGC GC_ROTATE, 0 ;select data unmodified
|
||||||
; logical function...
|
; logical function...
|
||||||
mov di,0
|
mov di,0
|
||||||
call DrawVerticalBox ;...and draw box
|
call DrawVerticalBox ;...and draw box
|
||||||
;
|
;
|
||||||
SETGC GC_ROTATE, 08h ;select AND logical function...
|
SETGC GC_ROTATE, 08h ;select AND logical function...
|
||||||
mov di,10
|
mov di,10
|
||||||
call DrawVerticalBox ;...and draw box
|
call DrawVerticalBox ;...and draw box
|
||||||
;
|
;
|
||||||
SETGC GC_ROTATE, 10h ;select OR logical function...
|
SETGC GC_ROTATE, 10h ;select OR logical function...
|
||||||
mov di,20
|
mov di,20
|
||||||
call DrawVerticalBox ;...and draw box
|
call DrawVerticalBox ;...and draw box
|
||||||
;
|
;
|
||||||
SETGC GC_ROTATE, 18h ;select XOR logical function...
|
SETGC GC_ROTATE, 18h ;select XOR logical function...
|
||||||
mov di,30
|
mov di,30
|
||||||
call DrawVerticalBox ;...and draw box
|
call DrawVerticalBox ;...and draw box
|
||||||
;
|
;
|
||||||
; Reset the logical function to data unmodified, the default state.
|
; Reset the logical function to data unmodified, the default state.
|
||||||
;
|
;
|
||||||
SETGC GC_ROTATE, 0
|
SETGC GC_ROTATE, 0
|
||||||
;
|
;
|
||||||
; Label the screen.
|
; Label the screen.
|
||||||
;
|
;
|
||||||
push ds
|
push ds
|
||||||
pop es ;strings we'll display are passed to BIOS
|
pop es ;strings we'll display are passed to BIOS
|
||||||
; by pointing ES:BP to them
|
; by pointing ES:BP to them
|
||||||
;
|
;
|
||||||
; Label the logical functions, using the VGA BIOS's
|
; Label the logical functions, using the VGA BIOS's
|
||||||
; write string function.
|
; write string function.
|
||||||
;
|
;
|
||||||
TEXT_UP LabelString, LABEL_STRING_LENGTH, 24, 0
|
TEXT_UP LabelString, LABEL_STRING_LENGTH, 24, 0
|
||||||
;
|
;
|
||||||
; Label the fill patterns, using the VGA BIOS's
|
; Label the fill patterns, using the VGA BIOS's
|
||||||
; write string function.
|
; write string function.
|
||||||
;
|
;
|
||||||
TEXT_UP FillPatternFF, FILL_PATTERN_FF_LENGTH, 3, 42
|
TEXT_UP FillPatternFF, FILL_PATTERN_FF_LENGTH, 3, 42
|
||||||
TEXT_UP FillPattern00, FILL_PATTERN_00_LENGTH, 9, 42
|
TEXT_UP FillPattern00, FILL_PATTERN_00_LENGTH, 9, 42
|
||||||
TEXT_UP FillPatternVert, FILL_PATTERN_VERT_LENGTH, 15, 42
|
TEXT_UP FillPatternVert, FILL_PATTERN_VERT_LENGTH, 15, 42
|
||||||
TEXT_UP FillPatternHorz, FILL_PATTERN_HORZ_LENGTH, 21, 42
|
TEXT_UP FillPatternHorz, FILL_PATTERN_HORZ_LENGTH, 21, 42
|
||||||
;
|
;
|
||||||
; Wait until a key's been hit to reset screen mode & exit.
|
; Wait until a key's been hit to reset screen mode & exit.
|
||||||
;
|
;
|
||||||
WaitForKey:
|
WaitForKey:
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 16h
|
int 16h
|
||||||
jz WaitForKey
|
jz WaitForKey
|
||||||
;
|
;
|
||||||
; Finished. Clear key, reset screen mode and exit.
|
; Finished. Clear key, reset screen mode and exit.
|
||||||
;
|
;
|
||||||
Done:
|
Done:
|
||||||
mov ah,0 ;clear key that we just detected
|
mov ah,0 ;clear key that we just detected
|
||||||
int 16h
|
int 16h
|
||||||
;
|
;
|
||||||
mov ax,3 ;reset to text mode
|
mov ax,3 ;reset to text mode
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
mov ah,4ch ;exit to DOS
|
mov ah,4ch ;exit to DOS
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
start endp
|
start endp
|
||||||
;
|
;
|
||||||
; Subroutine to draw a box 80x336 in size, using currently selected
|
; Subroutine to draw a box 80x336 in size, using currently selected
|
||||||
; logical function, with upper left corner at the display memory offset
|
; logical function, with upper left corner at the display memory offset
|
||||||
; in DI. Box is filled with four patterns. Top quarter of area is
|
; in DI. Box is filled with four patterns. Top quarter of area is
|
||||||
; filled with 0FFh (solid) pattern, next quarter is filled with 00h
|
; filled with 0FFh (solid) pattern, next quarter is filled with 00h
|
||||||
; (empty) pattern, next quarter is filled with 33h (double pixel wide
|
; (empty) pattern, next quarter is filled with 33h (double pixel wide
|
||||||
; vertical bar) pattern, and bottom quarter is filled with double pixel
|
; vertical bar) pattern, and bottom quarter is filled with double pixel
|
||||||
; high horizontal bar pattern.
|
; high horizontal bar pattern.
|
||||||
;
|
;
|
||||||
; Macro to draw a column of the specified width in bytes, one-quarter
|
; Macro to draw a column of the specified width in bytes, one-quarter
|
||||||
; of the height of the box, with the specified fill pattern.
|
; of the height of the box, with the specified fill pattern.
|
||||||
;
|
;
|
||||||
DRAW_BOX_QUARTER macro FILL, WIDTH
|
DRAW_BOX_QUARTER macro FILL, WIDTH
|
||||||
local RowLoop, ColumnLoop
|
local RowLoop, ColumnLoop
|
||||||
mov al,FILL ;fill pattern
|
mov al,FILL ;fill pattern
|
||||||
mov dx,DEMO_AREA_HEIGHT / 4 ;1/4 of the full box height
|
mov dx,DEMO_AREA_HEIGHT / 4 ;1/4 of the full box height
|
||||||
RowLoop:
|
RowLoop:
|
||||||
mov cx,WIDTH
|
mov cx,WIDTH
|
||||||
ColumnLoop:
|
ColumnLoop:
|
||||||
mov ah,es:[di] ;load display memory contents into
|
mov ah,es:[di] ;load display memory contents into
|
||||||
; GC latches (we don't actually care
|
; GC latches (we don't actually care
|
||||||
; about value read into AH)
|
; about value read into AH)
|
||||||
|
|
@ -215,8 +216,8 @@ pages: 453-458
|
||||||
dec dx
|
dec dx
|
||||||
jnz RowLoop
|
jnz RowLoop
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
DrawVerticalBox proc near
|
DrawVerticalBox proc near
|
||||||
DRAW_BOX_QUARTER 0ffh, VERTICAL_BOX_WIDTH_IN_BYTES
|
DRAW_BOX_QUARTER 0ffh, VERTICAL_BOX_WIDTH_IN_BYTES
|
||||||
;first fill pattern: solid fill
|
;first fill pattern: solid fill
|
||||||
DRAW_BOX_QUARTER 0, VERTICAL_BOX_WIDTH_IN_BYTES
|
DRAW_BOX_QUARTER 0, VERTICAL_BOX_WIDTH_IN_BYTES
|
||||||
|
|
@ -229,37 +230,38 @@ pages: 453-458
|
||||||
; sets of 4 scan lines
|
; sets of 4 scan lines
|
||||||
sub ax,ax
|
sub ax,ax
|
||||||
mov si,VERTICAL_BOX_WIDTH_IN_BYTES ;width of fill area
|
mov si,VERTICAL_BOX_WIDTH_IN_BYTES ;width of fill area
|
||||||
HorzBarLoop:
|
HorzBarLoop:
|
||||||
dec ax ;0ffh fill (smaller to do word than byte DEC)
|
dec ax ;0ffh fill (smaller to do word than byte DEC)
|
||||||
mov cx,si ;width to fill
|
mov cx,si ;width to fill
|
||||||
HBLoop1:
|
HBLoop1:
|
||||||
mov bl,es:[di] ;load latches (don't care about value)
|
mov bl,es:[di] ;load latches (don't care about value)
|
||||||
stosb ;write solid pattern, through ALUs
|
stosb ;write solid pattern, through ALUs
|
||||||
loop HBLoop1
|
loop HBLoop1
|
||||||
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
||||||
mov cx,si ;width to fill
|
mov cx,si ;width to fill
|
||||||
HBLoop2:
|
HBLoop2:
|
||||||
mov bl,es:[di] ;load latches
|
mov bl,es:[di] ;load latches
|
||||||
stosb ;write solid pattern, through ALUs
|
stosb ;write solid pattern, through ALUs
|
||||||
loop HBLoop2
|
loop HBLoop2
|
||||||
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
||||||
inc ax ;0 fill (smaller to do word than byte DEC)
|
inc ax ;0 fill (smaller to do word than byte DEC)
|
||||||
mov cx,si ;width to fill
|
mov cx,si ;width to fill
|
||||||
HBLoop3:
|
HBLoop3:
|
||||||
mov bl,es:[di] ;load latches
|
mov bl,es:[di] ;load latches
|
||||||
stosb ;write empty pattern, through ALUs
|
stosb ;write empty pattern, through ALUs
|
||||||
loop HBLoop3
|
loop HBLoop3
|
||||||
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
||||||
mov cx,si ;width to fill
|
mov cx,si ;width to fill
|
||||||
HBLoop4:
|
HBLoop4:
|
||||||
mov bl,es:[di] ;load latches
|
mov bl,es:[di] ;load latches
|
||||||
stosb ;write empty pattern, through ALUs
|
stosb ;write empty pattern, through ALUs
|
||||||
loop HBLoop4
|
loop HBLoop4
|
||||||
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
|
||||||
dec dx
|
dec dx
|
||||||
jnz HorzBarLoop
|
jnz HorzBarLoop
|
||||||
;
|
;
|
||||||
ret
|
ret
|
||||||
DrawVerticalBox endp
|
DrawVerticalBox endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
11
24-03.md
11
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
|
||||||
|
|
||||||
MOV DL,VALUE1
|
```nasm
|
||||||
MOV DH,VALUE2
|
MOV DL,VALUE1
|
||||||
|
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
|
||||||
|
|
||||||
MOV DX,(VALUE2 SHL 8) OR VALUE1
|
```nasm
|
||||||
|
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
|
||||||
|
|
|
||||||
246
25-02.md
246
25-02.md
|
|
@ -12,111 +12,112 @@ pages: 466-470
|
||||||
|
|
||||||
**LISTING 25.1 L25-1.ASM**
|
**LISTING 25.1 L25-1.ASM**
|
||||||
|
|
||||||
; Program to illustrate operation of data rotate and bit mask
|
```nasm
|
||||||
; features of Graphics Controller. Draws 8x8 character at
|
; Program to illustrate operation of data rotate and bit mask
|
||||||
; specified location, using VGA's 8x8 ROM font. Designed
|
; features of Graphics Controller. Draws 8x8 character at
|
||||||
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
|
; specified location, using VGA's 8x8 ROM font. Designed
|
||||||
; By Michael Abrash.
|
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
|
||||||
;
|
; By Michael Abrash.
|
||||||
stack segment para stack ‘STACK'
|
;
|
||||||
|
stack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
||||||
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
|
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
|
||||||
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
|
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
|
||||||
;
|
;
|
||||||
; VGA register equates.
|
; VGA register equates.
|
||||||
;
|
;
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_ROTATE equ 3 ;GC data rotate/logical function
|
GC_ROTATE equ 3 ;GC data rotate/logical function
|
||||||
; register index
|
; register index
|
||||||
GC_BIT_MASK equ 8 ;GC bit mask register index
|
GC_BIT_MASK equ 8 ;GC bit mask register index
|
||||||
;
|
;
|
||||||
dseg segment para common ‘DATA'
|
dseg segment para common ‘DATA'
|
||||||
TEST_TEXT_ROW equ 69 ;row to display test text at
|
TEST_TEXT_ROW equ 69 ;row to display test text at
|
||||||
TEST_TEXT_COL equ 17 ;column to display test text at
|
TEST_TEXT_COL equ 17 ;column to display test text at
|
||||||
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
|
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
|
||||||
|
|
||||||
TestString label byte
|
TestString label byte
|
||||||
db ‘Hello, world!',0 ;test string to print.
|
db ‘Hello, world!',0 ;test string to print.
|
||||||
FontPointer dd ? ;font offset
|
FontPointer dd ? ;font offset
|
||||||
dseg ends
|
dseg ends
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of GC chip to SETTING.
|
; Macro to set indexed register INDEX of GC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETGC macro INDEX, SETTING
|
SETGC macro INDEX, SETTING
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov ax,(SETTING SHL 8) OR INDEX
|
mov ax,(SETTING SHL 8) OR INDEX
|
||||||
out dx,ax
|
out dx,ax
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE'
|
cseg segment para public ‘CODE'
|
||||||
assume cs:cseg, ds:dseg
|
assume cs:cseg, ds:dseg
|
||||||
start proc near
|
start proc near
|
||||||
mov ax,dseg
|
mov ax,dseg
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
;
|
;
|
||||||
; Select 640x480 graphics mode.
|
; Select 640x480 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,012h
|
mov ax,012h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Set driver to use the 8x8 font.
|
; Set driver to use the 8x8 font.
|
||||||
;
|
;
|
||||||
mov ah,11h ;VGA BIOS character generator function,
|
mov ah,11h ;VGA BIOS character generator function,
|
||||||
mov al,30h ; return info subfunction
|
mov al,30h ; return info subfunction
|
||||||
mov bh,3;get 8x8 font pointer
|
mov bh,3;get 8x8 font pointer
|
||||||
int 10h
|
int 10h
|
||||||
call SelectFont
|
call SelectFont
|
||||||
;
|
;
|
||||||
; Print the test string.
|
; Print the test string.
|
||||||
;
|
;
|
||||||
mov si,offset TestString
|
mov si,offset TestString
|
||||||
mov bx,TEST_TEXT_ROW
|
mov bx,TEST_TEXT_ROW
|
||||||
mov cx,TEST_TEXT_COL
|
mov cx,TEST_TEXT_COL
|
||||||
StringOutLoop:
|
StringOutLoop:
|
||||||
lodsb
|
lodsb
|
||||||
and al,al
|
and al,al
|
||||||
jz StringOutDone
|
jz StringOutDone
|
||||||
call DrawChar
|
call DrawChar
|
||||||
add cx,TEST_TEXT_WIDTH
|
add cx,TEST_TEXT_WIDTH
|
||||||
jmp StringOutLoop
|
jmp StringOutLoop
|
||||||
StringOutDone:
|
StringOutDone:
|
||||||
;
|
;
|
||||||
; Reset the data rotate and bit mask registers.
|
; Reset the data rotate and bit mask registers.
|
||||||
;
|
;
|
||||||
SETGC GC_ROTATE, 0
|
SETGC GC_ROTATE, 0
|
||||||
SETGC GC_BIT_MASK, 0ffh
|
SETGC GC_BIT_MASK, 0ffh
|
||||||
;
|
;
|
||||||
; Wait for a keystroke.
|
; Wait for a keystroke.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Return to text mode.
|
; Return to text mode.
|
||||||
;
|
;
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Exit to DOS.
|
; Exit to DOS.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
Start endp
|
Start endp
|
||||||
;
|
;
|
||||||
; Subroutine to draw a text character in a linear graphics mode
|
; Subroutine to draw a text character in a linear graphics mode
|
||||||
; (0Dh, 0Eh, 0Fh, 010h, 012h).
|
; (0Dh, 0Eh, 0Fh, 010h, 012h).
|
||||||
; Font used should be pointed to by FontPointer.
|
; Font used should be pointed to by FontPointer.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; AL = character to draw
|
; AL = character to draw
|
||||||
; BX = row to draw text character at
|
; BX = row to draw text character at
|
||||||
; CX = column to draw text character at
|
; CX = column to draw text character at
|
||||||
;
|
;
|
||||||
; Forces ALU function to "move".
|
; Forces ALU function to "move".
|
||||||
;
|
;
|
||||||
DrawChar proc near
|
DrawChar proc near
|
||||||
push ax
|
push ax
|
||||||
push bx
|
push bx
|
||||||
push cx
|
push cx
|
||||||
|
|
@ -125,15 +126,15 @@ pages: 466-470
|
||||||
push di
|
push di
|
||||||
push bp
|
push bp
|
||||||
push ds
|
push ds
|
||||||
;
|
;
|
||||||
; Set DS:SI to point to font and ES to point to display memory.
|
; Set DS:SI to point to font and ES to point to display memory.
|
||||||
;
|
;
|
||||||
lds si,[FontPointer] ;point to font
|
lds si,[FontPointer] ;point to font
|
||||||
mov dx,VGA_VIDEO_SEGMENT
|
mov dx,VGA_VIDEO_SEGMENT
|
||||||
mov es,dx ;point to display memory
|
mov es,dx ;point to display memory
|
||||||
;
|
;
|
||||||
; Calculate screen address of byte character starts in.
|
; Calculate screen address of byte character starts in.
|
||||||
;
|
;
|
||||||
push ds ;point to BIOS data segment
|
push ds ;point to BIOS data segment
|
||||||
sub dx,dx
|
sub dx,dx
|
||||||
mov ds,dx
|
mov ds,dx
|
||||||
|
|
@ -149,78 +150,78 @@ pages: 466-470
|
||||||
shr di,1
|
shr di,1
|
||||||
shr di,1 ;divide column by 8 to make a byte address
|
shr di,1 ;divide column by 8 to make a byte address
|
||||||
add di,ax ;and point to byte
|
add di,ax ;and point to byte
|
||||||
;
|
;
|
||||||
; Calculate font address of character.
|
; Calculate font address of character.
|
||||||
;
|
;
|
||||||
sub bh,bh
|
sub bh,bh
|
||||||
shl bx,1 ;assumes 8 bytes per character; use
|
shl bx,1 ;assumes 8 bytes per character; use
|
||||||
shl bx,1 ; a multiply otherwise
|
shl bx,1 ; a multiply otherwise
|
||||||
shl bx,1 ;offset in font of character
|
shl bx,1 ;offset in font of character
|
||||||
add si,bx ;offset in font segment of character
|
add si,bx ;offset in font segment of character
|
||||||
;
|
;
|
||||||
; Set up the GC rotation.
|
; Set up the GC rotation.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_ROTATE
|
mov al,GC_ROTATE
|
||||||
mov ah,cl
|
mov ah,cl
|
||||||
out dx,ax
|
out dx,ax
|
||||||
;
|
;
|
||||||
; Set up BH as bit mask for left half,
|
; Set up BH as bit mask for left half,
|
||||||
; BL as rotation for right half.
|
; BL as rotation for right half.
|
||||||
;
|
;
|
||||||
mov bx,0ffffh
|
mov bx,0ffffh
|
||||||
shr bh,cl
|
shr bh,cl
|
||||||
neg cl
|
neg cl
|
||||||
add cl,8
|
add cl,8
|
||||||
shl bl,cl
|
shl bl,cl
|
||||||
;
|
;
|
||||||
; Draw the character, left half first, then right half in the
|
; Draw the character, left half first, then right half in the
|
||||||
; succeeding byte, using the data rotation to position the character
|
; succeeding byte, using the data rotation to position the character
|
||||||
; across the byte boundary and then using the bit mask to get the
|
; across the byte boundary and then using the bit mask to get the
|
||||||
; proper portion of the character into each byte.
|
; proper portion of the character into each byte.
|
||||||
; Does not check for case where character is byte-aligned and
|
; Does not check for case where character is byte-aligned and
|
||||||
; no rotation and only one write is required.
|
; no rotation and only one write is required.
|
||||||
;
|
;
|
||||||
mov bp,FONT_CHARACTER_SIZE
|
mov bp,FONT_CHARACTER_SIZE
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
pop cx ;get back screen width
|
pop cx ;get back screen width
|
||||||
dec cx
|
dec cx
|
||||||
dec cx ; -2 because do two bytes for each char
|
dec cx ; -2 because do two bytes for each char
|
||||||
CharacterLoop:
|
CharacterLoop:
|
||||||
;
|
;
|
||||||
; Set the bit mask for the left half of the character.
|
; Set the bit mask for the left half of the character.
|
||||||
;
|
;
|
||||||
mov al,GC_BIT_MASK
|
mov al,GC_BIT_MASK
|
||||||
mov ah,bh
|
mov ah,bh
|
||||||
out dx,ax
|
out dx,ax
|
||||||
;
|
;
|
||||||
; Get the next character byte & write it to display memory.
|
; Get the next character byte & write it to display memory.
|
||||||
; (Left half of character.)
|
; (Left half of character.)
|
||||||
;
|
;
|
||||||
mov al,[si] ;get character byte
|
mov al,[si] ;get character byte
|
||||||
mov ah,es:[di] ;load latches
|
mov ah,es:[di] ;load latches
|
||||||
stosb ;write character byte
|
stosb ;write character byte
|
||||||
;
|
;
|
||||||
; Set the bit mask for the right half of the character.
|
; Set the bit mask for the right half of the character.
|
||||||
;
|
;
|
||||||
mov al,GC_BIT_MASK
|
mov al,GC_BIT_MASK
|
||||||
mov ah,bl
|
mov ah,bl
|
||||||
out dx,ax
|
out dx,ax
|
||||||
;
|
;
|
||||||
; Get the character byte again & write it to display memory.
|
; Get the character byte again & write it to display memory.
|
||||||
; (Right half of character.)
|
; (Right half of character.)
|
||||||
;
|
;
|
||||||
lodsb ;get character byte
|
lodsb ;get character byte
|
||||||
mov ah,es:[di] ;load latches
|
mov ah,es:[di] ;load latches
|
||||||
stosb ;write character byte
|
stosb ;write character byte
|
||||||
;
|
;
|
||||||
; Point to next line of character in display memory.
|
; Point to next line of character in display memory.
|
||||||
;
|
;
|
||||||
add di,cx
|
add di,cx
|
||||||
;
|
;
|
||||||
dec bp
|
dec bp
|
||||||
jnz CharacterLoop
|
jnz CharacterLoop
|
||||||
;
|
;
|
||||||
pop ds
|
pop ds
|
||||||
pop bp
|
pop bp
|
||||||
pop di
|
pop di
|
||||||
|
|
@ -230,18 +231,19 @@ pages: 466-470
|
||||||
pop bx
|
pop bx
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
DrawChar endp
|
DrawChar endp
|
||||||
;
|
;
|
||||||
; Set the pointer to the font to draw from to ES:BP.
|
; Set the pointer to the font to draw from to ES:BP.
|
||||||
;
|
;
|
||||||
SelectFont proc near
|
SelectFont proc near
|
||||||
mov word ptr [FontPointer],bp ;save pointer
|
mov word ptr [FontPointer],bp ;save pointer
|
||||||
mov word ptr [FontPointer+2],es
|
mov word ptr [FontPointer+2],es
|
||||||
ret
|
ret
|
||||||
SelectFont endp
|
SelectFont endp
|
||||||
;
|
;
|
||||||
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
|
||||||
|
|
|
||||||
88
25-04.md
88
25-04.md
|
|
@ -12,24 +12,25 @@ pages: 472-474
|
||||||
|
|
||||||
**LISTING 25.2 L25-2.ASM**
|
**LISTING 25.2 L25-2.ASM**
|
||||||
|
|
||||||
; Program to illustrate operation of Map Mask register when drawing
|
```nasm
|
||||||
; to memory that already contains data.
|
; Program to illustrate operation of Map Mask register when drawing
|
||||||
; By Michael Abrash.
|
; to memory that already contains data.
|
||||||
;
|
; By Michael Abrash.
|
||||||
stack segment para stack ‘STACK'
|
;
|
||||||
|
stack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
|
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
|
||||||
;
|
;
|
||||||
; EGA register equates.
|
; EGA register equates.
|
||||||
;
|
;
|
||||||
SC_INDEX equ 3c4h ;SC index register
|
SC_INDEX equ 3c4h ;SC index register
|
||||||
SC_MAP_MASK equ 2 ;SC map mask register
|
SC_MAP_MASK equ 2 ;SC map mask register
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of SC chip to SETTING.
|
; Macro to set indexed register INDEX of SC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETSC macro INDEX, SETTING
|
SETSC macro INDEX, SETTING
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,INDEX
|
mov al,INDEX
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -38,36 +39,36 @@ pages: 472-474
|
||||||
out dx,al
|
out dx,al
|
||||||
dec dx
|
dec dx
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE#146;
|
cseg segment para public ‘CODE#146;
|
||||||
assume cs:cseg
|
assume cs:cseg
|
||||||
start proc near
|
start proc near
|
||||||
;
|
;
|
||||||
; Select 640x480 graphics mode.
|
; Select 640x480 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,012h
|
mov ax,012h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
mov ax,EGA_VIDEO_SEGMENT
|
mov ax,EGA_VIDEO_SEGMENT
|
||||||
mov es,ax ;point to video memory
|
mov es,ax ;point to video memory
|
||||||
;
|
;
|
||||||
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
|
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
|
||||||
;
|
;
|
||||||
SETSC SC_MAP_MASK,02h ;map mask setting enables only
|
SETSC SC_MAP_MASK,02h ;map mask setting enables only
|
||||||
; plane 1, the green plane
|
; plane 1, the green plane
|
||||||
sub di,di ;start at beginning of video memory
|
sub di,di ;start at beginning of video memory
|
||||||
mov al,0ffh
|
mov al,0ffh
|
||||||
mov bp,24 ;# bars to draw
|
mov bp,24 ;# bars to draw
|
||||||
HorzBarLoop:
|
HorzBarLoop:
|
||||||
mov cx,80*10 ;# bytes per horizontal bar
|
mov cx,80*10 ;# bytes per horizontal bar
|
||||||
rep stosb ;draw bar
|
rep stosb ;draw bar
|
||||||
add di,80*10 ;point to start of next bar
|
add di,80*10 ;point to start of next bar
|
||||||
dec bp
|
dec bp
|
||||||
jnz HorzBarLoop
|
jnz HorzBarLoop
|
||||||
;
|
;
|
||||||
; Fill screen with blue, using Map Mask register to enable writes
|
; Fill screen with blue, using Map Mask register to enable writes
|
||||||
; to blue plane only.
|
; to blue plane only.
|
||||||
;
|
;
|
||||||
SETSC SC_MAP_MASK,01h ;map mask setting enables only
|
SETSC SC_MAP_MASK,01h ;map mask setting enables only
|
||||||
; plane 0, the blue plane
|
; plane 0, the blue plane
|
||||||
sub di,di
|
sub di,di
|
||||||
|
|
@ -75,24 +76,25 @@ pages: 472-474
|
||||||
mov al,0ffh
|
mov al,0ffh
|
||||||
rep stosb ;perform fill (affects only
|
rep stosb ;perform fill (affects only
|
||||||
; plane 0, the blue plane)
|
; plane 0, the blue plane)
|
||||||
;
|
;
|
||||||
; Wait for a keystroke.
|
; Wait for a keystroke.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Restore text mode.
|
; Restore text mode.
|
||||||
;
|
;
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Exit to DOS.
|
; Exit to DOS.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
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}
|
||||||
|
|
||||||
|
|
|
||||||
218
25-05.md
218
25-05.md
|
|
@ -12,27 +12,28 @@ pages: 474-478
|
||||||
|
|
||||||
**LISTING 25.3 L25-3.ASM**
|
**LISTING 25.3 L25-3.ASM**
|
||||||
|
|
||||||
; Program to illustrate operation of set/reset circuitry to force
|
```nasm
|
||||||
; setting of memory that already contains data.
|
; Program to illustrate operation of set/reset circuitry to force
|
||||||
; By Michael Abrash.
|
; setting of memory that already contains data.
|
||||||
;
|
; By Michael Abrash.
|
||||||
stack segment para stack ‘STACK#146;
|
;
|
||||||
|
stack segment para stack ‘STACK#146;
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
|
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
|
||||||
;
|
;
|
||||||
; EGA register equates.
|
; EGA register equates.
|
||||||
;
|
;
|
||||||
SC_INDEX equ 3c4h ;SC index register
|
SC_INDEX equ 3c4h ;SC index register
|
||||||
SC_MAP_MASK equ 2 ;SC map mask register
|
SC_MAP_MASK equ 2 ;SC map mask register
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_SET_RESET equ 0 ;GC set/reset register
|
GC_SET_RESET equ 0 ;GC set/reset register
|
||||||
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
|
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of SC chip to SETTING.
|
; Macro to set indexed register INDEX of SC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETSC macro INDEX, SETTING
|
SETSC macro INDEX, SETTING
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,INDEX
|
mov al,INDEX
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -41,10 +42,10 @@ pages: 474-478
|
||||||
out dx,al
|
out dx,al
|
||||||
dec dx
|
dec dx
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of GC chip to SETTING.
|
; Macro to set indexed register INDEX of GC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETGC macro INDEX, SETTING
|
SETGC macro INDEX, SETTING
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,INDEX
|
mov al,INDEX
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -53,36 +54,36 @@ pages: 474-478
|
||||||
out dx,al
|
out dx,al
|
||||||
dec dx
|
dec dx
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE#146;
|
cseg segment para public ‘CODE#146;
|
||||||
assume cs:cseg
|
assume cs:cseg
|
||||||
start proc near
|
start proc near
|
||||||
;
|
;
|
||||||
; Select 640x480 graphics mode.
|
; Select 640x480 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,012h
|
mov ax,012h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
mov ax,EGA_VIDEO_SEGMENT
|
mov ax,EGA_VIDEO_SEGMENT
|
||||||
mov es,ax ;point to video memory
|
mov es,ax ;point to video memory
|
||||||
;
|
;
|
||||||
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
|
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
|
||||||
;
|
;
|
||||||
SETSC SC_MAP_MASK,02h ;map mask setting enables only
|
SETSC SC_MAP_MASK,02h ;map mask setting enables only
|
||||||
; plane 1, the green plane
|
; plane 1, the green plane
|
||||||
sub di,di ;start at beginning of video memory
|
sub di,di ;start at beginning of video memory
|
||||||
mov al,0ffh
|
mov al,0ffh
|
||||||
mov bp,24 ;# bars to draw
|
mov bp,24 ;# bars to draw
|
||||||
HorzBarLoop:
|
HorzBarLoop:
|
||||||
mov cx,80*10 ;# bytes per horizontal bar
|
mov cx,80*10 ;# bytes per horizontal bar
|
||||||
rep stosb ;draw bar
|
rep stosb ;draw bar
|
||||||
add di,80*10 ;point to start of next bar
|
add di,80*10 ;point to start of next bar
|
||||||
dec bp
|
dec bp
|
||||||
jnz HorzBarLoop
|
jnz HorzBarLoop
|
||||||
;
|
;
|
||||||
; Fill screen with blue, using set/reset to force plane 0 to 1#146;s and all
|
; Fill screen with blue, using set/reset to force plane 0 to 1#146;s and all
|
||||||
; other plane to 0#146;s.
|
; other plane to 0#146;s.
|
||||||
;
|
;
|
||||||
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
|
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
|
||||||
; planes, so set/reset values can
|
; planes, so set/reset values can
|
||||||
; be written to memory
|
; be written to memory
|
||||||
|
|
@ -98,28 +99,29 @@ pages: 474-478
|
||||||
; only the act of writing is
|
; only the act of writing is
|
||||||
; important
|
; important
|
||||||
rep stosb ;perform fill (affects all planes)
|
rep stosb ;perform fill (affects all planes)
|
||||||
;
|
;
|
||||||
; Turn off set/reset.
|
; Turn off set/reset.
|
||||||
;
|
;
|
||||||
SETGC GC_ENABLE_SET_RESET,0
|
SETGC GC_ENABLE_SET_RESET,0
|
||||||
;
|
;
|
||||||
; Wait for a keystroke.
|
; Wait for a keystroke.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Restore text mode.
|
; Restore text mode.
|
||||||
;
|
;
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Exit to DOS.
|
; Exit to DOS.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
start endp
|
start endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
||||||
#### Manipulating Planes Individually {#Heading7}
|
#### Manipulating Planes Individually {#Heading7}
|
||||||
|
|
||||||
|
|
@ -142,27 +144,28 @@ be used to control individual pixels.
|
||||||
|
|
||||||
**LISTING 25.4 L25-4.ASM**
|
**LISTING 25.4 L25-4.ASM**
|
||||||
|
|
||||||
; Program to illustrate operation of set/reset circuitry in conjunction
|
```nasm
|
||||||
; with CPU data to modify setting of memory that already contains data.
|
; Program to illustrate operation of set/reset circuitry in conjunction
|
||||||
; By Michael Abrash.
|
; with CPU data to modify setting of memory that already contains data.
|
||||||
;
|
; By Michael Abrash.
|
||||||
stack segment para stack ‘STACK#146;
|
;
|
||||||
|
stack segment para stack ‘STACK#146;
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
|
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
|
||||||
;
|
;
|
||||||
; EGA register equates.
|
; EGA register equates.
|
||||||
;
|
;
|
||||||
SC_INDEX equ 3c4h ;SC index register
|
SC_INDEX equ 3c4h ;SC index register
|
||||||
SC_MAP_MASK equ 2 ;SC map mask register
|
SC_MAP_MASK equ 2 ;SC map mask register
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_SET_RESET equ 0 ;GC set/reset register
|
GC_SET_RESET equ 0 ;GC set/reset register
|
||||||
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
|
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of SC chip to SETTING.
|
; Macro to set indexed register INDEX of SC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETSC macro INDEX, SETTING
|
SETSC macro INDEX, SETTING
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,INDEX
|
mov al,INDEX
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -171,10 +174,10 @@ be used to control individual pixels.
|
||||||
out dx,al
|
out dx,al
|
||||||
dec dx
|
dec dx
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
; Macro to set indexed register INDEX of GC chip to SETTING.
|
; Macro to set indexed register INDEX of GC chip to SETTING.
|
||||||
;
|
;
|
||||||
SETGC macro INDEX, SETTING
|
SETGC macro INDEX, SETTING
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,INDEX
|
mov al,INDEX
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -183,36 +186,36 @@ be used to control individual pixels.
|
||||||
out dx,al
|
out dx,al
|
||||||
dec dx
|
dec dx
|
||||||
endm
|
endm
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE#146;
|
cseg segment para public ‘CODE#146;
|
||||||
assume cs:cseg
|
assume cs:cseg
|
||||||
start proc near
|
start proc near
|
||||||
;
|
;
|
||||||
; Select 640x350 graphics mode.
|
; Select 640x350 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,010h
|
mov ax,010h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
mov ax,EGA_VIDEO_SEGMENT
|
mov ax,EGA_VIDEO_SEGMENT
|
||||||
mov es,ax ;point to video memory
|
mov es,ax ;point to video memory
|
||||||
;
|
;
|
||||||
; Draw 18 10-scan-line high horizontal bars in green, 10 scan lines apart.
|
; Draw 18 10-scan-line high horizontal bars in green, 10 scan lines apart.
|
||||||
;
|
;
|
||||||
SETSC SC_MAP_MASK,02h;map mask setting enables only
|
SETSC SC_MAP_MASK,02h;map mask setting enables only
|
||||||
; plane 1, the green plane
|
; plane 1, the green plane
|
||||||
sub di,di;start at beginning of video memory
|
sub di,di;start at beginning of video memory
|
||||||
mov al,0ffh
|
mov al,0ffh
|
||||||
mov bp,18;# bars to draw
|
mov bp,18;# bars to draw
|
||||||
HorzBarLoop:
|
HorzBarLoop:
|
||||||
mov cx,80*10;# bytes per horizontal bar
|
mov cx,80*10;# bytes per horizontal bar
|
||||||
rep stosb;draw bar
|
rep stosb;draw bar
|
||||||
add di,80*10;point to start of next bar
|
add di,80*10;point to start of next bar
|
||||||
dec bp
|
dec bp
|
||||||
jnz HorzBarLoop
|
jnz HorzBarLoop
|
||||||
;
|
;
|
||||||
; Fill screen with alternating bars of red and brown, using CPU data
|
; Fill screen with alternating bars of red and brown, using CPU data
|
||||||
; to set plane 1 and set/reset to set planes 0, 2 & 3.
|
; to set plane 1 and set/reset to set planes 0, 2 & 3.
|
||||||
;
|
;
|
||||||
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
|
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
|
||||||
; planes, so set/reset values can
|
; planes, so set/reset values can
|
||||||
; be written to planes 0, 2 & 3
|
; be written to planes 0, 2 & 3
|
||||||
|
|
@ -228,25 +231,26 @@ be used to control individual pixels.
|
||||||
mov ax,07e0h ;CPU data controls only plane 1;
|
mov ax,07e0h ;CPU data controls only plane 1;
|
||||||
; set/reset controls other planes
|
; set/reset controls other planes
|
||||||
rep stosw ;perform fill (affects all planes)
|
rep stosw ;perform fill (affects all planes)
|
||||||
;
|
;
|
||||||
; Turn off set/reset.
|
; Turn off set/reset.
|
||||||
;
|
;
|
||||||
SETGC GC_ENABLE_SET_RESET,0
|
SETGC GC_ENABLE_SET_RESET,0
|
||||||
;
|
;
|
||||||
; Wait for a keystroke.
|
; Wait for a keystroke.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Restore text mode.
|
; Restore text mode.
|
||||||
;
|
;
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Exit to DOS.
|
; Exit to DOS.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
start endp
|
start endp
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
284
26-02.md
284
26-02.md
|
|
@ -12,58 +12,59 @@ pages: 484-489
|
||||||
|
|
||||||
**LISTING 26.1 L26-1.ASM**
|
**LISTING 26.1 L26-1.ASM**
|
||||||
|
|
||||||
; Program to illustrate operation of write mode 3 of the VGA.
|
```nasm
|
||||||
; Draws 8x8 characters at arbitrary locations without disturbing
|
; Program to illustrate operation of write mode 3 of the VGA.
|
||||||
; the background, using VGA's 8x8 ROM font. Designed
|
; Draws 8x8 characters at arbitrary locations without disturbing
|
||||||
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
|
; the background, using VGA's 8x8 ROM font. Designed
|
||||||
; Runs only on VGAs (in Models 50 & up and IBM Display Adapter
|
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
|
||||||
; and 100% compatibles).
|
; Runs only on VGAs (in Models 50 & up and IBM Display Adapter
|
||||||
; Assembled with MASM
|
; and 100% compatibles).
|
||||||
; By Michael Abrash
|
; Assembled with MASM
|
||||||
;
|
; By Michael Abrash
|
||||||
stack segment para stack ‘STACK'
|
;
|
||||||
|
stack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
||||||
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
|
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
|
||||||
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
|
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
|
||||||
;
|
;
|
||||||
; VGA register equates.
|
; VGA register equates.
|
||||||
;
|
;
|
||||||
SC_INDEX equ 3c4h ;SC index register
|
SC_INDEX equ 3c4h ;SC index register
|
||||||
SC_MAP_MASK equ 2 ;SC map mask register index
|
SC_MAP_MASK equ 2 ;SC map mask register index
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_SET_RESET equ 0 ;GC set/reset register index
|
GC_SET_RESET equ 0 ;GC set/reset register index
|
||||||
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register index
|
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register index
|
||||||
GC_ROTATE equ 3 ;GC data rotate/logical function
|
GC_ROTATE equ 3 ;GC data rotate/logical function
|
||||||
; register index
|
; register index
|
||||||
GC_MODE equ 5 ;GC Mode register
|
GC_MODE equ 5 ;GC Mode register
|
||||||
GC_BIT_MASK equ 8 ;GC bit mask register index
|
GC_BIT_MASK equ 8 ;GC bit mask register index
|
||||||
;
|
;
|
||||||
dseg segment para common ‘DATA'
|
dseg segment para common ‘DATA'
|
||||||
TEST_TEXT_ROW equ 69 ;row to display test text at
|
TEST_TEXT_ROW equ 69 ;row to display test text at
|
||||||
TEST_TEXT_COL equ 17 ;column to display test text at
|
TEST_TEXT_COL equ 17 ;column to display test text at
|
||||||
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
|
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
|
||||||
TestString label byte
|
TestString label byte
|
||||||
db ‘Hello, world!',0 ;test string to print.
|
db ‘Hello, world!',0 ;test string to print.
|
||||||
FontPointer dd ? ;font offset
|
FontPointer dd ? ;font offset
|
||||||
dseg ends
|
dseg ends
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE'
|
cseg segment para public ‘CODE'
|
||||||
assume cs:cseg, ds:dseg
|
assume cs:cseg, ds:dseg
|
||||||
start proc near
|
start proc near
|
||||||
mov ax,dseg
|
mov ax,dseg
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
;
|
;
|
||||||
; Select 640x480 graphics mode.
|
; Select 640x480 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,012h
|
mov ax,012h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Set the screen to all blue, using the readability of VGA registers
|
; Set the screen to all blue, using the readability of VGA registers
|
||||||
; to preserve reserved bits.
|
; to preserve reserved bits.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_SET_RESET
|
mov al,GC_SET_RESET
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -87,22 +88,22 @@ pages: 484-489
|
||||||
mov ax,0ffffh ;because of set/reset, the value
|
mov ax,0ffffh ;because of set/reset, the value
|
||||||
; written actually doesn't matter
|
; written actually doesn't matter
|
||||||
rep stosw ;fill with blue
|
rep stosw ;fill with blue
|
||||||
;
|
;
|
||||||
; Set driver to use the 8x8 font.
|
; Set driver to use the 8x8 font.
|
||||||
;
|
;
|
||||||
mov ah,11h ;VGA BIOS character generator function,
|
mov ah,11h ;VGA BIOS character generator function,
|
||||||
mov al,30h ; return info subfunction
|
mov al,30h ; return info subfunction
|
||||||
mov bh,3 ;get 8x8 font pointer
|
mov bh,3 ;get 8x8 font pointer
|
||||||
int 10h
|
int 10h
|
||||||
call SelectFont
|
call SelectFont
|
||||||
;
|
;
|
||||||
; Print the test string, cycling through colors.
|
; Print the test string, cycling through colors.
|
||||||
;
|
;
|
||||||
mov si,offset TestString
|
mov si,offset TestString
|
||||||
mov bx,TEST_TEXT_ROW
|
mov bx,TEST_TEXT_ROW
|
||||||
mov cx,TEST_TEXT_COL
|
mov cx,TEST_TEXT_COL
|
||||||
mov ah,0 ;start with color 0
|
mov ah,0 ;start with color 0
|
||||||
StringOutLoop:
|
StringOutLoop:
|
||||||
lodsb
|
lodsb
|
||||||
and al,al
|
and al,al
|
||||||
jz StringOutDone
|
jz StringOutDone
|
||||||
|
|
@ -113,36 +114,36 @@ pages: 484-489
|
||||||
and ah,0fh ;colors range from 0 to 15
|
and ah,0fh ;colors range from 0 to 15
|
||||||
add cx,TEST_TEXT_WIDTH
|
add cx,TEST_TEXT_WIDTH
|
||||||
jmp StringOutLoop
|
jmp StringOutLoop
|
||||||
StringOutDone:
|
StringOutDone:
|
||||||
;
|
;
|
||||||
; Wait for a key, then set to text mode & end.
|
; Wait for a key, then set to text mode & end.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 21h ;wait for a key
|
int 21h ;wait for a key
|
||||||
mov ax,3
|
mov ax,3
|
||||||
int 10h ;restore text mode
|
int 10h ;restore text mode
|
||||||
;
|
;
|
||||||
; Exit to DOS.
|
; Exit to DOS.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
Start endp
|
Start endp
|
||||||
;
|
;
|
||||||
; Subroutine to draw a text character in a linear graphics mode
|
; Subroutine to draw a text character in a linear graphics mode
|
||||||
; (0Dh, 0Eh, 0Fh, 010h, 012h). Background around the pixels that
|
; (0Dh, 0Eh, 0Fh, 010h, 012h). Background around the pixels that
|
||||||
; make up the character is preserved.
|
; make up the character is preserved.
|
||||||
; Font used should be pointed to by FontPointer.
|
; Font used should be pointed to by FontPointer.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; AL = character to draw
|
; AL = character to draw
|
||||||
; AH = color to draw character in (0-15)
|
; AH = color to draw character in (0-15)
|
||||||
; BX = row to draw text character at
|
; BX = row to draw text character at
|
||||||
; CX = column to draw text character at
|
; CX = column to draw text character at
|
||||||
;
|
;
|
||||||
; Forces ALU function to "move".
|
; Forces ALU function to "move".
|
||||||
; Forces write mode 3.
|
; Forces write mode 3.
|
||||||
;
|
;
|
||||||
DrawChar proc near
|
DrawChar proc near
|
||||||
push ax
|
push ax
|
||||||
push bx
|
push bx
|
||||||
push cx
|
push cx
|
||||||
|
|
@ -152,10 +153,10 @@ pages: 484-489
|
||||||
push bp
|
push bp
|
||||||
push ds
|
push ds
|
||||||
push ax ;preserve character to draw in AL
|
push ax ;preserve character to draw in AL
|
||||||
;
|
;
|
||||||
; Set up set/reset to produce character color, using the readability
|
; Set up set/reset to produce character color, using the readability
|
||||||
; of VGA register to preserve the setting of reserved bits 7-4.
|
; of VGA register to preserve the setting of reserved bits 7-4.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_SET_RESET
|
mov al,GC_SET_RESET
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -165,10 +166,10 @@ pages: 484-489
|
||||||
and ah,0fh
|
and ah,0fh
|
||||||
or al,ah
|
or al,ah
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Select write mode 3, using the readability of VGA registers
|
; Select write mode 3, using the readability of VGA registers
|
||||||
; to leave bits other than the write mode bits unchanged.
|
; to leave bits other than the write mode bits unchanged.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_MODE
|
mov al,GC_MODE
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -176,15 +177,15 @@ pages: 484-489
|
||||||
in al,dx
|
in al,dx
|
||||||
or al,3
|
or al,3
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Set DS:SI to point to font and ES to point to display memory.
|
; Set DS:SI to point to font and ES to point to display memory.
|
||||||
;
|
;
|
||||||
lds si,[FontPointer] ;point to font
|
lds si,[FontPointer] ;point to font
|
||||||
mov dx,VGA_VIDEO_SEGMENT
|
mov dx,VGA_VIDEO_SEGMENT
|
||||||
mov es,dx ;point to display memory
|
mov es,dx ;point to display memory
|
||||||
;
|
;
|
||||||
; Calculate screen address of byte character starts in.
|
; Calculate screen address of byte character starts in.
|
||||||
;
|
;
|
||||||
pop ax ;get back character to draw in AL
|
pop ax ;get back character to draw in AL
|
||||||
|
|
||||||
push ds ;point to BIOS data segment
|
push ds ;point to BIOS data segment
|
||||||
|
|
@ -202,20 +203,20 @@ pages: 484-489
|
||||||
shr di,1
|
shr di,1
|
||||||
shr di,1 ;divide column by 8 to make a byte address
|
shr di,1 ;divide column by 8 to make a byte address
|
||||||
add di,ax ;and point to byte
|
add di,ax ;and point to byte
|
||||||
;
|
;
|
||||||
; Calculate font address of character.
|
; Calculate font address of character.
|
||||||
;
|
;
|
||||||
sub bh,bh
|
sub bh,bh
|
||||||
shl bx,1 ;assumes 8 bytes per character; use
|
shl bx,1 ;assumes 8 bytes per character; use
|
||||||
shl bx,1 ; a multiply otherwise
|
shl bx,1 ; a multiply otherwise
|
||||||
shl bx,1 ;offset in font of character
|
shl bx,1 ;offset in font of character
|
||||||
add si,bx ;offset in font segment of character
|
add si,bx ;offset in font segment of character
|
||||||
;
|
;
|
||||||
; Set up the GC rotation. In write mode 3, this is the rotation
|
; Set up the GC rotation. In write mode 3, this is the rotation
|
||||||
; of CPU data before it is ANDed with the Bit Mask register to
|
; of CPU data before it is ANDed with the Bit Mask register to
|
||||||
; form the bit mask. Force the ALU function to "move". Uses the
|
; form the bit mask. Force the ALU function to "move". Uses the
|
||||||
; readability of VGA registers to leave reserved bits unchanged.
|
; readability of VGA registers to leave reserved bits unchanged.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_ROTATE
|
mov al,GC_ROTATE
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -224,66 +225,66 @@ pages: 484-489
|
||||||
and al,0e0h
|
and al,0e0h
|
||||||
or al,cl
|
or al,cl
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Set up BH as bit mask for left half, BL as rotation for right half.
|
; Set up BH as bit mask for left half, BL as rotation for right half.
|
||||||
;
|
;
|
||||||
mov bx,0ffffh
|
mov bx,0ffffh
|
||||||
shr bh,cl
|
shr bh,cl
|
||||||
neg cl
|
neg cl
|
||||||
add cl,8
|
add cl,8
|
||||||
shl bl,cl
|
shl bl,cl
|
||||||
;
|
;
|
||||||
; Draw the character, left half first, then right half in the
|
; Draw the character, left half first, then right half in the
|
||||||
; succeeding byte, using the data rotation to position the character
|
; succeeding byte, using the data rotation to position the character
|
||||||
; across the byte boundary and then using write mode 3 to combine the
|
; across the byte boundary and then using write mode 3 to combine the
|
||||||
; character data with the bit mask to allow the set/reset value (the
|
; character data with the bit mask to allow the set/reset value (the
|
||||||
; character color) through only for the proper portion (where the
|
; character color) through only for the proper portion (where the
|
||||||
; font bits for the character are 1) of the character for each byte.
|
; font bits for the character are 1) of the character for each byte.
|
||||||
; Wherever the font bits for the character are 0, the background
|
; Wherever the font bits for the character are 0, the background
|
||||||
; color is preserved.
|
; color is preserved.
|
||||||
; Does not check for case where character is byte-aligned and
|
; Does not check for case where character is byte-aligned and
|
||||||
; no rotation and only one write is required.
|
; no rotation and only one write is required.
|
||||||
;
|
;
|
||||||
mov bp,FONT_CHARACTER_SIZE
|
mov bp,FONT_CHARACTER_SIZE
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
pop cx ;get back screen width
|
pop cx ;get back screen width
|
||||||
dec cx
|
dec cx
|
||||||
dec cx ; -2 because do two bytes for each char
|
dec cx ; -2 because do two bytes for each char
|
||||||
CharacterLoop:
|
CharacterLoop:
|
||||||
;
|
;
|
||||||
; Set the bit mask for the left half of the character.
|
; Set the bit mask for the left half of the character.
|
||||||
;
|
;
|
||||||
mov al,GC_BIT_MASK
|
mov al,GC_BIT_MASK
|
||||||
mov ah,bh
|
mov ah,bh
|
||||||
out dx,ax
|
out dx,ax
|
||||||
;
|
;
|
||||||
; Get the next character byte & write it to display memory.
|
; Get the next character byte & write it to display memory.
|
||||||
; (Left half of character.)
|
; (Left half of character.)
|
||||||
;
|
;
|
||||||
mov al,[si] ;get character byte
|
mov al,[si] ;get character byte
|
||||||
mov ah,es:[di] ;load latches
|
mov ah,es:[di] ;load latches
|
||||||
stosb ;write character byte
|
stosb ;write character byte
|
||||||
;
|
;
|
||||||
; Set the bit mask for the right half of the character.
|
; Set the bit mask for the right half of the character.
|
||||||
;
|
;
|
||||||
mov al,GC_BIT_MASK
|
mov al,GC_BIT_MASK
|
||||||
mov ah,bl
|
mov ah,bl
|
||||||
out dx,ax
|
out dx,ax
|
||||||
;
|
;
|
||||||
; Get the character byte again & write it to display memory.
|
; Get the character byte again & write it to display memory.
|
||||||
; (Right half of character.)
|
; (Right half of character.)
|
||||||
;
|
;
|
||||||
lodsb ;get character byte
|
lodsb ;get character byte
|
||||||
mov ah,es:[di] ;load latches
|
mov ah,es:[di] ;load latches
|
||||||
stosb ;write character byte
|
stosb ;write character byte
|
||||||
;
|
;
|
||||||
; Point to next line of character in display memory.
|
; Point to next line of character in display memory.
|
||||||
;
|
;
|
||||||
add di,cx
|
add di,cx
|
||||||
;
|
;
|
||||||
dec bp
|
dec bp
|
||||||
jnz CharacterLoop
|
jnz CharacterLoop
|
||||||
;
|
;
|
||||||
pop ds
|
pop ds
|
||||||
pop bp
|
pop bp
|
||||||
pop di
|
pop di
|
||||||
|
|
@ -293,15 +294,16 @@ pages: 484-489
|
||||||
pop bx
|
pop bx
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
DrawChar endp
|
DrawChar endp
|
||||||
;
|
;
|
||||||
; Set the pointer to the font to draw from to ES:BP.
|
; Set the pointer to the font to draw from to ES:BP.
|
||||||
;
|
;
|
||||||
SelectFont proc near
|
SelectFont proc near
|
||||||
mov word ptr [FontPointer],bp ;save pointer
|
mov word ptr [FontPointer],bp ;save pointer
|
||||||
mov word ptr [FontPointer+2],es
|
mov word ptr [FontPointer+2],es
|
||||||
ret
|
ret
|
||||||
SelectFont endp
|
SelectFont endp
|
||||||
;
|
;
|
||||||
cseg ends
|
cseg ends
|
||||||
end start
|
end start
|
||||||
|
```
|
||||||
|
|
|
||||||
308
26-03.md
308
26-03.md
|
|
@ -80,59 +80,60 @@ 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**
|
||||||
|
|
||||||
; Program to illustrate high-speed text-drawing operation of
|
```nasm
|
||||||
; write mode 3 of the VGA.
|
; Program to illustrate high-speed text-drawing operation of
|
||||||
; Draws a string of 8x14 characters at arbitrary locations
|
; write mode 3 of the VGA.
|
||||||
; without disturbing the background, using VGA's 8x14 ROM font.
|
; Draws a string of 8x14 characters at arbitrary locations
|
||||||
; Designed for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
|
; without disturbing the background, using VGA's 8x14 ROM font.
|
||||||
; Runs only on VGAs (in Models 50 & up and IBM Display Adapter
|
; Designed for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
|
||||||
; and 100% compatibles).
|
; Runs only on VGAs (in Models 50 & up and IBM Display Adapter
|
||||||
; Assembled with MASM
|
; and 100% compatibles).
|
||||||
; By Michael Abrash
|
; Assembled with MASM
|
||||||
;
|
; By Michael Abrash
|
||||||
stack segment para stack ‘STACK'
|
;
|
||||||
|
stack segment para stack ‘STACK'
|
||||||
db 512 dup(?)
|
db 512 dup(?)
|
||||||
stack ends
|
stack ends
|
||||||
;
|
;
|
||||||
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
|
||||||
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
|
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
|
||||||
FONT_CHARACTER_SIZE equ 14 ;# bytes in each font char
|
FONT_CHARACTER_SIZE equ 14 ;# bytes in each font char
|
||||||
;
|
;
|
||||||
; VGA register equates.
|
; VGA register equates.
|
||||||
;
|
;
|
||||||
SC_INDEX equ 3c4h ;SC index register
|
SC_INDEX equ 3c4h ;SC index register
|
||||||
SC_MAP_MASK equ 2 ;SC map mask register index
|
SC_MAP_MASK equ 2 ;SC map mask register index
|
||||||
GC_INDEX equ 3ceh ;GC index register
|
GC_INDEX equ 3ceh ;GC index register
|
||||||
GC_SET_RESET equ 0 ;GC set/reset register index
|
GC_SET_RESET equ 0 ;GC set/reset register index
|
||||||
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register index
|
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register index
|
||||||
GC_ROTATE equ 3 ;GC data rotate/logical function
|
GC_ROTATE equ 3 ;GC data rotate/logical function
|
||||||
; register index
|
; register index
|
||||||
GC_MODE equ 5 ;GC Mode register
|
GC_MODE equ 5 ;GC Mode register
|
||||||
GC_BIT_MASK equ 8 ;GC bit mask register index
|
GC_BIT_MASK equ 8 ;GC bit mask register index
|
||||||
;
|
;
|
||||||
dseg segment para common ‘DATA'
|
dseg segment para common ‘DATA'
|
||||||
TEST_TEXT_ROW equ 69 ;row to display test text at
|
TEST_TEXT_ROW equ 69 ;row to display test text at
|
||||||
TEST_TEXT_COL equ 17 ;column to display test text at
|
TEST_TEXT_COL equ 17 ;column to display test text at
|
||||||
TEST_TEXT_COLOR equ 0fh ;high intensity white
|
TEST_TEXT_COLOR equ 0fh ;high intensity white
|
||||||
TestString label byte
|
TestString label byte
|
||||||
db ‘Hello, world!',0 ;test string to print.
|
db ‘Hello, world!',0 ;test string to print.
|
||||||
FontPointer dd ? ;font offset
|
FontPointer dd ? ;font offset
|
||||||
dseg ends
|
dseg ends
|
||||||
;
|
;
|
||||||
cseg segment para public ‘CODE'
|
cseg segment para public ‘CODE'
|
||||||
assume cs:cseg, ds:dseg
|
assume cs:cseg, ds:dseg
|
||||||
start proc near
|
start proc near
|
||||||
mov ax,dseg
|
mov ax,dseg
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
;
|
;
|
||||||
; Select 640x480 graphics mode.
|
; Select 640x480 graphics mode.
|
||||||
;
|
;
|
||||||
mov ax,012h
|
mov ax,012h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Set the screen to all blue, using the readability of VGA registers
|
; Set the screen to all blue, using the readability of VGA registers
|
||||||
; to preserve reserved bits.
|
; to preserve reserved bits.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_SET_RESET
|
mov al,GC_SET_RESET
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -156,52 +157,52 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
mov ax,0ffffh ;because of set/reset, the value
|
mov ax,0ffffh ;because of set/reset, the value
|
||||||
; written actually doesn't matter
|
; written actually doesn't matter
|
||||||
rep stosw ;fill with blue
|
rep stosw ;fill with blue
|
||||||
;
|
;
|
||||||
; Set driver to use the 8x14 font.
|
; Set driver to use the 8x14 font.
|
||||||
;
|
;
|
||||||
mov ah,11h ;VGA BIOS character generator function,
|
mov ah,11h ;VGA BIOS character generator function,
|
||||||
mov al,30h ; return info subfunction
|
mov al,30h ; return info subfunction
|
||||||
mov bh,2 ;get 8x14 font pointer
|
mov bh,2 ;get 8x14 font pointer
|
||||||
int 10h
|
int 10h
|
||||||
call SelectFont
|
call SelectFont
|
||||||
;
|
;
|
||||||
; Print the test string.
|
; Print the test string.
|
||||||
;
|
;
|
||||||
mov si,offset TestString
|
mov si,offset TestString
|
||||||
mov bx,TEST_TEXT_ROW
|
mov bx,TEST_TEXT_ROW
|
||||||
mov cx,TEST_TEXT_COL
|
mov cx,TEST_TEXT_COL
|
||||||
mov ah,TEST_TEXT_COLOR
|
mov ah,TEST_TEXT_COLOR
|
||||||
call DrawString
|
call DrawString
|
||||||
;
|
;
|
||||||
; Wait for a key, then set to text mode & end.
|
; Wait for a key, then set to text mode & end.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 21h ;wait for a key
|
int 21h ;wait for a key
|
||||||
mov ax,3
|
mov ax,3
|
||||||
int 10h ;restore text mode
|
int 10h ;restore text mode
|
||||||
;
|
;
|
||||||
; Exit to DOS.
|
; Exit to DOS.
|
||||||
;
|
;
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
Start endp
|
Start endp
|
||||||
;
|
;
|
||||||
; Subroutine to draw a text string left-to-right in a linear
|
; Subroutine to draw a text string left-to-right in a linear
|
||||||
; graphics mode (0Dh, 0Eh, 0Fh, 010h, 012h) with 8-dot-wide
|
; graphics mode (0Dh, 0Eh, 0Fh, 010h, 012h) with 8-dot-wide
|
||||||
; characters. Background around the pixels that make up the
|
; characters. Background around the pixels that make up the
|
||||||
; characters is preserved.
|
; characters is preserved.
|
||||||
; Font used should be pointed to by FontPointer.
|
; Font used should be pointed to by FontPointer.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; AH = color to draw string in
|
; AH = color to draw string in
|
||||||
; BX = row to draw string on
|
; BX = row to draw string on
|
||||||
; CX = column to start string at
|
; CX = column to start string at
|
||||||
; DS:SI = string to draw
|
; DS:SI = string to draw
|
||||||
;
|
;
|
||||||
; Forces ALU function to "move".
|
; Forces ALU function to "move".
|
||||||
; Forces write mode 3.
|
; Forces write mode 3.
|
||||||
;
|
;
|
||||||
DrawString proc near
|
DrawString proc near
|
||||||
push ax
|
push ax
|
||||||
push bx
|
push bx
|
||||||
push cx
|
push cx
|
||||||
|
|
@ -210,10 +211,10 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
push di
|
push di
|
||||||
push bp
|
push bp
|
||||||
push ds
|
push ds
|
||||||
;
|
;
|
||||||
; Set up set/reset to produce character color, using the readability
|
; Set up set/reset to produce character color, using the readability
|
||||||
; of VGA register to preserve the setting of reserved bits 7-4.
|
; of VGA register to preserve the setting of reserved bits 7-4.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_SET_RESET
|
mov al,GC_SET_RESET
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -223,10 +224,10 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
and ah,0fh
|
and ah,0fh
|
||||||
or al,ah
|
or al,ah
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Select write mode 3, using the readability of VGA registers
|
; Select write mode 3, using the readability of VGA registers
|
||||||
; to leave bits other than the write mode bits unchanged.
|
; to leave bits other than the write mode bits unchanged.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_MODE
|
mov al,GC_MODE
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -236,9 +237,9 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
out dx,al
|
out dx,al
|
||||||
mov dx,VGA_VIDEO_SEGMENT
|
mov dx,VGA_VIDEO_SEGMENT
|
||||||
mov es,dx ;point to display memory
|
mov es,dx ;point to display memory
|
||||||
;
|
;
|
||||||
; Calculate screen address of byte character starts in.
|
; Calculate screen address of byte character starts in.
|
||||||
;
|
;
|
||||||
push ds ;point to BIOS data segment
|
push ds ;point to BIOS data segment
|
||||||
sub dx,dx
|
sub dx,dx
|
||||||
mov ds,dx
|
mov ds,dx
|
||||||
|
|
@ -254,12 +255,12 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
shr di,1
|
shr di,1
|
||||||
shr di,1 ;divide column by 8 to make a byte address
|
shr di,1 ;divide column by 8 to make a byte address
|
||||||
add di,ax ;and point to byte
|
add di,ax ;and point to byte
|
||||||
;
|
;
|
||||||
; Set up the GC rotation. In write mode 3, this is the rotation
|
; Set up the GC rotation. In write mode 3, this is the rotation
|
||||||
; of CPU data before it is ANDed with the Bit Mask register to
|
; of CPU data before it is ANDed with the Bit Mask register to
|
||||||
; form the bit mask. Force the ALU function to "move". Uses the
|
; form the bit mask. Force the ALU function to "move". Uses the
|
||||||
; readability of VGA registers to leave reserved bits unchanged.
|
; readability of VGA registers to leave reserved bits unchanged.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_ROTATE
|
mov al,GC_ROTATE
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -268,71 +269,71 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
and al,0e0h
|
and al,0e0h
|
||||||
or al,cl
|
or al,cl
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Set up BH as bit mask for left half, BL as rotation for right half.
|
; Set up BH as bit mask for left half, BL as rotation for right half.
|
||||||
;
|
;
|
||||||
mov bx,0ffffh
|
mov bx,0ffffh
|
||||||
shr bh,cl
|
shr bh,cl
|
||||||
neg cl
|
neg cl
|
||||||
add cl,8
|
add cl,8
|
||||||
shl bl,cl
|
shl bl,cl
|
||||||
;
|
;
|
||||||
; Draw all characters, left portion first, then right portion in the
|
; Draw all characters, left portion first, then right portion in the
|
||||||
; succeeding byte, using the data rotation to position the character
|
; succeeding byte, using the data rotation to position the character
|
||||||
; across the byte boundary and then using write mode 3 to combine the
|
; across the byte boundary and then using write mode 3 to combine the
|
||||||
; character data with the bit mask to allow the set/reset value (the
|
; character data with the bit mask to allow the set/reset value (the
|
||||||
; character color) through only for the proper portion (where the
|
; character color) through only for the proper portion (where the
|
||||||
; font bits for the character are 1) of the character for each byte.
|
; font bits for the character are 1) of the character for each byte.
|
||||||
; Wherever the font bits for the character are 0, the background
|
; Wherever the font bits for the character are 0, the background
|
||||||
; color is preserved.
|
; color is preserved.
|
||||||
; Does not check for case where character is byte-aligned and
|
; Does not check for case where character is byte-aligned and
|
||||||
; no rotation and only one write is required.
|
; no rotation and only one write is required.
|
||||||
;
|
;
|
||||||
; Draw the left portion of each character in the string.
|
; Draw the left portion of each character in the string.
|
||||||
;
|
;
|
||||||
pop cx ;get back screen width
|
pop cx ;get back screen width
|
||||||
push si
|
push si
|
||||||
push di
|
push di
|
||||||
push bx
|
push bx
|
||||||
;
|
;
|
||||||
; Set the bit mask for the left half of the character.
|
; Set the bit mask for the left half of the character.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_BIT_MASK
|
mov al,GC_BIT_MASK
|
||||||
mov ah,bh
|
mov ah,bh
|
||||||
out dx,ax
|
out dx,ax
|
||||||
LeftHalfLoop:
|
LeftHalfLoop:
|
||||||
lodsb
|
lodsb
|
||||||
and al,al
|
and al,al
|
||||||
jz LeftHalfLoopDone
|
jz LeftHalfLoopDone
|
||||||
call CharacterUp
|
call CharacterUp
|
||||||
inc di ;point to next character location
|
inc di ;point to next character location
|
||||||
jmp LeftHalfLoop
|
jmp LeftHalfLoop
|
||||||
LeftHalfLoopDone:
|
LeftHalfLoopDone:
|
||||||
pop bx
|
pop bx
|
||||||
pop di
|
pop di
|
||||||
pop si
|
pop si
|
||||||
;
|
;
|
||||||
; Draw the right portion of each character in the string.
|
; Draw the right portion of each character in the string.
|
||||||
;
|
;
|
||||||
inc di ;right portion of each character is across
|
inc di ;right portion of each character is across
|
||||||
; byte boundary
|
; byte boundary
|
||||||
;
|
;
|
||||||
; Set the bit mask for the right half of the character.
|
; Set the bit mask for the right half of the character.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GC_BIT_MASK
|
mov al,GC_BIT_MASK
|
||||||
mov ah,bl
|
mov ah,bl
|
||||||
out dx,ax
|
out dx,ax
|
||||||
RightHalfLoop:
|
RightHalfLoop:
|
||||||
lodsb
|
lodsb
|
||||||
and al,al
|
and al,al
|
||||||
jz RightHalfLoopDone
|
jz RightHalfLoopDone
|
||||||
call CharacterUp
|
call CharacterUp
|
||||||
inc di ;point to next character location
|
inc di ;point to next character location
|
||||||
jmp RightHalfLoop
|
jmp RightHalfLoop
|
||||||
RightHalfLoopDone:
|
RightHalfLoopDone:
|
||||||
;
|
;
|
||||||
pop ds
|
pop ds
|
||||||
pop bp
|
pop bp
|
||||||
pop di
|
pop di
|
||||||
|
|
@ -342,62 +343,63 @@ along with the tables used to alter the 8x14 and 8x16 ROM fonts into
|
||||||
pop bx
|
pop bx
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
DrawString endp
|
DrawString endp
|
||||||
;
|
;
|
||||||
; Draw a character.
|
; Draw a character.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; AL = character
|
; AL = character
|
||||||
; CX = screen width
|
; CX = screen width
|
||||||
; ES:DI = address to draw character at
|
; ES:DI = address to draw character at
|
||||||
;
|
;
|
||||||
CharacterUp proc near
|
CharacterUp proc near
|
||||||
push cx
|
push cx
|
||||||
push si
|
push si
|
||||||
push di
|
push di
|
||||||
push ds
|
push ds
|
||||||
;
|
;
|
||||||
; Set DS:SI to point to font and ES to point to display memory.
|
; Set DS:SI to point to font and ES to point to display memory.
|
||||||
;
|
;
|
||||||
lds si,[FontPointer] ;point to font
|
lds si,[FontPointer] ;point to font
|
||||||
;
|
;
|
||||||
; Calculate font address of character.
|
; Calculate font address of character.
|
||||||
;
|
;
|
||||||
mov bl,14 ;14 bytes per character
|
mov bl,14 ;14 bytes per character
|
||||||
mul bl
|
mul bl
|
||||||
add si,ax ;offset in font segment of character
|
add si,ax ;offset in font segment of character
|
||||||
|
|
||||||
mov bp,FONT_CHARACTER_SIZE
|
mov bp,FONT_CHARACTER_SIZE
|
||||||
dec cx ; -1 because one byte per char
|
dec cx ; -1 because one byte per char
|
||||||
CharacterLoop:
|
CharacterLoop:
|
||||||
lodsb ;get character byte
|
lodsb ;get character byte
|
||||||
mov ah,es:[di] ;load latches
|
mov ah,es:[di] ;load latches
|
||||||
stosb ;write character byte
|
stosb ;write character byte
|
||||||
;
|
;
|
||||||
; Point to next line of character in display memory.
|
; Point to next line of character in display memory.
|
||||||
;
|
;
|
||||||
add di,cx
|
add di,cx
|
||||||
;
|
;
|
||||||
dec bp
|
dec bp
|
||||||
jnz CharacterLoop
|
jnz CharacterLoop
|
||||||
;
|
;
|
||||||
pop ds
|
pop ds
|
||||||
pop di
|
pop di
|
||||||
pop si
|
pop si
|
||||||
pop cx
|
pop cx
|
||||||
ret
|
ret
|
||||||
CharacterUp endp
|
CharacterUp endp
|
||||||
;
|
;
|
||||||
; Set the pointer to the font to draw from to ES:BP.
|
; Set the pointer to the font to draw from to ES:BP.
|
||||||
;
|
;
|
||||||
SelectFont proc near
|
SelectFont proc near
|
||||||
mov word ptr [FontPointer],bp ;save pointer
|
mov word ptr [FontPointer],bp ;save pointer
|
||||||
mov word ptr [FontPointer+2],es
|
mov word ptr [FontPointer+2],es
|
||||||
ret
|
ret
|
||||||
SelectFont endp
|
SelectFont endp
|
||||||
;
|
;
|
||||||
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,
|
||||||
|
|
|
||||||
178
27-02.md
178
27-02.md
|
|
@ -52,37 +52,38 @@ image.
|
||||||
|
|
||||||
**LISTING 27.1 L27-1.ASM**
|
**LISTING 27.1 L27-1.ASM**
|
||||||
|
|
||||||
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
```nasm
|
||||||
; animating the image of an "A" drawn by copying it from a chunky
|
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
||||||
; bit-map in system memory to a planar bit-map in VGA or EGA memory.
|
; 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.
|
||||||
; Assemble with MASM or TASM
|
;
|
||||||
;
|
; Assemble with MASM or TASM
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
Stack segment para stack ‘STACK'
|
;
|
||||||
|
Stack segment para stack ‘STACK'
|
||||||
db 512 dup(0)
|
db 512 dup(0)
|
||||||
Stack ends
|
Stack ends
|
||||||
|
|
||||||
SCREEN_WIDTH_IN_BYTES equ 80
|
SCREEN_WIDTH_IN_BYTES equ 80
|
||||||
DISPLAY_MEMORY_SEGMENT equ 0a000h
|
DISPLAY_MEMORY_SEGMENT equ 0a000h
|
||||||
SC_INDEX equ 3c4h ;Sequence Controller Index register
|
SC_INDEX equ 3c4h ;Sequence Controller Index register
|
||||||
MAP_MASK equ 2 ;index of Map Mask register
|
MAP_MASK equ 2 ;index of Map Mask register
|
||||||
GC_INDEX equ 03ceh ;Graphics Controller Index reg
|
GC_INDEX equ 03ceh ;Graphics Controller Index reg
|
||||||
GRAPHICS_MODE equ 5 ;index of Graphics Mode reg
|
GRAPHICS_MODE equ 5 ;index of Graphics Mode reg
|
||||||
BIT_MASKequ 8 ;index of Bit Mask reg
|
BIT_MASKequ 8 ;index of Bit Mask reg
|
||||||
|
|
||||||
Data segment para common ‘DATA'
|
Data segment para common ‘DATA'
|
||||||
;
|
;
|
||||||
; Current location of "A" as it is animated across the screen.
|
; Current location of "A" as it is animated across the screen.
|
||||||
;
|
;
|
||||||
CurrentX dw ?
|
CurrentX dw ?
|
||||||
CurrentY dw ?
|
CurrentY dw ?
|
||||||
RemainingLength dw ?
|
RemainingLength dw ?
|
||||||
;
|
;
|
||||||
; Chunky bit-map image of a yellow "A" on a bright blue background
|
; Chunky bit-map image of a yellow "A" on a bright blue background
|
||||||
;
|
;
|
||||||
AImage label byte
|
AImage label byte
|
||||||
dw 13, 13 ;width, height in pixels
|
dw 13, 13 ;width, height in pixels
|
||||||
db 000h, 000h, 000h, 000h, 000h, 000h, 000h
|
db 000h, 000h, 000h, 000h, 000h, 000h, 000h
|
||||||
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
|
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
|
||||||
|
|
@ -97,27 +98,27 @@ image.
|
||||||
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
|
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
|
||||||
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
|
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
|
||||||
db 000h, 000h, 000h, 000h, 000h, 000h, 000h
|
db 000h, 000h, 000h, 000h, 000h, 000h, 000h
|
||||||
Data ends
|
Data ends
|
||||||
|
|
||||||
Code segment para public ‘CODE'
|
Code segment para public ‘CODE'
|
||||||
assume cs:Code, ds:Data
|
assume cs:Code, ds:Data
|
||||||
Start proc near
|
Start proc near
|
||||||
mov ax,Data
|
mov ax,Data
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov ax,10h
|
mov ax,10h
|
||||||
int 10h ;select video mode 10h (640x350)
|
int 10h ;select video mode 10h (640x350)
|
||||||
;
|
;
|
||||||
; Prepare for animation.
|
; Prepare for animation.
|
||||||
;
|
;
|
||||||
mov [CurrentX],0
|
mov [CurrentX],0
|
||||||
mov [CurrentY],200
|
mov [CurrentY],200
|
||||||
mov [RemainingLength],600 ;move 600 times
|
mov [RemainingLength],600 ;move 600 times
|
||||||
;
|
;
|
||||||
; Animate, repeating RemainingLength times. It's unnecessary to erase
|
; Animate, repeating RemainingLength times. It's unnecessary to erase
|
||||||
; the old image, since the one pixel of blank fringe around the image
|
; the old image, since the one pixel of blank fringe around the image
|
||||||
; erases the part of the old image not overlapped by the new image.
|
; erases the part of the old image not overlapped by the new image.
|
||||||
;
|
;
|
||||||
AnimationLoop:
|
AnimationLoop:
|
||||||
mov bx,[CurrentX]
|
mov bx,[CurrentX]
|
||||||
mov cx,[CurrentY]
|
mov cx,[CurrentY]
|
||||||
mov si,offset AImage
|
mov si,offset AImage
|
||||||
|
|
@ -125,67 +126,67 @@ image.
|
||||||
inc [CurrentX] ;move one pixel to the right
|
inc [CurrentX] ;move one pixel to the right
|
||||||
|
|
||||||
mov cx,0 ;delay so we don't move the
|
mov cx,0 ;delay so we don't move the
|
||||||
DelayLoop: ; image too fast; adjust as
|
DelayLoop: ; image too fast; adjust as
|
||||||
; needed
|
; needed
|
||||||
loop DelayLoop
|
loop DelayLoop
|
||||||
|
|
||||||
dec [RemainingLength]
|
dec [RemainingLength]
|
||||||
jnz AnimationLoop
|
jnz AnimationLoop
|
||||||
;
|
;
|
||||||
; Wait for a key before returning to text mode and ending.
|
; Wait for a key before returning to text mode and ending.
|
||||||
;
|
;
|
||||||
mov ah,01h
|
mov ah,01h
|
||||||
int 21h
|
int 21h
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
Start endp
|
Start endp
|
||||||
;
|
;
|
||||||
; Draw an image stored in a chunky-bit map into planar VGA/EGA memory
|
; Draw an image stored in a chunky-bit map into planar VGA/EGA memory
|
||||||
; at the specified location.
|
; at the specified location.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; BX = X screen location at which to draw the upper-left corner
|
; BX = X screen location at which to draw the upper-left corner
|
||||||
; of the image
|
; of the image
|
||||||
; CX = Y screen location at which to draw the upper-left corner
|
; CX = Y screen location at which to draw the upper-left corner
|
||||||
; of the image
|
; of the image
|
||||||
; DS:SI = pointer to chunky image to draw, as follows:
|
; DS:SI = pointer to chunky image to draw, as follows:
|
||||||
; word at 0: width of image, in pixels
|
; word at 0: width of image, in pixels
|
||||||
; word at 2: height of image, in pixels
|
; word at 2: height of image, in pixels
|
||||||
; byte at 4: msb/lsb = first & second chunky pixels,
|
; byte at 4: msb/lsb = first & second chunky pixels,
|
||||||
; repeating for the remainder of the scan line
|
; repeating for the remainder of the scan line
|
||||||
; of the image, then for all scan lines. Images
|
; of the image, then for all scan lines. Images
|
||||||
; with odd widths have an unused null nibble
|
; with odd widths have an unused null nibble
|
||||||
; padding each scan line out to a byte width
|
; padding each scan line out to a byte width
|
||||||
;
|
;
|
||||||
; AX, BX, CX, DX, SI, DI, ES destroyed.
|
; AX, BX, CX, DX, SI, DI, ES destroyed.
|
||||||
;
|
;
|
||||||
DrawFromChunkyBitmap proc near
|
DrawFromChunkyBitmap proc near
|
||||||
cld
|
cld
|
||||||
;
|
;
|
||||||
; Select write mode 2.
|
; Select write mode 2.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GRAPHICS_MODE
|
mov al,GRAPHICS_MODE
|
||||||
out dx,al
|
out dx,al
|
||||||
inc dx
|
inc dx
|
||||||
mov al,02h
|
mov al,02h
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Enable writes to all 4 planes.
|
; Enable writes to all 4 planes.
|
||||||
;
|
;
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,MAP_MASK
|
mov al,MAP_MASK
|
||||||
out dx,al
|
out dx,al
|
||||||
inc dx
|
inc dx
|
||||||
mov al,0fh
|
mov al,0fh
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Point ES:DI to the display memory byte in which the first pixel
|
; Point ES:DI to the display memory byte in which the first pixel
|
||||||
; of the image goes, with AH set up as the bit mask to access that
|
; of the image goes, with AH set up as the bit mask to access that
|
||||||
; pixel within the addressed byte.
|
; pixel within the addressed byte.
|
||||||
;
|
;
|
||||||
mov ax,SCREEN_WIDTH_IN_BYTES
|
mov ax,SCREEN_WIDTH_IN_BYTES
|
||||||
mul cx ;offset of start of top scan line
|
mul cx ;offset of start of top scan line
|
||||||
mov di,ax
|
mov di,ax
|
||||||
|
|
@ -200,9 +201,9 @@ image.
|
||||||
mov bx,DISPLAY_MEMORY_SEGMENT
|
mov bx,DISPLAY_MEMORY_SEGMENT
|
||||||
mov es,bx ;ES:DI points to the byte at which the
|
mov es,bx ;ES:DI points to the byte at which the
|
||||||
; upper left of the image goes
|
; upper left of the image goes
|
||||||
;
|
;
|
||||||
; Get the width and height of the image.
|
; Get the width and height of the image.
|
||||||
;
|
;
|
||||||
mov cx,[si] ;get the width
|
mov cx,[si] ;get the width
|
||||||
inc si
|
inc si
|
||||||
inc si
|
inc si
|
||||||
|
|
@ -213,13 +214,13 @@ image.
|
||||||
mov al,BIT_MASK
|
mov al,BIT_MASK
|
||||||
out dx,al ;leave the GC Index register pointing
|
out dx,al ;leave the GC Index register pointing
|
||||||
inc dx ; to the Bit Mask register
|
inc dx ; to the Bit Mask register
|
||||||
RowLoop:
|
RowLoop:
|
||||||
|
|
||||||
push ax ;preserve the left column's bit mask
|
push ax ;preserve the left column's bit mask
|
||||||
push cx ;preserve the width
|
push cx ;preserve the width
|
||||||
push di ;preserve the destination offset
|
push di ;preserve the destination offset
|
||||||
|
|
||||||
ColumnLoop:
|
ColumnLoop:
|
||||||
mov al,ah
|
mov al,ah
|
||||||
out dx,al ;set the bit mask to draw this pixel
|
out dx,al ;set the bit mask to draw this pixel
|
||||||
mov al,es:[di] ;load the latches
|
mov al,es:[di] ;load the latches
|
||||||
|
|
@ -233,7 +234,7 @@ image.
|
||||||
jc CheckMorePixels ;is next pixel in the adjacent byte?
|
jc CheckMorePixels ;is next pixel in the adjacent byte?
|
||||||
dec di ;no
|
dec di ;no
|
||||||
|
|
||||||
CheckMorePixels:
|
CheckMorePixels:
|
||||||
dec cx ;see if there are any more pixels
|
dec cx ;see if there are any more pixels
|
||||||
jz AdvanceToNextScanLine ; across in image
|
jz AdvanceToNextScanLine ; across in image
|
||||||
mov al,ah
|
mov al,ah
|
||||||
|
|
@ -247,16 +248,16 @@ image.
|
||||||
jc CheckMorePixels2 ;is next pixel in the adjacent byte?
|
jc CheckMorePixels2 ;is next pixel in the adjacent byte?
|
||||||
dec di ;no
|
dec di ;no
|
||||||
|
|
||||||
CheckMorePixels2:
|
CheckMorePixels2:
|
||||||
loop ColumnLoop ;see if there are any more pixels
|
loop ColumnLoop ;see if there are any more pixels
|
||||||
; across in the image
|
; across in the image
|
||||||
jmp short CheckMoreScanLines
|
jmp short CheckMoreScanLines
|
||||||
|
|
||||||
AdvanceToNextScanLine:
|
AdvanceToNextScanLine:
|
||||||
inc si ;advance to the start of the next
|
inc si ;advance to the start of the next
|
||||||
; scan line in the image
|
; scan line in the image
|
||||||
|
|
||||||
CheckMoreScanLines:
|
CheckMoreScanLines:
|
||||||
pop di ;get back the destination offset
|
pop di ;get back the destination offset
|
||||||
pop cx ;get back the width
|
pop cx ;get back the width
|
||||||
pop ax ;get back the left column's bit mask
|
pop ax ;get back the left column's bit mask
|
||||||
|
|
@ -266,6 +267,7 @@ image.
|
||||||
dec bx ;see if there are any more scan lines
|
dec bx ;see if there are any more scan lines
|
||||||
jnz RowLoop ; in the image
|
jnz RowLoop ; in the image
|
||||||
ret
|
ret
|
||||||
DrawFromChunkyBitmap endp
|
DrawFromChunkyBitmap endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
322
27-03.md
322
27-03.md
|
|
@ -53,95 +53,96 @@ 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**
|
||||||
|
|
||||||
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
```nasm
|
||||||
; drawing lines in color patterns.
|
; Program to illustrate one use of write mode 2 of the VGA and EGA by
|
||||||
;
|
; drawing lines in color patterns.
|
||||||
; Assemble with MASM or TASM
|
;
|
||||||
;
|
; Assemble with MASM or TASM
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
Stack segment para stack ‘STACK'
|
;
|
||||||
|
Stack segment para stack ‘STACK'
|
||||||
db 512 dup(0)
|
db 512 dup(0)
|
||||||
Stack ends
|
Stack ends
|
||||||
|
|
||||||
SCREEN_WIDTH_IN_BYTES equ 80
|
SCREEN_WIDTH_IN_BYTES equ 80
|
||||||
GRAPHICS_SEGMENT equ 0a000h ;mode 10 bit-map segment
|
GRAPHICS_SEGMENT equ 0a000h ;mode 10 bit-map segment
|
||||||
SC_INDEX equ 3c4h ;Sequence Controller Index register
|
SC_INDEX equ 3c4h ;Sequence Controller Index register
|
||||||
MAP_MASK equ 2 ;index of Map Mask register
|
MAP_MASK equ 2 ;index of Map Mask register
|
||||||
GC_INDEX equ 03ceh ;Graphics Controller Index reg
|
GC_INDEX equ 03ceh ;Graphics Controller Index reg
|
||||||
GRAPHICS_MODE equ 5 ;index of Graphics Mode reg
|
GRAPHICS_MODE equ 5 ;index of Graphics Mode reg
|
||||||
BIT_MASK equ 8 ;index of Bit Mask reg
|
BIT_MASK equ 8 ;index of Bit Mask reg
|
||||||
|
|
||||||
Data segment para common ‘DATA'
|
Data segment para common ‘DATA'
|
||||||
Pattern0 db 16
|
Pattern0 db 16
|
||||||
db 0, 1, 2, 3, 4, 5, 6, 7, 8
|
db 0, 1, 2, 3, 4, 5, 6, 7, 8
|
||||||
db 9, 10, 11, 12, 13, 14, 15
|
db 9, 10, 11, 12, 13, 14, 15
|
||||||
Pattern1 db 6
|
Pattern1 db 6
|
||||||
db 2, 2, 2, 10, 10, 10
|
db 2, 2, 2, 10, 10, 10
|
||||||
Pattern2 db 8
|
Pattern2 db 8
|
||||||
db 15, 15, 15, 0, 0, 15, 0, 0
|
db 15, 15, 15, 0, 0, 15, 0, 0
|
||||||
Pattern3 db 9
|
Pattern3 db 9
|
||||||
db 1, 1, 1, 2, 2, 2, 4, 4, 4
|
db 1, 1, 1, 2, 2, 2, 4, 4, 4
|
||||||
Data ends
|
Data ends
|
||||||
|
|
||||||
Code segment para public ‘CODE'
|
Code segment para public ‘CODE'
|
||||||
assume cs:Code, ds:Data
|
assume cs:Code, ds:Data
|
||||||
Start proc near
|
Start proc near
|
||||||
mov ax,Data
|
mov ax,Data
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov ax,10h
|
mov ax,10h
|
||||||
int 10h ;select video mode 10h (640x350)
|
int 10h ;select video mode 10h (640x350)
|
||||||
;
|
;
|
||||||
; Draw 8 radial lines in upper-left quadrant in pattern 0.
|
; Draw 8 radial lines in upper-left quadrant in pattern 0.
|
||||||
;
|
;
|
||||||
mov bx,0
|
mov bx,0
|
||||||
mov cx,0
|
mov cx,0
|
||||||
mov si,offset Pattern0
|
mov si,offset Pattern0
|
||||||
call QuadrantUp
|
call QuadrantUp
|
||||||
;
|
;
|
||||||
; Draw 8 radial lines in upper-right quadrant in pattern 1.
|
; Draw 8 radial lines in upper-right quadrant in pattern 1.
|
||||||
;
|
;
|
||||||
mov bx,320
|
mov bx,320
|
||||||
mov cx,0
|
mov cx,0
|
||||||
mov si,offset Pattern1
|
mov si,offset Pattern1
|
||||||
call QuadrantUp
|
call QuadrantUp
|
||||||
;
|
;
|
||||||
; Draw 8 radial lines in lower-left quadrant in pattern 2.
|
; Draw 8 radial lines in lower-left quadrant in pattern 2.
|
||||||
;
|
;
|
||||||
mov bx,0
|
mov bx,0
|
||||||
mov cx,175
|
mov cx,175
|
||||||
mov si,offset Pattern2
|
mov si,offset Pattern2
|
||||||
call QuadrantUp
|
call QuadrantUp
|
||||||
;
|
;
|
||||||
; Draw 8 radial lines in lower-right quadrant in pattern 3.
|
; Draw 8 radial lines in lower-right quadrant in pattern 3.
|
||||||
;
|
;
|
||||||
mov bx,320
|
mov bx,320
|
||||||
mov cx,175
|
mov cx,175
|
||||||
mov si,offset Pattern3
|
mov si,offset Pattern3
|
||||||
call QuadrantUp
|
call QuadrantUp
|
||||||
;
|
;
|
||||||
; Wait for a key before returning to text mode and ending.
|
; Wait for a key before returning to text mode and ending.
|
||||||
;
|
;
|
||||||
mov ah,01h
|
mov ah,01h
|
||||||
int 21h
|
int 21h
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Draws 8 radial lines with specified pattern in specified mode 10h
|
; Draws 8 radial lines with specified pattern in specified mode 10h
|
||||||
; quadrant.
|
; quadrant.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; BX = X coordinate of upper left corner of quadrant
|
; BX = X coordinate of upper left corner of quadrant
|
||||||
; CX = Y coordinate of upper left corner of quadrant
|
; CX = Y coordinate of upper left corner of quadrant
|
||||||
; SI = pointer to pattern, in following form:
|
; SI = pointer to pattern, in following form:
|
||||||
; Byte 0: Length of pattern
|
; Byte 0: Length of pattern
|
||||||
; Byte 1: Start of pattern, one color per byte
|
; Byte 1: Start of pattern, one color per byte
|
||||||
;
|
;
|
||||||
; AX, BX, CX, DX destroyed
|
; AX, BX, CX, DX destroyed
|
||||||
;
|
;
|
||||||
QuadrantUp proc near
|
QuadrantUp proc near
|
||||||
add bx,160
|
add bx,160
|
||||||
add cx,87 ;point to the center of the quadrant
|
add cx,87 ;point to the center of the quadrant
|
||||||
mov ax,0
|
mov ax,0
|
||||||
|
|
@ -169,74 +170,74 @@ the CPU byte in write mode 2 to select the color in which to draw.
|
||||||
mov dx,88
|
mov dx,88
|
||||||
call LineUp ;draw diagonal line to bottom right
|
call LineUp ;draw diagonal line to bottom right
|
||||||
ret
|
ret
|
||||||
QuadrantUp endp
|
QuadrantUp endp
|
||||||
;
|
;
|
||||||
; Draws a horizontal, vertical, or diagonal line (one of the eight
|
; Draws a horizontal, vertical, or diagonal line (one of the eight
|
||||||
; possible radial lines) of the specified length from the specified
|
; possible radial lines) of the specified length from the specified
|
||||||
; starting point.
|
; starting point.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; AX = line direction, as follows:
|
; AX = line direction, as follows:
|
||||||
; 3 2 1
|
; 3 2 1
|
||||||
; 4 * 0
|
; 4 * 0
|
||||||
; 5 6 7
|
; 5 6 7
|
||||||
; BX = X coordinate of starting point
|
; BX = X coordinate of starting point
|
||||||
; CX = Y coordinate of starting point
|
; CX = Y coordinate of starting point
|
||||||
; DX = length of line (number of pixels drawn)
|
; DX = length of line (number of pixels drawn)
|
||||||
;
|
;
|
||||||
; All registers preserved.
|
; All registers preserved.
|
||||||
;
|
;
|
||||||
; Table of vectors to routines for each of the 8 possible lines.
|
; Table of vectors to routines for each of the 8 possible lines.
|
||||||
;
|
;
|
||||||
LineUpVectors label word
|
LineUpVectors label word
|
||||||
dw LineUp0, LineUp1, LineUp2, LineUp3
|
dw LineUp0, LineUp1, LineUp2, LineUp3
|
||||||
dw LineUp4, LineUp5, LineUp6, LineUp7
|
dw LineUp4, LineUp5, LineUp6, LineUp7
|
||||||
|
|
||||||
;
|
;
|
||||||
; Macro to draw horizontal, vertical, or diagonal line.
|
; Macro to draw horizontal, vertical, or diagonal line.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; XParm = 1 to draw right, -1 to draw left, 0 to not move horz.
|
; XParm = 1 to draw right, -1 to draw left, 0 to not move horz.
|
||||||
; YParm = 1 to draw up, -1 to draw down, 0 to not move vert.
|
; YParm = 1 to draw up, -1 to draw down, 0 to not move vert.
|
||||||
; BX = X start location
|
; BX = X start location
|
||||||
; CX = Y start location
|
; CX = Y start location
|
||||||
; DX = number of pixels to draw
|
; DX = number of pixels to draw
|
||||||
; DS:SI = line pattern
|
; DS:SI = line pattern
|
||||||
;
|
;
|
||||||
MLineUp macro XParm, YParm
|
MLineUp macro XParm, YParm
|
||||||
local LineUpLoop, CheckMoreLine
|
local LineUpLoop, CheckMoreLine
|
||||||
mov di,si ;set aside start offset of pattern
|
mov di,si ;set aside start offset of pattern
|
||||||
lodsb ;get length of pattern
|
lodsb ;get length of pattern
|
||||||
mov ah,al
|
mov ah,al
|
||||||
|
|
||||||
LineUpLoop:
|
LineUpLoop:
|
||||||
lodsb ;get color of this pixel...
|
lodsb ;get color of this pixel...
|
||||||
call DotUpInColor ;...and draw it
|
call DotUpInColor ;...and draw it
|
||||||
if XParm EQ 1
|
if XParm EQ 1
|
||||||
inc bx
|
inc bx
|
||||||
endif
|
endif
|
||||||
if XParm EQ -1
|
if XParm EQ -1
|
||||||
dec bx
|
dec bx
|
||||||
endif
|
endif
|
||||||
if YParm EQ 1
|
if YParm EQ 1
|
||||||
inc cx
|
inc cx
|
||||||
endif
|
endif
|
||||||
if YParm EQ -1
|
if YParm EQ -1
|
||||||
dec cx
|
dec cx
|
||||||
endif
|
endif
|
||||||
dec ah ;at end of pattern?
|
dec ah ;at end of pattern?
|
||||||
jnz CheckMoreLine
|
jnz CheckMoreLine
|
||||||
mov si,di ;get back start of pattern
|
mov si,di ;get back start of pattern
|
||||||
lodsb
|
lodsb
|
||||||
mov ah,al ;reset pattern count
|
mov ah,al ;reset pattern count
|
||||||
|
|
||||||
CheckMoreLine:
|
CheckMoreLine:
|
||||||
dec dx
|
dec dx
|
||||||
jnz LineUpLoop
|
jnz LineUpLoop
|
||||||
jmp LineUpEnd
|
jmp LineUpEnd
|
||||||
endm
|
endm
|
||||||
|
|
||||||
LineUp proc near
|
LineUp proc near
|
||||||
push ax
|
push ax
|
||||||
push bx
|
push bx
|
||||||
push cx
|
push cx
|
||||||
|
|
@ -251,73 +252,73 @@ the CPU byte in write mode 2 to select the color in which to draw.
|
||||||
mov es,ax
|
mov es,ax
|
||||||
|
|
||||||
push dx ;save line length
|
push dx ;save line length
|
||||||
;
|
;
|
||||||
; Enable writes to all planes.
|
; Enable writes to all planes.
|
||||||
;
|
;
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,MAP_MASK
|
mov al,MAP_MASK
|
||||||
out dx,al
|
out dx,al
|
||||||
inc dx
|
inc dx
|
||||||
mov al,0fh
|
mov al,0fh
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Select write mode 2.
|
; Select write mode 2.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,GRAPHICS_MODE
|
mov al,GRAPHICS_MODE
|
||||||
out dx,al
|
out dx,al
|
||||||
inc dx
|
inc dx
|
||||||
mov al,02h
|
mov al,02h
|
||||||
out dx,al
|
out dx,al
|
||||||
;
|
;
|
||||||
; Vector to proper routine.
|
; Vector to proper routine.
|
||||||
;
|
;
|
||||||
pop dx ;get back line length
|
pop dx ;get back line length
|
||||||
|
|
||||||
shl di,1
|
shl di,1
|
||||||
jmp cs:[LineUpVectors+di]
|
jmp cs:[LineUpVectors+di]
|
||||||
;
|
;
|
||||||
; Horizontal line to right.
|
; Horizontal line to right.
|
||||||
;
|
;
|
||||||
LineUp0:
|
LineUp0:
|
||||||
MLineUp 1, 0
|
MLineUp 1, 0
|
||||||
;
|
;
|
||||||
; Diagonal line to upper right.
|
; Diagonal line to upper right.
|
||||||
;
|
;
|
||||||
LineUp1:
|
LineUp1:
|
||||||
MLineUp 1, -1
|
MLineUp 1, -1
|
||||||
;
|
;
|
||||||
; Vertical line to top.
|
; Vertical line to top.
|
||||||
;
|
;
|
||||||
LineUp2:
|
LineUp2:
|
||||||
MLineUp 0, -1
|
MLineUp 0, -1
|
||||||
;
|
;
|
||||||
; Diagonal line to upper left.
|
; Diagonal line to upper left.
|
||||||
;
|
;
|
||||||
LineUp3:
|
LineUp3:
|
||||||
MLineUp -1, -1
|
MLineUp -1, -1
|
||||||
;
|
;
|
||||||
; Horizontal line to left.
|
; Horizontal line to left.
|
||||||
;
|
;
|
||||||
LineUp4:
|
LineUp4:
|
||||||
MLineUp -1, 0
|
MLineUp -1, 0
|
||||||
;
|
;
|
||||||
; Diagonal line to bottom left.
|
; Diagonal line to bottom left.
|
||||||
;
|
;
|
||||||
LineUp5:
|
LineUp5:
|
||||||
MLineUp -1, 1
|
MLineUp -1, 1
|
||||||
;
|
;
|
||||||
; Vertical line to bottom.
|
; Vertical line to bottom.
|
||||||
;
|
;
|
||||||
LineUp6:
|
LineUp6:
|
||||||
MLineUp 0, 1
|
MLineUp 0, 1
|
||||||
;
|
;
|
||||||
; Diagonal line to bottom right.
|
; Diagonal line to bottom right.
|
||||||
;
|
;
|
||||||
LineUp7:
|
LineUp7:
|
||||||
MLineUp 1, 1
|
MLineUp 1, 1
|
||||||
|
|
||||||
LineUpEnd:
|
LineUpEnd:
|
||||||
pop es
|
pop es
|
||||||
pop di
|
pop di
|
||||||
pop si
|
pop si
|
||||||
|
|
@ -326,29 +327,29 @@ the CPU byte in write mode 2 to select the color in which to draw.
|
||||||
pop bx
|
pop bx
|
||||||
pop ax
|
pop ax
|
||||||
ret
|
ret
|
||||||
LineUp endp
|
LineUp endp
|
||||||
;
|
;
|
||||||
; Draws a dot in the specified color at the specified location.
|
; Draws a dot in the specified color at the specified location.
|
||||||
; Assumes that the VGA is in write mode 2 with writes to all planes
|
; Assumes that the VGA is in write mode 2 with writes to all planes
|
||||||
; enabled and that ES points to display memory.
|
; enabled and that ES points to display memory.
|
||||||
;
|
;
|
||||||
; Input:
|
; Input:
|
||||||
; AL = dot color
|
; AL = dot color
|
||||||
; BX = X coordinate of dot
|
; BX = X coordinate of dot
|
||||||
; CX = Y coordinate of dot
|
; CX = Y coordinate of dot
|
||||||
; ES = display memory segment
|
; ES = display memory segment
|
||||||
;
|
;
|
||||||
; All registers preserved.
|
; All registers preserved.
|
||||||
;
|
;
|
||||||
DotUpInColor proc near
|
DotUpInColor proc near
|
||||||
push bx
|
push bx
|
||||||
push cx
|
push cx
|
||||||
push dx
|
push dx
|
||||||
push di
|
push di
|
||||||
;
|
;
|
||||||
; Point ES:DI to the display memory byte in which the pixel goes, with
|
; Point ES:DI to the display memory byte in which the pixel goes, with
|
||||||
; the bit mask set up to access that pixel within the addressed byte.
|
; the bit mask set up to access that pixel within the addressed byte.
|
||||||
;
|
;
|
||||||
push ax ;preserve dot color
|
push ax ;preserve dot color
|
||||||
mov ax,SCREEN_WIDTH_IN_BYTES
|
mov ax,SCREEN_WIDTH_IN_BYTES
|
||||||
mul cx ;offset of start of top scan line
|
mul cx ;offset of start of top scan line
|
||||||
|
|
@ -375,7 +376,8 @@ the CPU byte in write mode 2 to select the color in which to draw.
|
||||||
pop cx
|
pop cx
|
||||||
pop bx
|
pop bx
|
||||||
ret
|
ret
|
||||||
DotUpInColor endp
|
DotUpInColor endp
|
||||||
Start endp
|
Start endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
138
27-05.md
138
27-05.md
|
|
@ -53,53 +53,54 @@ rewarding!) VGA to cover.
|
||||||
|
|
||||||
**LISTING 27.3 L27-3.ASM**
|
**LISTING 27.3 L27-3.ASM**
|
||||||
|
|
||||||
; Program to illustrate flipping from bit-mapped graphics mode to
|
```nasm
|
||||||
; text mode and back without losing any of the graphics bit-map.
|
; Program to illustrate flipping from bit-mapped graphics mode to
|
||||||
;
|
; text mode and back without losing any of the graphics bit-map.
|
||||||
; Assemble with MASM or TASM
|
;
|
||||||
;
|
; Assemble with MASM or TASM
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
Stack segment para stack ‘STACK'
|
;
|
||||||
|
Stack segment para stack ‘STACK'
|
||||||
db 512 dup(0)
|
db 512 dup(0)
|
||||||
Stack ends
|
Stack ends
|
||||||
|
|
||||||
GRAPHICS_SEGMENT equ 0a000h ;mode 10 bit-map segment
|
GRAPHICS_SEGMENT equ 0a000h ;mode 10 bit-map segment
|
||||||
TEXT_SEGMENT equ 0b800h ;mode 3 bit-map segment
|
TEXT_SEGMENT equ 0b800h ;mode 3 bit-map segment
|
||||||
SC_INDEX equ 3c4h ;Sequence Controller Index register
|
SC_INDEX equ 3c4h ;Sequence Controller Index register
|
||||||
MAP_MASK equ 2 ;index of Map Mask register
|
MAP_MASK equ 2 ;index of Map Mask register
|
||||||
GC_INDEX equ 3ceh ;Graphics Controller Index register
|
GC_INDEX equ 3ceh ;Graphics Controller Index register
|
||||||
READ_MAP equ 4 ;index of Read Map register
|
READ_MAP equ 4 ;index of Read Map register
|
||||||
|
|
||||||
Data segment para common ‘DATA'
|
Data segment para common ‘DATA'
|
||||||
|
|
||||||
GStrikeAnyKeyMsg0 label byte
|
GStrikeAnyKeyMsg0 label byte
|
||||||
db 0dh, 0ah, ‘Graphics mode', 0dh, 0ah
|
db 0dh, 0ah, ‘Graphics mode', 0dh, 0ah
|
||||||
db ‘Strike any key to continue...', 0dh, 0ah, ‘$'
|
db ‘Strike any key to continue...', 0dh, 0ah, ‘$'
|
||||||
|
|
||||||
GStrikeAnyKeyMsg1 label byte
|
GStrikeAnyKeyMsg1 label byte
|
||||||
db 0dh, 0ah, ‘Graphics mode again', 0dh, 0ah
|
db 0dh, 0ah, ‘Graphics mode again', 0dh, 0ah
|
||||||
db ‘Strike any key to continue...', 0dh, 0ah, ‘$'
|
db ‘Strike any key to continue...', 0dh, 0ah, ‘$'
|
||||||
|
|
||||||
TStrikeAnyKeyMsg label byte
|
TStrikeAnyKeyMsg label byte
|
||||||
db 0dh, 0ah, ‘Text mode', 0dh, 0ah
|
db 0dh, 0ah, ‘Text mode', 0dh, 0ah
|
||||||
db ‘Strike any key to continue...', 0dh, 0ah, ‘$'
|
db ‘Strike any key to continue...', 0dh, 0ah, ‘$'
|
||||||
|
|
||||||
Plane2Save db 2000h dup (?) ;save area for plane 2 data
|
Plane2Save db 2000h dup (?) ;save area for plane 2 data
|
||||||
; where font gets loaded
|
; where font gets loaded
|
||||||
CharAttSave db 4000 dup (?) ;save area for memory wiped
|
CharAttSave db 4000 dup (?) ;save area for memory wiped
|
||||||
; out by character/attribute
|
; out by character/attribute
|
||||||
; data in text mode
|
; data in text mode
|
||||||
Data ends
|
Data ends
|
||||||
|
|
||||||
Code segment para public ‘CODE'
|
Code segment para public ‘CODE'
|
||||||
assume cs:Code, ds:Data
|
assume cs:Code, ds:Data
|
||||||
Start proc near
|
Start proc near
|
||||||
mov ax,10h
|
mov ax,10h
|
||||||
int 10h ;select video mode 10h (640x350)
|
int 10h ;select video mode 10h (640x350)
|
||||||
;
|
;
|
||||||
; Fill the graphics bit-map with a colored pattern.
|
; Fill the graphics bit-map with a colored pattern.
|
||||||
;
|
;
|
||||||
cld
|
cld
|
||||||
mov ax,GRAPHICS_SEGMENT
|
mov ax,GRAPHICS_SEGMENT
|
||||||
mov es,ax
|
mov es,ax
|
||||||
|
|
@ -110,7 +111,7 @@ rewarding!) VGA to cover.
|
||||||
out dx,al ;leave the SC Index pointing to the
|
out dx,al ;leave the SC Index pointing to the
|
||||||
inc dx ; Map Mask register
|
inc dx ; Map Mask register
|
||||||
|
|
||||||
FillBitMap:
|
FillBitMap:
|
||||||
mov al,10h
|
mov al,10h
|
||||||
shr al,cl ;generate map mask for this plane
|
shr al,cl ;generate map mask for this plane
|
||||||
out dx,al ;set map mask for this plane
|
out dx,al ;set map mask for this plane
|
||||||
|
|
@ -123,22 +124,22 @@ rewarding!) VGA to cover.
|
||||||
shl ah,1
|
shl ah,1
|
||||||
shl ah,1
|
shl ah,1
|
||||||
loop FillBitMap
|
loop FillBitMap
|
||||||
;
|
;
|
||||||
; Put up "strike any key" message.
|
; Put up "strike any key" message.
|
||||||
;
|
;
|
||||||
mov ax,Data
|
mov ax,Data
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov dx,offset GStrikeAnyKeyMsg0
|
mov dx,offset GStrikeAnyKeyMsg0
|
||||||
mov ah,9
|
mov ah,9
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Wait for a key.
|
; Wait for a key.
|
||||||
;
|
;
|
||||||
mov ah,01h
|
mov ah,01h
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Save the 8K of plane 2 that will be used by the font.
|
; Save the 8K of plane 2 that will be used by the font.
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
mov al,READ_MAP
|
mov al,READ_MAP
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -153,14 +154,14 @@ rewarding!) VGA to cover.
|
||||||
mov di,offset Plane2Save
|
mov di,offset Plane2Save
|
||||||
mov cx,2000h/2 ;save 8K (length of default font)
|
mov cx,2000h/2 ;save 8K (length of default font)
|
||||||
rep movsw
|
rep movsw
|
||||||
;
|
;
|
||||||
; Go to text mode without clearing display memory.
|
; Go to text mode without clearing display memory.
|
||||||
;
|
;
|
||||||
mov ax,083h
|
mov ax,083h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Save the text mode bit-map.
|
; Save the text mode bit-map.
|
||||||
;
|
;
|
||||||
mov ax,Data
|
mov ax,Data
|
||||||
mov es,ax
|
mov es,ax
|
||||||
mov ax,TEXT_SEGMENT
|
mov ax,TEXT_SEGMENT
|
||||||
|
|
@ -169,10 +170,10 @@ rewarding!) VGA to cover.
|
||||||
mov di,offset CharAttSave
|
mov di,offset CharAttSave
|
||||||
mov cx,4000/2 ;length of one text screen in words
|
mov cx,4000/2 ;length of one text screen in words
|
||||||
rep movsw
|
rep movsw
|
||||||
;
|
;
|
||||||
; Fill the text mode screen with dots and put up "strike any key"
|
; Fill the text mode screen with dots and put up "strike any key"
|
||||||
; message.
|
; message.
|
||||||
;
|
;
|
||||||
mov ax,TEXT_SEGMENT
|
mov ax,TEXT_SEGMENT
|
||||||
mov es,ax
|
mov es,ax
|
||||||
sub di,di
|
sub di,di
|
||||||
|
|
@ -185,15 +186,15 @@ rewarding!) VGA to cover.
|
||||||
mov dx,offset TStrikeAnyKeyMsg
|
mov dx,offset TStrikeAnyKeyMsg
|
||||||
mov ah,9
|
mov ah,9
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Wait for a key.
|
; Wait for a key.
|
||||||
;
|
;
|
||||||
mov ah,01h
|
mov ah,01h
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Restore the text mode screen to the state it was in on entering
|
; Restore the text mode screen to the state it was in on entering
|
||||||
; text mode.
|
; text mode.
|
||||||
;
|
;
|
||||||
mov ax,Data
|
mov ax,Data
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov ax,TEXT_SEGMENT
|
mov ax,TEXT_SEGMENT
|
||||||
|
|
@ -202,14 +203,14 @@ rewarding!) VGA to cover.
|
||||||
sub di,di
|
sub di,di
|
||||||
mov cx,4000/2 ;length of one text screen in words
|
mov cx,4000/2 ;length of one text screen in words
|
||||||
rep movsw
|
rep movsw
|
||||||
;
|
;
|
||||||
; Return to mode 10h without clearing display memory.
|
; Return to mode 10h without clearing display memory.
|
||||||
;
|
;
|
||||||
mov ax,90h
|
mov ax,90h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Restore the portion of plane 2 that was wiped out by the font.
|
; Restore the portion of plane 2 that was wiped out by the font.
|
||||||
;
|
;
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,MAP_MASK
|
mov al,MAP_MASK
|
||||||
out dx,al
|
out dx,al
|
||||||
|
|
@ -224,23 +225,24 @@ rewarding!) VGA to cover.
|
||||||
sub di,di
|
sub di,di
|
||||||
mov cx,2000h/2 ;restore 8K (length of default font)
|
mov cx,2000h/2 ;restore 8K (length of default font)
|
||||||
rep movsw
|
rep movsw
|
||||||
;
|
;
|
||||||
; Put up "strike any key" message.
|
; Put up "strike any key" message.
|
||||||
;
|
;
|
||||||
mov ax,Data
|
mov ax,Data
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov dx,offset GStrikeAnyKeyMsg1
|
mov dx,offset GStrikeAnyKeyMsg1
|
||||||
mov ah,9
|
mov ah,9
|
||||||
int 21h
|
int 21h
|
||||||
;
|
;
|
||||||
; Wait for a key before returning to text mode and ending.
|
; Wait for a key before returning to text mode and ending.
|
||||||
;
|
;
|
||||||
mov ah,01h
|
mov ah,01h
|
||||||
int 21h
|
int 21h
|
||||||
mov ax,03h
|
mov ax,03h
|
||||||
int 10h
|
int 10h
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
Start endp
|
Start endp
|
||||||
Code ends
|
Code ends
|
||||||
end Start
|
end Start
|
||||||
|
```
|
||||||
|
|
|
||||||
226
28-02.md
226
28-02.md
|
|
@ -12,138 +12,139 @@ pages: 526-530
|
||||||
|
|
||||||
**LISTING 28.1 L28-1.ASM**
|
**LISTING 28.1 L28-1.ASM**
|
||||||
|
|
||||||
; Program to illustrate the use of the Read Map register in read mode 0.
|
```nasm
|
||||||
; Animates by copying a 16-color image from VGA memory to system memory,
|
; Program to illustrate the use of the Read Map register in read mode 0.
|
||||||
; one plane at a time, then copying the image back to a new location
|
; Animates by copying a 16-color image from VGA memory to system memory,
|
||||||
; in VGA memory.
|
; one plane at a time, then copying the image back to a new location
|
||||||
;
|
; in VGA memory.
|
||||||
; By Michael Abrash
|
;
|
||||||
;
|
; By Michael Abrash
|
||||||
stacksegmentword stack 'STACK'
|
;
|
||||||
db512 dup (?)
|
stacksegmentword stack 'STACK'
|
||||||
stackends
|
db512 dup (?)
|
||||||
;
|
stackends
|
||||||
datasegment word 'DATA'
|
;
|
||||||
IMAGE_WIDTHEQU 4 ;in bytes
|
datasegment word 'DATA'
|
||||||
IMAGE_HEIGHT EQU 32 ;in pixels
|
IMAGE_WIDTHEQU 4 ;in bytes
|
||||||
LEFT_BOUND EQU 10 ;in bytes
|
IMAGE_HEIGHT EQU 32 ;in pixels
|
||||||
RIGHT_BOUND EQU 66 ;in bytes
|
LEFT_BOUND EQU 10 ;in bytes
|
||||||
VGA_SEGMENT EQU 0a000h
|
RIGHT_BOUND EQU 66 ;in bytes
|
||||||
SCREEN_WIDTH EQU 80 ;in bytes
|
VGA_SEGMENT EQU 0a000h
|
||||||
SC_INDEX EQU 3c4h ;Sequence Controller Index register
|
SCREEN_WIDTH EQU 80 ;in bytes
|
||||||
GC_INDEX EQU 3ceh ;Graphics Controller Index register
|
SC_INDEX EQU 3c4h ;Sequence Controller Index register
|
||||||
MAP_MASK EQU 2 ;Map Mask register index in SC
|
GC_INDEX EQU 3ceh ;Graphics Controller Index register
|
||||||
READ_MAP EQU 4 ;Read Map register index in GC
|
MAP_MASK EQU 2 ;Map Mask register index in SC
|
||||||
;
|
READ_MAP EQU 4 ;Read Map register index in GC
|
||||||
|
;
|
||||||
; Base pattern for 16-color image.
|
; Base pattern for 16-color image.
|
||||||
;
|
;
|
||||||
PatternPlane0 label byte
|
PatternPlane0 label byte
|
||||||
db 32 dup (0ffh,0ffh,0,0)
|
db 32 dup (0ffh,0ffh,0,0)
|
||||||
PatternPlane1 labelbyte
|
PatternPlane1 labelbyte
|
||||||
db 32 dup (0ffh,0,0ffh,0)
|
db 32 dup (0ffh,0,0ffh,0)
|
||||||
PatternPlane2 labelbyte
|
PatternPlane2 labelbyte
|
||||||
db 32 dup (0f0h,0f0h,0f0h,0f0h)
|
db 32 dup (0f0h,0f0h,0f0h,0f0h)
|
||||||
PatternPlane3 labelbyte
|
PatternPlane3 labelbyte
|
||||||
db 32 dup (0cch,0cch,0cch,0cch)
|
db 32 dup (0cch,0cch,0cch,0cch)
|
||||||
;
|
;
|
||||||
; Temporary storage for 16-color image during animation.
|
; Temporary storage for 16-color image during animation.
|
||||||
;
|
;
|
||||||
ImagePlane0 db 32*4 dup (?)
|
ImagePlane0 db 32*4 dup (?)
|
||||||
ImagePlane1 db 32*4 dup (?)
|
ImagePlane1 db 32*4 dup (?)
|
||||||
ImagePlane2 db 32*4 dup (?)
|
ImagePlane2 db 32*4 dup (?)
|
||||||
ImagePlane3 db 32*4 dup (?)
|
ImagePlane3 db 32*4 dup (?)
|
||||||
;
|
;
|
||||||
; Current image location & direction.
|
; Current image location & direction.
|
||||||
;
|
;
|
||||||
ImageX dw 40 ;in bytes
|
ImageX dw 40 ;in bytes
|
||||||
ImageY dw 100 ;in pixels
|
ImageY dw 100 ;in pixels
|
||||||
ImageXDirection dw 1 ;in bytes
|
ImageXDirection dw 1 ;in bytes
|
||||||
dataends
|
dataends
|
||||||
;
|
;
|
||||||
code segment word 'CODE'
|
code segment word 'CODE'
|
||||||
assume cs:code,ds:data
|
assume cs:code,ds:data
|
||||||
Start proc near
|
Start proc near
|
||||||
cld
|
cld
|
||||||
mov ax,data
|
mov ax,data
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
;
|
;
|
||||||
; Select graphics mode 10h.
|
; Select graphics mode 10h.
|
||||||
;
|
;
|
||||||
mov ax,10h
|
mov ax,10h
|
||||||
int 10h
|
int 10h
|
||||||
;
|
;
|
||||||
; Draw the initial image.
|
; Draw the initial image.
|
||||||
;
|
;
|
||||||
mov si,offset PatternPlane0
|
mov si,offset PatternPlane0
|
||||||
call DrawImage
|
call DrawImage
|
||||||
;
|
;
|
||||||
; Loop to animate by copying the image from VGA memory to system memory,
|
; Loop to animate by copying the image from VGA memory to system memory,
|
||||||
; erasing the image, and copying the image from system memory to a new
|
; erasing the image, and copying the image from system memory to a new
|
||||||
; location in VGA memory. Ends when a key is hit.
|
; location in VGA memory. Ends when a key is hit.
|
||||||
;
|
;
|
||||||
AnimateLoop:
|
AnimateLoop:
|
||||||
;
|
;
|
||||||
; Copy the image from VGA memory to system memory.
|
; Copy the image from VGA memory to system memory.
|
||||||
;
|
;
|
||||||
mov di,offset ImagePlane0
|
mov di,offset ImagePlane0
|
||||||
call GetImage
|
call GetImage
|
||||||
;
|
;
|
||||||
; Clear the image from VGA memory.
|
; Clear the image from VGA memory.
|
||||||
;
|
;
|
||||||
call EraseImage
|
call EraseImage
|
||||||
;
|
;
|
||||||
; Advance the image X coordinate, reversing direction if either edge
|
; Advance the image X coordinate, reversing direction if either edge
|
||||||
; of the screen has been reached.
|
; of the screen has been reached.
|
||||||
;
|
;
|
||||||
mov ax,[ImageX]
|
mov ax,[ImageX]
|
||||||
cmp ax,LEFT_BOUND
|
cmp ax,LEFT_BOUND
|
||||||
jz ReverseDirection
|
jz ReverseDirection
|
||||||
cmp ax,RIGHT_BOUND
|
cmp ax,RIGHT_BOUND
|
||||||
jnz SetNewX
|
jnz SetNewX
|
||||||
ReverseDirection:
|
ReverseDirection:
|
||||||
neg [ImageXDirection]
|
neg [ImageXDirection]
|
||||||
SetNewX:
|
SetNewX:
|
||||||
add ax,[ImageXDirection]
|
add ax,[ImageXDirection]
|
||||||
mov [ImageX],ax
|
mov [ImageX],ax
|
||||||
;
|
;
|
||||||
; Draw the image by copying it from system memory to VGA memory.
|
; Draw the image by copying it from system memory to VGA memory.
|
||||||
;
|
;
|
||||||
mov si,offset ImagePlane0
|
mov si,offset ImagePlane0
|
||||||
call DrawImage
|
call DrawImage
|
||||||
;
|
;
|
||||||
; Slow things down a bit for visibility (adjust as needed).
|
; Slow things down a bit for visibility (adjust as needed).
|
||||||
;
|
;
|
||||||
mov cx,0
|
mov cx,0
|
||||||
DelayLoop:
|
DelayLoop:
|
||||||
loop DelayLoop
|
loop DelayLoop
|
||||||
;
|
;
|
||||||
; See if a key has been hit, ending the program.
|
; See if a key has been hit, ending the program.
|
||||||
;
|
;
|
||||||
mov ah,1
|
mov ah,1
|
||||||
int 16h
|
int 16h
|
||||||
jz AnimateLoop
|
jz AnimateLoop
|
||||||
;
|
;
|
||||||
; Clear the key, return to text mode, and return to DOS.
|
; Clear the key, return to text mode, and return to DOS.
|
||||||
;
|
;
|
||||||
sub ah,ah
|
sub ah,ah
|
||||||
int 16h
|
int 16h
|
||||||
mov ax,3
|
mov ax,3
|
||||||
int 10h
|
int 10h
|
||||||
mov ah,4ch
|
mov ah,4ch
|
||||||
int 21h
|
int 21h
|
||||||
Startendp
|
Startendp
|
||||||
;
|
;
|
||||||
; Draws the image at offset DS:SI to the current image location in
|
; Draws the image at offset DS:SI to the current image location in
|
||||||
; VGA memory.
|
; VGA memory.
|
||||||
;
|
;
|
||||||
DrawImageprocnear
|
DrawImageprocnear
|
||||||
mov ax,VGA_SEGMENT
|
mov ax,VGA_SEGMENT
|
||||||
mov es,ax
|
mov es,ax
|
||||||
call GetImageOffset ;ES:DI is the destination address for the
|
call GetImageOffset ;ES:DI is the destination address for the
|
||||||
; image in VGA memory
|
; image in VGA memory
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,1 ;do plane 0 first
|
mov al,1 ;do plane 0 first
|
||||||
DrawImagePlaneLoop:
|
DrawImagePlaneLoop:
|
||||||
push di ;image is drawn at the same offset in
|
push di ;image is drawn at the same offset in
|
||||||
; each plane
|
; each plane
|
||||||
push ax ;preserve plane select
|
push ax ;preserve plane select
|
||||||
|
|
@ -155,7 +156,7 @@ pages: 526-530
|
||||||
; the plane of interest
|
; the plane of interest
|
||||||
dec dx ;point back to SC Data register
|
dec dx ;point back to SC Data register
|
||||||
mov bx,IMAGE_HEIGHT ;# of scan lines in image
|
mov bx,IMAGE_HEIGHT ;# of scan lines in image
|
||||||
DrawImageLoop:
|
DrawImageLoop:
|
||||||
mov cx,IMAGE_WIDTH ;# of bytes across image
|
mov cx,IMAGE_WIDTH ;# of bytes across image
|
||||||
rep movsb
|
rep movsb
|
||||||
add di,SCREEN_WIDTH-IMAGE_WIDTH
|
add di,SCREEN_WIDTH-IMAGE_WIDTH
|
||||||
|
|
@ -167,12 +168,12 @@ pages: 526-530
|
||||||
cmp al,10h ;have we done all four planes?
|
cmp al,10h ;have we done all four planes?
|
||||||
jnz DrawImagePlaneLoop
|
jnz DrawImagePlaneLoop
|
||||||
ret
|
ret
|
||||||
DrawImageendp
|
DrawImageendp
|
||||||
;
|
;
|
||||||
; Copies the image from its current location in VGA memory into the
|
; Copies the image from its current location in VGA memory into the
|
||||||
; buffer at DS:DI.
|
; buffer at DS:DI.
|
||||||
;
|
;
|
||||||
GetImage proc near
|
GetImage proc near
|
||||||
mov si,di ;move destination offset into SI
|
mov si,di ;move destination offset into SI
|
||||||
call GetImageOffset ;DI is offset of image in VGA memory
|
call GetImageOffset ;DI is offset of image in VGA memory
|
||||||
xchg si,di ;SI is offset of image, DI is destination offset
|
xchg si,di ;SI is offset of image, DI is destination offset
|
||||||
|
|
@ -180,10 +181,10 @@ pages: 526-530
|
||||||
pop es ;ES:DI is destination
|
pop es ;ES:DI is destination
|
||||||
mov ax,VGA_SEGMENT
|
mov ax,VGA_SEGMENT
|
||||||
mov ds,ax ;DS:SI is source
|
mov ds,ax ;DS:SI is source
|
||||||
;
|
;
|
||||||
mov dx,GC_INDEX
|
mov dx,GC_INDEX
|
||||||
sub al,al;do plane 0 first
|
sub al,al;do plane 0 first
|
||||||
GetImagePlaneLoop:
|
GetImagePlaneLoop:
|
||||||
push si ;image comes from same offset in each plane
|
push si ;image comes from same offset in each plane
|
||||||
push ax ;preserve plane select
|
push ax ;preserve plane select
|
||||||
mov al,READ_MAP ;Read Map index
|
mov al,READ_MAP ;Read Map index
|
||||||
|
|
@ -194,7 +195,7 @@ pages: 526-530
|
||||||
; the plane of interest
|
; the plane of interest
|
||||||
dec dx ;point back to GC data register
|
dec dx ;point back to GC data register
|
||||||
mov bx,IMAGE_HEIGHT ;# of scan lines in image
|
mov bx,IMAGE_HEIGHT ;# of scan lines in image
|
||||||
GetImageLoop:
|
GetImageLoop:
|
||||||
mov cx,IMAGE_WIDTH ;# of bytes across image
|
mov cx,IMAGE_WIDTH ;# of bytes across image
|
||||||
rep movsb
|
rep movsb
|
||||||
add si,SCREEN_WIDTH-IMAGE_WIDTH
|
add si,SCREEN_WIDTH-IMAGE_WIDTH
|
||||||
|
|
@ -208,11 +209,11 @@ pages: 526-530
|
||||||
push es
|
push es
|
||||||
pop ds ;restore original DS
|
pop ds ;restore original DS
|
||||||
ret
|
ret
|
||||||
GetImageendp
|
GetImageendp
|
||||||
;
|
;
|
||||||
; Erases the image at its current location.
|
; Erases the image at its current location.
|
||||||
;
|
;
|
||||||
EraseImage proc near
|
EraseImage proc near
|
||||||
mov dx,SC_INDEX
|
mov dx,SC_INDEX
|
||||||
mov al,MAP_MASK
|
mov al,MAP_MASK
|
||||||
out dx,al ;point SC Index to the Map Mask register
|
out dx,al ;point SC Index to the Map Mask register
|
||||||
|
|
@ -226,7 +227,7 @@ pages: 526-530
|
||||||
; of the image
|
; of the image
|
||||||
sub al,al ;erase with zeros
|
sub al,al ;erase with zeros
|
||||||
mov bx,IMAGE_HEIGHT ;# of scan lines in image
|
mov bx,IMAGE_HEIGHT ;# of scan lines in image
|
||||||
EraseImageLoop:
|
EraseImageLoop:
|
||||||
mov cx,IMAGE_WIDTH ;# of bytes across image
|
mov cx,IMAGE_WIDTH ;# of bytes across image
|
||||||
rep stosb
|
rep stosb
|
||||||
add di,SCREEN_WIDTH-IMAGE_WIDTH
|
add di,SCREEN_WIDTH-IMAGE_WIDTH
|
||||||
|
|
@ -234,16 +235,17 @@ pages: 526-530
|
||||||
dec bx ;any more scan lines?
|
dec bx ;any more scan lines?
|
||||||
jnz EraseImageLoop
|
jnz EraseImageLoop
|
||||||
ret
|
ret
|
||||||
EraseImage endp
|
EraseImage endp
|
||||||
;
|
;
|
||||||
; Returns the current offset of the image in the VGA segment in DI.
|
; Returns the current offset of the image in the VGA segment in DI.
|
||||||
;
|
;
|
||||||
GetImageOffset proc near
|
GetImageOffset proc near
|
||||||
mov ax,SCREEN_WIDTH
|
mov ax,SCREEN_WIDTH
|
||||||
mul [ImageY]
|
mul [ImageY]
|
||||||
add ax,[ImageX]
|
add ax,[ImageX]
|
||||||
mov di,ax
|
mov di,ax
|
||||||
ret
|
ret
|
||||||
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