home *** CD-ROM | disk | FTP | other *** search
- (*
- FASTWR.PAS 2.0
-
- FASTWR.PAS contains six fast, snow-and-flicker-free routines for writing
- directly to (or reading from) the video memory of IBM PC/XT/AT's and close
- compatibles.
-
- CHANGES TO FASTWR.PAS
-
- New routines:
- -------------
- Attribute : Given Foreground and BackGround colors, Attribute will return
- a properly coded video attribute, with the blink bit masked out.
- EGAInstalled : Returns a Boolean result indicating whether an EGA is
- installed or not.
- ChangeAttribute : Changes the video attribute in the specified region of
- the screen, leaving the characters untouched. I use this in moving bar
- menus, myself.
- FastWriteNA : By popular request, a version of FastWrite that uses the
- existing screen attributes, so that you don't have to specify one.
- NA stands for No Attribute.
- MoveToScreen, MoveFromScreen : These are slightly optimized versions of
- Bela Lubkin's routines of the same name. They differ in several minor
- respects, most notably (a) the Length parameter asks for the number of
- WORDS (integers) to move rather than BYTES and (b) they are a bit
- faster with snow prevention off. If you haven't grabbed it already,
- be sure to get Bela's WINDOW.PAS, which has several handy routines for
- handling windows. My versions of MoveToScreen and MoveFromScreen can
- be plugged in in place of his, but be sure to eliminate the "Shl 1"
- instructions that go with the Length parameters in all his calls to
- these routines.
-
- Changes to old routines (FastWrite, FastWriteV, GetVideoMode):
- --------------------------------------------------------------
- The first two have been further optimized for speed. FastWriteV is up to
- 40% faster than the previous version; FastWrite is faster too, but
- proportionately the difference isn't so great. The most significant change
- is in the algorithm used to determine when to write to the screen on a
- CGA. This improvement was suggested to me by Bela. A slight optimization
- in the calculation of offsets from BaseOfScreen (based on Row and Column
- coordinates) is owed to Jim LeMay. The other improvements are minor, but
- together they all make a difference.
-
- If you don't like the order of the parameters, change them. (Please don't
- send me messages telling me that your preferred order is right and mine
- is wrong.) In the previous version of FASTWR the paramaters were harder
- to change/rearrange; now there should be no problems.
-
- GetVideoMode now uses inline code to get the current video mode from the
- BIOS. It also sets WaitForRetrace to False if an EGA is being used.
-
- General notes:
- --------------
- FASTWR.PAS is designed to make it easily used as an $Include file. The
- demo program below is commented out -- delete the indicated line to run
- it.
-
- The Inline code in EGAInstalled and GetVideoMode isn't there for speed, but
- rather to avoid declaring any more global types and variables than were
- necessary.
-
- In a vain attempt to keep this file small, I have cut out many of the
- comments within the inline code. FastWrite is fully commented, though,
- and anything that isn't commented in the other routines is duplicating
- code in FastWrite. (Or, in the case of MoveToScreen, the code in
- MoveFromScreen.)
-
- InLine code was assembled with Dave Baldwin's INLINE.COM, and uses its
- notation.
-
- Please read all warnings that appear in the comments! In particular,
- read everything about GetVideoMode, WaitForRetrace, and BaseOfScreen.
-
- Copyright (c) 1986 by Brian Foley
- These routines may be freely used and distributed, for both private
- and commercial use, so long as the routines themselves are not sold for
- profit.
-
- Brian Foley [76317,3247]
- TurboPower Software
- *)
-
- {---------------------- begin FASTWR.INC -----------}
-
- TYPE
- String80 = String[ 80 ];
- VAR
- WaitForRetrace : Boolean; { If False, FastWrite et al. will use the faster
- "Mono" routine, regardless of display type. }
- BaseOfScreen : Integer; { Base address of screen memory. Note: Making
- this a typed constant will screw things up!
- FastWrite expects this to be a global variable
- located in the data segment. The same applies
- to WaitForRetrace. }
-
- FUNCTION Attribute(Foreground, Background : Byte) : Byte;
- {-Translates foreground and background colors into video attributes.
- "And 127" masks out the blink bit. Add 128 to the result to set it.}
- BEGIN
- Attribute := ((Background Shl 4) + Foreground) And 127;
- END;
-
- FUNCTION EgaInstalled : Boolean;
- {-Test for presence of the EGA. I have little idea how this works, but
- it does.}
- BEGIN
- Inline(
- $B8/$00/$12 { MOV AX,$1200}
- /$BB/$10/$00 { MOV BX,$10}
- /$B9/$FF/$FF { MOV CX,$FFFF}
- /$CD/$10 { INT $10}
- /$31/$C0 { XOR AX,AX}
- /$81/$F9/$FF/$FF { CMP CX,$FFFF}
- /$74/$01 { JE DONE}
- /$40 { INC AX}
- /$88/$46/$04 {DONE: MOV [BP+$04],AL}
- );
- END;
-
- PROCEDURE GetVideoMode;
- {-Video mode of 7 indicates mono display; all other modes are for color
- displays. This routine MUST be called before any of the screen writing
- routines are used!}
- VAR
- Mode : Integer;
- BEGIN
- Inline(
- $B4/$0F {MOV AH,$F}
- /$CD/$10 {INT $10}
- /$30/$E4 {XOR AH,AH}
- /$89/$46/<Mode {MOV [BP+<Mode],AX}
- );
- IF Mode = 7 THEN BaseOfScreen := $B000 { Mono }
- ELSE BaseOfScreen := $B800; { Color }
- WaitForRetrace := (BaseOfScreen = $B800) And Not EgaInstalled;
- { If WaitForRetrace is True, you may want to allow the user to decide
- whether to forego snow prevention in favor of faster screen updates.
- *VERY IMPORTANT* WaitForRetrace MUST be false if BaseOfScreen = $B000. }
- END;
-
- PROCEDURE FastWrite( St : String80; Row, Col, Attr : Byte );
- {-Write St directly to video memory, without snow.}
- BEGIN
- Inline(
- $1E { PUSH DS ;Save DS}
- /$31/$C0 { XOR AX,AX ;AX = 0}
- /$88/$C1 { MOV CL,AL ;CL = 0}
- /$8A/$AE/>Row { MOV CH,[BP+>Row] ;CX = Row * 256}
- /$FE/$CD { DEC CH ;Row to 0..24 range}
- /$D1/$E9 { SHR CX,1 ;CX = Row * 128}
- /$89/$CF { MOV DI,CX ;Store in DI}
- /$D1/$EF { SHR DI,1 ;DI = Row * 64}
- /$D1/$EF { SHR DI,1 ;DI = Row * 32}
- /$01/$CF { ADD DI,CX ;DI = (Row * 160)}
- /$8B/$8E/>Col { MOV CX,[BP+>Col] ;CX = Column}
- /$49 { DEC CX ;Col to 0..79 range}
- /$D1/$E1 { SHL CX,1 ;Account for attribute bytes}
- /$01/$CF { ADD DI,CX ;DI = (Row * 160) + (Col * 2)}
- /$8E/$06/>BaseOfScreen { MOV ES,[>BaseOfScreen] ;ES:DI points to Base:Row,Col}
- /$8A/$0E/>WaitForRetrace{ MOV CL,[>WaitForRetrace] ;Grab this before changing DS}
- /$8C/$D2 { MOV DX,SS ;Move SS...}
- /$8E/$DA { MOV DS,DX ; into DS}
- /$8D/$B6/>St { LEA SI,[BP+>St] ;DS:SI points to St[0]}
- /$AC { LODSB ;AX = Length(St); DS:SI -> St[1]}
- /$91 { XCHG AX,CX ;CX = Length; AL = Wait}
- /$E3/$2A { JCXZ Exit ;If string empty, Exit}
- /$8A/$A6/>Attr { MOV AH,[BP+>Attr] ;AH = Attribute}
- /$FC { CLD ;Set direction to forward}
- /$D0/$D8 { RCR AL,1 ;If WaitForRetrace is False...}
- /$73/$1D { JNC NoWait ; use NoWait routine}
- /$BA/$DA/$03 { MOV DX,$03DA ;Point DX to CGA status port}
- /$AC {Next: LODSB ;Load next character into AL}
- { ; AH already has Attr}
- /$89/$C3 { MOV BX,AX ;Store video word in BX}
- /$FA { CLI ;No interrupts now}
- /$EC {WaitNoH: IN AL,DX ;Get 6845 status}
- /$A8/$08 { TEST AL,8 ;Check for vertical retrace}
- /$75/$09 { JNZ Store ; In progress? go}
- /$D0/$D8 { RCR AL,1 ;Else, wait for end of}
- /$72/$F7 { JC WaitNoH ; horizontal retrace}
- /$EC {WaitH: IN AL,DX ;Get 6845 status again}
- /$D0/$D8 { RCR AL,1 ;Wait for horizontal}
- /$73/$FB { JNC WaitH ; retrace}
- /$89/$D8 {Store: MOV AX,BX ;Move word back to AX...}
- /$AB { STOSW ; and then to screen}
- /$FB { STI ;Allow interrupts}
- /$E2/$E8 { LOOP Next ;Get next character}
- /$EB/$04 { JMP SHORT Exit ;Done}
- /$AC {NoWait: LODSB ;Load next character into AL}
- { ; AH already has Attr}
- /$AB { STOSW ;Move video word into place}
- /$E2/$FC { LOOP NoWait ;Get next character}
- /$1F {Exit: POP DS ;Restore DS}
- );
- END;
-
-
- PROCEDURE FastWriteV( VAR St; Row, Col, Attr : Byte );
- {-Works with string variables ONLY. (I made St an untyped parameter
- only to make this easier to use when type checking is on.) This is
- just FastWrite optimized for use with string Variables, for times
- when speed really matters.}
- BEGIN
- Inline(
- $1E { PUSH DS}
- /$31/$C0 { XOR AX,AX}
- /$88/$C1 { MOV CL,AL}
- /$8A/$6E/<Row { MOV CH,[BP+<Row]}
- /$FE/$CD { DEC CH}
- /$D1/$E9 { SHR CX,1}
- /$89/$CF { MOV DI,CX}
- /$D1/$EF { SHR DI,1}
- /$D1/$EF { SHR DI,1}
- /$01/$CF { ADD DI,CX}
- /$8B/$4E/<Col { MOV CX,[BP+<Col]}
- /$49 { DEC CX}
- /$D1/$E1 { SHL CX,1}
- /$01/$CF { ADD DI,CX}
- /$8E/$06/>BaseOfScreen { MOV ES,[>BaseOfScreen]}
- /$8A/$0E/>WaitForRetrace{ MOV CL,[>WaitForRetrace]}
- /$C5/$76/<St { LDS SI,[BP+<St] ;DS:SI points to St[0]}
- /$AC { LODSB}
- /$91 { XCHG AX,CX}
- /$E3/$29 { JCXZ Exit}
- /$8A/$66/<Attr { MOV AH,[BP+<Attr]}
- /$FC { CLD}
- /$D0/$D8 { RCR AL,1}
- /$73/$1D { JNC NoWait}
- /$BA/$DA/$03 { MOV DX,$03DA}
- /$AC {Next: LODSB}
- /$89/$C3 { MOV BX,AX}
- /$FA { CLI}
- /$EC {WaitNoH: IN AL,DX}
- /$A8/$08 { TEST AL,8}
- /$75/$09 { JNZ Store}
- /$D0/$D8 { RCR AL,1}
- /$72/$F7 { JC WaitNoH}
- /$EC {WaitH: IN AL,DX}
- /$D0/$D8 { RCR AL,1}
- /$73/$FB { JNC WaitH}
- /$89/$D8 {Store: MOV AX,BX}
- /$AB { STOSW}
- /$FB { STI}
- /$E2/$E8 { LOOP Next}
- /$EB/$04 { JMP SHORT Exit}
- /$AC {NoWait: LODSB}
- /$AB { STOSW}
- /$E2/$FC { LOOP NoWait}
- /$1F {Exit: POP DS}
- );
- END;
-
- PROCEDURE FastWriteNA( St : String80; Row, Col : Byte );
- {-Same as FastWrite, but no attribute byte paramater. String appears
- in whatever attribute(s) was/were there to begin with.}
- BEGIN
- Inline(
- $8C/$DB { MOV BX,DS ;Save DS in BX}
- /$31/$C0 { XOR AX,AX}
- /$88/$C1 { MOV CL,AL}
- /$8A/$AE/>Row { MOV CH,[BP+>Row]}
- /$FE/$CD { DEC CH}
- /$D1/$E9 { SHR CX,1}
- /$89/$CF { MOV DI,CX}
- /$D1/$EF { SHR DI,1}
- /$D1/$EF { SHR DI,1}
- /$01/$CF { ADD DI,CX}
- /$8B/$8E/>Col { MOV CX,[BP+>Col]}
- /$49 { DEC CX}
- /$D1/$E1 { SHL CX,1}
- /$01/$CF { ADD DI,CX}
- /$8E/$06/>BaseOfScreen { MOV ES,[>BaseOfScreen]}
- /$8A/$0E/>WaitForRetrace{ MOV CL,[>WaitForRetrace]}
- /$8C/$D2 { MOV DX,SS}
- /$8E/$DA { MOV DS,DX}
- /$8D/$B6/>St { LEA SI,[BP+>St]}
- /$AC { LODSB}
- /$91 { XCHG AX,CX}
- /$E3/$27 { JCXZ Exit}
- /$FC { CLD}
- /$D0/$D8 { RCR AL,1}
- /$73/$1E { JNC NoWait}
- /$BA/$DA/$03 { MOV DX,$03DA}
- /$AC {Next: LODSB}
- /$88/$C4 { MOV AH,AL ;Store char in AH}
- /$FA { CLI}
- /$EC {WaitNoH: IN AL,DX}
- /$A8/$08 { TEST AL,8}
- /$75/$09 { JNZ Store}
- /$D0/$D8 { RCR AL,1}
- /$72/$F7 { JC WaitNoH}
- /$EC {WaitH: IN AL,DX}
- /$D0/$D8 { RCR AL,1}
- /$73/$FB { JNC WaitH}
- /$88/$E0 {Store: MOV AL,AH ;Move char back to AL...}
- /$AA { STOSB ; and then to screen}
- /$FB { STI}
- /$47 { INC DI}
- /$E2/$E7 { LOOP Next}
- /$EB/$04 { JMP SHORT Exit}
- /$A4 {NoWait: MOVSB ;Move character to screen}
- /$47 { INC DI ;Skip attribute bytes}
- /$E2/$FC { LOOP NoWait}
- /$8E/$DB {Exit: MOV DS,BX ;Restore DS from BX}
- );
- END;
-
-
- PROCEDURE ChangeAttribute( Number : Integer; Row, Col, Attr : Byte );
- {-Number is the number of attribute bytes to change. Row and Col
- indicate where to start changing video attributes. Attr is the
- video attribute to use.}
- BEGIN
- Inline(
- $8B/$4E/<Number { MOV CX,[BP+<Number] ;CX = Number to change}
- /$E3/$49 { JCXZ Exit ;If zero, Exit}
- /$31/$C0 { XOR AX,AX}
- /$8A/$66/<Row { MOV AH,[BP+<Row] ;AX = Row * 256}
- /$FE/$CC { DEC AH ;Row to 0..24 range}
- /$D1/$E8 { SHR AX,1 ;AX = Row * 128}
- /$89/$C7 { MOV DI,AX ;Store in DI}
- /$D1/$EF { SHR DI,1 ;DI = Row * 64}
- /$D1/$EF { SHR DI,1 ;DI = Row * 32}
- /$01/$C7 { ADD DI,AX ;DI = (Row * 160)}
- /$8B/$46/<Col { MOV AX,[BP+<Col] ;AX = Column}
- /$D1/$E0 { SHL AX,1 ;Account for attribute bytes}
- /$01/$C7 { ADD DI,AX ;DI = (Row*160) + ((Col+1)*2)}
- /$4F { DEC DI ;Adjust Col (-2), skip char (+1)}
- /$8E/$06/>BaseOfScreen { MOV ES,[>BaseOfScreen]}
- /$8A/$46/<Attr { MOV AL,[BP+<Attr] ;AL = Attribute}
- /$FC { CLD}
- /$80/$3E/>WaitForRetrace/$01{ CMP Byte [>WaitForRetrace],1 ;Get wait state}
- /$75/$1D { JNE NoWait ;If WaitForRetrace is False}
- { ; use NoWait routine}
- /$88/$C4 { MOV AH,AL ;Store attribute in AH}
- /$BA/$DA/$03 { MOV DX,$03DA}
- /$FA {Next: CLI}
- /$EC {WaitNoH: IN AL,DX}
- /$A8/$08 { TEST AL,8}
- /$75/$09 { JNZ Store}
- /$D0/$D8 { RCR AL,1}
- /$72/$F7 { JC WaitNoH}
- /$EC {WaitH: IN AL,DX}
- /$D0/$D8 { RCR AL,1}
- /$73/$FB { JNC WaitH}
- /$88/$E0 {Store: MOV AL,AH ;Move attr back to AL...}
- /$AA { STOSB ; and then to screen}
- /$FB { STI}
- /$47 { INC DI ;Skip characters}
- /$E2/$EA { LOOP Next}
- /$EB/$04 { JMP SHORT Exit}
- /$AA {NoWait: STOSB ;Change the attribute}
- /$47 { INC DI ;Skip characters}
- /$E2/$FC { LOOP NoWait ;Get next character}
- {Exit: ;Next instruction}
- );
- END;
-
- PROCEDURE MoveFromScreen(VAR Source, Dest; Length : Integer);
- {-Length = number of WORDS to move from video memory (source) to
- dest.}
- BEGIN
- Inline(
- $8C/$DB { MOV BX,DS ;Save DS in BX}
- /$A0/>WaitForRetrace { MOV AL,[>WaitForRetrace] ;Grab before changing DS}
- /$C4/$7E/<Dest { LES DI,[BP+<Dest] ;ES:DI points to Dest}
- /$C5/$76/<Source { LDS SI,[BP+<Source] ;DS:SI points to Source}
- /$8B/$4E/<Length { MOV CX,[BP+<Length] ;CX = Length}
- /$FC { CLD}
- /$D0/$D8 { RCR AL,1}
- /$73/$19 { JNC NoWait}
- /$BA/$DA/$03 { MOV DX,$03DA}
- /$FA {Next: CLI}
- /$EC {WaitNoH: IN AL,DX}
- /$A8/$08 { TEST AL,8}
- /$75/$09 { JNZ Store}
- /$D0/$D8 { RCR AL,1}
- /$72/$F7 { JC WaitNoH}
- /$EC {WaitH: IN AL,DX}
- /$D0/$D8 { RCR AL,1}
- /$73/$FB { JNC WaitH}
- /$AD {Store: LODSW ;Load next video word into AX}
- /$FB { STI ;Allow interrupts}
- /$AB { STOSW ;Store video word in Dest}
- /$E2/$EC { LOOP Next}
- /$EB/$02 { JMP SHORT Exit}
- /$F2/$A5 {NoWait: REP MOVSW ;That's it!}
- /$8E/$DB {Exit: MOV DS,BX ;Restore DS}
- );
- END;
-
- PROCEDURE MoveToScreen(VAR Source, Dest; Length : Integer);
- {-Length = number of WORDS to move to video memory (dest) from source}
- BEGIN
- Inline(
- $1E { PUSH DS ;Save DS}
- /$A0/>WaitForRetrace { MOV AL,[>WaitForRetrace]}
- /$C4/$7E/<Dest { LES DI,[BP+<Dest]}
- /$C5/$76/<Source { LDS SI,[BP+<Source]}
- /$8B/$4E/<Length { MOV CX,[BP+<Length]}
- /$FC { CLD}
- /$D0/$D8 { RCR AL,1}
- /$73/$1D { JNC NoWait}
- /$BA/$DA/$03 { MOV DX,$03DA}
- /$AD {Next: LODSW ;Load next video word into AX}
- /$89/$C3 { MOV BX,AX ;Store video word in BX}
- /$FA { CLI}
- /$EC {WaitNoH: IN AL,DX}
- /$A8/$08 { TEST AL,8}
- /$75/$09 { JNZ Store}
- /$D0/$D8 { RCR AL,1}
- /$72/$F7 { JC WaitNoH}
- /$EC {WaitH: IN AL,DX}
- /$D0/$D8 { RCR AL,1}
- /$73/$FB { JNC WaitH}
- /$89/$D8 {Store: MOV AX,BX ;Move word back to AX...}
- /$AB { STOSW ; and then to screen}
- /$FB { STI ;Allow interrupts}
- /$E2/$E8 { LOOP Next}
- /$EB/$02 { JMP SHORT Exit}
- /$F2/$A5 {NoWait: REP MOVSW}
- /$1F {Exit: POP DS ;Restore DS}
- );
- END;
- {---------------------- end of FASTWR.INC -----------}
-
- { Demonstration program. Delete next line to enable. }
- (*
- VAR
- Fore, Back,
- I, Attr : Byte;
- ScreenBuffer : Array[1..2000] of Integer;
- N : Integer;
- C : Char;
- CONST
- Bullet : String80 = '* FASTER THAN A SPEEDING BULLET! *';
- BEGIN
- GetVideoMode;
- IF WaitForRetrace THEN BEGIN
- Write('Does your screen generate snow? ');
- Read(KBD, C);
- WaitForRetrace := (UpCase(C) = 'Y');
- END;
- IF BaseOfScreen = $B000 THEN BEGIN
- Fore := White;
- Back := Black;
- END
- ELSE BEGIN
- Fore := White; { make these whatever you want }
- Back := Magenta;
- END;
- Attr := Attribute( Fore, Back );
- TextColor( Fore );
- TextBackground( Back );
- ClrScr;
- FastWrite( 'FastWriting is still even...', 6, 26, Attr );
- Delay( 2000 );
- FastWrite( '**********************************', 9, 23, Attr );
- FOR I := 10 TO 20 DO
- FastWriteV( Bullet, I, 23, Attr );
- FastWrite( '**********************************', 21, 23, Attr );
- { now save the entire screen }
- MoveFromScreen( Mem[BaseOfScreen:0], ScreenBuffer, 2000 );
- { now let's play with the saved screen, setting blink bits }
- FOR N := 1 TO 2000 DO
- ScreenBuffer[N] := ScreenBuffer[N] Xor $8000;
- Delay( 2000 );
- FastWrite( 'Changing attribute bytes....', 6, 26, Attr );
- Delay( 2000 );
- Attr := Attribute( Back, Fore );
- { now some reverse video }
- ChangeAttribute( 2000, 1, 1, Attr );
- FOR I := 10 TO 20 DO
- FastWrite( 'IS PRETTY DARNED FAST NOW TOO!', I, 25, Attr );
- Delay( 2000 );
- FastWrite( 'But who says you need to....', 6, 26, Attr );
- Delay( 2000 );
- { now write fast without messing with attributes }
- FOR I := 10 TO 20 DO
- FastWriteNA( 'MESS WITH THE ATTRIBUTE BYTES?', I, 25 );
- Delay( 2000 );
- { now restore the blinking version of the screen we saved }
- MoveToScreen( ScreenBuffer, Mem[BaseOfScreen:0], 2000 );
- END.
- (**)