Remove html versions
This commit is contained in:
parent
3c76d884ef
commit
209ba6d8be
362 changed files with 0 additions and 71636 deletions
106
01-01.html
106
01-01.html
File diff suppressed because one or more lines are too long
157
01-02.html
157
01-02.html
File diff suppressed because one or more lines are too long
307
01-03.html
307
01-03.html
File diff suppressed because one or more lines are too long
144
01-04.html
144
01-04.html
File diff suppressed because one or more lines are too long
196
01-05.html
196
01-05.html
File diff suppressed because one or more lines are too long
138
01-06.html
138
01-06.html
File diff suppressed because one or more lines are too long
117
02-01.html
117
02-01.html
File diff suppressed because one or more lines are too long
97
02-02.html
97
02-02.html
File diff suppressed because one or more lines are too long
91
02-03.html
91
02-03.html
File diff suppressed because one or more lines are too long
98
03-01.html
98
03-01.html
File diff suppressed because one or more lines are too long
502
03-02.html
502
03-02.html
|
|
@ -1,502 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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: Assume Nothing</title>
|
|
||||||
<meta name="chapter" content="03" />
|
|
||||||
<meta name="pages" content="035-042" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="03-01.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="03-03.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<p><b>LISTING 3.1 PZTIMER.ASM</b></p>
|
|
||||||
<pre>
|
|
||||||
; The precision Zen timer (PZTIMER.ASM)
|
|
||||||
;
|
|
||||||
; Uses the 8253 timer to time the performance of code that takes
|
|
||||||
; less than about 54 milliseconds to execute, with a resolution
|
|
||||||
; of better than 10 microseconds.
|
|
||||||
;
|
|
||||||
; By Michael Abrash
|
|
||||||
;
|
|
||||||
; Externally callable routines:
|
|
||||||
;
|
|
||||||
; ZTimerOn: Starts the Zen timer, with interrupts disabled.
|
|
||||||
;
|
|
||||||
; ZTimerOff: Stops the Zen timer, saves the timer count,
|
|
||||||
; times the overhead code, and restores interrupts to the
|
|
||||||
; state they were in when ZTimerOn was called.
|
|
||||||
;
|
|
||||||
; ZTimerReport: Prints the net time that passed between starting
|
|
||||||
; and stopping the timer.
|
|
||||||
;
|
|
||||||
; Note: If longer than about 54 ms passes between ZTimerOn and
|
|
||||||
; ZTimerOff calls, the timer turns over and the count is
|
|
||||||
; inaccurate. When this happens, an error message is displayed
|
|
||||||
; instead of a count. The long-period Zen timer should be used
|
|
||||||
; in such cases.
|
|
||||||
;
|
|
||||||
; Note: Interrupts *MUST* be left off between calls to ZTimerOn
|
|
||||||
; and ZTimerOff for accurate timing and for detection of
|
|
||||||
; timer overflow.
|
|
||||||
;
|
|
||||||
; Note: These routines can introduce slight inaccuracies into the
|
|
||||||
; system clock count for each code section timed even if
|
|
||||||
; timer 0 doesn’t overflow. If timer 0 does overflow, the
|
|
||||||
; system clock can become slow by virtually any amount of
|
|
||||||
; time, since the system clock can’t advance while the
|
|
||||||
; precison timer is timing. Consequently, it’s a good idea
|
|
||||||
; to reboot at the end of each timing session. (The
|
|
||||||
; battery-backed clock, if any, is not affected by the Zen
|
|
||||||
; timer.)
|
|
||||||
;
|
|
||||||
; All registers, and all flags except the interrupt flag, are
|
|
||||||
; preserved by all routines. Interrupts are enabled and then disabled
|
|
||||||
; by ZTimerOn, and are restored by ZTimerOff to the state they were
|
|
||||||
; in when ZTimerOn was called.
|
|
||||||
;
|
|
||||||
|
|
||||||
Code segment word public ‘CODE’
|
|
||||||
assumecs: Code, ds:nothing
|
|
||||||
public ZTimerOn, ZTimerOff, ZTimerReport
|
|
||||||
|
|
||||||
;
|
|
||||||
; Base address of the 8253 timer chip.
|
|
||||||
;
|
|
||||||
BASE_8253equ40h
|
|
||||||
;
|
|
||||||
; The address of the timer 0 count registers in the 8253.
|
|
||||||
;
|
|
||||||
TIMER_0_8253 equBASE_8253 + 0
|
|
||||||
;
|
|
||||||
; The address of the mode register in the 8253.
|
|
||||||
;
|
|
||||||
MODE_8253 equBASE_8253 + 3
|
|
||||||
;
|
|
||||||
; The address of Operation Command Word 3 in the 8259 Programmable
|
|
||||||
; Interrupt Controller (PIC) (write only, and writable only when
|
|
||||||
; bit 4 of the byte written to this address is 0 and bit 3 is 1).
|
|
||||||
;
|
|
||||||
OCW3 equ20h
|
|
||||||
;
|
|
||||||
; The address of the Interrupt Request register in the 8259 PIC
|
|
||||||
; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
|
|
||||||
; of OCW3 = 0).
|
|
||||||
;
|
|
||||||
IRR equ20h
|
|
||||||
;
|
|
||||||
; Macro to emulate a POPF instruction in order to fix the bug in some
|
|
||||||
; 80286 chips which allows interrupts to occur during a POPF even when
|
|
||||||
; interrupts remain disabled.
|
|
||||||
;
|
|
||||||
MPOPF macro
|
|
||||||
local p1, p2
|
|
||||||
jmp short p2
|
|
||||||
p1: iret ; jump to pushed address & pop flags
|
|
||||||
p2: push cs ; construct far return address to
|
|
||||||
call p1 ; the next instruction
|
|
||||||
endm
|
|
||||||
|
|
||||||
;
|
|
||||||
; Macro to delay briefly to ensure that enough time has elapsed
|
|
||||||
; between successive I/O accesses so that the device being accessed
|
|
||||||
; can respond to both accesses even on a very fast PC.
|
|
||||||
;
|
|
||||||
DELAY macro
|
|
||||||
jmp $+2
|
|
||||||
jmp $+2
|
|
||||||
jmp $+2
|
|
||||||
endm
|
|
||||||
|
|
||||||
OriginalFlags db ? ; storage for upper byte of
|
|
||||||
; FLAGS register when
|
|
||||||
; ZTimerOn called
|
|
||||||
TimedCount dw ? ; timer 0 count when the timer
|
|
||||||
; is stopped
|
|
||||||
ReferenceCount dw ; number of counts required to
|
|
||||||
; execute timer overhead code
|
|
||||||
OverflowFlag db ? ; used to indicate whether the
|
|
||||||
; timer overflowed during the
|
|
||||||
; timing interval
|
|
||||||
;
|
|
||||||
; String printed to report results.
|
|
||||||
;
|
|
||||||
OutputStr label byte
|
|
||||||
db 0dh, 0ah, ‘Timed count: ‘, 5 dup (?)
|
|
||||||
ASCIICountEnd labelbyte
|
|
||||||
db ‘ microseconds’, 0dh, 0ah
|
|
||||||
db ‘$’
|
|
||||||
;
|
|
||||||
; String printed to report timer overflow.
|
|
||||||
;
|
|
||||||
OverflowStr label byte
|
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘****************************************************’
|
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘* The timer overflowed, so the interval timed was *’
|
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘* too long for the precision timer to measure. *’
|
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘* Please perform the timing test again with the *’
|
|
||||||
db0dh, 0ah
|
|
||||||
db ‘* long-period timer. *’
|
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘****************************************************’
|
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘$’
|
|
||||||
|
|
||||||
; ********************************************************************
|
|
||||||
; * Routine called to start timing. *
|
|
||||||
; ********************************************************************
|
|
||||||
|
|
||||||
ZTimerOn proc near
|
|
||||||
|
|
||||||
;
|
|
||||||
; Save the context of the program being timed.
|
|
||||||
;
|
|
||||||
push ax
|
|
||||||
pushf
|
|
||||||
pop ax ; get flags so we can keep
|
|
||||||
; interrupts off when leaving
|
|
||||||
; this routine
|
|
||||||
mov cs:[OriginalFlags],ah ; remember the state of the
|
|
||||||
; Interrupt flag
|
|
||||||
and ah,0fdh ; set pushed interrupt flag
|
|
||||||
; to 0
|
|
||||||
push ax
|
|
||||||
;
|
|
||||||
; Turn on interrupts, so the timer interrupt can occur if it’s
|
|
||||||
; pending.
|
|
||||||
;
|
|
||||||
sti
|
|
||||||
;
|
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
|
||||||
; linear counting rather than count-by-two counting. Also
|
|
||||||
; leaves the 8253 waiting for the initial timer 0 count to
|
|
||||||
; be loaded.
|
|
||||||
;
|
|
||||||
mov al,00110100b ;mode 2
|
|
||||||
out MODE_8253,al
|
|
||||||
;
|
|
||||||
; Set the timer count to 0, so we know we won’t get another
|
|
||||||
; timer interrupt right away.
|
|
||||||
; Note: this introduces an inaccuracy of up to 54 ms in the system
|
|
||||||
; clock count each time it is executed.
|
|
||||||
;
|
|
||||||
DELAY
|
|
||||||
sub al,al
|
|
||||||
out TIMER_0_8253,al ;lsb
|
|
||||||
DELAY
|
|
||||||
out TIMER_0_8253,al ;msb
|
|
||||||
;
|
|
||||||
; Wait before clearing interrupts to allow the interrupt generated
|
|
||||||
; when switching from mode 3 to mode 2 to be recognized. The delay
|
|
||||||
; must be at least 210 ns long to allow time for that interrupt to
|
|
||||||
; occur. Here, 10 jumps are used for the delay to ensure that the
|
|
||||||
; delay time will be more than long enough even on a very fast PC.
|
|
||||||
;
|
|
||||||
rept 10
|
|
||||||
jmp $+2
|
|
||||||
endm
|
|
||||||
;
|
|
||||||
; Disable interrupts to get an accurate count.
|
|
||||||
;
|
|
||||||
cli
|
|
||||||
;
|
|
||||||
; Set the timer count to 0 again to start the timing interval.
|
|
||||||
;
|
|
||||||
mov al,00110100b ; set up to load initial
|
|
||||||
out MODE_8253,al ; timer count
|
|
||||||
DELAY
|
|
||||||
sub al,al
|
|
||||||
out TIMER_0_8253,al ; load count lsb
|
|
||||||
DELAY
|
|
||||||
out TIMER_0_8253,al; load count msb
|
|
||||||
;
|
|
||||||
; Restore the context and return.
|
|
||||||
;
|
|
||||||
MPOPF ; keeps interrupts off
|
|
||||||
pop ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
ZTimerOn endp
|
|
||||||
|
|
||||||
;********************************************************************
|
|
||||||
;* Routine called to stop timing and get count. *
|
|
||||||
;********************************************************************
|
|
||||||
|
|
||||||
ZTimerOff proc near
|
|
||||||
|
|
||||||
;
|
|
||||||
; Save the context of the program being timed.
|
|
||||||
;
|
|
||||||
push ax
|
|
||||||
push cx
|
|
||||||
pushf
|
|
||||||
;
|
|
||||||
; Latch the count.
|
|
||||||
;
|
|
||||||
mov al,00000000b ; latch timer 0
|
|
||||||
out MODE_8253,al
|
|
||||||
;
|
|
||||||
; See if the timer has overflowed by checking the 8259 for a pending
|
|
||||||
; timer interrupt.
|
|
||||||
;
|
|
||||||
mov al,00001010b ; OCW3, set up to read
|
|
||||||
out OCW3,al; Int errupt Request register
|
|
||||||
DELAY
|
|
||||||
ina l,IRR; read Interrupt Request
|
|
||||||
; register
|
|
||||||
and al,1 ; set AL to 1 if IRQ0 (the
|
|
||||||
; timer interrupt) is pending
|
|
||||||
mov cs:[OverflowFlag],al; store the timer overflow
|
|
||||||
; status
|
|
||||||
;
|
|
||||||
; Allow interrupts to happen again.
|
|
||||||
;
|
|
||||||
sti
|
|
||||||
;
|
|
||||||
; Read out the count we latched earlier.
|
|
||||||
;
|
|
||||||
in al,TIMER_0_8253 ; least significant byte
|
|
||||||
DELAY
|
|
||||||
mov ah,al
|
|
||||||
in al,TIMER_0_8253 ; most significant byte
|
|
||||||
xchg ah,al
|
|
||||||
neg ax ; convert from countdown
|
|
||||||
; remaining to elapsed
|
|
||||||
; count
|
|
||||||
mov cs:[TimedCount],ax
|
|
||||||
; Time a zero-length code fragment, to get a reference for how
|
|
||||||
; much overhead this routine has. Time it 16 times and average it,
|
|
||||||
; for accuracy, rounding the result.
|
|
||||||
;
|
|
||||||
mov cs:[ReferenceCount],0
|
|
||||||
mov cx,16
|
|
||||||
cli ; interrupts off to allow a
|
|
||||||
; precise reference count
|
|
||||||
RefLoop:
|
|
||||||
call ReferenceZTimerOn
|
|
||||||
call ReferenceZTimerOff
|
|
||||||
loop RefLoop
|
|
||||||
sti
|
|
||||||
add cs:[ReferenceCount],8; total + (0.5 * 16)
|
|
||||||
mov cl,4
|
|
||||||
shr cs:[ReferenceCount],cl; (total) / 16 + 0.5
|
|
||||||
;
|
|
||||||
; Restore original interrupt state.
|
|
||||||
;
|
|
||||||
pop ax ; retrieve flags when called
|
|
||||||
mov ch,cs:[OriginalFlags] ; get back the original upper
|
|
||||||
; byte of the FLAGS register
|
|
||||||
and ch,not 0fdh ; only care about original
|
|
||||||
; interrupt flag...
|
|
||||||
and ah,0fdh ; ...keep all other flags in
|
|
||||||
; their current condition
|
|
||||||
or ah,ch ; make flags word with original
|
|
||||||
; interrupt flag
|
|
||||||
push ax ; prepare flags to be popped
|
|
||||||
;
|
|
||||||
; Restore the context of the program being timed and return to it.
|
|
||||||
;
|
|
||||||
MPOPF ; restore the flags with the
|
|
||||||
; original interrupt state
|
|
||||||
pop cx
|
|
||||||
pop ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
ZTimerOff endp
|
|
||||||
|
|
||||||
;
|
|
||||||
; Called by ZTimerOff to start timer for overhead measurements.
|
|
||||||
;
|
|
||||||
|
|
||||||
ReferenceZTimerOnproc near
|
|
||||||
;
|
|
||||||
; Save the context of the program being timed.
|
|
||||||
;
|
|
||||||
push ax
|
|
||||||
pushf ; interrupts are already off
|
|
||||||
;
|
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
|
||||||
; linear counting rather than count-by-two counting.
|
|
||||||
;
|
|
||||||
mov al,00110100b ; set up to load
|
|
||||||
out MODE_8253,al ; initial timer count
|
|
||||||
DELAY
|
|
||||||
;
|
|
||||||
; Set the timer count to 0.
|
|
||||||
;
|
|
||||||
sub al,al
|
|
||||||
out TIMER_0_8253,al; load count lsb
|
|
||||||
DELAY
|
|
||||||
out TIMER_0_8253,al; load count msb
|
|
||||||
;
|
|
||||||
; Restore the context of the program being timed and return to it.
|
|
||||||
;
|
|
||||||
MPOPF
|
|
||||||
pop ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
ReferenceZTimerOnendp
|
|
||||||
|
|
||||||
;
|
|
||||||
; Called by ZTimerOff to stop timer and add result to ReferenceCount
|
|
||||||
; for overhead measurements.
|
|
||||||
;
|
|
||||||
|
|
||||||
ReferenceZTimerOff proc near
|
|
||||||
;
|
|
||||||
; Save the context of the program being timed.
|
|
||||||
;
|
|
||||||
push ax
|
|
||||||
push cx
|
|
||||||
pushf
|
|
||||||
;
|
|
||||||
; Latch the count and read it.
|
|
||||||
;
|
|
||||||
mov al,00000000b ; latch timer 0
|
|
||||||
out MODE_8253,al
|
|
||||||
DELAY
|
|
||||||
in al,TIMER_0_8253 ; lsb
|
|
||||||
DELAY
|
|
||||||
mov ah,al
|
|
||||||
in al,TIMER_0_8253 ; msb
|
|
||||||
xchg ah,al
|
|
||||||
neg ax ; convert from countdown
|
|
||||||
; remaining to amount
|
|
||||||
; counted down
|
|
||||||
add cs:[ReferenceCount],ax
|
|
||||||
;
|
|
||||||
; Restore the context of the program being timed and return to it.
|
|
||||||
;
|
|
||||||
MPOPF
|
|
||||||
pop cx
|
|
||||||
pop ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
ReferenceZTimerOff endp
|
|
||||||
|
|
||||||
; ********************************************************************
|
|
||||||
; * Routine called to report timing results. *
|
|
||||||
; ********************************************************************
|
|
||||||
|
|
||||||
ZTimerReport procnear
|
|
||||||
|
|
||||||
pushf
|
|
||||||
push ax
|
|
||||||
push bx
|
|
||||||
push cx
|
|
||||||
push dx
|
|
||||||
push si
|
|
||||||
push ds
|
|
||||||
;
|
|
||||||
push cs ; DOS functions require that DS point
|
|
||||||
pop ds ; to text to be displayed on the screen
|
|
||||||
assume ds :Code
|
|
||||||
;
|
|
||||||
; Check for timer 0 overflow.
|
|
||||||
;
|
|
||||||
cmp [OverflowFlag],0
|
|
||||||
jz PrintGoodCount
|
|
||||||
mov dx,offset OverflowStr
|
|
||||||
mov ah,9
|
|
||||||
int 21h
|
|
||||||
jmp short EndZTimerReport
|
|
||||||
;
|
|
||||||
; Convert net count to decimal ASCII in microseconds.
|
|
||||||
;
|
|
||||||
PrintGoodCount:
|
|
||||||
mov ax,[TimedCount]
|
|
||||||
sub ax,[ReferenceCount]
|
|
||||||
mov si,offset ASCIICountEnd - 1
|
|
||||||
;
|
|
||||||
; Convert count to microseconds by multiplying by .8381.
|
|
||||||
;
|
|
||||||
mov dx, 8381
|
|
||||||
mul dx
|
|
||||||
mov bx, 10000
|
|
||||||
div bx ;* .8381 = * 8381 / 10000
|
|
||||||
;
|
|
||||||
; Convert time in microseconds to 5 decimal ASCII digits.
|
|
||||||
;
|
|
||||||
mov bx, 10
|
|
||||||
mov cx, 5
|
|
||||||
CTSLoop:
|
|
||||||
sub dx, dx
|
|
||||||
div bx
|
|
||||||
add dl,’0’
|
|
||||||
mov [si],dl
|
|
||||||
dec si
|
|
||||||
loop CTSLoop
|
|
||||||
;
|
|
||||||
; Print the results.
|
|
||||||
;
|
|
||||||
mov ah, 9
|
|
||||||
mov dx, offset OutputStr
|
|
||||||
int 21h
|
|
||||||
;
|
|
||||||
EndZTimerReport:
|
|
||||||
pop ds
|
|
||||||
pop si
|
|
||||||
pop dx
|
|
||||||
pop cx
|
|
||||||
pop bx
|
|
||||||
pop ax
|
|
||||||
MPOPF
|
|
||||||
ret
|
|
||||||
|
|
||||||
ZTimerReport endp
|
|
||||||
|
|
||||||
Code ends
|
|
||||||
end
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="03-01.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="03-03.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
96
03-03.html
96
03-03.html
File diff suppressed because one or more lines are too long
99
03-04.html
99
03-04.html
File diff suppressed because one or more lines are too long
150
03-05.html
150
03-05.html
File diff suppressed because one or more lines are too long
152
03-06.html
152
03-06.html
File diff suppressed because one or more lines are too long
713
03-07.html
713
03-07.html
File diff suppressed because one or more lines are too long
139
03-08.html
139
03-08.html
File diff suppressed because one or more lines are too long
175
03-09.html
175
03-09.html
File diff suppressed because one or more lines are too long
117
03-10.html
117
03-10.html
File diff suppressed because one or more lines are too long
114
04-01.html
114
04-01.html
File diff suppressed because one or more lines are too long
139
04-02.html
139
04-02.html
File diff suppressed because one or more lines are too long
134
04-03.html
134
04-03.html
File diff suppressed because one or more lines are too long
125
04-04.html
125
04-04.html
File diff suppressed because one or more lines are too long
122
04-05.html
122
04-05.html
File diff suppressed because one or more lines are too long
94
04-06.html
94
04-06.html
File diff suppressed because one or more lines are too long
117
04-07.html
117
04-07.html
File diff suppressed because one or more lines are too long
98
04-08.html
98
04-08.html
File diff suppressed because one or more lines are too long
131
04-09.html
131
04-09.html
File diff suppressed because one or more lines are too long
109
04-10.html
109
04-10.html
File diff suppressed because one or more lines are too long
104
05-01.html
104
05-01.html
File diff suppressed because one or more lines are too long
105
05-02.html
105
05-02.html
File diff suppressed because one or more lines are too long
228
05-03.html
228
05-03.html
|
|
@ -1,228 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<meta name="chapter" content="05" />
|
|
||||||
<meta name="pages" content="118-121" />
|
|
||||||
</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><b>LISTING 5.1 SEARCH.C</b></p>
|
|
||||||
<pre>
|
|
||||||
/* Program to search the file specified by the first command-line
|
|
||||||
* argument for the string specified by the second command-line
|
|
||||||
* argument. Performs the search by reading and searching blocks
|
|
||||||
* of size BLOCK_SIZE. */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <alloc.h> /* alloc.h for Borland compilers,
|
|
||||||
malloc.h for Microsoft compilers */
|
|
||||||
|
|
||||||
#define BLOCK_SIZE 0x4000 /* we’ll process the file in 16K blocks */
|
|
||||||
|
|
||||||
/* Searches the specified number of sequences in the specified
|
|
||||||
buffer for matches to SearchString of SearchStringLength. Note
|
|
||||||
that the calling code should already have shortened SearchLength
|
|
||||||
if necessary to compensate for the distance from the end of the
|
|
||||||
buffer to the last possible start of a matching sequence in the
|
|
||||||
buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int SearchForString(unsigned char *Buffer, int SearchLength,
|
|
||||||
unsigned char *SearchString, int SearchStringLength)
|
|
||||||
{
|
|
||||||
unsigned char *PotentialMatch;
|
|
||||||
|
|
||||||
/* Search so long as there are potential-match locations
|
|
||||||
remaining */
|
|
||||||
while ( SearchLength ) {
|
|
||||||
/* See if the first character of SearchString can be found */
|
|
||||||
if ( (PotentialMatch =
|
|
||||||
memchr(Buffer, *SearchString, SearchLength)) == NULL ) {
|
|
||||||
break; /* No matches in this buffer */
|
|
||||||
}
|
|
||||||
/* The first character matches; see if the rest of the string
|
|
||||||
also matches */
|
|
||||||
if ( SearchStringLength == 1 ) {
|
|
||||||
return(1); /* That one matching character was the whole
|
|
||||||
search string, so we’ve got a match */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Check whether the remaining characters match */
|
|
||||||
if ( !memcmp(PotentialMatch + 1, SearchString + 1,
|
|
||||||
SearchStringLength - 1) ) {
|
|
||||||
return(1); /* We’ve got a match */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* The string doesn’t match; keep going by pointing past the
|
|
||||||
potential match location we just rejected */
|
|
||||||
SearchLength -= PotentialMatch - Buffer + 1;
|
|
||||||
Buffer = PotentialMatch + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(0); /* No match found */
|
|
||||||
}
|
|
||||||
|
|
||||||
main(int argc, char *argv[]) {
|
|
||||||
int Done; /* Indicates whether search is done */
|
|
||||||
int Handle; /* Handle of file being searched */
|
|
||||||
int WorkingLength; /* Length of current block */
|
|
||||||
int SearchStringLength; /* Length of string to search for */
|
|
||||||
int BlockSearchLength; /* Length to search in current block */
|
|
||||||
int Found; /* Indicates final search completion
|
|
||||||
status */
|
|
||||||
int NextLoadCount; /* # of bytes to read into next block,
|
|
||||||
accounting for bytes copied from the
|
|
||||||
last block */
|
|
||||||
unsigned char *WorkingBlock; /* Block storage buffer */
|
|
||||||
unsigned char *SearchString; /* Pointer to the string to search for */
|
|
||||||
unsigned char *NextLoadPtr; /* Offset at which to start loading
|
|
||||||
the next block, accounting for
|
|
||||||
bytes copied from the last block */
|
|
||||||
|
|
||||||
/* Check for the proper number of arguments */
|
|
||||||
if ( argc != 3 ) {
|
|
||||||
printf(“usage: search filename search-string\n”);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to open the file to be searched */
|
|
||||||
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
|
|
||||||
printf(“Can’t open file: %s\n”, argv[1]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* Calculate the length of text to search for */
|
|
||||||
SearchString = argv[2];
|
|
||||||
SearchStringLength = strlen(SearchString);
|
|
||||||
/* Try to get memory in which to buffer the data */
|
|
||||||
if ( (WorkingBlock = malloc(BLOCK_SIZE)) == NULL ) {
|
|
||||||
printf(“Can’t get enough memory\n”);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load the first block at the start of the buffer, and try to
|
|
||||||
fill the entire buffer */
|
|
||||||
NextLoadPtr = WorkingBlock;
|
|
||||||
NextLoadCount = BLOCK_SIZE;
|
|
||||||
Done = 0; /* Not done with search yet */
|
|
||||||
Found = 0; /* Assume we won’t find a match */
|
|
||||||
/* Search the file in BLOCK_SIZE chunks */
|
|
||||||
do {
|
|
||||||
/* Read in however many bytes are needed to fill out the block
|
|
||||||
(accounting for bytes copied over from the last block), or
|
|
||||||
the rest of the bytes in the file, whichever is less */
|
|
||||||
if ( (WorkingLength = read(Handle, NextLoadPtr,
|
|
||||||
NextLoadCount)) == -1 ) {
|
|
||||||
printf(“Error reading file %s\n”, argv[1]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* If we didn’t read all the bytes we requested, we’re done
|
|
||||||
after this block, whether we find a match or not */
|
|
||||||
if ( WorkingLength != NextLoadCount ) {
|
|
||||||
Done = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Account for any bytes we copied from the end of the last
|
|
||||||
block in the total length of this block */
|
|
||||||
WorkingLength += NextLoadPtr - WorkingBlock;
|
|
||||||
/* Calculate the number of bytes in this block that could
|
|
||||||
possibly be the start of a matching sequence that lies
|
|
||||||
entirely in this block (sequences that run off the end of
|
|
||||||
the block will be transferred to the next block and found
|
|
||||||
when that block is searched)
|
|
||||||
*/
|
|
||||||
if ( (BlockSearchLength =
|
|
||||||
WorkingLength - SearchStringLength + 1) <= 0 ) {
|
|
||||||
Done = 1; /* Too few characters in this block for
|
|
||||||
there to be any possible matches, so this
|
|
||||||
is the final block and we’re done without
|
|
||||||
finding a match
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Search this block */
|
|
||||||
if ( SearchForString(WorkingBlock, BlockSearchLength,
|
|
||||||
SearchString, SearchStringLength) ) {
|
|
||||||
Found = 1; /* We’ve found a match */
|
|
||||||
Done = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Copy any bytes from the end of the block that start
|
|
||||||
potentially-matching sequences that would run off
|
|
||||||
the end of the block over to the next block */
|
|
||||||
if ( SearchStringLength > 1 ) {
|
|
||||||
memcpy(WorkingBlock,
|
|
||||||
WorkingBlock+BLOCK_SIZE - SearchStringLength + 1,
|
|
||||||
SearchStringLength - 1);
|
|
||||||
}
|
|
||||||
/* Set up to load the next bytes from the file after the
|
|
||||||
bytes copied from the end of the current block */
|
|
||||||
NextLoadPtr = WorkingBlock + SearchStringLength - 1;
|
|
||||||
NextLoadCount = BLOCK_SIZE - SearchStringLength + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while ( !Done );
|
|
||||||
|
|
||||||
/* Report the results */
|
|
||||||
if ( Found ) {
|
|
||||||
printf(“String found\n”);
|
|
||||||
} else {
|
|
||||||
printf(“String not found\n”);
|
|
||||||
}
|
|
||||||
exit(Found); /* Return the found/not found status as the
|
|
||||||
DOS errorlevel */
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="05-02.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="05-04.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
83
05-04.html
83
05-04.html
File diff suppressed because one or more lines are too long
95
05-05.html
95
05-05.html
File diff suppressed because one or more lines are too long
115
06-01.html
115
06-01.html
File diff suppressed because one or more lines are too long
166
06-02.html
166
06-02.html
File diff suppressed because one or more lines are too long
118
07-01.html
118
07-01.html
File diff suppressed because one or more lines are too long
103
07-02.html
103
07-02.html
File diff suppressed because one or more lines are too long
157
07-03.html
157
07-03.html
File diff suppressed because one or more lines are too long
201
07-04.html
201
07-04.html
File diff suppressed because one or more lines are too long
187
07-05.html
187
07-05.html
File diff suppressed because one or more lines are too long
110
08-01.html
110
08-01.html
File diff suppressed because one or more lines are too long
146
08-02.html
146
08-02.html
File diff suppressed because one or more lines are too long
222
08-03.html
222
08-03.html
File diff suppressed because one or more lines are too long
319
08-04.html
319
08-04.html
File diff suppressed because one or more lines are too long
215
08-05.html
215
08-05.html
File diff suppressed because one or more lines are too long
169
09-01.html
169
09-01.html
File diff suppressed because one or more lines are too long
144
09-02.html
144
09-02.html
File diff suppressed because one or more lines are too long
169
09-03.html
169
09-03.html
File diff suppressed because one or more lines are too long
195
09-04.html
195
09-04.html
|
|
@ -1,195 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<meta name="chapter" content="09" />
|
|
||||||
<meta name="pages" content="178-180" />
|
|
||||||
</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><b>LISTING 9.2 L9-2.ASM</b></p>
|
|
||||||
<pre>
|
|
||||||
; Searches a text buffer for a text string. Uses REPNZ SCASB to scan
|
|
||||||
; the buffer for locations that match a specified character of the
|
|
||||||
; searched-for string, then uses REPZ CMPS to check fully only those
|
|
||||||
; locations that REPNZ SCASB has identified as potential matches.
|
|
||||||
;
|
|
||||||
; C small model-callable as:
|
|
||||||
; unsigned char * FindString(unsigned char * Buffer,
|
|
||||||
; unsigned int BufferLength, unsigned char * SearchString,
|
|
||||||
; unsigned int SearchStringLength,
|
|
||||||
; unsigned int ScanCharOffset);
|
|
||||||
;
|
|
||||||
; Returns a pointer to the first match for SearchString in Buffer,or
|
|
||||||
; a NULL pointer if no match is found. Buffer should not start at
|
|
||||||
; offset 0 in the data segment to avoid confusing a match at 0 with
|
|
||||||
; no match found.
|
|
||||||
Parms struc
|
|
||||||
dw 2 dup(?) ;pushed BP/return address
|
|
||||||
Buffer dw ? ;pointer to buffer to search
|
|
||||||
BufferLength dw ? ;length of buffer to search
|
|
||||||
SearchString dw ? ;pointer to string for which to search
|
|
||||||
SearchStringLength dw ? ;length of string for which to search
|
|
||||||
ScanCharOffset dw ? ;offset in string of character for
|
|
||||||
; which to scan
|
|
||||||
Parmsends
|
|
||||||
.model small
|
|
||||||
.code
|
|
||||||
public _FindString
|
|
||||||
_FindStringprocnear
|
|
||||||
push bp ;preserve caller’s stack frame
|
|
||||||
mov bp,sp ;point to our stack frame
|
|
||||||
push si ;preserve caller’s register variables
|
|
||||||
push di
|
|
||||||
cld ;make string instructions increment pointers
|
|
||||||
mov si,[bp+SearchString] ;pointer to string to search for
|
|
||||||
mov cx,[bp+SearchStringLength] ;length of string
|
|
||||||
jcxz FindStringNotFound ;no match if string is 0 length
|
|
||||||
mov dx,[bp+BufferLength] ;length of buffer
|
|
||||||
sub dx,cx ;difference between buffer and search
|
|
||||||
; lengths
|
|
||||||
jc FindStringNotFound ;no match if search string is
|
|
||||||
; longer than buffer
|
|
||||||
inc dx ; difference between buffer and search string
|
|
||||||
; lengths, plus 1 (# of possible string start
|
|
||||||
; locations to check in the buffer)
|
|
||||||
mov di,ds
|
|
||||||
mov es,di
|
|
||||||
mov di,[bp+Buffer] ;point ES:DI to buffer to search thru
|
|
||||||
mov bx,[bp+ScanCharOffset] ;offset in string of character
|
|
||||||
; on which to scan
|
|
||||||
add di,bx ;point ES:DI to first buffer byte to scan
|
|
||||||
mov al,[si+bx] ;put the scan character in AL
|
|
||||||
inc bx ;set BX to the offset back to the start of the
|
|
||||||
; potential full match after a scan match,
|
|
||||||
; accounting for the 1-byte overrun of
|
|
||||||
; REPNZ SCASB
|
|
||||||
FindStringLoop:
|
|
||||||
mov cx,dx ;put remaining buffer search length in CX
|
|
||||||
repnz scasb ;scan for the scan byte
|
|
||||||
jnz FindStringNotFound ;not found, so there’s no match
|
|
||||||
;found, so we have a potential match-check the
|
|
||||||
; rest of this candidate location
|
|
||||||
push di ;remember the address of the next byte to scan
|
|
||||||
mov dx,cx ;set aside the remaining length to search in
|
|
||||||
; the buffer
|
|
||||||
sub di,bx ;point back to the potential start of the
|
|
||||||
; match in the buffer
|
|
||||||
mov si,[bp+SearchString] ;point to the start of the string
|
|
||||||
mov cx,[bp+SearchStringLength] ;string length
|
|
||||||
shr cx,1 ;convert to word for faster search
|
|
||||||
jnc FindStringWord ;do word search if no odd byte
|
|
||||||
cmpsb ;compare the odd byte
|
|
||||||
jnz FindStringNoMatch ;odd byte doesn’t match, so we
|
|
||||||
; haven’t found the search string here
|
|
||||||
FindStringWord:
|
|
||||||
jcxz FindStringFound ;if the string is only 1 byte long,
|
|
||||||
; we’ve found a match
|
|
||||||
repz cmpsw ;check the rest of the string a word at a time
|
|
||||||
jz FindStringFound ;it’s a match
|
|
||||||
FindStringNoMatch:
|
|
||||||
pop di ;get back pointer to the next byte to scan
|
|
||||||
and dx,dx ;is there anything left to check?
|
|
||||||
jnz FindStringLoop ;yes-check next byte
|
|
||||||
FindStringNotFound:
|
|
||||||
sub ax,ax ;return a NULL pointer indicating that the
|
|
||||||
jmp FindStringDone ; string was not found
|
|
||||||
FindStringFound:
|
|
||||||
pop ax ;point to the buffer location at which the
|
|
||||||
sub ax,bx ; string was found (earlier we pushed the
|
|
||||||
; address of the byte after the scan match)
|
|
||||||
FindStringDone:
|
|
||||||
pop di ;restore caller’s register variables
|
|
||||||
pop si
|
|
||||||
pop bp ;restore caller’s stack frame
|
|
||||||
ret
|
|
||||||
_FindStringendp
|
|
||||||
end
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p><b>LISTING 9.3 L9-3.C</b></p>
|
|
||||||
<pre>
|
|
||||||
/* Program to exercise buffer-search routines in Listings 9.1 & 9.2 */
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define DISPLAY_LENGTH 40
|
|
||||||
extern unsigned char * FindString(unsigned char *, unsigned int,
|
|
||||||
unsigned char *, unsigned int, unsigned int);
|
|
||||||
void main(void);
|
|
||||||
static unsigned char TestBuffer[] = “When, in the course of human \
|
|
||||||
events, it becomes necessary for one people to dissolve the \
|
|
||||||
political bands which have connected them with another, and to \
|
|
||||||
assume among the powers of the earth the separate and equal station \
|
|
||||||
to which the laws of nature and of nature’s God entitle them...”;
|
|
||||||
void main() {
|
|
||||||
static unsigned char TestString[] = “equal”;
|
|
||||||
unsigned char TempBuffer[DISPLAY_LENGTH+1];
|
|
||||||
unsigned char *MatchPtr;
|
|
||||||
/* Search for TestString and report the results */
|
|
||||||
if ((MatchPtr = FindString(TestBuffer,
|
|
||||||
(unsigned int) strlen(TestBuffer), TestString,
|
|
||||||
(unsigned int) strlen(TestString), 1)) == NULL) {
|
|
||||||
/* TestString wasn’t found */
|
|
||||||
printf(“\”%s\“ not found\n”, TestString);
|
|
||||||
} else {
|
|
||||||
/* TestString was found. Zero-terminate TempBuffer; strncpy
|
|
||||||
won’t do it if DISPLAY_LENGTH characters are copied */
|
|
||||||
TempBuffer[DISPLAY_LENGTH] = 0;
|
|
||||||
printf(“\”%s\“ found. Next %d characters at match:\n\”%s\“\n”,
|
|
||||||
TestString, DISPLAY_LENGTH,
|
|
||||||
strncpy(TempBuffer, MatchPtr, DISPLAY_LENGTH));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="09-03.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="09-05.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
116
09-05.html
116
09-05.html
File diff suppressed because one or more lines are too long
167
09-06.html
167
09-06.html
File diff suppressed because one or more lines are too long
140
09-07.html
140
09-07.html
File diff suppressed because one or more lines are too long
100
10-01.html
100
10-01.html
File diff suppressed because one or more lines are too long
306
10-02.html
306
10-02.html
File diff suppressed because one or more lines are too long
174
10-03.html
174
10-03.html
File diff suppressed because one or more lines are too long
168
10-04.html
168
10-04.html
File diff suppressed because one or more lines are too long
110
11-01.html
110
11-01.html
File diff suppressed because one or more lines are too long
118
11-02.html
118
11-02.html
File diff suppressed because one or more lines are too long
163
11-03.html
163
11-03.html
File diff suppressed because one or more lines are too long
144
11-04.html
144
11-04.html
File diff suppressed because one or more lines are too long
103
11-05.html
103
11-05.html
File diff suppressed because one or more lines are too long
109
11-06.html
109
11-06.html
File diff suppressed because one or more lines are too long
102
11-07.html
102
11-07.html
File diff suppressed because one or more lines are too long
132
11-08.html
132
11-08.html
File diff suppressed because one or more lines are too long
130
12-01.html
130
12-01.html
File diff suppressed because one or more lines are too long
154
12-02.html
154
12-02.html
File diff suppressed because one or more lines are too long
149
12-03.html
149
12-03.html
File diff suppressed because one or more lines are too long
169
12-04.html
169
12-04.html
File diff suppressed because one or more lines are too long
111
13-01.html
111
13-01.html
File diff suppressed because one or more lines are too long
117
13-02.html
117
13-02.html
File diff suppressed because one or more lines are too long
118
13-03.html
118
13-03.html
File diff suppressed because one or more lines are too long
106
13-04.html
106
13-04.html
File diff suppressed because one or more lines are too long
106
14-01.html
106
14-01.html
File diff suppressed because one or more lines are too long
100
14-02.html
100
14-02.html
File diff suppressed because one or more lines are too long
226
14-03.html
226
14-03.html
File diff suppressed because one or more lines are too long
208
14-04.html
208
14-04.html
File diff suppressed because one or more lines are too long
223
14-05.html
223
14-05.html
|
|
@ -1,223 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<meta name="chapter" content="14" />
|
|
||||||
<meta name="pages" content="271-274" />
|
|
||||||
</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><b>LISTING 14.3 L14-3.ASM</b></p>
|
|
||||||
<pre>
|
|
||||||
; Searches a buffer for a specified pattern. In case of a mismatch,
|
|
||||||
; uses the value of the mismatched byte to skip across as many
|
|
||||||
; potential match locations as possible (partial Boyer-Moore).
|
|
||||||
; Returns start offset of first match searching forward, or NULL if
|
|
||||||
; no match is found.
|
|
||||||
; Tested with TASM.
|
|
||||||
; C near-callable as:
|
|
||||||
; unsigned char * FindString(unsigned char * BufferPtr,
|
|
||||||
; unsigned int BufferLength, unsigned char * PatternPtr,
|
|
||||||
; unsigned int PatternLength);
|
|
||||||
|
|
||||||
parms struc
|
|
||||||
dw 2 dup(?) ;pushed BP & return address
|
|
||||||
BufferPtr dw ? ;pointer to buffer to be searched
|
|
||||||
BufferLength dw ? ;# of bytes in buffer to be searched
|
|
||||||
PatternPtr dw ? ;pointer to pattern for which to search
|
|
||||||
PatternLength dw ? ;length of pattern for which to search
|
|
||||||
parms ends
|
|
||||||
|
|
||||||
.model small
|
|
||||||
.code
|
|
||||||
public _FindString
|
|
||||||
_FindString proc near
|
|
||||||
cld
|
|
||||||
push bp ;preserve caller’s stack frame
|
|
||||||
mov bp,sp ;point to our stack frame
|
|
||||||
push si ;preserve caller’s register variables
|
|
||||||
push di
|
|
||||||
sub sp,256*2 ;allocate space for SkipTable
|
|
||||||
; Create the table of distances by which to skip ahead on mismatches
|
|
||||||
; for every possible byte value. First, initialize all skips to the
|
|
||||||
; pattern length; this is the skip distance for bytes that don’t
|
|
||||||
; appear in the pattern.
|
|
||||||
mov ax,[bp+PatternLength]
|
|
||||||
and ax,ax ;return an instant match if the pattern is
|
|
||||||
jz InstantMatch ;0-length
|
|
||||||
mov di,ds
|
|
||||||
mov es,di ;ES=DS=SS
|
|
||||||
mov di,sp ;point to SkipBuffer
|
|
||||||
mov cx,256
|
|
||||||
rep stosw
|
|
||||||
dec ax ;from now on, we only need
|
|
||||||
mov [bp+PatternLength],ax ; PatternLength - 1
|
|
||||||
; Point to last (rightmost) byte of first potential pattern match
|
|
||||||
; location in buffer.
|
|
||||||
add [bp+BufferPtr],ax
|
|
||||||
; Reject if buffer is too small, and set the count of the number of
|
|
||||||
; potential pattern match locations in the buffer.
|
|
||||||
sub [bp+BufferLength],ax
|
|
||||||
jbe NoMatch
|
|
||||||
; Set the skip values for the bytes that do appear in the pattern to
|
|
||||||
; the distance from the byte location to the end of the pattern.
|
|
||||||
; When there are multiple instances of the same byte, the rightmost
|
|
||||||
; instance’s skip value is used. Note that the rightmost byte of the
|
|
||||||
; pattern isn’t entered in the skip table; if we get that value for
|
|
||||||
; a mismatch, we know for sure that the right end of the pattern has
|
|
||||||
; already passed the mismatch location, so this is not a relevant byte
|
|
||||||
; for skipping purposes.
|
|
||||||
mov si,[bp+PatternPtr] ;point to start of pattern
|
|
||||||
and ax,ax ;are there any skips to set?
|
|
||||||
jz SetSkipDone ;no
|
|
||||||
mov di,sp ;point to SkipBuffer
|
|
||||||
SetSkipLoop:
|
|
||||||
sub bx,bx ;prepare for word addressing off byte value
|
|
||||||
mov bl,[si] ;get the next pattern byte
|
|
||||||
inc si ;advance the pattern pointer
|
|
||||||
shl bx,1 ;prepare for word lookup
|
|
||||||
mov [di+bx],ax ;set the skip value when this byte value is
|
|
||||||
; the mismatch value in the buffer
|
|
||||||
dec ax
|
|
||||||
jnz SetSkipLoop
|
|
||||||
SetSkipDone:
|
|
||||||
mov dl,[si] ;DL=rightmost pattern byte from now on
|
|
||||||
dec si ;point to next-to-rightmost byte of pattern
|
|
||||||
mov [bp+PatternPtr],si ; from now on
|
|
||||||
; Search the buffer.
|
|
||||||
std ;for backward REPZ CMPSB
|
|
||||||
mov di,[bp+BufferPtr] ;point to first search location
|
|
||||||
mov cx,[bp+BufferLength] ;# of match locations to check
|
|
||||||
SearchLoop:
|
|
||||||
mov si,sp ;point SI to SkipTable
|
|
||||||
; Skip through until there’s a match for the rightmost pattern byte.
|
|
||||||
QuickSearchLoop:
|
|
||||||
mov bl,[di] ;rightmost buffer byte at this location
|
|
||||||
cmp dl,bl ;does it match the rightmost pattern byte?
|
|
||||||
jz FullCompare ;yes, so keep going
|
|
||||||
sub bh,bh ;convert to a word
|
|
||||||
add bx,bx ;prepare for look-up in SkipTable
|
|
||||||
mov ax,[si+bx] ;get skip value from skip table for this
|
|
||||||
; mismatch value
|
|
||||||
add di,ax ;BufferPtr += Skip;
|
|
||||||
sub cx,ax ;BufferLength -= Skip;
|
|
||||||
ja QuickSearchLoop ;continue if any buffer left
|
|
||||||
jmp short NoMatch
|
|
||||||
; Return a pointer to the start of the buffer (for 0-length pattern).
|
|
||||||
align 2
|
|
||||||
InstantMatch:
|
|
||||||
mov ax,[bp+BufferPtr]
|
|
||||||
jmp short Done
|
|
||||||
; Compare the pattern and the buffer location, searching from high
|
|
||||||
; memory toward low (right to left).
|
|
||||||
align 2
|
|
||||||
FullCompare:
|
|
||||||
mov [bp+BufferPtr],di ;save the current state of
|
|
||||||
mov [bp+BufferLength],cx ; the search
|
|
||||||
mov cx,[bp+PatternLength] ;# of bytes yet to compare
|
|
||||||
jcxz Match ;done if only one character
|
|
||||||
mov si,[bp+PatternPtr] ;point to next-to-rightmost bytes
|
|
||||||
dec di ; of buffer location and pattern
|
|
||||||
repz cmpsb ;compare the rest of the pattern
|
|
||||||
jz Match ;that’s it; we’ve found a match
|
|
||||||
; It’s a mismatch; let’s see what we can learn from it.
|
|
||||||
inc di ;compensate for 1-byte overrun of REPZ CMPSB;
|
|
||||||
; point to mismatch location in buffer
|
|
||||||
; # of bytes that did match.
|
|
||||||
mov si,[bp+BufferPtr]
|
|
||||||
sub si,di
|
|
||||||
; If, based on the mismatch character, we can’t even skip ahead as far
|
|
||||||
; as where we started this particular comparison, then just advance by
|
|
||||||
; 1 to the next potential match; otherwise, skip ahead from this
|
|
||||||
; comparison location by the skip distance for the mismatch character,
|
|
||||||
; less the distance covered by the partial match.
|
|
||||||
sub bx,bx ;prepare for word addressing off byte value
|
|
||||||
mov bl,[di] ;get the value of the mismatch byte in buffer
|
|
||||||
add bx,bx ;prepare for word look-up
|
|
||||||
add bx,sp ;SP points to SkipTable
|
|
||||||
mov cx,[bx] ;get the skip value for this mismatch
|
|
||||||
mov ax,1 ;assume we’ll just advance to the next
|
|
||||||
; potential match location
|
|
||||||
sub cx,si ;is the skip far enough to be worth taking?
|
|
||||||
jna MoveAhead ;no, go with the default advance of 1
|
|
||||||
mov ax,cx ;yes; this is the distance to skip ahead from
|
|
||||||
; the last potential match location checked
|
|
||||||
MoveAhead:
|
|
||||||
; Skip ahead and perform the next comparison, if there’s any buffer
|
|
||||||
; left to check.
|
|
||||||
mov di,[bp+BufferPtr]
|
|
||||||
add di,ax ;BufferPtr += Skip;
|
|
||||||
mov cx,[bp+BufferLength]
|
|
||||||
sub cx,ax ;BufferLength -= Skip;
|
|
||||||
ja SearchLoop ;continue if any buffer left
|
|
||||||
; Return a NULL pointer for no match.
|
|
||||||
align 2
|
|
||||||
NoMatch:
|
|
||||||
sub ax,ax
|
|
||||||
jmp short Done
|
|
||||||
; Return start of match in buffer (BufferPtr - (PatternLength - 1)).
|
|
||||||
align 2
|
|
||||||
Match:
|
|
||||||
mov ax,[bp+BufferPtr]
|
|
||||||
sub ax,[bp+PatternLength]
|
|
||||||
Done:
|
|
||||||
cld ;restore default direction flag
|
|
||||||
add sp,256*2 ;deallocate space for SkipTable
|
|
||||||
pop di ;restore caller’s register variables
|
|
||||||
pop si
|
|
||||||
pop bp ;restore caller’s stack frame
|
|
||||||
ret
|
|
||||||
_FindString endp
|
|
||||||
end
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="14-04.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="14-06.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
225
14-06.html
225
14-06.html
File diff suppressed because one or more lines are too long
99
15-01.html
99
15-01.html
File diff suppressed because one or more lines are too long
172
15-02.html
172
15-02.html
File diff suppressed because one or more lines are too long
171
15-03.html
171
15-03.html
File diff suppressed because one or more lines are too long
250
15-04.html
250
15-04.html
File diff suppressed because one or more lines are too long
227
16-01.html
227
16-01.html
File diff suppressed because one or more lines are too long
211
16-02.html
211
16-02.html
File diff suppressed because one or more lines are too long
176
16-03.html
176
16-03.html
File diff suppressed because one or more lines are too long
110
16-04.html
110
16-04.html
File diff suppressed because one or more lines are too long
369
16-05.html
369
16-05.html
|
|
@ -1,369 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<meta name="chapter" content="16" />
|
|
||||||
<meta name="pages" content="308-311" />
|
|
||||||
</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>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>
|
|
||||||
<pre>
|
|
||||||
; QSCAN3.ASM
|
|
||||||
; David Stafford
|
|
||||||
|
|
||||||
|
|
||||||
COMMENT $
|
|
||||||
|
|
||||||
How it works
|
|
||||||
——————
|
|
||||||
The idea is to go through the buffer fetching each letter-pair (words
|
|
||||||
rather than bytes). The carry flag indicates whether we are
|
|
||||||
currently in a (text) word or not. The letter-pair fetched from the
|
|
||||||
buffer is converted to a 16-bit address by shifting it left one bit
|
|
||||||
(losing the high bit of the second character) and putting the carry
|
|
||||||
flag in the low bit. The high bit of the count register is set to
|
|
||||||
1. Then the count register is added to the byte found at the given
|
|
||||||
address in a large (64K, naturally) table. The byte at the given
|
|
||||||
address will contain a 1 in the high bit if the last character of the
|
|
||||||
letter-pair is a word-letter (alphanumeric or apostrophe). This will
|
|
||||||
set the carry flag since the high bit of the count register is also a
|
|
||||||
1. The low bit of the byte found at the given address will be one if
|
|
||||||
the second character of the previous letter-pair was a word-letter
|
|
||||||
and the first character of this letter-pair is not a word-letter. It
|
|
||||||
will also be 1 if the first character of this letter-pair is a
|
|
||||||
word-letter but the second character is not. This process is
|
|
||||||
repeated. Finally, the carry flag is saved to indicate the final
|
|
||||||
in-a-word/not-in-a-word status. The count register is masked to
|
|
||||||
remove the high bit and the count of words remains in the count
|
|
||||||
register.
|
|
||||||
Sound complicated? You’re right! But it’s fast!
|
|
||||||
|
|
||||||
The beauty of this method is that no jumps are required, the
|
|
||||||
operations are fast, it requires only one table and the process can
|
|
||||||
be repeated (unrolled) many times. QSCAN3 can read 256 bytes without
|
|
||||||
jumping.
|
|
||||||
|
|
||||||
COMMEND $
|
|
||||||
.model small
|
|
||||||
.code
|
|
||||||
|
|
||||||
Test1 macro x,y ;9 or 10 bytes
|
|
||||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
|
||||||
adc di,di
|
|
||||||
or ax,si
|
|
||||||
add al,[di]
|
|
||||||
endm
|
|
||||||
|
|
||||||
Test2 macro x,y ;7 or 8 bytes
|
|
||||||
Addr&x: mov di,[bp+y] ;3 or 4 bytes
|
|
||||||
adc di,di
|
|
||||||
add ah,[di]
|
|
||||||
endm
|
|
||||||
|
|
||||||
Scan = 128 ;scan 256 bytes at a time
|
|
||||||
Buffer = 4 ;parms
|
|
||||||
BufferLength = 6
|
|
||||||
CharFlag = 8
|
|
||||||
WordCount = 10
|
|
||||||
|
|
||||||
public _ScanBuffer
|
|
||||||
_ScanBuffer proc near
|
|
||||||
push bp
|
|
||||||
mov bp,sp
|
|
||||||
push si
|
|
||||||
push di
|
|
||||||
|
|
||||||
xor cx,cx
|
|
||||||
mov si,[bp+Buffer] ;si = text buffer
|
|
||||||
mov ax,[bp+BufferLength] ;dx = length in bytes
|
|
||||||
shr ax,1 ;dx = length in words
|
|
||||||
jnz NormalBuf
|
|
||||||
OneByteBuf:
|
|
||||||
mov ax,seg WordTable
|
|
||||||
mov es,ax
|
|
||||||
|
|
||||||
mov di,[bp+CharFlag]
|
|
||||||
mov bh,[di] ;bh = old CharFlag
|
|
||||||
mov bl,[si] ;bl = character
|
|
||||||
add bh,‘A’-1 ;make bh into character
|
|
||||||
add bx,bx ;prepare to index
|
|
||||||
mov al,es:[bx]
|
|
||||||
cbw ;get hi bit in ah (then bh)
|
|
||||||
shr al,1 ;get low bit
|
|
||||||
adc cx,cx ;cx = 0 or 1
|
|
||||||
xchg ax,bx
|
|
||||||
jmp CleanUp
|
|
||||||
NormalBuf:
|
|
||||||
push bp ;(1)
|
|
||||||
pushf ;(2)
|
|
||||||
|
|
||||||
cwd ;dx = 0
|
|
||||||
mov cl,Scan
|
|
||||||
div cx
|
|
||||||
or dx,dx ;remainder?
|
|
||||||
jz StartAtTheTop ;nope, do the whole banana
|
|
||||||
sub cx,dx
|
|
||||||
sub si,cx ;adjust buf pointer
|
|
||||||
sub si,cx
|
|
||||||
inc ax ;adjust for partial read
|
|
||||||
|
|
||||||
StartAtTheTop: mov bx,dx ;get index for start...
|
|
||||||
shl bx,1
|
|
||||||
mov di,LoopEntry[bx] ;...address in di
|
|
||||||
xchg dx,ax ;dx is the loop counter
|
|
||||||
xor cx,cx ;total word count
|
|
||||||
mov bx,[bp+CharFlag]
|
|
||||||
mov bl,[bx] ;bl = old CharFlag
|
|
||||||
mov bp,seg WordTable
|
|
||||||
mov ds,bp
|
|
||||||
mov bp,si ;scan buffer with bp
|
|
||||||
mov si,8080h ;hi bits
|
|
||||||
mov ax,si ;init local word counter
|
|
||||||
shr bl,1 ;carry = old CharFlag
|
|
||||||
jmp di
|
|
||||||
|
|
||||||
align 2
|
|
||||||
Top: add bx,bx ;restore carry
|
|
||||||
n = 0
|
|
||||||
rept Scan/2
|
|
||||||
Test1 %n,%n*2
|
|
||||||
Test2 %n+1,%n*2+2
|
|
||||||
n = n+2
|
|
||||||
endm
|
|
||||||
EndCount:
|
|
||||||
sbb bx,bx ;save carry
|
|
||||||
if Scan ge 128 ;because al+ah may equal 128!
|
|
||||||
or ax,si
|
|
||||||
add al,ah
|
|
||||||
mov ah,0
|
|
||||||
else
|
|
||||||
add al,ah
|
|
||||||
and ax,7fh ;mask
|
|
||||||
endif
|
|
||||||
add cx,ax ;update word count
|
|
||||||
mov ax,si
|
|
||||||
add bp,Scan*2
|
|
||||||
dec dx ;any left?
|
|
||||||
jng Quit
|
|
||||||
jmp Top
|
|
||||||
|
|
||||||
Quit: popf ;(2) even or odd buffer?
|
|
||||||
jnc ItsEven
|
|
||||||
clc
|
|
||||||
Test1 Odd,-1
|
|
||||||
sbb bx,bx ;save carry
|
|
||||||
shr ax,1
|
|
||||||
adc cx,0
|
|
||||||
ItsEven:
|
|
||||||
push ss ;restore ds
|
|
||||||
pop ds
|
|
||||||
pop bp ;(1)
|
|
||||||
CleanUp:
|
|
||||||
mov si,[bp+WordCount]
|
|
||||||
add [si],cx
|
|
||||||
adc word ptr [si+2],0
|
|
||||||
and bh,1 ;save only the carry flag
|
|
||||||
mov si,[bp+CharFlag]
|
|
||||||
mov [si],bh
|
|
||||||
pop di
|
|
||||||
pop si
|
|
||||||
pop bp
|
|
||||||
ret
|
|
||||||
_ScanBuffer endp
|
|
||||||
|
|
||||||
.data
|
|
||||||
Address macro X
|
|
||||||
dw Addr&X
|
|
||||||
endm
|
|
||||||
|
|
||||||
LoopEntry label word
|
|
||||||
n = Scan
|
|
||||||
REPT Scan
|
|
||||||
Address %n MOD Scan
|
|
||||||
n = n - 1
|
|
||||||
ENDM
|
|
||||||
|
|
||||||
.fardata WordTable
|
|
||||||
include qscan3.inc ;built by MAKETAB
|
|
||||||
end
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="16-04.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="16-06.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
85
16-06.html
85
16-06.html
File diff suppressed because one or more lines are too long
181
16-07.html
181
16-07.html
File diff suppressed because one or more lines are too long
148
16-08.html
148
16-08.html
File diff suppressed because one or more lines are too long
90
17-01.html
90
17-01.html
File diff suppressed because one or more lines are too long
317
17-02.html
317
17-02.html
|
|
@ -1,317 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<meta name="chapter" content="17" />
|
|
||||||
<meta name="pages" content="325-329" />
|
|
||||||
</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><b>LISTING 17.1 L17-1.CPP</b></p>
|
|
||||||
<pre>
|
|
||||||
/* C++ Game of Life implementation for any mode for which mode set
|
|
||||||
and draw pixel functions can be provided.
|
|
||||||
Tested with Borland C++ in the small model. */
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <iostream.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <dos.h>
|
|
||||||
#include <bios.h>
|
|
||||||
#include <mem.h>
|
|
||||||
|
|
||||||
#define ON_COLOR 15 // on-cell pixel color
|
|
||||||
#define OFF_COLOR 0 // off-cell pixel color
|
|
||||||
#define MSG_LINE 10 // row for text messages
|
|
||||||
#define GENERATION_LINE 12 // row for generation # display
|
|
||||||
#define LIMIT_18_HZ 1 // set 1 for maximum frame rate = 18Hz
|
|
||||||
#define WRAP_EDGES 1 // set to 0 to disable wrapping around
|
|
||||||
// at cell map edges
|
|
||||||
class cellmap {
|
|
||||||
private:
|
|
||||||
unsigned char *cells;
|
|
||||||
unsigned int width;
|
|
||||||
unsigned int width_in_bytes;
|
|
||||||
unsigned int height;
|
|
||||||
unsigned int length_in_bytes;
|
|
||||||
public:
|
|
||||||
cellmap(unsigned int h, unsigned int v);
|
|
||||||
~cellmap(void);
|
|
||||||
void copy_cells(cellmap &sourcemap);
|
|
||||||
void set_cell(unsigned int x, unsigned int y);
|
|
||||||
void clear_cell(unsigned int x, unsigned int y);
|
|
||||||
int cell_state(int x, int y);
|
|
||||||
void next_generation(cellmap& dest_map);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void enter_display_mode(void);
|
|
||||||
extern void exit_display_mode(void);
|
|
||||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
|
||||||
unsigned int Color);
|
|
||||||
extern void show_text(int x, int y, char *text);
|
|
||||||
|
|
||||||
/* Controls the size of the cell map. Must be within the capabilities
|
|
||||||
of the display mode, and must be limited to leave room for text
|
|
||||||
display at right. */
|
|
||||||
unsigned int cellmap_width = 96;
|
|
||||||
unsigned int cellmap_height = 96;
|
|
||||||
/* Width & height in pixels of each cell as displayed on screen. */
|
|
||||||
unsigned int magnifier = 2;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
unsigned int init_length, x, y, seed;
|
|
||||||
unsigned long generation = 0;
|
|
||||||
char gen_text[80];
|
|
||||||
long bios_time, start_bios_time;
|
|
||||||
|
|
||||||
cellmap current_map(cellmap_height, cellmap_width);
|
|
||||||
cellmap next_map(cellmap_height, cellmap_width);
|
|
||||||
|
|
||||||
// Get the seed; seed randomly if 0 entered
|
|
||||||
cout << “Seed (0 for random seed): ”;
|
|
||||||
cin >> seed;
|
|
||||||
if (seed == 0) seed = (unsigned) time(NULL);
|
|
||||||
|
|
||||||
// Randomly initialize the initial cell map
|
|
||||||
cout << “Initializing...”;
|
|
||||||
srand(seed);
|
|
||||||
init_length = (cellmap_height * cellmap_width) / 2;
|
|
||||||
do {
|
|
||||||
x = random(cellmap_width);
|
|
||||||
y = random(cellmap_height);
|
|
||||||
next_map.set_cell(x, y);
|
|
||||||
} while (—init_length);
|
|
||||||
current_map.copy_cells(next_map); // put init map in current_map
|
|
||||||
|
|
||||||
enter_display_mode();
|
|
||||||
|
|
||||||
// Keep recalculating and redisplaying generations until a key
|
|
||||||
// is pressed
|
|
||||||
show_text(0, MSG_LINE, “Generation: ”);
|
|
||||||
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
|
||||||
do {
|
|
||||||
generation++;
|
|
||||||
sprintf(gen_text, “%10lu”, generation);
|
|
||||||
show_text(1, GENERATION_LINE, gen_text);
|
|
||||||
// Recalculate and draw the next generation
|
|
||||||
current_map.next_generation(next_map);
|
|
||||||
// Make current_map current again
|
|
||||||
current_map.copy_cells(next_map);
|
|
||||||
#if LIMIT_18_HZ
|
|
||||||
// Limit to a maximum of 18.2 frames per second,for visibility
|
|
||||||
do {
|
|
||||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
|
||||||
} while (start_bios_time == bios_time);
|
|
||||||
start_bios_time = bios_time;
|
|
||||||
#endif
|
|
||||||
} while (!kbhit());
|
|
||||||
getch(); // clear keypress
|
|
||||||
exit_display_mode();
|
|
||||||
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
|
||||||
seed << “\n”;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cellmap constructor. */
|
|
||||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
|
||||||
{
|
|
||||||
width = w;
|
|
||||||
width_in_bytes = (w + 7) / 8;
|
|
||||||
height = h;
|
|
||||||
length_in_bytes = width_in_bytes * h;
|
|
||||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
|
||||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cellmap destructor. */
|
|
||||||
cellmap::~cellmap(void)
|
|
||||||
{
|
|
||||||
delete[] cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copies one cellmap’s cells to another cellmap. Both cellmaps are
|
|
||||||
assumed to be the same size. */
|
|
||||||
void cellmap::copy_cells(cellmap &sourcemap)
|
|
||||||
{
|
|
||||||
memcpy(cells, sourcemap.cells, length_in_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turns cell on. */
|
|
||||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
unsigned char *cell_ptr =
|
|
||||||
cells + (y * width_in_bytes) + (x / 8);
|
|
||||||
|
|
||||||
*(cell_ptr) |= 0x80 >> (x & 0x07);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turns cell off. */
|
|
||||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
unsigned char *cell_ptr =
|
|
||||||
cells + (y * width_in_bytes) + (x / 8);
|
|
||||||
|
|
||||||
*(cell_ptr) &= ~(0x80 >> (x & 0x07));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns cell state (1=on or 0=off), optionally wrapping at the
|
|
||||||
borders around to the opposite edge. */
|
|
||||||
int cellmap::cell_state(int x, int y)
|
|
||||||
{
|
|
||||||
unsigned char *cell_ptr;
|
|
||||||
|
|
||||||
#if WRAP_EDGES
|
|
||||||
while (x < 0) x += width; // wrap, if necessary
|
|
||||||
while (x >= width) x -= width;
|
|
||||||
while (y < 0) y += height;
|
|
||||||
while (y >= height) y -= height;
|
|
||||||
#else
|
|
||||||
if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
|
|
||||||
return 0; // return 0 for off edges if no wrapping
|
|
||||||
#endif
|
|
||||||
cell_ptr = cells + (y * width_in_bytes) + (x / 8);
|
|
||||||
return (*cell_ptr & (0x80 >> (x & 0x07))) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculates the next generation of a cellmap and stores it in
|
|
||||||
next_map. */
|
|
||||||
void cellmap::next_generation(cellmap& next_map)
|
|
||||||
{
|
|
||||||
unsigned int x, y, neighbor_count;
|
|
||||||
|
|
||||||
for (y=0; y<height; y++) {
|
|
||||||
for (x=0; x<width; x++) {
|
|
||||||
// Figure out how many neighbors this cell has
|
|
||||||
neighbor_count = cell_state(x-1, y-1) + cell_state(x, y-1) +
|
|
||||||
cell_state(x+1, y-1) + cell_state(x-1, y) +
|
|
||||||
cell_state(x+1, y) + cell_state(x-1, y+1) +
|
|
||||||
cell_state(x, y+1) + cell_state(x+1, y+1);
|
|
||||||
if (cell_state(x, y) == 1) {
|
|
||||||
// The cell is on; does it stay on?
|
|
||||||
if ((neighbor_count != 2) && (neighbor_count != 3)) {
|
|
||||||
next_map.clear_cell(x, y); // turn it off
|
|
||||||
draw_pixel(x, y, OFF_COLOR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The cell is off; does it turn on?
|
|
||||||
if (neighbor_count == 3) {
|
|
||||||
next_map.set_cell(x, y); // turn it on
|
|
||||||
draw_pixel(x, y, ON_COLOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p><b>LISTING 17.2 L17-2.CPP</b></p>
|
|
||||||
<pre>
|
|
||||||
/* VGA mode 13h functions for Game of Life.
|
|
||||||
Tested with Borland C++. */
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#include <dos.h>
|
|
||||||
|
|
||||||
#define TEXT_X_OFFSET 27
|
|
||||||
#define SCREEN_WIDTH_IN_BYTES 320
|
|
||||||
|
|
||||||
/* Width & height in pixels of each cell. */
|
|
||||||
extern unsigned int magnifier;
|
|
||||||
|
|
||||||
/* Mode 13h draw pixel function. Pixels are of width & height
|
|
||||||
specified by magnifier. */
|
|
||||||
void draw_pixel(unsigned int x, unsigned int y, unsigned int color)
|
|
||||||
{
|
|
||||||
#define SCREEN_SEGMENT 0xA000
|
|
||||||
unsigned char far *screen_ptr;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
FP_SEG(screen_ptr) = SCREEN_SEGMENT;
|
|
||||||
FP_OFF(screen_ptr) =
|
|
||||||
y * magnifier * SCREEN_WIDTH_IN_BYTES + x * magnifier;
|
|
||||||
for (i=0; i<magnifier; i++) {
|
|
||||||
for (j=0; j<magnifier; j++) {
|
|
||||||
*(screen_ptr+j) = color;
|
|
||||||
}
|
|
||||||
screen_ptr += SCREEN_WIDTH_IN_BYTES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mode 13h mode-set function. */
|
|
||||||
void enter_display_mode()
|
|
||||||
{
|
|
||||||
union REGS regset;
|
|
||||||
|
|
||||||
regset.x.ax = 0x0013;
|
|
||||||
int86(0x10, &regset, &regset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Text mode mode-set function. */
|
|
||||||
void exit_display_mode()
|
|
||||||
{
|
|
||||||
union REGS regset;
|
|
||||||
|
|
||||||
regset.x.ax = 0x0003;
|
|
||||||
int86(0x10, &regset, &regset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Text display function. Offsets text to non-graphics area of
|
|
||||||
screen. */
|
|
||||||
void show_text(int x, int y, char *text)
|
|
||||||
{
|
|
||||||
gotoxy(TEXT_X_OFFSET + x, y);
|
|
||||||
puts(text);
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="17-01.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="17-03.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
211
17-03.html
211
17-03.html
File diff suppressed because one or more lines are too long
236
17-04.html
236
17-04.html
File diff suppressed because one or more lines are too long
169
17-05.html
169
17-05.html
File diff suppressed because one or more lines are too long
110
17-06.html
110
17-06.html
File diff suppressed because one or more lines are too long
336
17-07.html
336
17-07.html
|
|
@ -1,336 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<meta name="chapter" content="17" />
|
|
||||||
<meta name="pages" content="340-345" />
|
|
||||||
</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><b>LISTING 17.5 L17-5.CPP</b></p>
|
|
||||||
<pre>
|
|
||||||
/* C++ Game of Life implementation for any mode for which mode set
|
|
||||||
and draw pixel functions can be provided. The cellmap stores the
|
|
||||||
neighbor count for each cell as well as the state of each cell;
|
|
||||||
this allows very fast next-state determination. Edges always wrap
|
|
||||||
in this implementation.
|
|
||||||
Tested with Borland C++. To run, link with Listing 17.2
|
|
||||||
in the large model. */
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <iostream.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <dos.h>
|
|
||||||
#include <bios.h>
|
|
||||||
#include <mem.h>
|
|
||||||
|
|
||||||
#define ON_COLOR 15 // on-cell pixel color
|
|
||||||
#define OFF_COLOR 0 // off-cell pixel color
|
|
||||||
#define MSG_LINE 10 // row for text messages
|
|
||||||
#define GENERATION_LINE 12 // row for generation # display
|
|
||||||
#define LIMIT_18_HZ 0 // set 1 to to maximum frame rate = 18Hz
|
|
||||||
|
|
||||||
class cellmap {
|
|
||||||
private:
|
|
||||||
unsigned char *cells;
|
|
||||||
unsigned char *temp_cells;
|
|
||||||
unsigned int width;
|
|
||||||
unsigned int height;
|
|
||||||
unsigned int length_in_bytes;
|
|
||||||
public:
|
|
||||||
cellmap(unsigned int h, unsigned int v);
|
|
||||||
~cellmap(void);
|
|
||||||
void set_cell(unsigned int x, unsigned int y);
|
|
||||||
void clear_cell(unsigned int x, unsigned int y);
|
|
||||||
int cell_state(int x, int y);
|
|
||||||
int count_neighbors(int x, int y);
|
|
||||||
void next_generation(void);
|
|
||||||
void init(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void enter_display_mode(void);
|
|
||||||
extern void exit_display_mode(void);
|
|
||||||
extern void draw_pixel(unsigned int X, unsigned int Y,
|
|
||||||
unsigned int Color);
|
|
||||||
extern void show_text(int x, int y, char *text);
|
|
||||||
|
|
||||||
/* Controls the size of the cell map. Must be within the capabilities
|
|
||||||
of the display mode, and must be limited to leave room for text
|
|
||||||
display at right. */
|
|
||||||
unsigned int cellmap_width = 96;
|
|
||||||
unsigned int cellmap_height = 96;
|
|
||||||
|
|
||||||
/* Width & height in pixels of each cell. */
|
|
||||||
unsigned int magnifier = 2;
|
|
||||||
|
|
||||||
/* Randomizing seed */
|
|
||||||
unsigned int seed;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
unsigned long generation = 0;
|
|
||||||
char gen_text[80];
|
|
||||||
long bios_time, start_bios_time;
|
|
||||||
|
|
||||||
cellmap current_map(cellmap_height, cellmap_width);
|
|
||||||
|
|
||||||
current_map.init(); // randomly initialize cell map
|
|
||||||
|
|
||||||
enter_display_mode();
|
|
||||||
|
|
||||||
// Keep recalculating and redisplaying generations until any key
|
|
||||||
// is pressed
|
|
||||||
show_text(0, MSG_LINE, “Generation: ”);
|
|
||||||
start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
|
||||||
do {
|
|
||||||
generation++;
|
|
||||||
sprintf(gen_text, “%10lu”, generation);
|
|
||||||
show_text(1, GENERATION_LINE, gen_text);
|
|
||||||
// Recalculate and draw the next generation
|
|
||||||
current_map.next_generation();
|
|
||||||
#if LIMIT_18_HZ
|
|
||||||
// Limit to a maximum of 18.2 frames per second, for visibility
|
|
||||||
do {
|
|
||||||
_bios_timeofday(_TIME_GETCLOCK, &bios_time);
|
|
||||||
} while (start_bios_time == bios_time);
|
|
||||||
start_bios_time = bios_time;
|
|
||||||
#endif
|
|
||||||
} while (!kbhit());
|
|
||||||
|
|
||||||
getch(); // clear keypress
|
|
||||||
exit_display_mode();
|
|
||||||
cout << “Total generations: ” << generation << “\nSeed: ” <<
|
|
||||||
seed << “\n”;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cellmap constructor. */
|
|
||||||
cellmap::cellmap(unsigned int h, unsigned int w)
|
|
||||||
{
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
length_in_bytes = w * h;
|
|
||||||
cells = new unsigned char[length_in_bytes]; // cell storage
|
|
||||||
temp_cells = new unsigned char[length_in_bytes]; // temp cell storage
|
|
||||||
if ( (cells == NULL) || (temp_cells == NULL) ) {
|
|
||||||
printf(“Out of memory\n”);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
memset(cells, 0, length_in_bytes); // clear all cells, to start
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cellmap destructor. */
|
|
||||||
cellmap::~cellmap(void)
|
|
||||||
{
|
|
||||||
delete[] cells;
|
|
||||||
delete[] temp_cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turns an off-cell on, incrementing the on-neighbor count for the
|
|
||||||
eight neighboring cells. */
|
|
||||||
void cellmap::set_cell(unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
unsigned int w = width, h = height;
|
|
||||||
int xoleft, xoright, yoabove, yobelow;
|
|
||||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
|
||||||
|
|
||||||
// Calculate the offsets to the eight neighboring cells,
|
|
||||||
// accounting for wrapping around at the edges of the cell map
|
|
||||||
if (x == 0)
|
|
||||||
xoleft = w - 1;
|
|
||||||
else
|
|
||||||
xoleft = -1;
|
|
||||||
if (y == 0)
|
|
||||||
yoabove = length_in_bytes - w;
|
|
||||||
else
|
|
||||||
yoabove = -w;
|
|
||||||
if (x == (w - 1))
|
|
||||||
xoright = -(w - 1);
|
|
||||||
else
|
|
||||||
xoright = 1;
|
|
||||||
if (y == (h - 1))
|
|
||||||
yobelow = -(length_in_bytes - w);
|
|
||||||
else
|
|
||||||
yobelow = w;
|
|
||||||
|
|
||||||
*(cell_ptr) |= 0x01;
|
|
||||||
*(cell_ptr + yoabove + xoleft) += 2;
|
|
||||||
*(cell_ptr + yoabove) += 2;
|
|
||||||
*(cell_ptr + yoabove + xoright) += 2;
|
|
||||||
*(cell_ptr + xoleft) += 2;
|
|
||||||
*(cell_ptr + xoright) += 2;
|
|
||||||
*(cell_ptr + yobelow + xoleft) += 2;
|
|
||||||
*(cell_ptr + yobelow) += 2;
|
|
||||||
*(cell_ptr + yobelow + xoright) += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turns an on-cell off, decrementing the on-neighbor count for the
|
|
||||||
eight neighboring cells. */
|
|
||||||
void cellmap::clear_cell(unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
unsigned int w = width, h = height;
|
|
||||||
int xoleft, xoright, yoabove, yobelow;
|
|
||||||
unsigned char *cell_ptr = cells + (y * w) + x;
|
|
||||||
|
|
||||||
// Calculate the offsets to the eight neighboring cells,
|
|
||||||
// accounting for wrapping around at the edges of the cell map
|
|
||||||
if (x == 0)
|
|
||||||
xoleft = w - 1;
|
|
||||||
else
|
|
||||||
xoleft = -1;
|
|
||||||
if (y == 0)
|
|
||||||
yoabove = length_in_bytes - w;
|
|
||||||
else
|
|
||||||
yoabove = -w;
|
|
||||||
if (x == (w - 1))
|
|
||||||
xoright = -(w - 1);
|
|
||||||
else
|
|
||||||
xoright = 1;
|
|
||||||
if (y == (h - 1))
|
|
||||||
yobelow = -(length_in_bytes - w);
|
|
||||||
else
|
|
||||||
yobelow = w;
|
|
||||||
|
|
||||||
*(cell_ptr) &= ~0x01;
|
|
||||||
*(cell_ptr + yoabove + xoleft) -= 2;
|
|
||||||
*(cell_ptr + yoabove ) -= 2;
|
|
||||||
*(cell_ptr + yoabove + xoright) -= 2;
|
|
||||||
*(cell_ptr + xoleft) -= 2;
|
|
||||||
*(cell_ptr + xoright) -= 2;
|
|
||||||
*(cell_ptr + yobelow + xoleft) -= 2;
|
|
||||||
*(cell_ptr + yobelow) -= 2;
|
|
||||||
*(cell_ptr + yobelow + xoright) -= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns cell state (1=on or 0=off). */
|
|
||||||
int cellmap::cell_state(int x, int y)
|
|
||||||
{
|
|
||||||
unsigned char *cell_ptr;
|
|
||||||
|
|
||||||
cell_ptr = cells + (y * width) + x;
|
|
||||||
return *cell_ptr & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculates and displays the next generation of current_map */
|
|
||||||
void cellmap::next_generation()
|
|
||||||
{
|
|
||||||
unsigned int x, y, count;
|
|
||||||
unsigned int h = height, w = width;
|
|
||||||
unsigned char *cell_ptr, *row_cell_ptr;
|
|
||||||
|
|
||||||
// Copy to temp map, so we can have an unaltered version from
|
|
||||||
// which to work
|
|
||||||
memcpy(temp_cells, cells, length_in_bytes);
|
|
||||||
|
|
||||||
// Process all cells in the current cell map
|
|
||||||
cell_ptr = temp_cells; // first cell in cell map
|
|
||||||
for (y=0; y<h; y++) { // repeat for each row of cells
|
|
||||||
// Process all cells in the current row of the cell map
|
|
||||||
x = 0;
|
|
||||||
do { // repeat for each cell in row
|
|
||||||
// Zip quickly through as many off-cells with no
|
|
||||||
// neighbors as possible
|
|
||||||
while (*cell_ptr == 0) {
|
|
||||||
cell_ptr++; // advance to the next cell
|
|
||||||
if (++x >= w) goto RowDone;
|
|
||||||
}
|
|
||||||
// Found a cell that’s either on or has on-neighbors,
|
|
||||||
// so see if its state needs to be changed
|
|
||||||
count = *cell_ptr >> 1; // # of neighboring on-cells
|
|
||||||
if (*cell_ptr & 0x01) {
|
|
||||||
// Cell is on; turn it off if it doesn’t have
|
|
||||||
// 2 or 3 neighbors
|
|
||||||
if ((count != 2) && (count != 3)) {
|
|
||||||
clear_cell(x, y);
|
|
||||||
draw_pixel(x, y, OFF_COLOR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Cell is off; turn it on if it has exactly 3 neighbors
|
|
||||||
if (count == 3) {
|
|
||||||
set_cell(x, y);
|
|
||||||
draw_pixel(x, y, ON_COLOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Advance to the next cell
|
|
||||||
cell_ptr++; // advance to the next cell byte
|
|
||||||
} while (++x < w);
|
|
||||||
RowDone:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Randomly initializes the cellmap to about 50% on-pixels. */
|
|
||||||
void cellmap::init()
|
|
||||||
{
|
|
||||||
unsigned int x, y, init_length;
|
|
||||||
|
|
||||||
// Get the seed; seed randomly if 0 entered
|
|
||||||
cout << “Seed (0 for random seed): ”;
|
|
||||||
cin >> seed;
|
|
||||||
if (seed == 0) seed = (unsigned) time(NULL);
|
|
||||||
|
|
||||||
// Randomly initialize the initial cell map to 50% on-pixels
|
|
||||||
// (actually generally fewer, because some coordinates will be
|
|
||||||
// randomly selected more than once)
|
|
||||||
cout << “Initializing...”;
|
|
||||||
srand(seed);
|
|
||||||
init_length = (height * width) / 2;
|
|
||||||
do {
|
|
||||||
x = random(width);
|
|
||||||
y = random(height);
|
|
||||||
if (cell_state(x, y) == 0) {
|
|
||||||
set_cell(x, y);
|
|
||||||
}
|
|
||||||
} while (—init_length);
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<table border="1">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="17-06.html">Previous</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="index.html">Table of Contents</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a href="17-08.html">Next</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
<hr width="90%" size="1" noshade="noshade" />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
Graphics Programming Black Book © 2001 Michael Abrash
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
101
17-08.html
101
17-08.html
File diff suppressed because one or more lines are too long
92
18-01.html
92
18-01.html
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue