Tidy HTML

This commit is contained in:
James Gregory 2013-12-30 14:56:41 +11:00
commit 3760ddbdd3
362 changed files with 72376 additions and 62837 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1014
03-02.html

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1430
03-07.html

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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 &lt;stdio.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;string.h&gt;
#include &lt;alloc.h&gt; /* alloc.h for Borland compilers,
malloc.h for Microsoft compilers */
#define BLOCK_SIZE 0x4000 /* we&rsquo;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&rsquo;ve got a match */
}
else {
/* Check whether the remaining characters match */
if ( !memcmp(PotentialMatch + 1, SearchString + 1,
SearchStringLength - 1) ) {
return(1); /* We&rsquo;ve got a match */
}
}
/* The string doesn&rsquo;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(&ldquo;usage: search filename search-string\n&rdquo;);
exit(1);
}
/* Try to open the file to be searched */
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
printf(&ldquo;Can&rsquo;t open file: %s\n&rdquo;, 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(&ldquo;Can&rsquo;t get enough memory\n&rdquo;);
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&rsquo;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(&ldquo;Error reading file %s\n&rdquo;, argv[1]);
exit(1);
}
/* If we didn&rsquo;t read all the bytes we requested, we&rsquo;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) &lt;= 0 ) {
Done = 1; /* Too few characters in this block for
there to be any possible matches, so this
is the final block and we&rsquo;re done without
finding a match
*/
}
else {
/* Search this block */
if ( SearchForString(WorkingBlock, BlockSearchLength,
SearchString, SearchStringLength) ) {
Found = 1; /* We&rsquo;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 &gt; 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(&ldquo;String found\n&rdquo;);
} else {
printf(&ldquo;String not found\n&rdquo;);
}
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 &copy; 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 &lt;stdio.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;string.h&gt;
#include &lt;alloc.h&gt; /* alloc.h for Borland compilers,
malloc.h for Microsoft compilers */
#define BLOCK_SIZE 0x4000 /* we&rsquo;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&rsquo;ve got a match */
}
else {
/* Check whether the remaining characters match */
if ( !memcmp(PotentialMatch + 1, SearchString + 1,
SearchStringLength - 1) ) {
return(1); /* We&rsquo;ve got a match */
}
}
/* The string doesn&rsquo;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(&ldquo;usage: search filename search-string\n&rdquo;);
exit(1);
}
/* Try to open the file to be searched */
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
printf(&ldquo;Can&rsquo;t open file: %s\n&rdquo;, 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(&ldquo;Can&rsquo;t get enough memory\n&rdquo;);
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&rsquo;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(&ldquo;Error reading file %s\n&rdquo;, argv[1]);
exit(1);
}
/* If we didn&rsquo;t read all the bytes we requested, we&rsquo;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) &lt;= 0 ) {
Done = 1; /* Too few characters in this block for
there to be any possible matches, so this
is the final block and we&rsquo;re done without
finding a match
*/
}
else {
/* Search this block */
if ( SearchForString(WorkingBlock, BlockSearchLength,
SearchString, SearchStringLength) ) {
Found = 1; /* We&rsquo;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 &gt; 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(&ldquo;String found\n&rdquo;);
} else {
printf(&ldquo;String not found\n&rdquo;);
}
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 &copy; 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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&rsquo;s stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller&rsquo;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&rsquo;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&rsquo;t match, so we
; haven&rsquo;t found the search string here
FindStringWord:
jcxz FindStringFound ;if the string is only 1 byte long,
; we&rsquo;ve found a match
repz cmpsw ;check the rest of the string a word at a time
jz FindStringFound ;it&rsquo;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&rsquo;s register variables
pop si
pop bp ;restore caller&rsquo;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 &amp; 9.2 */
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#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[] = &ldquo;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&rsquo;s God entitle them...&rdquo;;
void main() {
static unsigned char TestString[] = &ldquo;equal&rdquo;;
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&rsquo;t found */
printf(&ldquo;\&rdquo;%s\&ldquo; not found\n&rdquo;, TestString);
} else {
/* TestString was found. Zero-terminate TempBuffer; strncpy
won&rsquo;t do it if DISPLAY_LENGTH characters are copied */
TempBuffer[DISPLAY_LENGTH] = 0;
printf(&ldquo;\&rdquo;%s\&ldquo; found. Next %d characters at match:\n\&rdquo;%s\&ldquo;\n&rdquo;,
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 &copy; 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&rsquo;s stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller&rsquo;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&rsquo;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&rsquo;t match, so we
; haven&rsquo;t found the search string here
FindStringWord:
jcxz FindStringFound ;if the string is only 1 byte long,
; we&rsquo;ve found a match
repz cmpsw ;check the rest of the string a word at a time
jz FindStringFound ;it&rsquo;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&rsquo;s register variables
pop si
pop bp ;restore caller&rsquo;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 &amp; 9.2 */
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#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[] = &ldquo;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&rsquo;s God entitle them...&rdquo;;
void main() {
static unsigned char TestString[] = &ldquo;equal&rdquo;;
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&rsquo;t found */
printf(&ldquo;\&rdquo;%s\&ldquo; not found\n&rdquo;, TestString);
} else {
/* TestString was found. Zero-terminate TempBuffer; strncpy
won&rsquo;t do it if DISPLAY_LENGTH characters are copied */
TempBuffer[DISPLAY_LENGTH] = 0;
printf(&ldquo;\&rdquo;%s\&ldquo; found. Next %d characters at match:\n\&rdquo;%s\&ldquo;\n&rdquo;,
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 &copy; 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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 &amp; 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&rsquo;s stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller&rsquo;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&rsquo;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&rsquo;s skip value is used. Note that the rightmost byte of the
; pattern isn&rsquo;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&rsquo;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&rsquo;s it; we&rsquo;ve found a match
; It&rsquo;s a mismatch; let&rsquo;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&rsquo;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&rsquo;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&rsquo;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&rsquo;s register variables
pop si
pop bp ;restore caller&rsquo;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 &copy; 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 &amp; 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&rsquo;s stack frame
mov bp,sp ;point to our stack frame
push si ;preserve caller&rsquo;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&rsquo;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&rsquo;s skip value is used. Note that the rightmost byte of the
; pattern isn&rsquo;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&rsquo;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&rsquo;s it; we&rsquo;ve found a match
; It&rsquo;s a mismatch; let&rsquo;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&rsquo;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&rsquo;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&rsquo;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&rsquo;s register variables
pop si
pop bp ;restore caller&rsquo;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 &copy; 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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
&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;
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&rsquo;re right! But it&rsquo;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&amp;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&amp;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,&lsquo;A&rsquo;-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&amp;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 &copy; 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
&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;
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&rsquo;re right! But it&rsquo;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&amp;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&amp;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,&lsquo;A&rsquo;-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&amp;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 &copy; 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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 &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;iostream.h&gt;
#include &lt;conio.h&gt;
#include &lt;time.h&gt;
#include &lt;dos.h&gt;
#include &lt;bios.h&gt;
#include &lt;mem.h&gt;
#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 &amp;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&amp; 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 &amp; 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 &lt;&lt; &ldquo;Seed (0 for random seed): &rdquo;;
cin &gt;&gt; seed;
if (seed == 0) seed = (unsigned) time(NULL);
// Randomly initialize the initial cell map
cout &lt;&lt; &ldquo;Initializing...&rdquo;;
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 (&mdash;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, &ldquo;Generation: &rdquo;);
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &amp;bios_time);
do {
generation++;
sprintf(gen_text, &ldquo;%10lu&rdquo;, 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, &amp;bios_time);
} while (start_bios_time == bios_time);
start_bios_time = bios_time;
#endif
} while (!kbhit());
getch(); // clear keypress
exit_display_mode();
cout &lt;&lt; &ldquo;Total generations: &rdquo; &lt;&lt; generation &lt;&lt; &ldquo;\nSeed: &rdquo; &lt;&lt;
seed &lt;&lt; &ldquo;\n&rdquo;;
}
/* 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&rsquo;s cells to another cellmap. Both cellmaps are
assumed to be the same size. */
void cellmap::copy_cells(cellmap &amp;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 &gt;&gt; (x &amp; 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) &amp;= ~(0x80 &gt;&gt; (x &amp; 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 &lt; 0) x += width; // wrap, if necessary
while (x &gt;= width) x -= width;
while (y &lt; 0) y += height;
while (y &gt;= height) y -= height;
#else
if ((x &lt; 0) || (x &gt;= width) || (y &lt; 0) || (y &gt;= height))
return 0; // return 0 for off edges if no wrapping
#endif
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
return (*cell_ptr &amp; (0x80 &gt;&gt; (x &amp; 0x07))) ? 1 : 0;
}
/* Calculates the next generation of a cellmap and stores it in
next_map. */
void cellmap::next_generation(cellmap&amp; next_map)
{
unsigned int x, y, neighbor_count;
for (y=0; y&lt;height; y++) {
for (x=0; x&lt;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) &amp;&amp; (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 &lt;stdio.h&gt;
#include &lt;conio.h&gt;
#include &lt;dos.h&gt;
#define TEXT_X_OFFSET 27
#define SCREEN_WIDTH_IN_BYTES 320
/* Width &amp; height in pixels of each cell. */
extern unsigned int magnifier;
/* Mode 13h draw pixel function. Pixels are of width &amp; 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&lt;magnifier; i++) {
for (j=0; j&lt;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, &amp;regset, &amp;regset);
}
/* Text mode mode-set function. */
void exit_display_mode()
{
union REGS regset;
regset.x.ax = 0x0003;
int86(0x10, &amp;regset, &amp;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 &copy; 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 &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;iostream.h&gt;
#include &lt;conio.h&gt;
#include &lt;time.h&gt;
#include &lt;dos.h&gt;
#include &lt;bios.h&gt;
#include &lt;mem.h&gt;
#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 &amp;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&amp; 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 &amp; 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 &lt;&lt; &ldquo;Seed (0 for random seed): &rdquo;;
cin &gt;&gt; seed;
if (seed == 0) seed = (unsigned) time(NULL);
// Randomly initialize the initial cell map
cout &lt;&lt; &ldquo;Initializing...&rdquo;;
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 (&mdash;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, &ldquo;Generation: &rdquo;);
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &amp;bios_time);
do {
generation++;
sprintf(gen_text, &ldquo;%10lu&rdquo;, 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, &amp;bios_time);
} while (start_bios_time == bios_time);
start_bios_time = bios_time;
#endif
} while (!kbhit());
getch(); // clear keypress
exit_display_mode();
cout &lt;&lt; &ldquo;Total generations: &rdquo; &lt;&lt; generation &lt;&lt; &ldquo;\nSeed: &rdquo; &lt;&lt;
seed &lt;&lt; &ldquo;\n&rdquo;;
}
/* 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&rsquo;s cells to another cellmap. Both cellmaps are
assumed to be the same size. */
void cellmap::copy_cells(cellmap &amp;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 &gt;&gt; (x &amp; 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) &amp;= ~(0x80 &gt;&gt; (x &amp; 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 &lt; 0) x += width; // wrap, if necessary
while (x &gt;= width) x -= width;
while (y &lt; 0) y += height;
while (y &gt;= height) y -= height;
#else
if ((x &lt; 0) || (x &gt;= width) || (y &lt; 0) || (y &gt;= height))
return 0; // return 0 for off edges if no wrapping
#endif
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
return (*cell_ptr &amp; (0x80 &gt;&gt; (x &amp; 0x07))) ? 1 : 0;
}
/* Calculates the next generation of a cellmap and stores it in
next_map. */
void cellmap::next_generation(cellmap&amp; next_map)
{
unsigned int x, y, neighbor_count;
for (y=0; y&lt;height; y++) {
for (x=0; x&lt;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) &amp;&amp; (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 &lt;stdio.h&gt;
#include &lt;conio.h&gt;
#include &lt;dos.h&gt;
#define TEXT_X_OFFSET 27
#define SCREEN_WIDTH_IN_BYTES 320
/* Width &amp; height in pixels of each cell. */
extern unsigned int magnifier;
/* Mode 13h draw pixel function. Pixels are of width &amp; 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&lt;magnifier; i++) {
for (j=0; j&lt;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, &amp;regset, &amp;regset);
}
/* Text mode mode-set function. */
void exit_display_mode()
{
union REGS regset;
regset.x.ax = 0x0003;
int86(0x10, &amp;regset, &amp;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 &copy; 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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 &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;iostream.h&gt;
#include &lt;conio.h&gt;
#include &lt;time.h&gt;
#include &lt;dos.h&gt;
#include &lt;bios.h&gt;
#include &lt;mem.h&gt;
#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 &amp; 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, &ldquo;Generation: &rdquo;);
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &amp;bios_time);
do {
generation++;
sprintf(gen_text, &ldquo;%10lu&rdquo;, 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, &amp;bios_time);
} while (start_bios_time == bios_time);
start_bios_time = bios_time;
#endif
} while (!kbhit());
getch(); // clear keypress
exit_display_mode();
cout &lt;&lt; &ldquo;Total generations: &rdquo; &lt;&lt; generation &lt;&lt; &ldquo;\nSeed: &rdquo; &lt;&lt;
seed &lt;&lt; &ldquo;\n&rdquo;;
}
/* 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(&ldquo;Out of memory\n&rdquo;);
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) &amp;= ~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 &amp; 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&lt;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 &gt;= w) goto RowDone;
}
// Found a cell that&rsquo;s either on or has on-neighbors,
// so see if its state needs to be changed
count = *cell_ptr &gt;&gt; 1; // # of neighboring on-cells
if (*cell_ptr &amp; 0x01) {
// Cell is on; turn it off if it doesn&rsquo;t have
// 2 or 3 neighbors
if ((count != 2) &amp;&amp; (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 &lt; 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 &lt;&lt; &ldquo;Seed (0 for random seed): &rdquo;;
cin &gt;&gt; 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 &lt;&lt; &ldquo;Initializing...&rdquo;;
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 (&mdash;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 &copy; 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 &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;iostream.h&gt;
#include &lt;conio.h&gt;
#include &lt;time.h&gt;
#include &lt;dos.h&gt;
#include &lt;bios.h&gt;
#include &lt;mem.h&gt;
#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 &amp; 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, &ldquo;Generation: &rdquo;);
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &amp;bios_time);
do {
generation++;
sprintf(gen_text, &ldquo;%10lu&rdquo;, 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, &amp;bios_time);
} while (start_bios_time == bios_time);
start_bios_time = bios_time;
#endif
} while (!kbhit());
getch(); // clear keypress
exit_display_mode();
cout &lt;&lt; &ldquo;Total generations: &rdquo; &lt;&lt; generation &lt;&lt; &ldquo;\nSeed: &rdquo; &lt;&lt;
seed &lt;&lt; &ldquo;\n&rdquo;;
}
/* 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(&ldquo;Out of memory\n&rdquo;);
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) &amp;= ~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 &amp; 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&lt;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 &gt;= w) goto RowDone;
}
// Found a cell that&rsquo;s either on or has on-neighbors,
// so see if its state needs to be changed
count = *cell_ptr &gt;&gt; 1; // # of neighboring on-cells
if (*cell_ptr &amp; 0x01) {
// Cell is on; turn it off if it doesn&rsquo;t have
// 2 or 3 neighbors
if ((count != 2) &amp;&amp; (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 &lt; 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 &lt;&lt; &ldquo;Seed (0 for random seed): &rdquo;;
cin &gt;&gt; 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 &lt;&lt; &ldquo;Initializing...&rdquo;;
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 (&mdash;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 &copy; 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>

File diff suppressed because one or more lines are too long

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