Initial scrape from http://www.phatcode.net/res/224/files/html/
83
about.html
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
<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: Foreword</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=Foreword//-->
|
||||||
|
<!--PAGES=xxxi-xxxii//-->
|
||||||
|
<!--UNASSIGNED1//-->
|
||||||
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
||||||
|
|
||||||
|
<CENTER>
|
||||||
|
<TABLE BORDER>
|
||||||
|
<TR>
|
||||||
|
<TD><A HREF="intro.html">Previous</A></TD>
|
||||||
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||||
|
<TD><A HREF="about_author.html">Next</A></TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
|
</CENTER>
|
||||||
|
<P><BR></P>
|
||||||
|
<H2><A NAME="Heading1"></A><FONT COLOR="#000077">Foreword</FONT></H2>
|
||||||
|
<P>I got my start programming on Apple II computers at school, and almost all of my early work was on the Apple platform. After graduating, it quickly became obvious that I was going to have trouble paying my rent working in the Apple II market in the late eighties, so I was forced to make a very rapid move into the Intel PC environment.
|
||||||
|
</P>
|
||||||
|
<P>What I was able to pick up over several years on the Apple, I needed to learn in the space of a few months on the PC.</P>
|
||||||
|
<P>The biggest benefit to me of actually making money as a programmer was the ability to buy all the books and magazines I wanted. I bought a lot. I was in territory that I new almost nothing about, so I read <I>everything</I> that I could get my hands on. Feature articles, editorials, even advertisements held information for me to assimilate.</P>
|
||||||
|
<P>John Romero clued me in early to the articles by Michael Abrash. The good stuff. Graphics hardware. Code optimization. Knowledge and wisdom for the aspiring developer. They were even fun to read. For a long time, my personal quest was to find a copy of Michael’s first book, <I>Zen of Assembly Language.</I> I looked in every bookstore I visited, but I never did find it. I made do with the articles I could dig up.</P>
|
||||||
|
<P>I learned the dark secrets of the EGA video controller there, and developed a few neat tricks of my own. Some of those tricks became the basis for the Commander Keen series of games, which launched id Software.</P>
|
||||||
|
<P>A year or two later, after Wolfenstein-3D, I bumped into Michael (in a virtual sense) for the first time. I was looking around on M&T Online, a BBS run by the Dr. Dobb’s publishers before the Internet explosion, when I saw some posts from the man himself. We traded email, and for a couple months we played tag-team gurus on the graphics forum before Doom’s development took over my life.</P>
|
||||||
|
<P>A friend of Michael’s at his new job put us back in touch with each other after Doom began to make its impact, and I finally got a chance to meet up with him in person.</P>
|
||||||
|
<P>I talked myself hoarse that day, explaining all the ins and outs of Doom to Michael and an interested group of his coworkers. Every few days afterwards, I would get an email from Michael asking for an elaboration on one of my points, or discussing an aspect of the future of graphics.</P>
|
||||||
|
<P>Eventually, I popped the question—I offered him a job at id. “Just think: no reporting to anyone, an opportunity to code all day, starting with a clean sheet of paper. A chance to do <I>the right thing</I> as a programmer.” It didn’t work. I kept at it though, and about a year later I finally convinced him to come down and take a look at id. I was working on Quake.</P>
|
||||||
|
<P>Going from Doom to Quake was a tremendous step. I knew where I wanted to end up, but I wasn’t at all clear what the steps were to get there. I was trying a huge number of approaches, and even the failures were teaching me a lot. My enthusiasm must have been contagious, because he took the job.</P>
|
||||||
|
<P>Much heroic programming ensued. Several hundred thousand lines of code were written. And rewritten. And rewritten. And rewritten.</P>
|
||||||
|
<P>In hindsight, I have plenty of regrets about various aspects of Quake, but it is a rare person that doesn’t freely acknowledge the technical triumph of it. We nailed it. Sure, a year from now I will have probably found a new perspective that will make me cringe at the clunkiness of some part of Quake, but at the moment it still looks pretty damn good to me.</P>
|
||||||
|
<P>I was very happy to have Michael describe much of the Quake technology in his ongoing magazine articles. We learned a lot, and I hope we managed to teach a bit.</P>
|
||||||
|
<P>When a non-programmer hears about Michael’s articles or the source code I have released, I usually get a stunned “WTF would you do that for???” look.</P>
|
||||||
|
<P>They don’t get it.</P>
|
||||||
|
<P>Programming is not a zero-sum game. Teaching something to a fellow programmer doesn’t take it away from you. I’m happy to share what I can, because I’m in it for the love of programming. The Ferraris are just gravy, honest!</P>
|
||||||
|
<P>This book contains many of the original articles that helped launch my programming career. I hope my contribution to the contents of the later articles can provide similar stepping stones for others.</P>
|
||||||
|
<P>—<I>John Carmack</I><BR>id Software</P><P><BR></P>
|
||||||
|
<CENTER>
|
||||||
|
<TABLE BORDER>
|
||||||
|
<TR>
|
||||||
|
<TD><A HREF="intro.html">Previous</A></TD>
|
||||||
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
||||||
|
<TD><A HREF="about_author.html">Next</A></TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
|
</CENTER>
|
||||||
|
|
||||||
|
<hr width="90%" size="1" noshade>
|
||||||
|
<div align="center">
|
||||||
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
||||||
|
</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 -->
|
||||||
|
|
||||||
|
|
||||||
65
about_author.html
Normal file
76
appendix-a.html
Normal file
4600
book-index.html
Normal file
89
ch01/01-01.html
Normal file
140
ch01/01-02.html
Normal file
238
ch01/01-03.html
Normal file
124
ch01/01-04.html
Normal file
192
ch01/01-05.html
Normal file
132
ch01/01-06.html
Normal file
BIN
ch01/images/01-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch01/images/01-02i.jpg
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
ch01/images/01-03i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch01/images/01-04i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch01/images/01-05i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch01/images/01-06i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
109
ch02/02-01.html
Normal file
86
ch02/02-02.html
Normal file
80
ch02/02-03.html
Normal file
BIN
ch02/images/02-01.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
ch02/images/02-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch02/images/02-02.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
ch02/images/02-02i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch02/images/02-03i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
82
ch03/03-01.html
Normal file
508
ch03/03-02.html
Normal file
|
|
@ -0,0 +1,508 @@
|
||||||
|
<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: Assume Nothing</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=03//-->
|
||||||
|
<!--PAGES=035-042//-->
|
||||||
|
<!--UNASSIGNED1//-->
|
||||||
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
||||||
|
|
||||||
|
<CENTER>
|
||||||
|
<TABLE BORDER>
|
||||||
|
<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><BR></P>
|
||||||
|
<P><B>LISTING 3.1 PZTIMER.ASM</B></P>
|
||||||
|
<!-- CODE //-->
|
||||||
|
<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>
|
||||||
|
<!-- END CODE //-->
|
||||||
|
<P><BR></P>
|
||||||
|
<CENTER>
|
||||||
|
<TABLE BORDER>
|
||||||
|
<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>
|
||||||
|
<div align="center">
|
||||||
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
||||||
|
</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 -->
|
||||||
|
|
||||||
|
|
||||||
85
ch03/03-03.html
Normal file
84
ch03/03-04.html
Normal file
145
ch03/03-05.html
Normal file
154
ch03/03-06.html
Normal file
713
ch03/03-07.html
Normal file
137
ch03/03-08.html
Normal file
181
ch03/03-09.html
Normal file
113
ch03/03-10.html
Normal file
BIN
ch03/images/03-01.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
ch03/images/03-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch03/images/03-02.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
ch03/images/03-03.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
93
ch04/04-01.html
Normal file
142
ch04/04-02.html
Normal file
136
ch04/04-03.html
Normal file
115
ch04/04-04.html
Normal file
111
ch04/04-05.html
Normal file
83
ch04/04-06.html
Normal file
110
ch04/04-07.html
Normal file
89
ch04/04-08.html
Normal file
131
ch04/04-09.html
Normal file
91
ch04/04-10.html
Normal file
BIN
ch04/images/04-01.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
ch04/images/04-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch04/images/04-02.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
ch04/images/04-02i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch04/images/04-03.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
ch04/images/04-03i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch04/images/04-04.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ch04/images/04-04i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch04/images/04-05.jpg
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
ch04/images/04-06.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
ch04/images/04-07.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
ch04/images/04-08.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
85
ch05/05-01.html
Normal file
88
ch05/05-02.html
Normal file
234
ch05/05-03.html
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
<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 LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
||||||
|
|
||||||
|
<CENTER>
|
||||||
|
<TABLE BORDER>
|
||||||
|
<TR>
|
||||||
|
<TD><A HREF="05-02.html">Previous</A></TD>
|
||||||
|
<TD><A HREF="../index.html">Table of Contents</A></TD>
|
||||||
|
<TD><A HREF="05-04.html">Next</A></TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
|
</CENTER>
|
||||||
|
<P><BR></P>
|
||||||
|
<P><B>LISTING 5.1 SEARCH.C</B></P>
|
||||||
|
<!-- CODE //-->
|
||||||
|
<PRE>
|
||||||
|
/* Program to search the file specified by the first command-line
|
||||||
|
* argument for the string specified by the second command-line
|
||||||
|
* argument. Performs the search by reading and searching blocks
|
||||||
|
* of size BLOCK_SIZE. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <alloc.h> /* alloc.h for Borland compilers,
|
||||||
|
malloc.h for Microsoft compilers */
|
||||||
|
|
||||||
|
#define BLOCK_SIZE 0x4000 /* we’ll process the file in 16K blocks */
|
||||||
|
|
||||||
|
/* Searches the specified number of sequences in the specified
|
||||||
|
buffer for matches to SearchString of SearchStringLength. Note
|
||||||
|
that the calling code should already have shortened SearchLength
|
||||||
|
if necessary to compensate for the distance from the end of the
|
||||||
|
buffer to the last possible start of a matching sequence in the
|
||||||
|
buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int SearchForString(unsigned char *Buffer, int SearchLength,
|
||||||
|
unsigned char *SearchString, int SearchStringLength)
|
||||||
|
{
|
||||||
|
unsigned char *PotentialMatch;
|
||||||
|
|
||||||
|
/* Search so long as there are potential-match locations
|
||||||
|
remaining */
|
||||||
|
while ( SearchLength ) {
|
||||||
|
/* See if the first character of SearchString can be found */
|
||||||
|
if ( (PotentialMatch =
|
||||||
|
memchr(Buffer, *SearchString, SearchLength)) == NULL ) {
|
||||||
|
break; /* No matches in this buffer */
|
||||||
|
}
|
||||||
|
/* The first character matches; see if the rest of the string
|
||||||
|
also matches */
|
||||||
|
if ( SearchStringLength == 1 ) {
|
||||||
|
return(1); /* That one matching character was the whole
|
||||||
|
search string, so we’ve got a match */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Check whether the remaining characters match */
|
||||||
|
if ( !memcmp(PotentialMatch + 1, SearchString + 1,
|
||||||
|
SearchStringLength - 1) ) {
|
||||||
|
return(1); /* We’ve got a match */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* The string doesn’t match; keep going by pointing past the
|
||||||
|
potential match location we just rejected */
|
||||||
|
SearchLength -= PotentialMatch - Buffer + 1;
|
||||||
|
Buffer = PotentialMatch + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(0); /* No match found */
|
||||||
|
}
|
||||||
|
|
||||||
|
main(int argc, char *argv[]) {
|
||||||
|
int Done; /* Indicates whether search is done */
|
||||||
|
int Handle; /* Handle of file being searched */
|
||||||
|
int WorkingLength; /* Length of current block */
|
||||||
|
int SearchStringLength; /* Length of string to search for */
|
||||||
|
int BlockSearchLength; /* Length to search in current block */
|
||||||
|
int Found; /* Indicates final search completion
|
||||||
|
status */
|
||||||
|
int NextLoadCount; /* # of bytes to read into next block,
|
||||||
|
accounting for bytes copied from the
|
||||||
|
last block */
|
||||||
|
unsigned char *WorkingBlock; /* Block storage buffer */
|
||||||
|
unsigned char *SearchString; /* Pointer to the string to search for */
|
||||||
|
unsigned char *NextLoadPtr; /* Offset at which to start loading
|
||||||
|
the next block, accounting for
|
||||||
|
bytes copied from the last block */
|
||||||
|
|
||||||
|
/* Check for the proper number of arguments */
|
||||||
|
if ( argc != 3 ) {
|
||||||
|
printf(“usage: search filename search-string\n”);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to open the file to be searched */
|
||||||
|
if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
|
||||||
|
printf(“Can’t open file: %s\n”, argv[1]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* Calculate the length of text to search for */
|
||||||
|
SearchString = argv[2];
|
||||||
|
SearchStringLength = strlen(SearchString);
|
||||||
|
/* Try to get memory in which to buffer the data */
|
||||||
|
if ( (WorkingBlock = malloc(BLOCK_SIZE)) == NULL ) {
|
||||||
|
printf(“Can’t get enough memory\n”);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the first block at the start of the buffer, and try to
|
||||||
|
fill the entire buffer */
|
||||||
|
NextLoadPtr = WorkingBlock;
|
||||||
|
NextLoadCount = BLOCK_SIZE;
|
||||||
|
Done = 0; /* Not done with search yet */
|
||||||
|
Found = 0; /* Assume we won’t find a match */
|
||||||
|
/* Search the file in BLOCK_SIZE chunks */
|
||||||
|
do {
|
||||||
|
/* Read in however many bytes are needed to fill out the block
|
||||||
|
(accounting for bytes copied over from the last block), or
|
||||||
|
the rest of the bytes in the file, whichever is less */
|
||||||
|
if ( (WorkingLength = read(Handle, NextLoadPtr,
|
||||||
|
NextLoadCount)) == -1 ) {
|
||||||
|
printf(“Error reading file %s\n”, argv[1]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* If we didn’t read all the bytes we requested, we’re done
|
||||||
|
after this block, whether we find a match or not */
|
||||||
|
if ( WorkingLength != NextLoadCount ) {
|
||||||
|
Done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Account for any bytes we copied from the end of the last
|
||||||
|
block in the total length of this block */
|
||||||
|
WorkingLength += NextLoadPtr - WorkingBlock;
|
||||||
|
/* Calculate the number of bytes in this block that could
|
||||||
|
possibly be the start of a matching sequence that lies
|
||||||
|
entirely in this block (sequences that run off the end of
|
||||||
|
the block will be transferred to the next block and found
|
||||||
|
when that block is searched)
|
||||||
|
*/
|
||||||
|
if ( (BlockSearchLength =
|
||||||
|
WorkingLength - SearchStringLength + 1) <= 0 ) {
|
||||||
|
Done = 1; /* Too few characters in this block for
|
||||||
|
there to be any possible matches, so this
|
||||||
|
is the final block and we’re done without
|
||||||
|
finding a match
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Search this block */
|
||||||
|
if ( SearchForString(WorkingBlock, BlockSearchLength,
|
||||||
|
SearchString, SearchStringLength) ) {
|
||||||
|
Found = 1; /* We’ve found a match */
|
||||||
|
Done = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Copy any bytes from the end of the block that start
|
||||||
|
potentially-matching sequences that would run off
|
||||||
|
the end of the block over to the next block */
|
||||||
|
if ( SearchStringLength > 1 ) {
|
||||||
|
memcpy(WorkingBlock,
|
||||||
|
WorkingBlock+BLOCK_SIZE - SearchStringLength + 1,
|
||||||
|
SearchStringLength - 1);
|
||||||
|
}
|
||||||
|
/* Set up to load the next bytes from the file after the
|
||||||
|
bytes copied from the end of the current block */
|
||||||
|
NextLoadPtr = WorkingBlock + SearchStringLength - 1;
|
||||||
|
NextLoadCount = BLOCK_SIZE - SearchStringLength + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while ( !Done );
|
||||||
|
|
||||||
|
/* Report the results */
|
||||||
|
if ( Found ) {
|
||||||
|
printf(“String found\n”);
|
||||||
|
} else {
|
||||||
|
printf(“String not found\n”);
|
||||||
|
}
|
||||||
|
exit(Found); /* Return the found/not found status as the
|
||||||
|
DOS errorlevel */
|
||||||
|
}
|
||||||
|
</PRE>
|
||||||
|
<!-- END CODE //-->
|
||||||
|
<P><BR></P>
|
||||||
|
<CENTER>
|
||||||
|
<TABLE BORDER>
|
||||||
|
<TR>
|
||||||
|
<TD><A HREF="05-02.html">Previous</A></TD>
|
||||||
|
<TD><A HREF="../index.html">Table of Contents</A></TD>
|
||||||
|
<TD><A HREF="05-04.html">Next</A></TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
|
</CENTER>
|
||||||
|
|
||||||
|
<hr width="90%" size="1" noshade>
|
||||||
|
<div align="center">
|
||||||
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
||||||
|
</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 -->
|
||||||
|
|
||||||
|
|
||||||
76
ch05/05-04.html
Normal file
81
ch05/05-05.html
Normal file
BIN
ch05/images/05-01.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
ch05/images/05-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch05/images/05-02.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
ch05/images/05-02i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch05/images/05-03i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch05/images/05-04i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
102
ch06/06-01.html
Normal file
162
ch06/06-02.html
Normal file
BIN
ch06/images/06-01.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
ch06/images/06-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch06/images/06-02.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
89
ch07/07-01.html
Normal file
91
ch07/07-02.html
Normal file
160
ch07/07-03.html
Normal file
200
ch07/07-04.html
Normal file
168
ch07/07-05.html
Normal file
BIN
ch07/images/07-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-02i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-03i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-04i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-05i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-06i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-07i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch07/images/07-08i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
86
ch08/08-01.html
Normal file
115
ch08/08-02.html
Normal file
227
ch08/08-03.html
Normal file
273
ch08/08-04.html
Normal file
220
ch08/08-05.html
Normal file
BIN
ch08/images/08-01.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
ch08/images/08-01i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch08/images/08-02.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
ch08/images/08-02i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
ch08/images/08-03.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
ch08/images/08-03i.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |