home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
pcmag
/
vol6n20.arc
/
FASTWR.PAS
next >
Wrap
Pascal/Delphi Source File
|
1987-08-08
|
24KB
|
491 lines
(*
FASTWR.PAS 2.1
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 first 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.)
Version 2.1 fixes small bugs in FastWrite, FastWriteV, and FastWriteNA
that appeared in version 2.0 only. (Misplaced CLD instructions -- they
came before the first LODSB instruction in each case.)
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, either alone or as part of a package.
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
"NoWait" 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]}
/$FC { CLD ;Set direction to forward}
/$AC { LODSB ;AX = Length(St); DS:SI -> St[1]}
/$91 { XCHG AX,CX ;CX = Length; AL = Wait}
/$E3/$29 { JCXZ Exit ;If string empty, Exit}
/$8A/$A6/>Attr { MOV AH,[BP+>Attr] ;AH = Attribute}
/$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]}
/$FC { CLD}
/$AC { LODSB}
/$91 { XCHG AX,CX}
/$E3/$28 { JCXZ Exit}
/$8A/$66/<Attr { MOV AH,[BP+<Attr]}
/$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]}
/$FC { CLD}
/$AC { LODSB}
/$91 { XCHG AX,CX}
/$E3/$26 { JCXZ Exit}
/$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 -----------}
{ Tacky demonstration program. (Reminds Kim of ComputerLand stores.) 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.
(**)