home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / pcmag / vol6n20.zip / FASTWR.PAS next >
Pascal/Delphi Source File  |  1987-08-08  |  24KB  |  491 lines

  1. (*
  2.                         FASTWR.PAS 2.1
  3.  
  4.  FASTWR.PAS contains six fast, snow-and-flicker-free routines for writing
  5.  directly to (or reading from) the video memory of IBM PC/XT/AT's and close
  6.  compatibles.
  7.  
  8.                      CHANGES TO FASTWR.PAS
  9.  
  10.  New routines:
  11.  -------------
  12.  Attribute : Given Foreground and BackGround colors, Attribute will return
  13.    a properly coded video attribute, with the blink bit masked out.
  14.  EGAInstalled : Returns a Boolean result indicating whether an EGA is
  15.    installed or not.
  16.  ChangeAttribute : Changes the video attribute in the specified region of
  17.    the screen, leaving the characters untouched. I use this in moving bar
  18.    menus, myself.
  19.  FastWriteNA : By popular request, a version of FastWrite that uses the
  20.    existing screen attributes, so that you don't have to specify one.
  21.    NA stands for No Attribute.
  22.  MoveToScreen, MoveFromScreen : These are slightly optimized versions of
  23.    Bela Lubkin's routines of the same name. They differ in several minor
  24.    respects, most notably (a) the Length parameter asks for the number of
  25.    WORDS (integers) to move rather than BYTES and (b) they are a bit
  26.    faster with snow prevention off. If you haven't grabbed it already,
  27.    be sure to get Bela's WINDOW.PAS, which has several handy routines for
  28.    handling windows. My versions of MoveToScreen and MoveFromScreen can
  29.    be plugged in in place of his, but be sure to eliminate the "Shl 1"
  30.    instructions that go with the Length parameters in all his calls to
  31.    these routines.
  32.  
  33.   Changes to old routines (FastWrite, FastWriteV, GetVideoMode):
  34.   --------------------------------------------------------------
  35.   The first two have been further optimized for speed. FastWriteV is up to
  36.   40% faster than the previous version; FastWrite is faster too, but
  37.   proportionately the difference isn't so great. The most significant change
  38.   is in the algorithm used to determine when to write to the screen on a
  39.   CGA. This improvement was suggested to me by Bela. A slight optimization
  40.   in the calculation of offsets from BaseOfScreen (based on Row and Column
  41.   coordinates) is owed to Jim LeMay. The other improvements are minor, but
  42.   together they all make a difference.
  43.  
  44.   If you don't like the order of the parameters, change them. (Please don't
  45.   send me messages telling me that your preferred order is right and mine
  46.   is wrong.) In the first version of FASTWR the paramaters were harder
  47.   to change/rearrange; now there should be no problems.
  48.  
  49.   GetVideoMode now uses inline code to get the current video mode from the
  50.   BIOS. It also sets WaitForRetrace to False if an EGA is being used.
  51.  
  52.   General notes:
  53.   --------------
  54.   FASTWR.PAS is designed to make it easily used as an $Include file.  The
  55.   demo program below is commented out -- delete the indicated line to run
  56.   it.
  57.  
  58.   The Inline code in EGAInstalled and GetVideoMode isn't there for speed, but
  59.   rather to avoid declaring any more global types and variables than were
  60.   necessary.
  61.  
  62.   In a vain attempt to keep this file small, I have cut out many of the
  63.   comments within the inline code. FastWrite is fully commented, though,
  64.   and anything that isn't commented in the other routines is duplicating
  65.   code in FastWrite. (Or, in the case of MoveToScreen, the code in
  66.   MoveFromScreen.)
  67.  
  68.   Version 2.1 fixes small bugs in FastWrite, FastWriteV, and FastWriteNA
  69.   that appeared in version 2.0 only. (Misplaced CLD instructions -- they
  70.   came before the first LODSB instruction in each case.)
  71.  
  72.   InLine code was assembled with Dave Baldwin's INLINE.COM, and uses its
  73.   notation.
  74.  
  75.   Please read all warnings that appear in the comments! In particular,
  76.   read everything about GetVideoMode, WaitForRetrace, and BaseOfScreen.
  77.  
  78.   Copyright (c) 1986 by Brian Foley
  79.   These routines may be freely used and distributed, for both private
  80.   and commercial use, so long as the routines themselves are not sold for
  81.   profit, either alone or as part of a package.
  82.  
  83.   Brian Foley [76317,3247]
  84.   TurboPower Software
  85. *)
  86.  
  87. {---------------------- begin FASTWR.INC -----------}
  88.  
  89. TYPE
  90.    String80 = String[ 80 ];
  91. VAR
  92.    WaitForRetrace : Boolean; { If False, FastWrite et al. will use the faster
  93.                                "NoWait" routine, regardless of display type. }
  94.    BaseOfScreen   : Integer; { Base address of screen memory.  Note: Making
  95.                                this a typed constant will screw things up!
  96.                                FastWrite expects this to be a global variable
  97.                                located in the data segment. The same applies
  98.                                to WaitForRetrace. }
  99.  
  100. FUNCTION Attribute(Foreground, Background : Byte) : Byte;
  101.   {-Translates foreground and background colors into video attributes.
  102.     "And 127" masks out the blink bit. Add 128 to the result to set it.}
  103. BEGIN
  104.    Attribute := ((Background Shl 4) + Foreground) And 127;
  105. END;
  106.  
  107. FUNCTION EgaInstalled : Boolean;
  108.   {-Test for presence of the EGA. I have little idea how this works, but
  109.     it does.}
  110. BEGIN
  111. Inline(
  112.   $B8/$00/$12      {      MOV AX,$1200}
  113.   /$BB/$10/$00     {      MOV BX,$10}
  114.   /$B9/$FF/$FF     {      MOV CX,$FFFF}
  115.   /$CD/$10         {      INT $10}
  116.   /$31/$C0         {      XOR AX,AX}
  117.   /$81/$F9/$FF/$FF {      CMP CX,$FFFF}
  118.   /$74/$01         {      JE DONE}
  119.   /$40             {      INC AX}
  120.   /$88/$46/$04     {DONE: MOV [BP+$04],AL}
  121. );
  122. END;
  123.  
  124. PROCEDURE GetVideoMode;
  125.   {-Video mode of 7 indicates mono display; all other modes are for color
  126.     displays. This routine MUST be called before any of the screen writing
  127.     routines are used!}
  128. VAR
  129.      Mode : Integer;
  130. BEGIN
  131.      Inline(
  132.        $B4/$0F        {MOV AH,$F}
  133.        /$CD/$10       {INT $10}
  134.        /$30/$E4       {XOR AH,AH}
  135.        /$89/$46/<Mode {MOV [BP+<Mode],AX}
  136.      );
  137.      IF Mode = 7 THEN BaseOfScreen := $B000  { Mono }
  138.                  ELSE BaseOfScreen := $B800; { Color }
  139.      WaitForRetrace := (BaseOfScreen = $B800) And Not EgaInstalled;
  140.      { If WaitForRetrace is True, you may want to allow the user to decide
  141.        whether to forego snow prevention in favor of faster screen updates.
  142.        *VERY IMPORTANT*  WaitForRetrace MUST be false if BaseOfScreen = $B000. }
  143. END;
  144.  
  145. PROCEDURE FastWrite( St : String80; Row, Col, Attr : Byte );
  146.   {-Write St directly to video memory, without snow.}
  147. BEGIN
  148. Inline(
  149.   $1E                    {         PUSH DS                  ;Save DS}
  150.   /$31/$C0               {         XOR AX,AX                ;AX = 0}
  151.   /$88/$C1               {         MOV CL,AL                ;CL = 0}
  152.   /$8A/$AE/>Row          {         MOV CH,[BP+>Row]         ;CX = Row * 256}
  153.   /$FE/$CD               {         DEC CH                   ;Row to 0..24 range}
  154.   /$D1/$E9               {         SHR CX,1                 ;CX = Row * 128}
  155.   /$89/$CF               {         MOV DI,CX                ;Store in DI}
  156.   /$D1/$EF               {         SHR DI,1                 ;DI = Row * 64}
  157.   /$D1/$EF               {         SHR DI,1                 ;DI = Row * 32}
  158.   /$01/$CF               {         ADD DI,CX                ;DI = (Row * 160)}
  159.   /$8B/$8E/>Col          {         MOV CX,[BP+>Col]         ;CX = Column}
  160.   /$49                   {         DEC CX                   ;Col to 0..79 range}
  161.   /$D1/$E1               {         SHL CX,1                 ;Account for attribute bytes}
  162.   /$01/$CF               {         ADD DI,CX                ;DI = (Row * 160) + (Col * 2)}
  163.   /$8E/$06/>BaseOfScreen {         MOV ES,[>BaseOfScreen]   ;ES:DI points to Base:Row,Col}
  164.   /$8A/$0E/>WaitForRetrace{        MOV CL,[>WaitForRetrace] ;Grab this before changing DS}
  165.   /$8C/$D2               {         MOV DX,SS                ;Move SS...}
  166.   /$8E/$DA               {         MOV DS,DX                ; into DS}
  167.   /$8D/$B6/>St           {         LEA SI,[BP+>St]          ;DS:SI points to St[0]}
  168.   /$FC                   {         CLD                      ;Set direction to forward}
  169.   /$AC                   {         LODSB                    ;AX = Length(St); DS:SI -> St[1]}
  170.   /$91                   {         XCHG AX,CX               ;CX = Length; AL = Wait}
  171.   /$E3/$29               {         JCXZ Exit                ;If string empty, Exit}
  172.   /$8A/$A6/>Attr         {         MOV AH,[BP+>Attr]        ;AH = Attribute}
  173.   /$D0/$D8               {         RCR AL,1                 ;If WaitForRetrace is False...}
  174.   /$73/$1D               {         JNC NoWait               ; use NoWait routine}
  175.   /$BA/$DA/$03           {         MOV DX,$03DA             ;Point DX to CGA status port}
  176.   /$AC                   {Next:    LODSB                    ;Load next character into AL}
  177.                          {                                  ; AH already has Attr}
  178.   /$89/$C3               {         MOV BX,AX                ;Store video word in BX}
  179.   /$FA                   {         CLI                      ;No interrupts now}
  180.   /$EC                   {WaitNoH: IN AL,DX                 ;Get 6845 status}
  181.   /$A8/$08               {         TEST AL,8                ;Check for vertical retrace}
  182.   /$75/$09               {         JNZ Store                ; In progress? go}
  183.   /$D0/$D8               {         RCR AL,1                 ;Else, wait for end of}
  184.   /$72/$F7               {         JC WaitNoH               ; horizontal retrace}
  185.   /$EC                   {WaitH:   IN AL,DX                 ;Get 6845 status again}
  186.   /$D0/$D8               {         RCR AL,1                 ;Wait for horizontal}
  187.   /$73/$FB               {         JNC WaitH                ; retrace}
  188.   /$89/$D8               {Store:   MOV AX,BX                ;Move word back to AX...}
  189.   /$AB                   {         STOSW                    ; and then to screen}
  190.   /$FB                   {         STI                      ;Allow interrupts}
  191.   /$E2/$E8               {         LOOP Next                ;Get next character}
  192.   /$EB/$04               {         JMP SHORT Exit           ;Done}
  193.   /$AC                   {NoWait:  LODSB                    ;Load next character into AL}
  194.                          {                                  ; AH already has Attr}
  195.   /$AB                   {         STOSW                    ;Move video word into place}
  196.   /$E2/$FC               {         LOOP NoWait              ;Get next character}
  197.   /$1F                   {Exit:    POP DS                   ;Restore DS}
  198. );
  199. END;
  200.  
  201.  
  202. PROCEDURE FastWriteV( VAR St; Row, Col, Attr : Byte );
  203.   {-Works with string variables ONLY. (I made St an untyped parameter
  204.     only to make this easier to use when type checking is on.) This is
  205.     just FastWrite optimized for use with string Variables, for times
  206.     when speed really matters.}
  207. BEGIN
  208. Inline(
  209.   $1E                    {         PUSH DS}
  210.   /$31/$C0               {         XOR AX,AX}
  211.   /$88/$C1               {         MOV CL,AL}
  212.   /$8A/$6E/<Row          {         MOV CH,[BP+<Row]}
  213.   /$FE/$CD               {         DEC CH}
  214.   /$D1/$E9               {         SHR CX,1}
  215.   /$89/$CF               {         MOV DI,CX}
  216.   /$D1/$EF               {         SHR DI,1}
  217.   /$D1/$EF               {         SHR DI,1}
  218.   /$01/$CF               {         ADD DI,CX}
  219.   /$8B/$4E/<Col          {         MOV CX,[BP+<Col]}
  220.   /$49                   {         DEC CX}
  221.   /$D1/$E1               {         SHL CX,1}
  222.   /$01/$CF               {         ADD DI,CX}
  223.   /$8E/$06/>BaseOfScreen {         MOV ES,[>BaseOfScreen]}
  224.   /$8A/$0E/>WaitForRetrace{        MOV CL,[>WaitForRetrace]}
  225.   /$C5/$76/<St           {         LDS SI,[BP+<St]          ;DS:SI points to St[0]}
  226.   /$FC                   {         CLD}
  227.   /$AC                   {         LODSB}
  228.   /$91                   {         XCHG AX,CX}
  229.   /$E3/$28               {         JCXZ Exit}
  230.   /$8A/$66/<Attr         {         MOV AH,[BP+<Attr]}
  231.   /$D0/$D8               {         RCR AL,1}
  232.   /$73/$1D               {         JNC NoWait}
  233.   /$BA/$DA/$03           {         MOV DX,$03DA}
  234.   /$AC                   {Next:    LODSB}
  235.   /$89/$C3               {         MOV BX,AX}
  236.   /$FA                   {         CLI}
  237.   /$EC                   {WaitNoH: IN AL,DX}
  238.   /$A8/$08               {         TEST AL,8}
  239.   /$75/$09               {         JNZ Store}
  240.   /$D0/$D8               {         RCR AL,1}
  241.   /$72/$F7               {         JC WaitNoH}
  242.   /$EC                   {WaitH:   IN AL,DX}
  243.   /$D0/$D8               {         RCR AL,1}
  244.   /$73/$FB               {         JNC WaitH}
  245.   /$89/$D8               {Store:   MOV AX,BX}
  246.   /$AB                   {         STOSW}
  247.   /$FB                   {         STI}
  248.   /$E2/$E8               {         LOOP Next}
  249.   /$EB/$04               {         JMP SHORT Exit}
  250.   /$AC                   {NoWait:  LODSB}
  251.   /$AB                   {         STOSW}
  252.   /$E2/$FC               {         LOOP NoWait}
  253.   /$1F                   {Exit:    POP DS}
  254. );
  255. END;
  256.  
  257. PROCEDURE FastWriteNA( St : String80; Row, Col : Byte );
  258.   {-Same as FastWrite, but no attribute byte paramater. String appears
  259.     in whatever attribute(s) was/were there to begin with.}
  260. BEGIN
  261. Inline(
  262.   $8C/$DB                {         MOV BX,DS                ;Save DS in BX}
  263.   /$31/$C0               {         XOR AX,AX}
  264.   /$88/$C1               {         MOV CL,AL}
  265.   /$8A/$AE/>Row          {         MOV CH,[BP+>Row]}
  266.   /$FE/$CD               {         DEC CH}
  267.   /$D1/$E9               {         SHR CX,1}
  268.   /$89/$CF               {         MOV DI,CX}
  269.   /$D1/$EF               {         SHR DI,1}
  270.   /$D1/$EF               {         SHR DI,1}
  271.   /$01/$CF               {         ADD DI,CX}
  272.   /$8B/$8E/>Col          {         MOV CX,[BP+>Col]}
  273.   /$49                   {         DEC CX}
  274.   /$D1/$E1               {         SHL CX,1}
  275.   /$01/$CF               {         ADD DI,CX}
  276.   /$8E/$06/>BaseOfScreen {         MOV ES,[>BaseOfScreen]}
  277.   /$8A/$0E/>WaitForRetrace{        MOV CL,[>WaitForRetrace]}
  278.   /$8C/$D2               {         MOV DX,SS}
  279.   /$8E/$DA               {         MOV DS,DX}
  280.   /$8D/$B6/>St           {         LEA SI,[BP+>St]}
  281.   /$FC                   {         CLD}
  282.   /$AC                   {         LODSB}
  283.   /$91                   {         XCHG AX,CX}
  284.   /$E3/$26               {         JCXZ Exit}
  285.   /$D0/$D8               {         RCR AL,1}
  286.   /$73/$1E               {         JNC NoWait}
  287.   /$BA/$DA/$03           {         MOV DX,$03DA}
  288.   /$AC                   {Next:    LODSB}
  289.   /$88/$C4               {         MOV AH,AL                ;Store char in AH}
  290.   /$FA                   {         CLI}
  291.   /$EC                   {WaitNoH: IN AL,DX}
  292.   /$A8/$08               {         TEST AL,8}
  293.   /$75/$09               {         JNZ Store}
  294.   /$D0/$D8               {         RCR AL,1}
  295.   /$72/$F7               {         JC WaitNoH}
  296.   /$EC                   {WaitH:   IN AL,DX}
  297.   /$D0/$D8               {         RCR AL,1}
  298.   /$73/$FB               {         JNC WaitH}
  299.   /$88/$E0               {Store:   MOV AL,AH                ;Move char back to AL...}
  300.   /$AA                   {         STOSB                    ; and then to screen}
  301.   /$FB                   {         STI}
  302.   /$47                   {         INC DI}
  303.   /$E2/$E7               {         LOOP Next}
  304.   /$EB/$04               {         JMP SHORT Exit}
  305.   /$A4                   {NoWait:  MOVSB                    ;Move character to screen}
  306.   /$47                   {         INC DI                   ;Skip attribute bytes}
  307.   /$E2/$FC               {         LOOP NoWait}
  308.   /$8E/$DB               {Exit:    MOV DS,BX                ;Restore DS from BX}
  309. );
  310. END;
  311.  
  312.  
  313. PROCEDURE ChangeAttribute( Number : Integer; Row, Col, Attr : Byte );
  314.   {-Number is the number of attribute bytes to change. Row and Col
  315.     indicate where to start changing video attributes. Attr is the
  316.     video attribute to use.}
  317. BEGIN
  318. Inline(
  319.   $8B/$4E/<Number        {         MOV CX,[BP+<Number]      ;CX = Number to change}
  320.   /$E3/$49               {         JCXZ Exit                ;If zero, Exit}
  321.   /$31/$C0               {         XOR AX,AX}
  322.   /$8A/$66/<Row          {         MOV AH,[BP+<Row]         ;AX = Row * 256}
  323.   /$FE/$CC               {         DEC AH                   ;Row to 0..24 range}
  324.   /$D1/$E8               {         SHR AX,1                 ;AX = Row * 128}
  325.   /$89/$C7               {         MOV DI,AX                ;Store in DI}
  326.   /$D1/$EF               {         SHR DI,1                 ;DI = Row * 64}
  327.   /$D1/$EF               {         SHR DI,1                 ;DI = Row * 32}
  328.   /$01/$C7               {         ADD DI,AX                ;DI = (Row * 160)}
  329.   /$8B/$46/<Col          {         MOV AX,[BP+<Col]         ;AX = Column}
  330.   /$D1/$E0               {         SHL AX,1                 ;Account for attribute bytes}
  331.   /$01/$C7               {         ADD DI,AX                ;DI = (Row*160) + ((Col+1)*2)}
  332.   /$4F                   {         DEC DI                   ;Adjust Col (-2), skip char (+1)}
  333.   /$8E/$06/>BaseOfScreen {         MOV ES,[>BaseOfScreen]}
  334.   /$8A/$46/<Attr         {         MOV AL,[BP+<Attr]        ;AL = Attribute}
  335.   /$FC                   {         CLD}
  336.   /$80/$3E/>WaitForRetrace/$01{    CMP Byte [>WaitForRetrace],1 ;Get wait state}
  337.   /$75/$1D               {         JNE  NoWait              ;If WaitForRetrace is False}
  338.                          {                                  ; use NoWait routine}
  339.   /$88/$C4               {         MOV AH,AL                ;Store attribute in AH}
  340.   /$BA/$DA/$03           {         MOV DX,$03DA}
  341.   /$FA                   {Next:    CLI}
  342.   /$EC                   {WaitNoH: IN AL,DX}
  343.   /$A8/$08               {         TEST AL,8}
  344.   /$75/$09               {         JNZ Store}
  345.   /$D0/$D8               {         RCR AL,1}
  346.   /$72/$F7               {         JC WaitNoH}
  347.   /$EC                   {WaitH:   IN AL,DX}
  348.   /$D0/$D8               {         RCR AL,1}
  349.   /$73/$FB               {         JNC WaitH}
  350.   /$88/$E0               {Store:   MOV AL,AH                ;Move attr back to AL...}
  351.   /$AA                   {         STOSB                    ; and then to screen}
  352.   /$FB                   {         STI}
  353.   /$47                   {         INC DI                   ;Skip characters}
  354.   /$E2/$EA               {         LOOP Next}
  355.   /$EB/$04               {         JMP SHORT Exit}
  356.   /$AA                   {NoWait:  STOSB                    ;Change the attribute}
  357.   /$47                   {         INC DI                   ;Skip characters}
  358.   /$E2/$FC               {         LOOP NoWait              ;Get next character}
  359.                          {Exit:                             ;Next instruction}
  360. );
  361. END;
  362.  
  363. PROCEDURE MoveFromScreen(VAR Source, Dest; Length : Integer);
  364.  {-Length = number of WORDS to move from video memory (source) to
  365.    dest.}
  366. BEGIN
  367.  Inline(
  368.   $8C/$DB              {         MOV BX,DS                ;Save DS in BX}
  369.   /$A0/>WaitForRetrace {         MOV AL,[>WaitForRetrace] ;Grab before changing DS}
  370.   /$C4/$7E/<Dest       {         LES DI,[BP+<Dest]        ;ES:DI points to Dest}
  371.   /$C5/$76/<Source     {         LDS SI,[BP+<Source]      ;DS:SI points to Source}
  372.   /$8B/$4E/<Length     {         MOV CX,[BP+<Length]      ;CX = Length}
  373.   /$FC                 {         CLD}
  374.   /$D0/$D8             {         RCR AL,1}
  375.   /$73/$19             {         JNC NoWait}
  376.   /$BA/$DA/$03         {         MOV DX,$03DA}
  377.   /$FA                 {Next:    CLI}
  378.   /$EC                 {WaitNoH: IN AL,DX}
  379.   /$A8/$08             {         TEST AL,8}
  380.   /$75/$09             {         JNZ Store}
  381.   /$D0/$D8             {         RCR AL,1}
  382.   /$72/$F7             {         JC WaitNoH}
  383.   /$EC                 {WaitH:   IN AL,DX}
  384.   /$D0/$D8             {         RCR AL,1}
  385.   /$73/$FB             {         JNC WaitH}
  386.   /$AD                 {Store:   LODSW                    ;Load next video word into AX}
  387.   /$FB                 {         STI                      ;Allow interrupts}
  388.   /$AB                 {         STOSW                    ;Store video word in Dest}
  389.   /$E2/$EC             {         LOOP Next}
  390.   /$EB/$02             {         JMP SHORT Exit}
  391.   /$F2/$A5             {NoWait:  REP MOVSW                ;That's it!}
  392.   /$8E/$DB             {Exit:    MOV DS,BX                ;Restore DS}
  393.  );
  394. END;
  395.  
  396. PROCEDURE MoveToScreen(VAR Source, Dest; Length : Integer);
  397.   {-Length = number of WORDS to move to video memory (dest) from source}
  398. BEGIN
  399.  Inline(
  400.   $1E                  {         PUSH DS                  ;Save DS}
  401.   /$A0/>WaitForRetrace {         MOV AL,[>WaitForRetrace]}
  402.   /$C4/$7E/<Dest       {         LES DI,[BP+<Dest]}
  403.   /$C5/$76/<Source     {         LDS SI,[BP+<Source]}
  404.   /$8B/$4E/<Length     {         MOV CX,[BP+<Length]}
  405.   /$FC                 {         CLD}
  406.   /$D0/$D8             {         RCR AL,1}
  407.   /$73/$1D             {         JNC NoWait}
  408.   /$BA/$DA/$03         {         MOV DX,$03DA}
  409.   /$AD                 {Next:    LODSW                    ;Load next video word into AX}
  410.   /$89/$C3             {         MOV BX,AX                ;Store video word in BX}
  411.   /$FA                 {         CLI}
  412.   /$EC                 {WaitNoH: IN AL,DX}
  413.   /$A8/$08             {         TEST AL,8}
  414.   /$75/$09             {         JNZ Store}
  415.   /$D0/$D8             {         RCR AL,1}
  416.   /$72/$F7             {         JC WaitNoH}
  417.   /$EC                 {WaitH:   IN AL,DX}
  418.   /$D0/$D8             {         RCR AL,1}
  419.   /$73/$FB             {         JNC WaitH}
  420.   /$89/$D8             {Store:   MOV AX,BX                ;Move word back to AX...}
  421.   /$AB                 {         STOSW                    ; and then to screen}
  422.   /$FB                 {         STI                      ;Allow interrupts}
  423.   /$E2/$E8             {         LOOP Next}
  424.   /$EB/$02             {         JMP SHORT Exit}
  425.   /$F2/$A5             {NoWait:  REP MOVSW}
  426.   /$1F                 {Exit:    POP DS                   ;Restore DS}
  427. );
  428. END;
  429. {---------------------- end of FASTWR.INC -----------}
  430.  
  431. { Tacky demonstration program. (Reminds Kim of ComputerLand stores.) Delete
  432.   next line to enable. }
  433. (*
  434. VAR
  435.    Fore, Back,
  436.    I, Attr        : Byte;
  437.    ScreenBuffer   : Array[1..2000] of Integer;
  438.    N : Integer;
  439.    C : Char;
  440. CONST
  441.    Bullet : String80 = '* FASTER THAN A SPEEDING BULLET! *';
  442. BEGIN
  443.      GetVideoMode;
  444.      IF WaitForRetrace THEN BEGIN
  445.         Write('Does your screen generate snow? ');
  446.         Read(KBD, C);
  447.         WaitForRetrace := (UpCase(C) = 'Y');
  448.      END;
  449.      IF BaseOfScreen = $B000 THEN BEGIN
  450.         Fore := White;
  451.         Back := Black;
  452.      END
  453.      ELSE BEGIN
  454.         Fore := White; { make these whatever you want }
  455.         Back := Magenta;
  456.      END;
  457.      Attr := Attribute( Fore, Back );
  458.      TextColor( Fore );
  459.      TextBackground( Back );
  460.      ClrScr;
  461.      FastWrite(   'FastWriting is still even...', 6, 26, Attr );
  462.      Delay( 2000 );
  463.      FastWrite( '**********************************', 9, 23, Attr );
  464.      FOR I := 10 TO 20 DO
  465.          FastWriteV( Bullet, I, 23, Attr );
  466.      FastWrite( '**********************************', 21, 23, Attr );
  467.      { now save the entire screen }
  468.      MoveFromScreen( Mem[BaseOfScreen:0], ScreenBuffer, 2000 );
  469.      { now let's play with the saved screen, setting blink bits }
  470.      FOR N := 1 TO 2000 DO
  471.           ScreenBuffer[N] := ScreenBuffer[N] Xor $8000;
  472.      Delay( 2000 );
  473.      FastWrite(   'Changing attribute bytes....', 6, 26, Attr );
  474.      Delay( 2000 );
  475.      Attr := Attribute( Back, Fore );
  476.      { now some reverse video }
  477.      ChangeAttribute( 2000, 1, 1, Attr );
  478.      FOR I := 10 TO 20 DO
  479.          FastWrite( 'IS PRETTY DARNED FAST NOW TOO!', I, 25, Attr );
  480.      Delay( 2000 );
  481.      FastWrite( 'But who says you need to....', 6, 26, Attr );
  482.      Delay( 2000 );
  483.      { now write fast without messing with attributes }
  484.      FOR I := 10 TO 20 DO
  485.          FastWriteNA( 'MESS WITH THE ATTRIBUTE BYTES?', I, 25 );
  486.      Delay( 2000 );
  487.      { now restore the blinking version of the screen we saved }
  488.      MoveToScreen( ScreenBuffer, Mem[BaseOfScreen:0], 2000 );
  489. END.
  490. (**)