Tidy HTML
This commit is contained in:
parent
b8e7ce7165
commit
3760ddbdd3
362 changed files with 72376 additions and 62837 deletions
197
01-01.html
197
01-01.html
File diff suppressed because one or more lines are too long
301
01-02.html
301
01-02.html
File diff suppressed because one or more lines are too long
549
01-03.html
549
01-03.html
File diff suppressed because one or more lines are too long
272
01-04.html
272
01-04.html
File diff suppressed because one or more lines are too long
392
01-05.html
392
01-05.html
File diff suppressed because one or more lines are too long
274
01-06.html
274
01-06.html
File diff suppressed because one or more lines are too long
229
02-01.html
229
02-01.html
File diff suppressed because one or more lines are too long
189
02-02.html
189
02-02.html
File diff suppressed because one or more lines are too long
175
02-03.html
175
02-03.html
File diff suppressed because one or more lines are too long
183
03-01.html
183
03-01.html
File diff suppressed because one or more lines are too long
1014
03-02.html
1014
03-02.html
File diff suppressed because it is too large
Load diff
186
03-03.html
186
03-03.html
File diff suppressed because one or more lines are too long
187
03-04.html
187
03-04.html
File diff suppressed because one or more lines are too long
299
03-05.html
299
03-05.html
File diff suppressed because one or more lines are too long
310
03-06.html
310
03-06.html
File diff suppressed because one or more lines are too long
1430
03-07.html
1430
03-07.html
File diff suppressed because one or more lines are too long
280
03-08.html
280
03-08.html
File diff suppressed because one or more lines are too long
360
03-09.html
360
03-09.html
File diff suppressed because one or more lines are too long
236
03-10.html
236
03-10.html
File diff suppressed because one or more lines are too long
210
04-01.html
210
04-01.html
File diff suppressed because one or more lines are too long
287
04-02.html
287
04-02.html
File diff suppressed because one or more lines are too long
274
04-03.html
274
04-03.html
File diff suppressed because one or more lines are too long
245
04-04.html
245
04-04.html
File diff suppressed because one or more lines are too long
238
04-05.html
238
04-05.html
File diff suppressed because one or more lines are too long
182
04-06.html
182
04-06.html
File diff suppressed because one or more lines are too long
231
04-07.html
231
04-07.html
File diff suppressed because one or more lines are too long
194
04-08.html
194
04-08.html
File diff suppressed because one or more lines are too long
266
04-09.html
266
04-09.html
File diff suppressed because one or more lines are too long
204
04-10.html
204
04-10.html
File diff suppressed because one or more lines are too long
192
05-01.html
192
05-01.html
File diff suppressed because one or more lines are too long
199
05-02.html
199
05-02.html
File diff suppressed because one or more lines are too long
466
05-03.html
466
05-03.html
|
|
@ -1,234 +1,232 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=05//-->
|
||||
<!--PAGES=118-121//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><body>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="05-02.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="05-04.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P><B>LISTING 5.1 SEARCH.C</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* Program to search the file specified by the first command-line
|
||||
* argument for the string specified by the second command-line
|
||||
* argument. Performs the search by reading and searching blocks
|
||||
* of size BLOCK_SIZE. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <alloc.h> /* alloc.h for Borland compilers,
|
||||
malloc.h for Microsoft compilers */
|
||||
|
||||
#define BLOCK_SIZE 0x4000 /* we’ll process the file in 16K blocks */
|
||||
|
||||
/* Searches the specified number of sequences in the specified
|
||||
buffer for matches to SearchString of SearchStringLength. Note
|
||||
that the calling code should already have shortened SearchLength
|
||||
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.
|
||||
*/
|
||||
|
||||
int SearchForString(unsigned char *Buffer, int SearchLength,
|
||||
unsigned char *SearchString, int SearchStringLength)
|
||||
{
|
||||
unsigned char *PotentialMatch;
|
||||
|
||||
/* Search so long as there are potential-match locations
|
||||
remaining */
|
||||
while ( SearchLength ) {
|
||||
/* See if the first character of SearchString can be found */
|
||||
if ( (PotentialMatch =
|
||||
memchr(Buffer, *SearchString, SearchLength)) == NULL ) {
|
||||
break; /* No matches in this buffer */
|
||||
}
|
||||
/* The first character matches; see if the rest of the string
|
||||
also matches */
|
||||
if ( SearchStringLength == 1 ) {
|
||||
return(1); /* That one matching character was the whole
|
||||
search string, so we’ve got a match */
|
||||
}
|
||||
else {
|
||||
/* Check whether the remaining characters match */
|
||||
if ( !memcmp(PotentialMatch + 1, SearchString + 1,
|
||||
SearchStringLength - 1) ) {
|
||||
return(1); /* We’ve got a match */
|
||||
}
|
||||
}
|
||||
/* The string doesn’t match; keep going by pointing past the
|
||||
potential match location we just rejected */
|
||||
SearchLength -= PotentialMatch - Buffer + 1;
|
||||
Buffer = PotentialMatch + 1;
|
||||
}
|
||||
|
||||
return(0); /* No match found */
|
||||
}
|
||||
|
||||
main(int argc, char *argv[]) {
|
||||
int Done; /* Indicates whether search is done */
|
||||
int Handle; /* Handle of file being searched */
|
||||
int WorkingLength; /* Length of current block */
|
||||
int SearchStringLength; /* Length of string to search for */
|
||||
int BlockSearchLength; /* Length to search in current block */
|
||||
int Found; /* Indicates final search completion
|
||||
status */
|
||||
int NextLoadCount; /* # of bytes to read into next block,
|
||||
accounting for bytes copied from the
|
||||
last block */
|
||||
unsigned char *WorkingBlock; /* Block storage buffer */
|
||||
unsigned char *SearchString; /* Pointer to the string to search for */
|
||||
unsigned char *NextLoadPtr; /* Offset at which to start loading
|
||||
the next block, accounting for
|
||||
bytes copied from the last block */
|
||||
|
||||
/* Check for the proper number of arguments */
|
||||
if ( argc != 3 ) {
|
||||
printf(“usage: search filename search-string\n”);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Try to open the file to be searched */
|
||||
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
|
||||
printf(“Can’t open file: %s\n”, argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
/* Calculate the length of text to search for */
|
||||
SearchString = argv[2];
|
||||
SearchStringLength = strlen(SearchString);
|
||||
/* Try to get memory in which to buffer the data */
|
||||
if ( (WorkingBlock = malloc(BLOCK_SIZE)) == NULL ) {
|
||||
printf(“Can’t get enough memory\n”);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Load the first block at the start of the buffer, and try to
|
||||
fill the entire buffer */
|
||||
NextLoadPtr = WorkingBlock;
|
||||
NextLoadCount = BLOCK_SIZE;
|
||||
Done = 0; /* Not done with search yet */
|
||||
Found = 0; /* Assume we won’t find a match */
|
||||
/* Search the file in BLOCK_SIZE chunks */
|
||||
do {
|
||||
/* Read in however many bytes are needed to fill out the block
|
||||
(accounting for bytes copied over from the last block), or
|
||||
the rest of the bytes in the file, whichever is less */
|
||||
if ( (WorkingLength = read(Handle, NextLoadPtr,
|
||||
NextLoadCount)) == -1 ) {
|
||||
printf(“Error reading file %s\n”, argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
/* If we didn’t read all the bytes we requested, we’re done
|
||||
after this block, whether we find a match or not */
|
||||
if ( WorkingLength != NextLoadCount ) {
|
||||
Done = 1;
|
||||
}
|
||||
|
||||
/* Account for any bytes we copied from the end of the last
|
||||
block in the total length of this block */
|
||||
WorkingLength += NextLoadPtr - WorkingBlock;
|
||||
/* Calculate the number of bytes in this block that could
|
||||
possibly be the start of a matching sequence that lies
|
||||
entirely in this block (sequences that run off the end of
|
||||
the block will be transferred to the next block and found
|
||||
when that block is searched)
|
||||
*/
|
||||
if ( (BlockSearchLength =
|
||||
WorkingLength - SearchStringLength + 1) <= 0 ) {
|
||||
Done = 1; /* Too few characters in this block for
|
||||
there to be any possible matches, so this
|
||||
is the final block and we’re done without
|
||||
finding a match
|
||||
*/
|
||||
}
|
||||
else {
|
||||
/* Search this block */
|
||||
if ( SearchForString(WorkingBlock, BlockSearchLength,
|
||||
SearchString, SearchStringLength) ) {
|
||||
Found = 1; /* We’ve found a match */
|
||||
Done = 1;
|
||||
}
|
||||
else {
|
||||
/* Copy any bytes from the end of the block that start
|
||||
potentially-matching sequences that would run off
|
||||
the end of the block over to the next block */
|
||||
if ( SearchStringLength > 1 ) {
|
||||
memcpy(WorkingBlock,
|
||||
WorkingBlock+BLOCK_SIZE - SearchStringLength + 1,
|
||||
SearchStringLength - 1);
|
||||
}
|
||||
/* Set up to load the next bytes from the file after the
|
||||
bytes copied from the end of the current block */
|
||||
NextLoadPtr = WorkingBlock + SearchStringLength - 1;
|
||||
NextLoadCount = BLOCK_SIZE - SearchStringLength + 1;
|
||||
}
|
||||
}
|
||||
} while ( !Done );
|
||||
|
||||
/* Report the results */
|
||||
if ( Found ) {
|
||||
printf(“String found\n”);
|
||||
} else {
|
||||
printf(“String not found\n”);
|
||||
}
|
||||
exit(Found); /* Return the found/not found status as the
|
||||
DOS errorlevel */
|
||||
}
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="05-02.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="05-04.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="vsisbn" content="1576101746" />
|
||||
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
||||
<meta name="vsauthor" content="Michael Abrash" />
|
||||
<meta name="vspublisher" content="The Coriolis Group" />
|
||||
<meta name="vspubdate" content="07/01/97" />
|
||||
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
||||
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Crossing the Border</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=05//-->
|
||||
<!--PAGES=118-121//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="05-02.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="05-04.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 5.1 SEARCH.C</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
/* Program to search the file specified by the first command-line
|
||||
* argument for the string specified by the second command-line
|
||||
* argument. Performs the search by reading and searching blocks
|
||||
* of size BLOCK_SIZE. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <alloc.h> /* alloc.h for Borland compilers,
|
||||
malloc.h for Microsoft compilers */
|
||||
|
||||
#define BLOCK_SIZE 0x4000 /* we’ll process the file in 16K blocks */
|
||||
|
||||
/* Searches the specified number of sequences in the specified
|
||||
buffer for matches to SearchString of SearchStringLength. Note
|
||||
that the calling code should already have shortened SearchLength
|
||||
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.
|
||||
*/
|
||||
|
||||
int SearchForString(unsigned char *Buffer, int SearchLength,
|
||||
unsigned char *SearchString, int SearchStringLength)
|
||||
{
|
||||
unsigned char *PotentialMatch;
|
||||
|
||||
/* Search so long as there are potential-match locations
|
||||
remaining */
|
||||
while ( SearchLength ) {
|
||||
/* See if the first character of SearchString can be found */
|
||||
if ( (PotentialMatch =
|
||||
memchr(Buffer, *SearchString, SearchLength)) == NULL ) {
|
||||
break; /* No matches in this buffer */
|
||||
}
|
||||
/* The first character matches; see if the rest of the string
|
||||
also matches */
|
||||
if ( SearchStringLength == 1 ) {
|
||||
return(1); /* That one matching character was the whole
|
||||
search string, so we’ve got a match */
|
||||
}
|
||||
else {
|
||||
/* Check whether the remaining characters match */
|
||||
if ( !memcmp(PotentialMatch + 1, SearchString + 1,
|
||||
SearchStringLength - 1) ) {
|
||||
return(1); /* We’ve got a match */
|
||||
}
|
||||
}
|
||||
/* The string doesn’t match; keep going by pointing past the
|
||||
potential match location we just rejected */
|
||||
SearchLength -= PotentialMatch - Buffer + 1;
|
||||
Buffer = PotentialMatch + 1;
|
||||
}
|
||||
|
||||
return(0); /* No match found */
|
||||
}
|
||||
|
||||
main(int argc, char *argv[]) {
|
||||
int Done; /* Indicates whether search is done */
|
||||
int Handle; /* Handle of file being searched */
|
||||
int WorkingLength; /* Length of current block */
|
||||
int SearchStringLength; /* Length of string to search for */
|
||||
int BlockSearchLength; /* Length to search in current block */
|
||||
int Found; /* Indicates final search completion
|
||||
status */
|
||||
int NextLoadCount; /* # of bytes to read into next block,
|
||||
accounting for bytes copied from the
|
||||
last block */
|
||||
unsigned char *WorkingBlock; /* Block storage buffer */
|
||||
unsigned char *SearchString; /* Pointer to the string to search for */
|
||||
unsigned char *NextLoadPtr; /* Offset at which to start loading
|
||||
the next block, accounting for
|
||||
bytes copied from the last block */
|
||||
|
||||
/* Check for the proper number of arguments */
|
||||
if ( argc != 3 ) {
|
||||
printf(“usage: search filename search-string\n”);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Try to open the file to be searched */
|
||||
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
|
||||
printf(“Can’t open file: %s\n”, argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
/* Calculate the length of text to search for */
|
||||
SearchString = argv[2];
|
||||
SearchStringLength = strlen(SearchString);
|
||||
/* Try to get memory in which to buffer the data */
|
||||
if ( (WorkingBlock = malloc(BLOCK_SIZE)) == NULL ) {
|
||||
printf(“Can’t get enough memory\n”);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Load the first block at the start of the buffer, and try to
|
||||
fill the entire buffer */
|
||||
NextLoadPtr = WorkingBlock;
|
||||
NextLoadCount = BLOCK_SIZE;
|
||||
Done = 0; /* Not done with search yet */
|
||||
Found = 0; /* Assume we won’t find a match */
|
||||
/* Search the file in BLOCK_SIZE chunks */
|
||||
do {
|
||||
/* Read in however many bytes are needed to fill out the block
|
||||
(accounting for bytes copied over from the last block), or
|
||||
the rest of the bytes in the file, whichever is less */
|
||||
if ( (WorkingLength = read(Handle, NextLoadPtr,
|
||||
NextLoadCount)) == -1 ) {
|
||||
printf(“Error reading file %s\n”, argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
/* If we didn’t read all the bytes we requested, we’re done
|
||||
after this block, whether we find a match or not */
|
||||
if ( WorkingLength != NextLoadCount ) {
|
||||
Done = 1;
|
||||
}
|
||||
|
||||
/* Account for any bytes we copied from the end of the last
|
||||
block in the total length of this block */
|
||||
WorkingLength += NextLoadPtr - WorkingBlock;
|
||||
/* Calculate the number of bytes in this block that could
|
||||
possibly be the start of a matching sequence that lies
|
||||
entirely in this block (sequences that run off the end of
|
||||
the block will be transferred to the next block and found
|
||||
when that block is searched)
|
||||
*/
|
||||
if ( (BlockSearchLength =
|
||||
WorkingLength - SearchStringLength + 1) <= 0 ) {
|
||||
Done = 1; /* Too few characters in this block for
|
||||
there to be any possible matches, so this
|
||||
is the final block and we’re done without
|
||||
finding a match
|
||||
*/
|
||||
}
|
||||
else {
|
||||
/* Search this block */
|
||||
if ( SearchForString(WorkingBlock, BlockSearchLength,
|
||||
SearchString, SearchStringLength) ) {
|
||||
Found = 1; /* We’ve found a match */
|
||||
Done = 1;
|
||||
}
|
||||
else {
|
||||
/* Copy any bytes from the end of the block that start
|
||||
potentially-matching sequences that would run off
|
||||
the end of the block over to the next block */
|
||||
if ( SearchStringLength > 1 ) {
|
||||
memcpy(WorkingBlock,
|
||||
WorkingBlock+BLOCK_SIZE - SearchStringLength + 1,
|
||||
SearchStringLength - 1);
|
||||
}
|
||||
/* Set up to load the next bytes from the file after the
|
||||
bytes copied from the end of the current block */
|
||||
NextLoadPtr = WorkingBlock + SearchStringLength - 1;
|
||||
NextLoadCount = BLOCK_SIZE - SearchStringLength + 1;
|
||||
}
|
||||
}
|
||||
} while ( !Done );
|
||||
|
||||
/* Report the results */
|
||||
if ( Found ) {
|
||||
printf(“String found\n”);
|
||||
} else {
|
||||
printf(“String not found\n”);
|
||||
}
|
||||
exit(Found); /* Return the found/not found status as the
|
||||
DOS errorlevel */
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="05-02.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="05-04.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<hr width="90%" size="1" noshade="noshade" />
|
||||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
163
05-04.html
163
05-04.html
File diff suppressed because one or more lines are too long
180
05-05.html
180
05-05.html
File diff suppressed because one or more lines are too long
220
06-01.html
220
06-01.html
File diff suppressed because one or more lines are too long
334
06-02.html
334
06-02.html
File diff suppressed because one or more lines are too long
210
07-01.html
210
07-01.html
File diff suppressed because one or more lines are too long
198
07-02.html
198
07-02.html
File diff suppressed because one or more lines are too long
321
07-03.html
321
07-03.html
File diff suppressed because one or more lines are too long
405
07-04.html
405
07-04.html
File diff suppressed because one or more lines are too long
359
07-05.html
359
07-05.html
File diff suppressed because one or more lines are too long
199
08-01.html
199
08-01.html
File diff suppressed because one or more lines are too long
266
08-02.html
266
08-02.html
File diff suppressed because one or more lines are too long
454
08-03.html
454
08-03.html
File diff suppressed because one or more lines are too long
596
08-04.html
596
08-04.html
File diff suppressed because one or more lines are too long
440
08-05.html
440
08-05.html
File diff suppressed because one or more lines are too long
336
09-01.html
336
09-01.html
File diff suppressed because one or more lines are too long
273
09-02.html
273
09-02.html
File diff suppressed because one or more lines are too long
341
09-03.html
341
09-03.html
File diff suppressed because one or more lines are too long
401
09-04.html
401
09-04.html
|
|
@ -1,202 +1,199 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=09//-->
|
||||
<!--PAGES=178-180//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><body>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="09-03.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="09-05.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P><B>LISTING 9.2 L9-2.ASM</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
||||
; the buffer for locations that match a specified character of the
|
||||
; 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,
|
||||
; unsigned int BufferLength, unsigned char * SearchString,
|
||||
; 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
|
||||
; offset 0 in the data segment to avoid confusing a match at 0 with
|
||||
; no match found.
|
||||
Parms struc
|
||||
dw 2 dup(?) ;pushed BP/return address
|
||||
Buffer dw ? ;pointer to buffer to search
|
||||
BufferLength dw ? ;length of buffer to search
|
||||
SearchString dw ? ;pointer to string for which to search
|
||||
SearchStringLength dw ? ;length of string for which to search
|
||||
ScanCharOffset dw ? ;offset in string of character for
|
||||
; which to scan
|
||||
Parmsends
|
||||
.model small
|
||||
.code
|
||||
public _FindString
|
||||
_FindStringprocnear
|
||||
push bp ;preserve caller’s stack frame
|
||||
mov bp,sp ;point to our stack frame
|
||||
push si ;preserve caller’s register variables
|
||||
push di
|
||||
cld ;make string instructions increment pointers
|
||||
mov si,[bp+SearchString] ;pointer to string to search for
|
||||
mov cx,[bp+SearchStringLength] ;length of string
|
||||
jcxz FindStringNotFound ;no match if string is 0 length
|
||||
mov dx,[bp+BufferLength] ;length of buffer
|
||||
sub dx,cx ;difference between buffer and search
|
||||
; lengths
|
||||
jc FindStringNotFound ;no match if search string is
|
||||
; longer than buffer
|
||||
inc dx ; difference between buffer and search string
|
||||
; lengths, plus 1 (# of possible string start
|
||||
; locations to check in the buffer)
|
||||
mov di,ds
|
||||
mov es,di
|
||||
mov di,[bp+Buffer] ;point ES:DI to buffer to search thru
|
||||
mov bx,[bp+ScanCharOffset] ;offset in string of character
|
||||
; on which to scan
|
||||
add di,bx ;point ES:DI to first buffer byte to scan
|
||||
mov al,[si+bx] ;put the scan character in AL
|
||||
inc bx ;set BX to the offset back to the start of the
|
||||
; potential full match after a scan match,
|
||||
; accounting for the 1-byte overrun of
|
||||
; REPNZ SCASB
|
||||
FindStringLoop:
|
||||
mov cx,dx ;put remaining buffer search length in CX
|
||||
repnz scasb ;scan for the scan byte
|
||||
jnz FindStringNotFound ;not found, so there’s no match
|
||||
;found, so we have a potential match-check the
|
||||
; rest of this candidate location
|
||||
push di ;remember the address of the next byte to scan
|
||||
mov dx,cx ;set aside the remaining length to search in
|
||||
; the buffer
|
||||
sub di,bx ;point back to the potential start of the
|
||||
; match in the buffer
|
||||
mov si,[bp+SearchString] ;point to the start of the string
|
||||
mov cx,[bp+SearchStringLength] ;string length
|
||||
shr cx,1 ;convert to word for faster search
|
||||
jnc FindStringWord ;do word search if no odd byte
|
||||
cmpsb ;compare the odd byte
|
||||
jnz FindStringNoMatch ;odd byte doesn’t match, so we
|
||||
; haven’t found the search string here
|
||||
FindStringWord:
|
||||
jcxz FindStringFound ;if the string is only 1 byte long,
|
||||
; we’ve found a match
|
||||
repz cmpsw ;check the rest of the string a word at a time
|
||||
jz FindStringFound ;it’s a match
|
||||
FindStringNoMatch:
|
||||
pop di ;get back pointer to the next byte to scan
|
||||
and dx,dx ;is there anything left to check?
|
||||
jnz FindStringLoop ;yes-check next byte
|
||||
FindStringNotFound:
|
||||
sub ax,ax ;return a NULL pointer indicating that the
|
||||
jmp FindStringDone ; string was not found
|
||||
FindStringFound:
|
||||
pop ax ;point to the buffer location at which the
|
||||
sub ax,bx ; string was found (earlier we pushed the
|
||||
; address of the byte after the scan match)
|
||||
FindStringDone:
|
||||
pop di ;restore caller’s register variables
|
||||
pop si
|
||||
pop bp ;restore caller’s stack frame
|
||||
ret
|
||||
_FindStringendp
|
||||
end
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><B>LISTING 9.3 L9-3.C</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DISPLAY_LENGTH 40
|
||||
extern unsigned char * FindString(unsigned char *, unsigned int,
|
||||
unsigned char *, unsigned int, unsigned int);
|
||||
void main(void);
|
||||
static unsigned char TestBuffer[] = “When, in the course of human \
|
||||
events, it becomes necessary for one people to dissolve the \
|
||||
political bands which have connected them with another, and to \
|
||||
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...”;
|
||||
void main() {
|
||||
static unsigned char TestString[] = “equal”;
|
||||
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
||||
unsigned char *MatchPtr;
|
||||
/* Search for TestString and report the results */
|
||||
if ((MatchPtr = FindString(TestBuffer,
|
||||
(unsigned int) strlen(TestBuffer), TestString,
|
||||
(unsigned int) strlen(TestString), 1)) == NULL) {
|
||||
/* TestString wasn’t found */
|
||||
printf(“\”%s\“ not found\n”, TestString);
|
||||
} else {
|
||||
/* TestString was found. Zero-terminate TempBuffer; strncpy
|
||||
won’t do it if DISPLAY_LENGTH characters are copied */
|
||||
TempBuffer[DISPLAY_LENGTH] = 0;
|
||||
printf(“\”%s\“ found. Next %d characters at match:\n\”%s\“\n”,
|
||||
TestString, DISPLAY_LENGTH,
|
||||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
||||
}
|
||||
}
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="09-03.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="09-05.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="vsisbn" content="1576101746" />
|
||||
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
||||
<meta name="vsauthor" content="Michael Abrash" />
|
||||
<meta name="vspublisher" content="The Coriolis Group" />
|
||||
<meta name="vspubdate" content="07/01/97" />
|
||||
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
||||
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Hints My Readers Gave Me</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=09//-->
|
||||
<!--PAGES=178-180//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="09-03.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="09-05.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 9.2 L9-2.ASM</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
||||
; the buffer for locations that match a specified character of the
|
||||
; 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,
|
||||
; unsigned int BufferLength, unsigned char * SearchString,
|
||||
; 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
|
||||
; offset 0 in the data segment to avoid confusing a match at 0 with
|
||||
; no match found.
|
||||
Parms struc
|
||||
dw 2 dup(?) ;pushed BP/return address
|
||||
Buffer dw ? ;pointer to buffer to search
|
||||
BufferLength dw ? ;length of buffer to search
|
||||
SearchString dw ? ;pointer to string for which to search
|
||||
SearchStringLength dw ? ;length of string for which to search
|
||||
ScanCharOffset dw ? ;offset in string of character for
|
||||
; which to scan
|
||||
Parmsends
|
||||
.model small
|
||||
.code
|
||||
public _FindString
|
||||
_FindStringprocnear
|
||||
push bp ;preserve caller’s stack frame
|
||||
mov bp,sp ;point to our stack frame
|
||||
push si ;preserve caller’s register variables
|
||||
push di
|
||||
cld ;make string instructions increment pointers
|
||||
mov si,[bp+SearchString] ;pointer to string to search for
|
||||
mov cx,[bp+SearchStringLength] ;length of string
|
||||
jcxz FindStringNotFound ;no match if string is 0 length
|
||||
mov dx,[bp+BufferLength] ;length of buffer
|
||||
sub dx,cx ;difference between buffer and search
|
||||
; lengths
|
||||
jc FindStringNotFound ;no match if search string is
|
||||
; longer than buffer
|
||||
inc dx ; difference between buffer and search string
|
||||
; lengths, plus 1 (# of possible string start
|
||||
; locations to check in the buffer)
|
||||
mov di,ds
|
||||
mov es,di
|
||||
mov di,[bp+Buffer] ;point ES:DI to buffer to search thru
|
||||
mov bx,[bp+ScanCharOffset] ;offset in string of character
|
||||
; on which to scan
|
||||
add di,bx ;point ES:DI to first buffer byte to scan
|
||||
mov al,[si+bx] ;put the scan character in AL
|
||||
inc bx ;set BX to the offset back to the start of the
|
||||
; potential full match after a scan match,
|
||||
; accounting for the 1-byte overrun of
|
||||
; REPNZ SCASB
|
||||
FindStringLoop:
|
||||
mov cx,dx ;put remaining buffer search length in CX
|
||||
repnz scasb ;scan for the scan byte
|
||||
jnz FindStringNotFound ;not found, so there’s no match
|
||||
;found, so we have a potential match-check the
|
||||
; rest of this candidate location
|
||||
push di ;remember the address of the next byte to scan
|
||||
mov dx,cx ;set aside the remaining length to search in
|
||||
; the buffer
|
||||
sub di,bx ;point back to the potential start of the
|
||||
; match in the buffer
|
||||
mov si,[bp+SearchString] ;point to the start of the string
|
||||
mov cx,[bp+SearchStringLength] ;string length
|
||||
shr cx,1 ;convert to word for faster search
|
||||
jnc FindStringWord ;do word search if no odd byte
|
||||
cmpsb ;compare the odd byte
|
||||
jnz FindStringNoMatch ;odd byte doesn’t match, so we
|
||||
; haven’t found the search string here
|
||||
FindStringWord:
|
||||
jcxz FindStringFound ;if the string is only 1 byte long,
|
||||
; we’ve found a match
|
||||
repz cmpsw ;check the rest of the string a word at a time
|
||||
jz FindStringFound ;it’s a match
|
||||
FindStringNoMatch:
|
||||
pop di ;get back pointer to the next byte to scan
|
||||
and dx,dx ;is there anything left to check?
|
||||
jnz FindStringLoop ;yes-check next byte
|
||||
FindStringNotFound:
|
||||
sub ax,ax ;return a NULL pointer indicating that the
|
||||
jmp FindStringDone ; string was not found
|
||||
FindStringFound:
|
||||
pop ax ;point to the buffer location at which the
|
||||
sub ax,bx ; string was found (earlier we pushed the
|
||||
; address of the byte after the scan match)
|
||||
FindStringDone:
|
||||
pop di ;restore caller’s register variables
|
||||
pop si
|
||||
pop bp ;restore caller’s stack frame
|
||||
ret
|
||||
_FindStringendp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><b>LISTING 9.3 L9-3.C</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DISPLAY_LENGTH 40
|
||||
extern unsigned char * FindString(unsigned char *, unsigned int,
|
||||
unsigned char *, unsigned int, unsigned int);
|
||||
void main(void);
|
||||
static unsigned char TestBuffer[] = “When, in the course of human \
|
||||
events, it becomes necessary for one people to dissolve the \
|
||||
political bands which have connected them with another, and to \
|
||||
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...”;
|
||||
void main() {
|
||||
static unsigned char TestString[] = “equal”;
|
||||
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
||||
unsigned char *MatchPtr;
|
||||
/* Search for TestString and report the results */
|
||||
if ((MatchPtr = FindString(TestBuffer,
|
||||
(unsigned int) strlen(TestBuffer), TestString,
|
||||
(unsigned int) strlen(TestString), 1)) == NULL) {
|
||||
/* TestString wasn’t found */
|
||||
printf(“\”%s\“ not found\n”, TestString);
|
||||
} else {
|
||||
/* TestString was found. Zero-terminate TempBuffer; strncpy
|
||||
won’t do it if DISPLAY_LENGTH characters are copied */
|
||||
TempBuffer[DISPLAY_LENGTH] = 0;
|
||||
printf(“\”%s\“ found. Next %d characters at match:\n\”%s\“\n”,
|
||||
TestString, DISPLAY_LENGTH,
|
||||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="09-03.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="09-05.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<hr width="90%" size="1" noshade="noshade" />
|
||||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
234
09-05.html
234
09-05.html
File diff suppressed because one or more lines are too long
334
09-06.html
334
09-06.html
File diff suppressed because one or more lines are too long
285
09-07.html
285
09-07.html
File diff suppressed because one or more lines are too long
187
10-01.html
187
10-01.html
File diff suppressed because one or more lines are too long
512
10-02.html
512
10-02.html
File diff suppressed because one or more lines are too long
347
10-03.html
347
10-03.html
File diff suppressed because one or more lines are too long
334
10-04.html
334
10-04.html
File diff suppressed because one or more lines are too long
213
11-01.html
213
11-01.html
File diff suppressed because one or more lines are too long
227
11-02.html
227
11-02.html
File diff suppressed because one or more lines are too long
314
11-03.html
314
11-03.html
File diff suppressed because one or more lines are too long
277
11-04.html
277
11-04.html
File diff suppressed because one or more lines are too long
193
11-05.html
193
11-05.html
File diff suppressed because one or more lines are too long
210
11-06.html
210
11-06.html
File diff suppressed because one or more lines are too long
203
11-07.html
203
11-07.html
File diff suppressed because one or more lines are too long
275
11-08.html
275
11-08.html
File diff suppressed because one or more lines are too long
246
12-01.html
246
12-01.html
File diff suppressed because one or more lines are too long
313
12-02.html
313
12-02.html
File diff suppressed because one or more lines are too long
301
12-03.html
301
12-03.html
File diff suppressed because one or more lines are too long
332
12-04.html
332
12-04.html
File diff suppressed because one or more lines are too long
216
13-01.html
216
13-01.html
File diff suppressed because one or more lines are too long
239
13-02.html
239
13-02.html
File diff suppressed because one or more lines are too long
235
13-03.html
235
13-03.html
File diff suppressed because one or more lines are too long
214
13-04.html
214
13-04.html
File diff suppressed because one or more lines are too long
194
14-01.html
194
14-01.html
File diff suppressed because one or more lines are too long
195
14-02.html
195
14-02.html
File diff suppressed because one or more lines are too long
372
14-03.html
372
14-03.html
File diff suppressed because one or more lines are too long
425
14-04.html
425
14-04.html
File diff suppressed because one or more lines are too long
456
14-05.html
456
14-05.html
|
|
@ -1,229 +1,227 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=14//-->
|
||||
<!--PAGES=271-274//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><body>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="14-04.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="14-06.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P><B>LISTING 14.3 L14-3.ASM</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||
; uses the value of the mismatched byte to skip across as many
|
||||
; potential match locations as possible (partial Boyer-Moore).
|
||||
; Returns start offset of first match searching forward, or NULL if
|
||||
; no match is found.
|
||||
; Tested with TASM.
|
||||
; C near-callable as:
|
||||
; unsigned char * FindString(unsigned char * BufferPtr,
|
||||
; unsigned int BufferLength, unsigned char * PatternPtr,
|
||||
; unsigned int PatternLength);
|
||||
|
||||
parms struc
|
||||
dw 2 dup(?) ;pushed BP & return address
|
||||
BufferPtr dw ? ;pointer to buffer to be searched
|
||||
BufferLength dw ? ;# of bytes in buffer to be searched
|
||||
PatternPtr dw ? ;pointer to pattern for which to search
|
||||
PatternLength dw ? ;length of pattern for which to search
|
||||
parms ends
|
||||
|
||||
.model small
|
||||
.code
|
||||
public _FindString
|
||||
_FindString proc near
|
||||
cld
|
||||
push bp ;preserve caller’s stack frame
|
||||
mov bp,sp ;point to our stack frame
|
||||
push si ;preserve caller’s register variables
|
||||
push di
|
||||
sub sp,256*2 ;allocate space for SkipTable
|
||||
; Create the table of distances by which to skip ahead on mismatches
|
||||
; for every possible byte value. First, initialize all skips to the
|
||||
; pattern length; this is the skip distance for bytes that don’t
|
||||
; appear in the pattern.
|
||||
mov ax,[bp+PatternLength]
|
||||
and ax,ax ;return an instant match if the pattern is
|
||||
jz InstantMatch ;0-length
|
||||
mov di,ds
|
||||
mov es,di ;ES=DS=SS
|
||||
mov di,sp ;point to SkipBuffer
|
||||
mov cx,256
|
||||
rep stosw
|
||||
dec ax ;from now on, we only need
|
||||
mov [bp+PatternLength],ax ; PatternLength - 1
|
||||
; Point to last (rightmost) byte of first potential pattern match
|
||||
; location in buffer.
|
||||
add [bp+BufferPtr],ax
|
||||
; Reject if buffer is too small, and set the count of the number of
|
||||
; potential pattern match locations in the buffer.
|
||||
sub [bp+BufferLength],ax
|
||||
jbe NoMatch
|
||||
; 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.
|
||||
; When there are multiple instances of the same byte, the rightmost
|
||||
; 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
|
||||
; 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
|
||||
; for skipping purposes.
|
||||
mov si,[bp+PatternPtr] ;point to start of pattern
|
||||
and ax,ax ;are there any skips to set?
|
||||
jz SetSkipDone ;no
|
||||
mov di,sp ;point to SkipBuffer
|
||||
SetSkipLoop:
|
||||
sub bx,bx ;prepare for word addressing off byte value
|
||||
mov bl,[si] ;get the next pattern byte
|
||||
inc si ;advance the pattern pointer
|
||||
shl bx,1 ;prepare for word lookup
|
||||
mov [di+bx],ax ;set the skip value when this byte value is
|
||||
; the mismatch value in the buffer
|
||||
dec ax
|
||||
jnz SetSkipLoop
|
||||
SetSkipDone:
|
||||
mov dl,[si] ;DL=rightmost pattern byte from now on
|
||||
dec si ;point to next-to-rightmost byte of pattern
|
||||
mov [bp+PatternPtr],si ; from now on
|
||||
; Search the buffer.
|
||||
std ;for backward REPZ CMPSB
|
||||
mov di,[bp+BufferPtr] ;point to first search location
|
||||
mov cx,[bp+BufferLength] ;# of match locations to check
|
||||
SearchLoop:
|
||||
mov si,sp ;point SI to SkipTable
|
||||
; Skip through until there’s a match for the rightmost pattern byte.
|
||||
QuickSearchLoop:
|
||||
mov bl,[di] ;rightmost buffer byte at this location
|
||||
cmp dl,bl ;does it match the rightmost pattern byte?
|
||||
jz FullCompare ;yes, so keep going
|
||||
sub bh,bh ;convert to a word
|
||||
add bx,bx ;prepare for look-up in SkipTable
|
||||
mov ax,[si+bx] ;get skip value from skip table for this
|
||||
; mismatch value
|
||||
add di,ax ;BufferPtr += Skip;
|
||||
sub cx,ax ;BufferLength -= Skip;
|
||||
ja QuickSearchLoop ;continue if any buffer left
|
||||
jmp short NoMatch
|
||||
; Return a pointer to the start of the buffer (for 0-length pattern).
|
||||
align 2
|
||||
InstantMatch:
|
||||
mov ax,[bp+BufferPtr]
|
||||
jmp short Done
|
||||
; Compare the pattern and the buffer location, searching from high
|
||||
; memory toward low (right to left).
|
||||
align 2
|
||||
FullCompare:
|
||||
mov [bp+BufferPtr],di ;save the current state of
|
||||
mov [bp+BufferLength],cx ; the search
|
||||
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
||||
jcxz Match ;done if only one character
|
||||
mov si,[bp+PatternPtr] ;point to next-to-rightmost bytes
|
||||
dec di ; of buffer location and pattern
|
||||
repz cmpsb ;compare the rest of the pattern
|
||||
jz Match ;that’s it; we’ve found a match
|
||||
; It’s a mismatch; let’s see what we can learn from it.
|
||||
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
||||
; point to mismatch location in buffer
|
||||
; # of bytes that did match.
|
||||
mov si,[bp+BufferPtr]
|
||||
sub si,di
|
||||
; 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
|
||||
; 1 to the next potential match; otherwise, skip ahead from this
|
||||
; comparison location by the skip distance for the mismatch character,
|
||||
; less the distance covered by the partial match.
|
||||
sub bx,bx ;prepare for word addressing off byte value
|
||||
mov bl,[di] ;get the value of the mismatch byte in buffer
|
||||
add bx,bx ;prepare for word look-up
|
||||
add bx,sp ;SP points to SkipTable
|
||||
mov cx,[bx] ;get the skip value for this mismatch
|
||||
mov ax,1 ;assume we’ll just advance to the next
|
||||
; potential match location
|
||||
sub cx,si ;is the skip far enough to be worth taking?
|
||||
jna MoveAhead ;no, go with the default advance of 1
|
||||
mov ax,cx ;yes; this is the distance to skip ahead from
|
||||
; the last potential match location checked
|
||||
MoveAhead:
|
||||
; Skip ahead and perform the next comparison, if there’s any buffer
|
||||
; left to check.
|
||||
mov di,[bp+BufferPtr]
|
||||
add di,ax ;BufferPtr += Skip;
|
||||
mov cx,[bp+BufferLength]
|
||||
sub cx,ax ;BufferLength -= Skip;
|
||||
ja SearchLoop ;continue if any buffer left
|
||||
; Return a NULL pointer for no match.
|
||||
align 2
|
||||
NoMatch:
|
||||
sub ax,ax
|
||||
jmp short Done
|
||||
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
||||
align 2
|
||||
Match:
|
||||
mov ax,[bp+BufferPtr]
|
||||
sub ax,[bp+PatternLength]
|
||||
Done:
|
||||
cld ;restore default direction flag
|
||||
add sp,256*2 ;deallocate space for SkipTable
|
||||
pop di ;restore caller’s register variables
|
||||
pop si
|
||||
pop bp ;restore caller’s stack frame
|
||||
ret
|
||||
_FindString endp
|
||||
end
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="14-04.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="14-06.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="vsisbn" content="1576101746" />
|
||||
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
||||
<meta name="vsauthor" content="Michael Abrash" />
|
||||
<meta name="vspublisher" content="The Coriolis Group" />
|
||||
<meta name="vspubdate" content="07/01/97" />
|
||||
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
||||
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Boyer-Moore String Searching</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=14//-->
|
||||
<!--PAGES=271-274//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="14-04.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="14-06.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 14.3 L14-3.ASM</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
||||
; uses the value of the mismatched byte to skip across as many
|
||||
; potential match locations as possible (partial Boyer-Moore).
|
||||
; Returns start offset of first match searching forward, or NULL if
|
||||
; no match is found.
|
||||
; Tested with TASM.
|
||||
; C near-callable as:
|
||||
; unsigned char * FindString(unsigned char * BufferPtr,
|
||||
; unsigned int BufferLength, unsigned char * PatternPtr,
|
||||
; unsigned int PatternLength);
|
||||
|
||||
parms struc
|
||||
dw 2 dup(?) ;pushed BP & return address
|
||||
BufferPtr dw ? ;pointer to buffer to be searched
|
||||
BufferLength dw ? ;# of bytes in buffer to be searched
|
||||
PatternPtr dw ? ;pointer to pattern for which to search
|
||||
PatternLength dw ? ;length of pattern for which to search
|
||||
parms ends
|
||||
|
||||
.model small
|
||||
.code
|
||||
public _FindString
|
||||
_FindString proc near
|
||||
cld
|
||||
push bp ;preserve caller’s stack frame
|
||||
mov bp,sp ;point to our stack frame
|
||||
push si ;preserve caller’s register variables
|
||||
push di
|
||||
sub sp,256*2 ;allocate space for SkipTable
|
||||
; Create the table of distances by which to skip ahead on mismatches
|
||||
; for every possible byte value. First, initialize all skips to the
|
||||
; pattern length; this is the skip distance for bytes that don’t
|
||||
; appear in the pattern.
|
||||
mov ax,[bp+PatternLength]
|
||||
and ax,ax ;return an instant match if the pattern is
|
||||
jz InstantMatch ;0-length
|
||||
mov di,ds
|
||||
mov es,di ;ES=DS=SS
|
||||
mov di,sp ;point to SkipBuffer
|
||||
mov cx,256
|
||||
rep stosw
|
||||
dec ax ;from now on, we only need
|
||||
mov [bp+PatternLength],ax ; PatternLength - 1
|
||||
; Point to last (rightmost) byte of first potential pattern match
|
||||
; location in buffer.
|
||||
add [bp+BufferPtr],ax
|
||||
; Reject if buffer is too small, and set the count of the number of
|
||||
; potential pattern match locations in the buffer.
|
||||
sub [bp+BufferLength],ax
|
||||
jbe NoMatch
|
||||
; 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.
|
||||
; When there are multiple instances of the same byte, the rightmost
|
||||
; 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
|
||||
; 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
|
||||
; for skipping purposes.
|
||||
mov si,[bp+PatternPtr] ;point to start of pattern
|
||||
and ax,ax ;are there any skips to set?
|
||||
jz SetSkipDone ;no
|
||||
mov di,sp ;point to SkipBuffer
|
||||
SetSkipLoop:
|
||||
sub bx,bx ;prepare for word addressing off byte value
|
||||
mov bl,[si] ;get the next pattern byte
|
||||
inc si ;advance the pattern pointer
|
||||
shl bx,1 ;prepare for word lookup
|
||||
mov [di+bx],ax ;set the skip value when this byte value is
|
||||
; the mismatch value in the buffer
|
||||
dec ax
|
||||
jnz SetSkipLoop
|
||||
SetSkipDone:
|
||||
mov dl,[si] ;DL=rightmost pattern byte from now on
|
||||
dec si ;point to next-to-rightmost byte of pattern
|
||||
mov [bp+PatternPtr],si ; from now on
|
||||
; Search the buffer.
|
||||
std ;for backward REPZ CMPSB
|
||||
mov di,[bp+BufferPtr] ;point to first search location
|
||||
mov cx,[bp+BufferLength] ;# of match locations to check
|
||||
SearchLoop:
|
||||
mov si,sp ;point SI to SkipTable
|
||||
; Skip through until there’s a match for the rightmost pattern byte.
|
||||
QuickSearchLoop:
|
||||
mov bl,[di] ;rightmost buffer byte at this location
|
||||
cmp dl,bl ;does it match the rightmost pattern byte?
|
||||
jz FullCompare ;yes, so keep going
|
||||
sub bh,bh ;convert to a word
|
||||
add bx,bx ;prepare for look-up in SkipTable
|
||||
mov ax,[si+bx] ;get skip value from skip table for this
|
||||
; mismatch value
|
||||
add di,ax ;BufferPtr += Skip;
|
||||
sub cx,ax ;BufferLength -= Skip;
|
||||
ja QuickSearchLoop ;continue if any buffer left
|
||||
jmp short NoMatch
|
||||
; Return a pointer to the start of the buffer (for 0-length pattern).
|
||||
align 2
|
||||
InstantMatch:
|
||||
mov ax,[bp+BufferPtr]
|
||||
jmp short Done
|
||||
; Compare the pattern and the buffer location, searching from high
|
||||
; memory toward low (right to left).
|
||||
align 2
|
||||
FullCompare:
|
||||
mov [bp+BufferPtr],di ;save the current state of
|
||||
mov [bp+BufferLength],cx ; the search
|
||||
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
||||
jcxz Match ;done if only one character
|
||||
mov si,[bp+PatternPtr] ;point to next-to-rightmost bytes
|
||||
dec di ; of buffer location and pattern
|
||||
repz cmpsb ;compare the rest of the pattern
|
||||
jz Match ;that’s it; we’ve found a match
|
||||
; It’s a mismatch; let’s see what we can learn from it.
|
||||
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
||||
; point to mismatch location in buffer
|
||||
; # of bytes that did match.
|
||||
mov si,[bp+BufferPtr]
|
||||
sub si,di
|
||||
; 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
|
||||
; 1 to the next potential match; otherwise, skip ahead from this
|
||||
; comparison location by the skip distance for the mismatch character,
|
||||
; less the distance covered by the partial match.
|
||||
sub bx,bx ;prepare for word addressing off byte value
|
||||
mov bl,[di] ;get the value of the mismatch byte in buffer
|
||||
add bx,bx ;prepare for word look-up
|
||||
add bx,sp ;SP points to SkipTable
|
||||
mov cx,[bx] ;get the skip value for this mismatch
|
||||
mov ax,1 ;assume we’ll just advance to the next
|
||||
; potential match location
|
||||
sub cx,si ;is the skip far enough to be worth taking?
|
||||
jna MoveAhead ;no, go with the default advance of 1
|
||||
mov ax,cx ;yes; this is the distance to skip ahead from
|
||||
; the last potential match location checked
|
||||
MoveAhead:
|
||||
; Skip ahead and perform the next comparison, if there’s any buffer
|
||||
; left to check.
|
||||
mov di,[bp+BufferPtr]
|
||||
add di,ax ;BufferPtr += Skip;
|
||||
mov cx,[bp+BufferLength]
|
||||
sub cx,ax ;BufferLength -= Skip;
|
||||
ja SearchLoop ;continue if any buffer left
|
||||
; Return a NULL pointer for no match.
|
||||
align 2
|
||||
NoMatch:
|
||||
sub ax,ax
|
||||
jmp short Done
|
||||
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
||||
align 2
|
||||
Match:
|
||||
mov ax,[bp+BufferPtr]
|
||||
sub ax,[bp+PatternLength]
|
||||
Done:
|
||||
cld ;restore default direction flag
|
||||
add sp,256*2 ;deallocate space for SkipTable
|
||||
pop di ;restore caller’s register variables
|
||||
pop si
|
||||
pop bp ;restore caller’s stack frame
|
||||
ret
|
||||
_FindString endp
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="14-04.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="14-06.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<hr width="90%" size="1" noshade="noshade" />
|
||||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
452
14-06.html
452
14-06.html
File diff suppressed because one or more lines are too long
190
15-01.html
190
15-01.html
File diff suppressed because one or more lines are too long
346
15-02.html
346
15-02.html
File diff suppressed because one or more lines are too long
350
15-03.html
350
15-03.html
File diff suppressed because one or more lines are too long
506
15-04.html
506
15-04.html
File diff suppressed because one or more lines are too long
416
16-01.html
416
16-01.html
File diff suppressed because one or more lines are too long
430
16-02.html
430
16-02.html
File diff suppressed because one or more lines are too long
348
16-03.html
348
16-03.html
File diff suppressed because one or more lines are too long
204
16-04.html
204
16-04.html
File diff suppressed because one or more lines are too long
680
16-05.html
680
16-05.html
|
|
@ -1,307 +1,373 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=16//-->
|
||||
<!--PAGES=308-311//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><body>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="16-04.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="16-06.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P>Table 16.2 has two times for each entry listed: the first value is the overall counting time, including time spent in the main program, disk I/O, and everything else; the second value is the time actually spent counting words, the time spent in <B>ScanBuffer</B> . The first value is the time perceived by the user, but the second value best reflects the quality of the optimization in each entry, since the rest of the overall execution time is fixed.</P>
|
||||
<TABLE WIDTH="85%"><TR>
|
||||
<TD COLSPAN="3"><HR>
|
||||
<TR>
|
||||
<TD VALIGN="TOP" ALIGN="LFFT">
|
||||
<TH ALIGN="CENTER" COLSPAN="2">Word-Counting Time
|
||||
<TR>
|
||||
<TH WIDTH="35%" ALIGN="LEFT" VALIGN="TOP">Name
|
||||
<TH WIDTH="25%" ALIGN="LEFT" VALIGN="TOP">Overall time
|
||||
<TH WIDTH="25%" ALIGN="LEFT" VALIGN="TOP">(ScanBuffer only)
|
||||
<TR>
|
||||
<TD COLSPAN="3"><HR>
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">David Stafford Listing 16.5 <TD ALIGN="LEFT" VALIGN="TOP">0.61 seconds
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.33 seconds
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Dave Methvin
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.66
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.39
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Mick Brown
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.70
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.41
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Wendell Neubert
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.92
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">0.65
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP"><B>For Comparison:</B>
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Michael Abrash
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">1.73
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">1.44
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">assembly code
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Listing 16.1
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Michael Abrash
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">4.70
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">4.43
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">C code
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">Listing 16.4
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP" COLSPAN="3"><SMALL><B>Note:</B> All times measured on a 20 MHz cached 386 DX.</SMALL>
|
||||
<TR>
|
||||
<TH ALIGN="LEFT" VALIGN="TOP" COLSPAN="3">Table 16.2 The top four word-counting entries.
|
||||
<TR>
|
||||
<TD COLSPAN="3"><HR>
|
||||
</TABLE>
|
||||
<P><B>LISTING 16.5 QSCAN3.ASM</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
; QSCAN3.ASM
|
||||
; David Stafford
|
||||
|
||||
|
||||
COMMENT $
|
||||
|
||||
How it works
|
||||
——————
|
||||
The idea is to go through the buffer fetching each letter-pair (words
|
||||
rather than bytes). The carry flag indicates whether we are
|
||||
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
|
||||
(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
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
remove the high bit and the count of words remains in the count
|
||||
register.
|
||||
Sound complicated? You’re right! But it’s fast!
|
||||
|
||||
The beauty of this method is that no jumps are required, the
|
||||
operations are fast, it requires only one table and the process can
|
||||
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
||||
jumping.
|
||||
|
||||
COMMEND $
|
||||
.model small
|
||||
.code
|
||||
|
||||
Test1 macro x,y ;9 or 10 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
or ax,si
|
||||
add al,[di]
|
||||
endm
|
||||
|
||||
Test2 macro x,y ;7 or 8 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
add ah,[di]
|
||||
endm
|
||||
|
||||
Scan = 128 ;scan 256 bytes at a time
|
||||
Buffer = 4 ;parms
|
||||
BufferLength = 6
|
||||
CharFlag = 8
|
||||
WordCount = 10
|
||||
|
||||
public _ScanBuffer
|
||||
_ScanBuffer proc near
|
||||
push bp
|
||||
mov bp,sp
|
||||
push si
|
||||
push di
|
||||
|
||||
xor cx,cx
|
||||
mov si,[bp+Buffer] ;si = text buffer
|
||||
mov ax,[bp+BufferLength] ;dx = length in bytes
|
||||
shr ax,1 ;dx = length in words
|
||||
jnz NormalBuf
|
||||
OneByteBuf:
|
||||
mov ax,seg WordTable
|
||||
mov es,ax
|
||||
|
||||
mov di,[bp+CharFlag]
|
||||
mov bh,[di] ;bh = old CharFlag
|
||||
mov bl,[si] ;bl = character
|
||||
add bh,‘A’-1 ;make bh into character
|
||||
add bx,bx ;prepare to index
|
||||
mov al,es:[bx]
|
||||
cbw ;get hi bit in ah (then bh)
|
||||
shr al,1 ;get low bit
|
||||
adc cx,cx ;cx = 0 or 1
|
||||
xchg ax,bx
|
||||
jmp CleanUp
|
||||
NormalBuf:
|
||||
push bp ;(1)
|
||||
pushf ;(2)
|
||||
|
||||
cwd ;dx = 0
|
||||
mov cl,Scan
|
||||
div cx
|
||||
or dx,dx ;remainder?
|
||||
jz StartAtTheTop ;nope, do the whole banana
|
||||
sub cx,dx
|
||||
sub si,cx ;adjust buf pointer
|
||||
sub si,cx
|
||||
inc ax ;adjust for partial read
|
||||
|
||||
StartAtTheTop: mov bx,dx ;get index for start...
|
||||
shl bx,1
|
||||
mov di,LoopEntry[bx] ;...address in di
|
||||
xchg dx,ax ;dx is the loop counter
|
||||
xor cx,cx ;total word count
|
||||
mov bx,[bp+CharFlag]
|
||||
mov bl,[bx] ;bl = old CharFlag
|
||||
mov bp,seg WordTable
|
||||
mov ds,bp
|
||||
mov bp,si ;scan buffer with bp
|
||||
mov si,8080h ;hi bits
|
||||
mov ax,si ;init local word counter
|
||||
shr bl,1 ;carry = old CharFlag
|
||||
jmp di
|
||||
|
||||
align 2
|
||||
Top: add bx,bx ;restore carry
|
||||
n = 0
|
||||
rept Scan/2
|
||||
Test1 %n,%n*2
|
||||
Test2 %n+1,%n*2+2
|
||||
n = n+2
|
||||
endm
|
||||
EndCount:
|
||||
sbb bx,bx ;save carry
|
||||
if Scan ge 128 ;because al+ah may equal 128!
|
||||
or ax,si
|
||||
add al,ah
|
||||
mov ah,0
|
||||
else
|
||||
add al,ah
|
||||
and ax,7fh ;mask
|
||||
endif
|
||||
add cx,ax ;update word count
|
||||
mov ax,si
|
||||
add bp,Scan*2
|
||||
dec dx ;any left?
|
||||
jng Quit
|
||||
jmp Top
|
||||
|
||||
Quit: popf ;(2) even or odd buffer?
|
||||
jnc ItsEven
|
||||
clc
|
||||
Test1 Odd,-1
|
||||
sbb bx,bx ;save carry
|
||||
shr ax,1
|
||||
adc cx,0
|
||||
ItsEven:
|
||||
push ss ;restore ds
|
||||
pop ds
|
||||
pop bp ;(1)
|
||||
CleanUp:
|
||||
mov si,[bp+WordCount]
|
||||
add [si],cx
|
||||
adc word ptr [si+2],0
|
||||
and bh,1 ;save only the carry flag
|
||||
mov si,[bp+CharFlag]
|
||||
mov [si],bh
|
||||
pop di
|
||||
pop si
|
||||
pop bp
|
||||
ret
|
||||
_ScanBuffer endp
|
||||
|
||||
.data
|
||||
Address macro X
|
||||
dw Addr&X
|
||||
endm
|
||||
|
||||
LoopEntry label word
|
||||
n = Scan
|
||||
REPT Scan
|
||||
Address %n MOD Scan
|
||||
n = n - 1
|
||||
ENDM
|
||||
|
||||
.fardata WordTable
|
||||
include qscan3.inc ;built by MAKETAB
|
||||
end
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="16-04.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="16-06.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="vsisbn" content="1576101746" />
|
||||
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
||||
<meta name="vsauthor" content="Michael Abrash" />
|
||||
<meta name="vspublisher" content="The Coriolis Group" />
|
||||
<meta name="vspubdate" content="07/01/97" />
|
||||
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
||||
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: There Ain't No Such Thing as the Fastest Code</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=16//-->
|
||||
<!--PAGES=308-311//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="16-04.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="16-06.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<p>Table 16.2 has two times for each entry listed: the first value is the overall counting time, including time spent in the main program, disk I/O, and everything else; the second value is the time actually spent counting words, the time spent in <b>ScanBuffer</b> . The first value is the time perceived by the user, but the second value best reflects the quality of the optimization in each entry, since the rest of the overall execution time is fixed.</p>
|
||||
|
||||
<table width="85%">
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<hr />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td valign="top" align="lfft"></td>
|
||||
|
||||
<th align="center" colspan="2">Word-Counting Time</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th width="35%" align="left" valign="top">Name</th>
|
||||
|
||||
<th width="25%" align="left" valign="top">Overall time</th>
|
||||
|
||||
<th width="25%" align="left" valign="top">(ScanBuffer only)</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<hr />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">David Stafford Listing 16.5</td>
|
||||
|
||||
<td align="left" valign="top">0.61 seconds</td>
|
||||
|
||||
<td align="left" valign="top">0.33 seconds</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Dave Methvin</td>
|
||||
|
||||
<td align="left" valign="top">0.66</td>
|
||||
|
||||
<td align="left" valign="top">0.39</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Mick Brown</td>
|
||||
|
||||
<td align="left" valign="top">0.70</td>
|
||||
|
||||
<td align="left" valign="top">0.41</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Wendell Neubert</td>
|
||||
|
||||
<td align="left" valign="top">0.92</td>
|
||||
|
||||
<td align="left" valign="top">0.65</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top"><b>For Comparison:</b></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Michael Abrash</td>
|
||||
|
||||
<td align="left" valign="top">1.73</td>
|
||||
|
||||
<td align="left" valign="top">1.44</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">assembly code</td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Listing 16.1</td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Michael Abrash</td>
|
||||
|
||||
<td align="left" valign="top">4.70</td>
|
||||
|
||||
<td align="left" valign="top">4.43</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">C code</td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top">Listing 16.4</td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
|
||||
<td align="left" valign="top"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align="left" valign="top" colspan="3"><small><b>Note:</b> All times measured on a 20 MHz cached 386 DX.</small></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th align="left" valign="top" colspan="3">Table 16.2 The top four word-counting entries.</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<hr />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p><b>LISTING 16.5 QSCAN3.ASM</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
; QSCAN3.ASM
|
||||
; David Stafford
|
||||
|
||||
|
||||
COMMENT $
|
||||
|
||||
How it works
|
||||
——————
|
||||
The idea is to go through the buffer fetching each letter-pair (words
|
||||
rather than bytes). The carry flag indicates whether we are
|
||||
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
|
||||
(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
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
remove the high bit and the count of words remains in the count
|
||||
register.
|
||||
Sound complicated? You’re right! But it’s fast!
|
||||
|
||||
The beauty of this method is that no jumps are required, the
|
||||
operations are fast, it requires only one table and the process can
|
||||
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
||||
jumping.
|
||||
|
||||
COMMEND $
|
||||
.model small
|
||||
.code
|
||||
|
||||
Test1 macro x,y ;9 or 10 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
or ax,si
|
||||
add al,[di]
|
||||
endm
|
||||
|
||||
Test2 macro x,y ;7 or 8 bytes
|
||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
||||
adc di,di
|
||||
add ah,[di]
|
||||
endm
|
||||
|
||||
Scan = 128 ;scan 256 bytes at a time
|
||||
Buffer = 4 ;parms
|
||||
BufferLength = 6
|
||||
CharFlag = 8
|
||||
WordCount = 10
|
||||
|
||||
public _ScanBuffer
|
||||
_ScanBuffer proc near
|
||||
push bp
|
||||
mov bp,sp
|
||||
push si
|
||||
push di
|
||||
|
||||
xor cx,cx
|
||||
mov si,[bp+Buffer] ;si = text buffer
|
||||
mov ax,[bp+BufferLength] ;dx = length in bytes
|
||||
shr ax,1 ;dx = length in words
|
||||
jnz NormalBuf
|
||||
OneByteBuf:
|
||||
mov ax,seg WordTable
|
||||
mov es,ax
|
||||
|
||||
mov di,[bp+CharFlag]
|
||||
mov bh,[di] ;bh = old CharFlag
|
||||
mov bl,[si] ;bl = character
|
||||
add bh,‘A’-1 ;make bh into character
|
||||
add bx,bx ;prepare to index
|
||||
mov al,es:[bx]
|
||||
cbw ;get hi bit in ah (then bh)
|
||||
shr al,1 ;get low bit
|
||||
adc cx,cx ;cx = 0 or 1
|
||||
xchg ax,bx
|
||||
jmp CleanUp
|
||||
NormalBuf:
|
||||
push bp ;(1)
|
||||
pushf ;(2)
|
||||
|
||||
cwd ;dx = 0
|
||||
mov cl,Scan
|
||||
div cx
|
||||
or dx,dx ;remainder?
|
||||
jz StartAtTheTop ;nope, do the whole banana
|
||||
sub cx,dx
|
||||
sub si,cx ;adjust buf pointer
|
||||
sub si,cx
|
||||
inc ax ;adjust for partial read
|
||||
|
||||
StartAtTheTop: mov bx,dx ;get index for start...
|
||||
shl bx,1
|
||||
mov di,LoopEntry[bx] ;...address in di
|
||||
xchg dx,ax ;dx is the loop counter
|
||||
xor cx,cx ;total word count
|
||||
mov bx,[bp+CharFlag]
|
||||
mov bl,[bx] ;bl = old CharFlag
|
||||
mov bp,seg WordTable
|
||||
mov ds,bp
|
||||
mov bp,si ;scan buffer with bp
|
||||
mov si,8080h ;hi bits
|
||||
mov ax,si ;init local word counter
|
||||
shr bl,1 ;carry = old CharFlag
|
||||
jmp di
|
||||
|
||||
align 2
|
||||
Top: add bx,bx ;restore carry
|
||||
n = 0
|
||||
rept Scan/2
|
||||
Test1 %n,%n*2
|
||||
Test2 %n+1,%n*2+2
|
||||
n = n+2
|
||||
endm
|
||||
EndCount:
|
||||
sbb bx,bx ;save carry
|
||||
if Scan ge 128 ;because al+ah may equal 128!
|
||||
or ax,si
|
||||
add al,ah
|
||||
mov ah,0
|
||||
else
|
||||
add al,ah
|
||||
and ax,7fh ;mask
|
||||
endif
|
||||
add cx,ax ;update word count
|
||||
mov ax,si
|
||||
add bp,Scan*2
|
||||
dec dx ;any left?
|
||||
jng Quit
|
||||
jmp Top
|
||||
|
||||
Quit: popf ;(2) even or odd buffer?
|
||||
jnc ItsEven
|
||||
clc
|
||||
Test1 Odd,-1
|
||||
sbb bx,bx ;save carry
|
||||
shr ax,1
|
||||
adc cx,0
|
||||
ItsEven:
|
||||
push ss ;restore ds
|
||||
pop ds
|
||||
pop bp ;(1)
|
||||
CleanUp:
|
||||
mov si,[bp+WordCount]
|
||||
add [si],cx
|
||||
adc word ptr [si+2],0
|
||||
and bh,1 ;save only the carry flag
|
||||
mov si,[bp+CharFlag]
|
||||
mov [si],bh
|
||||
pop di
|
||||
pop si
|
||||
pop bp
|
||||
ret
|
||||
_ScanBuffer endp
|
||||
|
||||
.data
|
||||
Address macro X
|
||||
dw Addr&X
|
||||
endm
|
||||
|
||||
LoopEntry label word
|
||||
n = Scan
|
||||
REPT Scan
|
||||
Address %n MOD Scan
|
||||
n = n - 1
|
||||
ENDM
|
||||
|
||||
.fardata WordTable
|
||||
include qscan3.inc ;built by MAKETAB
|
||||
end
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="16-04.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="16-06.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<hr width="90%" size="1" noshade="noshade" />
|
||||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
165
16-06.html
165
16-06.html
File diff suppressed because one or more lines are too long
365
16-07.html
365
16-07.html
File diff suppressed because one or more lines are too long
299
16-08.html
299
16-08.html
File diff suppressed because one or more lines are too long
174
17-01.html
174
17-01.html
File diff suppressed because one or more lines are too long
645
17-02.html
645
17-02.html
|
|
@ -1,324 +1,321 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=17//-->
|
||||
<!--PAGES=325-329//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><body>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="17-01.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="17-03.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P><B>LISTING 17.1 L17-1.CPP</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* C++ Game of Life implementation for any mode for which mode set
|
||||
and draw pixel functions can be provided.
|
||||
Tested with Borland C++ in the small model. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream.h>
|
||||
#include <conio.h>
|
||||
#include <time.h>
|
||||
#include <dos.h>
|
||||
#include <bios.h>
|
||||
#include <mem.h>
|
||||
|
||||
#define ON_COLOR 15 // on-cell pixel color
|
||||
#define OFF_COLOR 0 // off-cell pixel color
|
||||
#define MSG_LINE 10 // row for text messages
|
||||
#define GENERATION_LINE 12 // row for generation # display
|
||||
#define LIMIT_18_HZ 1 // set 1 for maximum frame rate = 18Hz
|
||||
#define WRAP_EDGES 1 // set to 0 to disable wrapping around
|
||||
// at cell map edges
|
||||
class cellmap {
|
||||
private:
|
||||
unsigned char *cells;
|
||||
unsigned int width;
|
||||
unsigned int width_in_bytes;
|
||||
unsigned int height;
|
||||
unsigned int length_in_bytes;
|
||||
public:
|
||||
cellmap(unsigned int h, unsigned int v);
|
||||
~cellmap(void);
|
||||
void copy_cells(cellmap &sourcemap);
|
||||
void set_cell(unsigned int x, unsigned int y);
|
||||
void clear_cell(unsigned int x, unsigned int y);
|
||||
int cell_state(int x, int y);
|
||||
void next_generation(cellmap& dest_map);
|
||||
};
|
||||
|
||||
extern void enter_display_mode(void);
|
||||
extern void exit_display_mode(void);
|
||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
||||
unsigned int Color);
|
||||
extern void show_text(int x, int y, char *text);
|
||||
|
||||
/* 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
|
||||
display at right. */
|
||||
unsigned int cellmap_width = 96;
|
||||
unsigned int cellmap_height = 96;
|
||||
/* Width & height in pixels of each cell as displayed on screen. */
|
||||
unsigned int magnifier = 2;
|
||||
|
||||
void main()
|
||||
{
|
||||
unsigned int init_length, x, y, seed;
|
||||
unsigned long generation = 0;
|
||||
char gen_text[80];
|
||||
long bios_time, start_bios_time;
|
||||
|
||||
cellmap current_map(cellmap_height, cellmap_width);
|
||||
cellmap next_map(cellmap_height, cellmap_width);
|
||||
|
||||
// Get the seed; seed randomly if 0 entered
|
||||
cout << “Seed (0 for random seed): ”;
|
||||
cin >> seed;
|
||||
if (seed == 0) seed = (unsigned) time(NULL);
|
||||
|
||||
// Randomly initialize the initial cell map
|
||||
cout << “Initializing...”;
|
||||
srand(seed);
|
||||
init_length = (cellmap_height * cellmap_width) / 2;
|
||||
do {
|
||||
x = random(cellmap_width);
|
||||
y = random(cellmap_height);
|
||||
next_map.set_cell(x, y);
|
||||
} while (—init_length);
|
||||
current_map.copy_cells(next_map); // put init map in current_map
|
||||
|
||||
enter_display_mode();
|
||||
|
||||
// Keep recalculating and redisplaying generations until a key
|
||||
// is pressed
|
||||
show_text(0, MSG_LINE, “Generation: ”);
|
||||
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
do {
|
||||
generation++;
|
||||
sprintf(gen_text, “%10lu”, generation);
|
||||
show_text(1, GENERATION_LINE, gen_text);
|
||||
// Recalculate and draw the next generation
|
||||
current_map.next_generation(next_map);
|
||||
// Make current_map current again
|
||||
current_map.copy_cells(next_map);
|
||||
#if LIMIT_18_HZ
|
||||
// Limit to a maximum of 18.2 frames per second,for visibility
|
||||
do {
|
||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
} while (start_bios_time == bios_time);
|
||||
start_bios_time = bios_time;
|
||||
#endif
|
||||
} while (!kbhit());
|
||||
getch(); // clear keypress
|
||||
exit_display_mode();
|
||||
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
||||
seed << “\n”;
|
||||
}
|
||||
|
||||
/* cellmap constructor. */
|
||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||
{
|
||||
width = w;
|
||||
width_in_bytes = (w + 7) / 8;
|
||||
height = h;
|
||||
length_in_bytes = width_in_bytes * h;
|
||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||
}
|
||||
|
||||
/* cellmap destructor. */
|
||||
cellmap::~cellmap(void)
|
||||
{
|
||||
delete[] cells;
|
||||
}
|
||||
|
||||
/* Copies one cellmap’s cells to another cellmap. Both cellmaps are
|
||||
assumed to be the same size. */
|
||||
void cellmap::copy_cells(cellmap &sourcemap)
|
||||
{
|
||||
memcpy(cells, sourcemap.cells, length_in_bytes);
|
||||
}
|
||||
|
||||
/* Turns cell on. */
|
||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned char *cell_ptr =
|
||||
cells + (y * width_in_bytes) + (x / 8);
|
||||
|
||||
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
||||
}
|
||||
|
||||
/* Turns cell off. */
|
||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned char *cell_ptr =
|
||||
cells + (y * width_in_bytes) + (x / 8);
|
||||
|
||||
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
||||
}
|
||||
|
||||
/* Returns cell state (1=on or 0=off), optionally wrapping at the
|
||||
borders around to the opposite edge. */
|
||||
int cellmap::cell_state(int x, int y)
|
||||
{
|
||||
unsigned char *cell_ptr;
|
||||
|
||||
#if WRAP_EDGES
|
||||
while (x < 0) x += width; // wrap, if necessary
|
||||
while (x >= width) x -= width;
|
||||
while (y < 0) y += height;
|
||||
while (y >= height) y -= height;
|
||||
#else
|
||||
if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
|
||||
return 0; // return 0 for off edges if no wrapping
|
||||
#endif
|
||||
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
|
||||
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Calculates the next generation of a cellmap and stores it in
|
||||
next_map. */
|
||||
void cellmap::next_generation(cellmap& next_map)
|
||||
{
|
||||
unsigned int x, y, neighbor_count;
|
||||
|
||||
for (y=0; y<height; y++) {
|
||||
for (x=0; x<width; x++) {
|
||||
// Figure out how many neighbors this cell has
|
||||
neighbor_count = cell_state(x-1, y-1) + cell_state(x, y-1) +
|
||||
cell_state(x+1, y-1) + cell_state(x-1, y) +
|
||||
cell_state(x+1, y) + cell_state(x-1, y+1) +
|
||||
cell_state(x, y+1) + cell_state(x+1, y+1);
|
||||
if (cell_state(x, y) == 1) {
|
||||
// The cell is on; does it stay on?
|
||||
if ((neighbor_count != 2) && (neighbor_count != 3)) {
|
||||
next_map.clear_cell(x, y); // turn it off
|
||||
draw_pixel(x, y, OFF_COLOR);
|
||||
}
|
||||
} else {
|
||||
// The cell is off; does it turn on?
|
||||
if (neighbor_count == 3) {
|
||||
next_map.set_cell(x, y); // turn it on
|
||||
draw_pixel(x, y, ON_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><B>LISTING 17.2 L17-2.CPP</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* VGA mode 13h functions for Game of Life.
|
||||
Tested with Borland C++. */
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <dos.h>
|
||||
|
||||
#define TEXT_X_OFFSET 27
|
||||
#define SCREEN_WIDTH_IN_BYTES 320
|
||||
|
||||
/* Width & height in pixels of each cell. */
|
||||
extern unsigned int magnifier;
|
||||
|
||||
/* Mode 13h draw pixel function. Pixels are of width & height
|
||||
specified by magnifier. */
|
||||
void draw_pixel(unsigned int x, unsigned int y, unsigned int color)
|
||||
{
|
||||
#define SCREEN_SEGMENT 0xA000
|
||||
unsigned char far *screen_ptr;
|
||||
int i, j;
|
||||
|
||||
FP_SEG(screen_ptr) = SCREEN_SEGMENT;
|
||||
FP_OFF(screen_ptr) =
|
||||
y * magnifier * SCREEN_WIDTH_IN_BYTES + x * magnifier;
|
||||
for (i=0; i<magnifier; i++) {
|
||||
for (j=0; j<magnifier; j++) {
|
||||
*(screen_ptr+j) = color;
|
||||
}
|
||||
screen_ptr += SCREEN_WIDTH_IN_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mode 13h mode-set function. */
|
||||
void enter_display_mode()
|
||||
{
|
||||
union REGS regset;
|
||||
|
||||
regset.x.ax = 0x0013;
|
||||
int86(0x10, &regset, &regset);
|
||||
}
|
||||
|
||||
/* Text mode mode-set function. */
|
||||
void exit_display_mode()
|
||||
{
|
||||
union REGS regset;
|
||||
|
||||
regset.x.ax = 0x0003;
|
||||
int86(0x10, &regset, &regset);
|
||||
}
|
||||
|
||||
/* Text display function. Offsets text to non-graphics area of
|
||||
screen. */
|
||||
void show_text(int x, int y, char *text)
|
||||
{
|
||||
gotoxy(TEXT_X_OFFSET + x, y);
|
||||
puts(text);
|
||||
}
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="17-01.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="17-03.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="vsisbn" content="1576101746" />
|
||||
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
||||
<meta name="vsauthor" content="Michael Abrash" />
|
||||
<meta name="vspublisher" content="The Coriolis Group" />
|
||||
<meta name="vspubdate" content="07/01/97" />
|
||||
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
||||
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=17//-->
|
||||
<!--PAGES=325-329//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="17-01.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="17-03.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 17.1 L17-1.CPP</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
/* C++ Game of Life implementation for any mode for which mode set
|
||||
and draw pixel functions can be provided.
|
||||
Tested with Borland C++ in the small model. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream.h>
|
||||
#include <conio.h>
|
||||
#include <time.h>
|
||||
#include <dos.h>
|
||||
#include <bios.h>
|
||||
#include <mem.h>
|
||||
|
||||
#define ON_COLOR 15 // on-cell pixel color
|
||||
#define OFF_COLOR 0 // off-cell pixel color
|
||||
#define MSG_LINE 10 // row for text messages
|
||||
#define GENERATION_LINE 12 // row for generation # display
|
||||
#define LIMIT_18_HZ 1 // set 1 for maximum frame rate = 18Hz
|
||||
#define WRAP_EDGES 1 // set to 0 to disable wrapping around
|
||||
// at cell map edges
|
||||
class cellmap {
|
||||
private:
|
||||
unsigned char *cells;
|
||||
unsigned int width;
|
||||
unsigned int width_in_bytes;
|
||||
unsigned int height;
|
||||
unsigned int length_in_bytes;
|
||||
public:
|
||||
cellmap(unsigned int h, unsigned int v);
|
||||
~cellmap(void);
|
||||
void copy_cells(cellmap &sourcemap);
|
||||
void set_cell(unsigned int x, unsigned int y);
|
||||
void clear_cell(unsigned int x, unsigned int y);
|
||||
int cell_state(int x, int y);
|
||||
void next_generation(cellmap& dest_map);
|
||||
};
|
||||
|
||||
extern void enter_display_mode(void);
|
||||
extern void exit_display_mode(void);
|
||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
||||
unsigned int Color);
|
||||
extern void show_text(int x, int y, char *text);
|
||||
|
||||
/* 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
|
||||
display at right. */
|
||||
unsigned int cellmap_width = 96;
|
||||
unsigned int cellmap_height = 96;
|
||||
/* Width & height in pixels of each cell as displayed on screen. */
|
||||
unsigned int magnifier = 2;
|
||||
|
||||
void main()
|
||||
{
|
||||
unsigned int init_length, x, y, seed;
|
||||
unsigned long generation = 0;
|
||||
char gen_text[80];
|
||||
long bios_time, start_bios_time;
|
||||
|
||||
cellmap current_map(cellmap_height, cellmap_width);
|
||||
cellmap next_map(cellmap_height, cellmap_width);
|
||||
|
||||
// Get the seed; seed randomly if 0 entered
|
||||
cout << “Seed (0 for random seed): ”;
|
||||
cin >> seed;
|
||||
if (seed == 0) seed = (unsigned) time(NULL);
|
||||
|
||||
// Randomly initialize the initial cell map
|
||||
cout << “Initializing...”;
|
||||
srand(seed);
|
||||
init_length = (cellmap_height * cellmap_width) / 2;
|
||||
do {
|
||||
x = random(cellmap_width);
|
||||
y = random(cellmap_height);
|
||||
next_map.set_cell(x, y);
|
||||
} while (—init_length);
|
||||
current_map.copy_cells(next_map); // put init map in current_map
|
||||
|
||||
enter_display_mode();
|
||||
|
||||
// Keep recalculating and redisplaying generations until a key
|
||||
// is pressed
|
||||
show_text(0, MSG_LINE, “Generation: ”);
|
||||
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
do {
|
||||
generation++;
|
||||
sprintf(gen_text, “%10lu”, generation);
|
||||
show_text(1, GENERATION_LINE, gen_text);
|
||||
// Recalculate and draw the next generation
|
||||
current_map.next_generation(next_map);
|
||||
// Make current_map current again
|
||||
current_map.copy_cells(next_map);
|
||||
#if LIMIT_18_HZ
|
||||
// Limit to a maximum of 18.2 frames per second,for visibility
|
||||
do {
|
||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
} while (start_bios_time == bios_time);
|
||||
start_bios_time = bios_time;
|
||||
#endif
|
||||
} while (!kbhit());
|
||||
getch(); // clear keypress
|
||||
exit_display_mode();
|
||||
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
||||
seed << “\n”;
|
||||
}
|
||||
|
||||
/* cellmap constructor. */
|
||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||
{
|
||||
width = w;
|
||||
width_in_bytes = (w + 7) / 8;
|
||||
height = h;
|
||||
length_in_bytes = width_in_bytes * h;
|
||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||
}
|
||||
|
||||
/* cellmap destructor. */
|
||||
cellmap::~cellmap(void)
|
||||
{
|
||||
delete[] cells;
|
||||
}
|
||||
|
||||
/* Copies one cellmap’s cells to another cellmap. Both cellmaps are
|
||||
assumed to be the same size. */
|
||||
void cellmap::copy_cells(cellmap &sourcemap)
|
||||
{
|
||||
memcpy(cells, sourcemap.cells, length_in_bytes);
|
||||
}
|
||||
|
||||
/* Turns cell on. */
|
||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned char *cell_ptr =
|
||||
cells + (y * width_in_bytes) + (x / 8);
|
||||
|
||||
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
||||
}
|
||||
|
||||
/* Turns cell off. */
|
||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned char *cell_ptr =
|
||||
cells + (y * width_in_bytes) + (x / 8);
|
||||
|
||||
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
||||
}
|
||||
|
||||
/* Returns cell state (1=on or 0=off), optionally wrapping at the
|
||||
borders around to the opposite edge. */
|
||||
int cellmap::cell_state(int x, int y)
|
||||
{
|
||||
unsigned char *cell_ptr;
|
||||
|
||||
#if WRAP_EDGES
|
||||
while (x < 0) x += width; // wrap, if necessary
|
||||
while (x >= width) x -= width;
|
||||
while (y < 0) y += height;
|
||||
while (y >= height) y -= height;
|
||||
#else
|
||||
if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
|
||||
return 0; // return 0 for off edges if no wrapping
|
||||
#endif
|
||||
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
|
||||
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Calculates the next generation of a cellmap and stores it in
|
||||
next_map. */
|
||||
void cellmap::next_generation(cellmap& next_map)
|
||||
{
|
||||
unsigned int x, y, neighbor_count;
|
||||
|
||||
for (y=0; y<height; y++) {
|
||||
for (x=0; x<width; x++) {
|
||||
// Figure out how many neighbors this cell has
|
||||
neighbor_count = cell_state(x-1, y-1) + cell_state(x, y-1) +
|
||||
cell_state(x+1, y-1) + cell_state(x-1, y) +
|
||||
cell_state(x+1, y) + cell_state(x-1, y+1) +
|
||||
cell_state(x, y+1) + cell_state(x+1, y+1);
|
||||
if (cell_state(x, y) == 1) {
|
||||
// The cell is on; does it stay on?
|
||||
if ((neighbor_count != 2) && (neighbor_count != 3)) {
|
||||
next_map.clear_cell(x, y); // turn it off
|
||||
draw_pixel(x, y, OFF_COLOR);
|
||||
}
|
||||
} else {
|
||||
// The cell is off; does it turn on?
|
||||
if (neighbor_count == 3) {
|
||||
next_map.set_cell(x, y); // turn it on
|
||||
draw_pixel(x, y, ON_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><b>LISTING 17.2 L17-2.CPP</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
/* VGA mode 13h functions for Game of Life.
|
||||
Tested with Borland C++. */
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <dos.h>
|
||||
|
||||
#define TEXT_X_OFFSET 27
|
||||
#define SCREEN_WIDTH_IN_BYTES 320
|
||||
|
||||
/* Width & height in pixels of each cell. */
|
||||
extern unsigned int magnifier;
|
||||
|
||||
/* Mode 13h draw pixel function. Pixels are of width & height
|
||||
specified by magnifier. */
|
||||
void draw_pixel(unsigned int x, unsigned int y, unsigned int color)
|
||||
{
|
||||
#define SCREEN_SEGMENT 0xA000
|
||||
unsigned char far *screen_ptr;
|
||||
int i, j;
|
||||
|
||||
FP_SEG(screen_ptr) = SCREEN_SEGMENT;
|
||||
FP_OFF(screen_ptr) =
|
||||
y * magnifier * SCREEN_WIDTH_IN_BYTES + x * magnifier;
|
||||
for (i=0; i<magnifier; i++) {
|
||||
for (j=0; j<magnifier; j++) {
|
||||
*(screen_ptr+j) = color;
|
||||
}
|
||||
screen_ptr += SCREEN_WIDTH_IN_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mode 13h mode-set function. */
|
||||
void enter_display_mode()
|
||||
{
|
||||
union REGS regset;
|
||||
|
||||
regset.x.ax = 0x0013;
|
||||
int86(0x10, &regset, &regset);
|
||||
}
|
||||
|
||||
/* Text mode mode-set function. */
|
||||
void exit_display_mode()
|
||||
{
|
||||
union REGS regset;
|
||||
|
||||
regset.x.ax = 0x0003;
|
||||
int86(0x10, &regset, &regset);
|
||||
}
|
||||
|
||||
/* Text display function. Offsets text to non-graphics area of
|
||||
screen. */
|
||||
void show_text(int x, int y, char *text)
|
||||
{
|
||||
gotoxy(TEXT_X_OFFSET + x, y);
|
||||
puts(text);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="17-01.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="17-03.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<hr width="90%" size="1" noshade="noshade" />
|
||||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
346
17-03.html
346
17-03.html
File diff suppressed because one or more lines are too long
483
17-04.html
483
17-04.html
File diff suppressed because one or more lines are too long
333
17-05.html
333
17-05.html
File diff suppressed because one or more lines are too long
206
17-06.html
206
17-06.html
File diff suppressed because one or more lines are too long
682
17-07.html
682
17-07.html
|
|
@ -1,342 +1,340 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<META name=vsisbn content="1576101746">
|
||||
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
||||
<META name=vsauthor content="Michael Abrash">
|
||||
<META name=vspublisher content="The Coriolis Group">
|
||||
<META name=vspubdate content="07/01/97">
|
||||
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</TITLE>
|
||||
|
||||
<!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=17//-->
|
||||
<!--PAGES=340-345//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//--></HEAD><body>
|
||||
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="17-06.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="17-08.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
<P><BR></P>
|
||||
<P><B>LISTING 17.5 L17-5.CPP</B></P>
|
||||
<!-- CODE //-->
|
||||
<PRE>
|
||||
/* C++ Game of Life implementation for any mode for which mode set
|
||||
and draw pixel functions can be provided. The cellmap stores the
|
||||
neighbor count for each cell as well as the state of each cell;
|
||||
this allows very fast next-state determination. Edges always wrap
|
||||
in this implementation.
|
||||
Tested with Borland C++. To run, link with Listing 17.2
|
||||
in the large model. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream.h>
|
||||
#include <conio.h>
|
||||
#include <time.h>
|
||||
#include <dos.h>
|
||||
#include <bios.h>
|
||||
#include <mem.h>
|
||||
|
||||
#define ON_COLOR 15 // on-cell pixel color
|
||||
#define OFF_COLOR 0 // off-cell pixel color
|
||||
#define MSG_LINE 10 // row for text messages
|
||||
#define GENERATION_LINE 12 // row for generation # display
|
||||
#define LIMIT_18_HZ 0 // set 1 to to maximum frame rate = 18Hz
|
||||
|
||||
class cellmap {
|
||||
private:
|
||||
unsigned char *cells;
|
||||
unsigned char *temp_cells;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int length_in_bytes;
|
||||
public:
|
||||
cellmap(unsigned int h, unsigned int v);
|
||||
~cellmap(void);
|
||||
void set_cell(unsigned int x, unsigned int y);
|
||||
void clear_cell(unsigned int x, unsigned int y);
|
||||
int cell_state(int x, int y);
|
||||
int count_neighbors(int x, int y);
|
||||
void next_generation(void);
|
||||
void init(void);
|
||||
};
|
||||
|
||||
extern void enter_display_mode(void);
|
||||
extern void exit_display_mode(void);
|
||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
||||
unsigned int Color);
|
||||
extern void show_text(int x, int y, char *text);
|
||||
|
||||
/* 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
|
||||
display at right. */
|
||||
unsigned int cellmap_width = 96;
|
||||
unsigned int cellmap_height = 96;
|
||||
|
||||
/* Width & height in pixels of each cell. */
|
||||
unsigned int magnifier = 2;
|
||||
|
||||
/* Randomizing seed */
|
||||
unsigned int seed;
|
||||
|
||||
void main()
|
||||
{
|
||||
unsigned long generation = 0;
|
||||
char gen_text[80];
|
||||
long bios_time, start_bios_time;
|
||||
|
||||
cellmap current_map(cellmap_height, cellmap_width);
|
||||
|
||||
current_map.init(); // randomly initialize cell map
|
||||
|
||||
enter_display_mode();
|
||||
|
||||
// Keep recalculating and redisplaying generations until any key
|
||||
// is pressed
|
||||
show_text(0, MSG_LINE, “Generation: ”);
|
||||
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
do {
|
||||
generation++;
|
||||
sprintf(gen_text, “%10lu”, generation);
|
||||
show_text(1, GENERATION_LINE, gen_text);
|
||||
// Recalculate and draw the next generation
|
||||
current_map.next_generation();
|
||||
#if LIMIT_18_HZ
|
||||
// Limit to a maximum of 18.2 frames per second, for visibility
|
||||
do {
|
||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
} while (start_bios_time == bios_time);
|
||||
start_bios_time = bios_time;
|
||||
#endif
|
||||
} while (!kbhit());
|
||||
|
||||
getch(); // clear keypress
|
||||
exit_display_mode();
|
||||
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
||||
seed << “\n”;
|
||||
}
|
||||
|
||||
/* cellmap constructor. */
|
||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
length_in_bytes = w * h;
|
||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
||||
temp_cells = new unsigned char[length_in_bytes]; // temp cell storage
|
||||
if ( (cells == NULL) || (temp_cells == NULL) ) {
|
||||
printf(“Out of memory\n”);
|
||||
exit(1);
|
||||
}
|
||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||
}
|
||||
|
||||
/* cellmap destructor. */
|
||||
cellmap::~cellmap(void)
|
||||
{
|
||||
delete[] cells;
|
||||
delete[] temp_cells;
|
||||
}
|
||||
|
||||
/* Turns an off-cell on, incrementing the on-neighbor count for the
|
||||
eight neighboring cells. */
|
||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int w = width, h = height;
|
||||
int xoleft, xoright, yoabove, yobelow;
|
||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
||||
|
||||
// Calculate the offsets to the eight neighboring cells,
|
||||
// accounting for wrapping around at the edges of the cell map
|
||||
if (x == 0)
|
||||
xoleft = w - 1;
|
||||
else
|
||||
xoleft = -1;
|
||||
if (y == 0)
|
||||
yoabove = length_in_bytes - w;
|
||||
else
|
||||
yoabove = -w;
|
||||
if (x == (w - 1))
|
||||
xoright = -(w - 1);
|
||||
else
|
||||
xoright = 1;
|
||||
if (y == (h - 1))
|
||||
yobelow = -(length_in_bytes - w);
|
||||
else
|
||||
yobelow = w;
|
||||
|
||||
*(cell_ptr) |= 0x01;
|
||||
*(cell_ptr + yoabove + xoleft) += 2;
|
||||
*(cell_ptr + yoabove) += 2;
|
||||
*(cell_ptr + yoabove + xoright) += 2;
|
||||
*(cell_ptr + xoleft) += 2;
|
||||
*(cell_ptr + xoright) += 2;
|
||||
*(cell_ptr + yobelow + xoleft) += 2;
|
||||
*(cell_ptr + yobelow) += 2;
|
||||
*(cell_ptr + yobelow + xoright) += 2;
|
||||
}
|
||||
|
||||
/* Turns an on-cell off, decrementing the on-neighbor count for the
|
||||
eight neighboring cells. */
|
||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int w = width, h = height;
|
||||
int xoleft, xoright, yoabove, yobelow;
|
||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
||||
|
||||
// Calculate the offsets to the eight neighboring cells,
|
||||
// accounting for wrapping around at the edges of the cell map
|
||||
if (x == 0)
|
||||
xoleft = w - 1;
|
||||
else
|
||||
xoleft = -1;
|
||||
if (y == 0)
|
||||
yoabove = length_in_bytes - w;
|
||||
else
|
||||
yoabove = -w;
|
||||
if (x == (w - 1))
|
||||
xoright = -(w - 1);
|
||||
else
|
||||
xoright = 1;
|
||||
if (y == (h - 1))
|
||||
yobelow = -(length_in_bytes - w);
|
||||
else
|
||||
yobelow = w;
|
||||
|
||||
*(cell_ptr) &= ~0x01;
|
||||
*(cell_ptr + yoabove + xoleft) -= 2;
|
||||
*(cell_ptr + yoabove ) -= 2;
|
||||
*(cell_ptr + yoabove + xoright) -= 2;
|
||||
*(cell_ptr + xoleft) -= 2;
|
||||
*(cell_ptr + xoright) -= 2;
|
||||
*(cell_ptr + yobelow + xoleft) -= 2;
|
||||
*(cell_ptr + yobelow) -= 2;
|
||||
*(cell_ptr + yobelow + xoright) -= 2;
|
||||
}
|
||||
|
||||
/* Returns cell state (1=on or 0=off). */
|
||||
int cellmap::cell_state(int x, int y)
|
||||
{
|
||||
unsigned char *cell_ptr;
|
||||
|
||||
cell_ptr = cells + (y * width) + x;
|
||||
return *cell_ptr & 0x01;
|
||||
}
|
||||
|
||||
/* Calculates and displays the next generation of current_map */
|
||||
void cellmap::next_generation()
|
||||
{
|
||||
unsigned int x, y, count;
|
||||
unsigned int h = height, w = width;
|
||||
unsigned char *cell_ptr, *row_cell_ptr;
|
||||
|
||||
// Copy to temp map, so we can have an unaltered version from
|
||||
// which to work
|
||||
memcpy(temp_cells, cells, length_in_bytes);
|
||||
|
||||
// Process all cells in the current cell map
|
||||
cell_ptr = temp_cells; // first cell in cell map
|
||||
for (y=0; y<h; y++) { // repeat for each row of cells
|
||||
// Process all cells in the current row of the cell map
|
||||
x = 0;
|
||||
do { // repeat for each cell in row
|
||||
// Zip quickly through as many off-cells with no
|
||||
// neighbors as possible
|
||||
while (*cell_ptr == 0) {
|
||||
cell_ptr++; // advance to the next cell
|
||||
if (++x >= w) goto RowDone;
|
||||
}
|
||||
// Found a cell that’s either on or has on-neighbors,
|
||||
// so see if its state needs to be changed
|
||||
count = *cell_ptr >> 1; // # of neighboring on-cells
|
||||
if (*cell_ptr & 0x01) {
|
||||
// Cell is on; turn it off if it doesn’t have
|
||||
// 2 or 3 neighbors
|
||||
if ((count != 2) && (count != 3)) {
|
||||
clear_cell(x, y);
|
||||
draw_pixel(x, y, OFF_COLOR);
|
||||
}
|
||||
} else {
|
||||
// Cell is off; turn it on if it has exactly 3 neighbors
|
||||
if (count == 3) {
|
||||
set_cell(x, y);
|
||||
draw_pixel(x, y, ON_COLOR);
|
||||
}
|
||||
}
|
||||
// Advance to the next cell
|
||||
cell_ptr++; // advance to the next cell byte
|
||||
} while (++x < w);
|
||||
RowDone:
|
||||
}
|
||||
}
|
||||
|
||||
/* Randomly initializes the cellmap to about 50% on-pixels. */
|
||||
void cellmap::init()
|
||||
{
|
||||
unsigned int x, y, init_length;
|
||||
|
||||
// Get the seed; seed randomly if 0 entered
|
||||
cout << “Seed (0 for random seed): ”;
|
||||
cin >> seed;
|
||||
if (seed == 0) seed = (unsigned) time(NULL);
|
||||
|
||||
// Randomly initialize the initial cell map to 50% on-pixels
|
||||
// (actually generally fewer, because some coordinates will be
|
||||
// randomly selected more than once)
|
||||
cout << “Initializing...”;
|
||||
srand(seed);
|
||||
init_length = (height * width) / 2;
|
||||
do {
|
||||
x = random(width);
|
||||
y = random(height);
|
||||
if (cell_state(x, y) == 0) {
|
||||
set_cell(x, y);
|
||||
}
|
||||
} while (—init_length);
|
||||
}
|
||||
</PRE>
|
||||
<!-- END CODE //-->
|
||||
<P><BR></P>
|
||||
<CENTER>
|
||||
<TABLE BORDER>
|
||||
<TR>
|
||||
<TD><A HREF="17-06.html">Previous</A></TD>
|
||||
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||
<TD><A HREF="17-08.html">Next</A></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
|
||||
<hr width="90%" size="1" noshade>
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div>
|
||||
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
<!-- END FOOTER -->
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="vsisbn" content="1576101746" />
|
||||
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
||||
<meta name="vsauthor" content="Michael Abrash" />
|
||||
<meta name="vspublisher" content="The Coriolis Group" />
|
||||
<meta name="vspubdate" content="07/01/97" />
|
||||
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
||||
|
||||
<title>Michael Abrash's Graphics Programming Black Book Special Edition: The Game of Life</title><!-- HEADER -->
|
||||
<!-- Empty Reference Subhead -->
|
||||
<!--ISBN=1576101746//-->
|
||||
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
||||
<!--AUTHOR=Michael Abrash//-->
|
||||
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
||||
<!--CHAPTER=17//-->
|
||||
<!--PAGES=340-345//-->
|
||||
<!--UNASSIGNED1//-->
|
||||
<!--UNASSIGNED2//-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="17-06.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="17-08.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<p><b>LISTING 17.5 L17-5.CPP</b></p><!-- CODE //-->
|
||||
<pre>
|
||||
/* C++ Game of Life implementation for any mode for which mode set
|
||||
and draw pixel functions can be provided. The cellmap stores the
|
||||
neighbor count for each cell as well as the state of each cell;
|
||||
this allows very fast next-state determination. Edges always wrap
|
||||
in this implementation.
|
||||
Tested with Borland C++. To run, link with Listing 17.2
|
||||
in the large model. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream.h>
|
||||
#include <conio.h>
|
||||
#include <time.h>
|
||||
#include <dos.h>
|
||||
#include <bios.h>
|
||||
#include <mem.h>
|
||||
|
||||
#define ON_COLOR 15 // on-cell pixel color
|
||||
#define OFF_COLOR 0 // off-cell pixel color
|
||||
#define MSG_LINE 10 // row for text messages
|
||||
#define GENERATION_LINE 12 // row for generation # display
|
||||
#define LIMIT_18_HZ 0 // set 1 to to maximum frame rate = 18Hz
|
||||
|
||||
class cellmap {
|
||||
private:
|
||||
unsigned char *cells;
|
||||
unsigned char *temp_cells;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int length_in_bytes;
|
||||
public:
|
||||
cellmap(unsigned int h, unsigned int v);
|
||||
~cellmap(void);
|
||||
void set_cell(unsigned int x, unsigned int y);
|
||||
void clear_cell(unsigned int x, unsigned int y);
|
||||
int cell_state(int x, int y);
|
||||
int count_neighbors(int x, int y);
|
||||
void next_generation(void);
|
||||
void init(void);
|
||||
};
|
||||
|
||||
extern void enter_display_mode(void);
|
||||
extern void exit_display_mode(void);
|
||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
||||
unsigned int Color);
|
||||
extern void show_text(int x, int y, char *text);
|
||||
|
||||
/* 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
|
||||
display at right. */
|
||||
unsigned int cellmap_width = 96;
|
||||
unsigned int cellmap_height = 96;
|
||||
|
||||
/* Width & height in pixels of each cell. */
|
||||
unsigned int magnifier = 2;
|
||||
|
||||
/* Randomizing seed */
|
||||
unsigned int seed;
|
||||
|
||||
void main()
|
||||
{
|
||||
unsigned long generation = 0;
|
||||
char gen_text[80];
|
||||
long bios_time, start_bios_time;
|
||||
|
||||
cellmap current_map(cellmap_height, cellmap_width);
|
||||
|
||||
current_map.init(); // randomly initialize cell map
|
||||
|
||||
enter_display_mode();
|
||||
|
||||
// Keep recalculating and redisplaying generations until any key
|
||||
// is pressed
|
||||
show_text(0, MSG_LINE, “Generation: ”);
|
||||
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
do {
|
||||
generation++;
|
||||
sprintf(gen_text, “%10lu”, generation);
|
||||
show_text(1, GENERATION_LINE, gen_text);
|
||||
// Recalculate and draw the next generation
|
||||
current_map.next_generation();
|
||||
#if LIMIT_18_HZ
|
||||
// Limit to a maximum of 18.2 frames per second, for visibility
|
||||
do {
|
||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
||||
} while (start_bios_time == bios_time);
|
||||
start_bios_time = bios_time;
|
||||
#endif
|
||||
} while (!kbhit());
|
||||
|
||||
getch(); // clear keypress
|
||||
exit_display_mode();
|
||||
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
||||
seed << “\n”;
|
||||
}
|
||||
|
||||
/* cellmap constructor. */
|
||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
length_in_bytes = w * h;
|
||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
||||
temp_cells = new unsigned char[length_in_bytes]; // temp cell storage
|
||||
if ( (cells == NULL) || (temp_cells == NULL) ) {
|
||||
printf(“Out of memory\n”);
|
||||
exit(1);
|
||||
}
|
||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
||||
}
|
||||
|
||||
/* cellmap destructor. */
|
||||
cellmap::~cellmap(void)
|
||||
{
|
||||
delete[] cells;
|
||||
delete[] temp_cells;
|
||||
}
|
||||
|
||||
/* Turns an off-cell on, incrementing the on-neighbor count for the
|
||||
eight neighboring cells. */
|
||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int w = width, h = height;
|
||||
int xoleft, xoright, yoabove, yobelow;
|
||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
||||
|
||||
// Calculate the offsets to the eight neighboring cells,
|
||||
// accounting for wrapping around at the edges of the cell map
|
||||
if (x == 0)
|
||||
xoleft = w - 1;
|
||||
else
|
||||
xoleft = -1;
|
||||
if (y == 0)
|
||||
yoabove = length_in_bytes - w;
|
||||
else
|
||||
yoabove = -w;
|
||||
if (x == (w - 1))
|
||||
xoright = -(w - 1);
|
||||
else
|
||||
xoright = 1;
|
||||
if (y == (h - 1))
|
||||
yobelow = -(length_in_bytes - w);
|
||||
else
|
||||
yobelow = w;
|
||||
|
||||
*(cell_ptr) |= 0x01;
|
||||
*(cell_ptr + yoabove + xoleft) += 2;
|
||||
*(cell_ptr + yoabove) += 2;
|
||||
*(cell_ptr + yoabove + xoright) += 2;
|
||||
*(cell_ptr + xoleft) += 2;
|
||||
*(cell_ptr + xoright) += 2;
|
||||
*(cell_ptr + yobelow + xoleft) += 2;
|
||||
*(cell_ptr + yobelow) += 2;
|
||||
*(cell_ptr + yobelow + xoright) += 2;
|
||||
}
|
||||
|
||||
/* Turns an on-cell off, decrementing the on-neighbor count for the
|
||||
eight neighboring cells. */
|
||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int w = width, h = height;
|
||||
int xoleft, xoright, yoabove, yobelow;
|
||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
||||
|
||||
// Calculate the offsets to the eight neighboring cells,
|
||||
// accounting for wrapping around at the edges of the cell map
|
||||
if (x == 0)
|
||||
xoleft = w - 1;
|
||||
else
|
||||
xoleft = -1;
|
||||
if (y == 0)
|
||||
yoabove = length_in_bytes - w;
|
||||
else
|
||||
yoabove = -w;
|
||||
if (x == (w - 1))
|
||||
xoright = -(w - 1);
|
||||
else
|
||||
xoright = 1;
|
||||
if (y == (h - 1))
|
||||
yobelow = -(length_in_bytes - w);
|
||||
else
|
||||
yobelow = w;
|
||||
|
||||
*(cell_ptr) &= ~0x01;
|
||||
*(cell_ptr + yoabove + xoleft) -= 2;
|
||||
*(cell_ptr + yoabove ) -= 2;
|
||||
*(cell_ptr + yoabove + xoright) -= 2;
|
||||
*(cell_ptr + xoleft) -= 2;
|
||||
*(cell_ptr + xoright) -= 2;
|
||||
*(cell_ptr + yobelow + xoleft) -= 2;
|
||||
*(cell_ptr + yobelow) -= 2;
|
||||
*(cell_ptr + yobelow + xoright) -= 2;
|
||||
}
|
||||
|
||||
/* Returns cell state (1=on or 0=off). */
|
||||
int cellmap::cell_state(int x, int y)
|
||||
{
|
||||
unsigned char *cell_ptr;
|
||||
|
||||
cell_ptr = cells + (y * width) + x;
|
||||
return *cell_ptr & 0x01;
|
||||
}
|
||||
|
||||
/* Calculates and displays the next generation of current_map */
|
||||
void cellmap::next_generation()
|
||||
{
|
||||
unsigned int x, y, count;
|
||||
unsigned int h = height, w = width;
|
||||
unsigned char *cell_ptr, *row_cell_ptr;
|
||||
|
||||
// Copy to temp map, so we can have an unaltered version from
|
||||
// which to work
|
||||
memcpy(temp_cells, cells, length_in_bytes);
|
||||
|
||||
// Process all cells in the current cell map
|
||||
cell_ptr = temp_cells; // first cell in cell map
|
||||
for (y=0; y<h; y++) { // repeat for each row of cells
|
||||
// Process all cells in the current row of the cell map
|
||||
x = 0;
|
||||
do { // repeat for each cell in row
|
||||
// Zip quickly through as many off-cells with no
|
||||
// neighbors as possible
|
||||
while (*cell_ptr == 0) {
|
||||
cell_ptr++; // advance to the next cell
|
||||
if (++x >= w) goto RowDone;
|
||||
}
|
||||
// Found a cell that’s either on or has on-neighbors,
|
||||
// so see if its state needs to be changed
|
||||
count = *cell_ptr >> 1; // # of neighboring on-cells
|
||||
if (*cell_ptr & 0x01) {
|
||||
// Cell is on; turn it off if it doesn’t have
|
||||
// 2 or 3 neighbors
|
||||
if ((count != 2) && (count != 3)) {
|
||||
clear_cell(x, y);
|
||||
draw_pixel(x, y, OFF_COLOR);
|
||||
}
|
||||
} else {
|
||||
// Cell is off; turn it on if it has exactly 3 neighbors
|
||||
if (count == 3) {
|
||||
set_cell(x, y);
|
||||
draw_pixel(x, y, ON_COLOR);
|
||||
}
|
||||
}
|
||||
// Advance to the next cell
|
||||
cell_ptr++; // advance to the next cell byte
|
||||
} while (++x < w);
|
||||
RowDone:
|
||||
}
|
||||
}
|
||||
|
||||
/* Randomly initializes the cellmap to about 50% on-pixels. */
|
||||
void cellmap::init()
|
||||
{
|
||||
unsigned int x, y, init_length;
|
||||
|
||||
// Get the seed; seed randomly if 0 entered
|
||||
cout << “Seed (0 for random seed): ”;
|
||||
cin >> seed;
|
||||
if (seed == 0) seed = (unsigned) time(NULL);
|
||||
|
||||
// Randomly initialize the initial cell map to 50% on-pixels
|
||||
// (actually generally fewer, because some coordinates will be
|
||||
// randomly selected more than once)
|
||||
cout << “Initializing...”;
|
||||
srand(seed);
|
||||
init_length = (height * width) / 2;
|
||||
do {
|
||||
x = random(width);
|
||||
y = random(height);
|
||||
if (cell_state(x, y) == 0) {
|
||||
set_cell(x, y);
|
||||
}
|
||||
} while (—init_length);
|
||||
}
|
||||
</pre><!-- END CODE //-->
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
<center>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td><a href="17-06.html">Previous</a></td>
|
||||
|
||||
<td><a href="index.html">Table of Contents</a></td>
|
||||
|
||||
<td><a href="17-08.html">Next</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<hr width="90%" size="1" noshade="noshade" />
|
||||
|
||||
<div align="center">
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
</div><!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
||||
<!-- reference_subfoot = footer -->
|
||||
<!-- reference_footer = subfoot -->
|
||||
<!-- BEGIN SUB FOOTER -->
|
||||
<!-- END FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
189
17-08.html
189
17-08.html
File diff suppressed because one or more lines are too long
174
18-01.html
174
18-01.html
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue