home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l045 / 2.ddi / GRAFHGC.DVR < prev    next >
Encoding:
Text File  |  1987-12-23  |  17.7 KB  |  622 lines

  1. (********************************************************************)
  2. (*                         GRAPHIX TOOLBOX 4.0                      *)
  3. (*       Copyright (c) 1985, 87 by  Borland International, Inc.     *)
  4. (*                                                                  *)
  5. (*         Graphics module for the Hercules Monochrome card         *)
  6. (********************************************************************)
  7.  
  8. unit GDriver;
  9.  
  10. interface
  11.  
  12. {$I Float.inc}  { Determines what type Float means. }
  13.  
  14. uses
  15.   Dos, Crt;
  16.  
  17. {$IFOPT N+}
  18. type
  19.   Float = Double; { 8 byte real, requires 8087 math chip }
  20.  
  21. {$ELSE}
  22. type
  23.   Float = real;   { 6 byte real, no math chip required }
  24.  
  25. {$ENDIF}
  26.  
  27. const
  28.   MaxWorldsGlb = 4;
  29.   MaxWindowsGlb = 16;
  30.   MaxPiesGlb = 10;
  31.   MaxPlotGlb = 100;
  32.   StringSizeGlb = 80;
  33.   HeaderSizeGlb = 10;
  34.   RamScreenGlb : boolean = true;
  35.   CharFile : string[StringSizeGlb] = '4x6.fon';
  36.   MaxProcsGlb = 27;
  37.   MaxErrsGlb = 7;
  38.   AspectFactor  = 0.6667;          { Aspect ratio for a true circle          }
  39.   ScreenSizeGlb = 16383;           { Total size in integers of the screen    }
  40.   HardwareGrafBase : word = $B000; { Location of the hardware screen }
  41.   XMaxGlb       = 89;              { Number of bytes -1 in one screen line   }
  42.   XScreenMaxGlb = 719;             { Number of pixels -1 in one screen line  }
  43.   YMaxGlb       = 349;             { Number of lines -1 on the screen        }
  44.   IVStepGlb     = 5;               { Initial value of VStepGlb }
  45.   VRowsGlb      = $58;             { Change to $57 if monitor loses horizontal hold }
  46.   MinForeground : word = 1;        { Lowest allowable foreground color }
  47.   MaxForeground : word = 1;        { Highest allowable foreground color }
  48.   MinBackground : word = 0;        { Lowest allowable background color }
  49.   MaxBackground : word = 0;        { Highest allowable background color }
  50.   RamScreenInCard  : boolean = false;
  51.   { Hercules: store RAM screen in the HGC's alternate bank?  Will interfere
  52.     with some color cards if present -- see Hercules manual (sets 'Full' mode) }
  53.  
  54.  
  55. type
  56.   WrkString = string[StringSizeGlb];
  57.   WrkStringPtr = ^WrkString;
  58.   WorldType = record
  59.                 X1, Y1, X2, Y2 : Float;
  60.               end;
  61.   WindowType = record
  62.                  X1, Y1, X2, Y2 : integer;
  63.                  Header : WrkString;
  64.                  Drawn, Top : boolean;
  65.                  Size : word;
  66.                end;
  67.   Worlds = array[1..MaxWorldsGlb] of WorldType;
  68.   Windows = array[1..MaxWindowsGlb] of WindowType;
  69.   PlotArray = array[1..MaxPlotGlb, 1..2] of Float;
  70.   Character = array[1..3] of byte;
  71.   CharArray = array[32..126] of character;
  72.   PieType = record
  73.               Area : Float;
  74.               Text : WrkString;
  75.             end;
  76.   PieArray = array[1..MaxPiesGlb] of PieType;
  77.   BackgroundArray = array[0..7] of byte;
  78.   LineStyleArray = array[0..7] of boolean;
  79.   ScreenType        = array[0..ScreenSizeGlb] of word;
  80.   ScreenPointer     = ^ScreenType;
  81.   WindowStackRecord = record
  82.                         W : WindowType;
  83.                         Contents : ScreenPointer;
  84.                       end;
  85.   Stacks            = array[1..MaxWindowsGlb] of WindowStackRecord;
  86.  
  87. var
  88.   X1WldGlb, X2WldGlb, Y1WldGlb, Y2WldGlb, AxGlb, AyGlb, BxGlb, ByGlb : Float;
  89.   X1RefGlb, X2RefGlb, Y1RefGlb, Y2RefGlb : integer;
  90.   LinestyleGlb, MaxWorldGlb, MaxWindowGlb, WindowNdxGlb, WorldNdxGlb : integer;
  91.   X1Glb, X2Glb, Y1Glb, Y2Glb : integer;
  92.   XTextGlb, YTextGlb, VStepGlb : integer;
  93.   PieGlb, DirectModeGlb, ClippingGlb, AxisGlb, HatchGlb : boolean;
  94.   MessageGlb, BrkGlb, HeaderGlb, TopGlb, GrafModeGlb : boolean;
  95.   CntGlb, ColorGlb : byte;
  96.   ErrCodeGlb : integer;
  97.   LineStyleArrayGlb : LineStyleArray;
  98.   ErrorProc : array[0..MaxProcsGlb] of WrkStringPtr;
  99.   ErrorCode : array[0..MaxErrsGlb] of WrkStringPtr;
  100.   PcGlb : string[40];
  101.   AspectGlb : Float;
  102.   GrafBase : word;
  103.   World : Worlds;
  104.   GrafWindow : Windows;
  105.   CharSet : CharArray;
  106.   ScreenGlb : ScreenPointer;
  107.   Stack : Stacks;
  108.  
  109. function BaseAddress(Y : word) : word;
  110. { Calculate the address of scanline Y }
  111.  
  112. procedure Error(ErrProc, ErrCode : integer);
  113.  
  114. function HardwarePresent : boolean;
  115. { Test for the presence of a graphics card }
  116.  
  117. procedure HerculesMode(Mode : word); { 0 = Text, 1 = Graphics }
  118.  
  119. procedure AllocateRAMScreen;
  120. { Allocates the RAM screen and makes sure that
  121.   ScreenGlb is on a segment (16 byte) boundary }
  122.  
  123. procedure LeaveGraphic;
  124. { Exit from graphics mode and clear the screen }
  125.  
  126. procedure DC(C : byte);
  127. { Draw the the character with ASCII code C at position (XTextGlb, YTextGlb). }
  128.  
  129. procedure SetForegroundColor(Color : word);
  130.  
  131. procedure SetBackgroundColor(Color : word);
  132.  
  133. procedure ClearScreen;
  134. { Clear the displayed screen }
  135.  
  136. procedure EnterGraphic;
  137. { Enter graphics mode }
  138.  
  139. procedure DP(X, Y : word);
  140. { Plot a pixel at (X, Y). }
  141.  
  142. function PD(X, Y : word) : boolean;
  143. { Return true if the color of the pixel at (X, Y) matches ColorGlb }
  144.  
  145. procedure SetBackground8(Background : BackgroundArray);
  146. { Fills the active display with the specified bit pattern }
  147.  
  148. procedure SetBackground(Byt : byte);
  149. { Determines the background pattern of the active window }
  150.  
  151. procedure DrawStraight(X1, X2, Y : word);
  152. { Draw a horizontal line from X1,Y to X2,Y }
  153.  
  154. procedure SaveScreen(FileName : WrkString);
  155. { Save the current screen on disk using FileName. }
  156.  
  157. procedure LoadScreen(FileName : WrkString);
  158. { Load screen from file FileName. }
  159.  
  160. procedure SwapScreen;
  161. { Exchanges the contents of the displayed
  162.   screen with the contents of the RAM screen }
  163.  
  164. procedure CopyScreen;
  165. { Copies the active screen onto the inactive screen }
  166.  
  167. procedure InvertScreen;
  168. { Inverts the image on the active screen }
  169.  
  170. implementation
  171.  
  172. const
  173.   FontLoaded         : boolean = false; { Has the font been loaded yet? }
  174.   ForegroundColorGlb : word = 1;
  175.  
  176. type
  177.   FontChar = array[0..13] of byte;
  178.   GrfFont  = array[0..255] of FontChar;
  179.  
  180. var
  181.   Font : GrfFont;
  182.  
  183. function BaseAddress{(Y : word) : word};
  184. { Calculate the address of scanline Y }
  185. begin
  186.   BaseAddress := (Y and 3) shl 13 + 90 * (Y shr 2);
  187. end; { BaseAddress }
  188.  
  189. procedure Error{(ErrProc, ErrCode : integer)};
  190. var
  191.   XLoc, YLoc : integer;
  192.   Ch : char;
  193.  
  194. begin { Error }
  195.   if not (ErrProc in [0..MaxProcsGlb]) then
  196.   begin
  197.     LeaveGraphic;
  198.     WriteLn('FATAL Error 1: illegal procedure number ', ErrProc);
  199.     Halt;
  200.   end;
  201.   if not (ErrCode in [0..MaxErrsGlb]) then
  202.   begin
  203.     LeaveGraphic;
  204.     WriteLn('FATAL Error 2: illegal Error code ', ErrCode);
  205.     Halt;
  206.   end;
  207.   ErrCodeGlb := ErrCode;
  208.   if BrkGlb then
  209.     LeaveGraphic;
  210.   if MessageGlb or BrkGlb then
  211.   begin
  212.     XLoc := XTextGlb;
  213.     YLoc := YTextGlb;
  214.     GotoXY(1, 24);
  215.     ClrEOL;
  216.     WriteLn('Turbo Graphix Error #', ErrCode, ' in procedure #', ErrProc);
  217.     if MessageGlb then
  218.     begin
  219.       ClrEOL;
  220.       Write('(', ErrorCode[ErrCode]^, ' in ', ErrorProc[ErrProc]^, ')');
  221.     end;
  222.   end;
  223.   if BrkGlb then
  224.     Halt
  225.   else if MessageGlb then
  226.     begin
  227.       Write('.  Hit enter: ');
  228.       repeat
  229.         Ch := ReadKey;
  230.       until (Ch = ^M) or (Ch = ^C);
  231.       if Ch = ^C then
  232.       begin
  233.         LeaveGraphic;
  234.         Halt;
  235.       end;
  236.       GotoXY(XLoc, YLoc);
  237.     end;
  238. end; { Error }
  239.  
  240. function HardwarePresent{ : boolean};
  241. { Test for the presence of a graphics card }
  242. var
  243.   Regs : Registers;
  244.  
  245. function HercPresent : boolean;
  246. begin
  247.   inline($BB/$00/$01/          {      MOV    BX,0100H             }
  248.          $BA/$BA/$03/          {      MOV    DX,03BAH             }
  249.          $EC/                  {      IN     AL,DX                }
  250.          $88/$C4/              {      MOV    AH,AL                }
  251.          $80/$E4/$80/          {      AND    AH,80H               }
  252.          $B9/$40/$00/          { L3:  MOV    CX,0040H             }
  253.          $EC/                  { L1:  IN     AL,DX                }
  254.          $24/$80/              {      AND    AL,80H               }
  255.          $38/$E0/              {      CMP    AL,AH                }
  256.          $E1/$F9/              {      LOOPZ  L1                   }
  257.          $75/$05/              {      JNZ    L2                   }
  258.          $4B/                  {      DEC    BX                   }
  259.          $75/$F1/              {      JNZ    L3                   }
  260.          $EB/$33/              {      JMP    L4                   }
  261.          $B8/$00/$B0/          { L2:  MOV    AX,B000H             }
  262.          $8E/$C0/              {      MOV    ES,AX                }
  263.          $E8/$11/$00/          {      CALL   L5                   }
  264.          $75/$0B/              {      JNZ    L6                   }
  265.          $B0/$01/              {      MOV    AL,01                }
  266.          $BA/$BF/$03/          {      MOV    DX,03BFH             }
  267.          $EE/                  {      OUT    DX,AL                }
  268.          $E8/$06/$00/          {      CALL   L5                   }
  269.          $74/$1E/              {      JZ     L4                   }
  270.          $B0/$01/              { L6:  MOV    AL,01                }
  271.          $EB/$1C/              {      JMP    L7                   }
  272.          $26/$8A/$1E/$FF/$7F/  { L5:  MOV    BL,ES:[7FFFH]        }
  273.          $26/$8A/$0E/$FF/$3F/  {      MOV    CL,ES:[3FFFH]        }
  274.          $26/$FE/$06/$FF/$3F/  {      INC    BYTE PTR ES:[3FFFH]  }
  275.          $26/$3A/$1E/$FF/$3F/  {      CMP    BL,ES:[3FFFH]        }
  276.          $26/$88/$0E/$FF/$3F/  {      MOV    ES:[3FFFH],CL        }
  277.          $C3/                  {      RET                         }
  278.          $30/$C0);             { L4:  XOR    AL,AL                }
  279.                                { L7:                              }
  280.  
  281. end; { HercPresent }
  282.  
  283. begin
  284.   Intr($11, Regs);
  285.   HardwarePresent := HercPresent and ((Regs.AX and 48) = 48);
  286. end; { HardwarePresent }
  287.  
  288. procedure HerculesMode{(Mode : word)}; { 0=text, 1=graphics }
  289. type
  290.   ModeDescriptor = record
  291.                      CRTMode : byte;
  292.                      R6845   : array[0..11] of byte;
  293.                    end;
  294. const
  295.   HercModes : array[0..1] of ModeDescriptor =
  296.   ((CRTMode : 32; R6845 : ($61,$50,$52,$0F,$19,$06,$19,$19,$02,$0D,$0B,$0C)),
  297.    (CRTMode : 2;  R6845 : ($35,$2D,$2E,$07,$5B,$02,$58,$58,$02,$03,$00,$00)));
  298.   ScreenOn          = 8;
  299.   IndexPort         = $3B4;
  300.   DataPort          = $3B5;
  301.   ControlPort       = $3B8;
  302.   ConfigurationPort = $3BF;
  303.  
  304. var
  305.   I : word;
  306.  
  307. begin
  308.   Port[ConfigurationPort] := 1;
  309.   Port[ControlPort] := HercModes[Mode].CRTMode;
  310.   for I := 0 to 11 do
  311.   begin
  312.     Port[IndexPort] := I;
  313.     Port[DataPort] := HercModes[Mode].R6845[I];
  314.   end;
  315.   Port[ControlPort] := HercModes[Mode].CRTMode or ScreenOn;
  316.   if RAMScreenInCard then
  317.     Port[ConfigurationPort] := 3;
  318. end; { HerculesMode }
  319.  
  320. procedure AllocateRAMScreen;
  321. { Allocates the RAM screen and makes sure that
  322.   ScreenGlb is on a segment (16 byte) boundary }
  323. var
  324.   Test : ^byte;
  325. begin
  326.   if RamScreenInCard then
  327.     ScreenGlb := Ptr($B800, $0000)
  328.   else
  329.     begin
  330.       New(ScreenGlb);
  331.       while Ofs(ScreenGlb^) <> 0 do
  332.       begin
  333.         Dispose(ScreenGlb);
  334.         New(Test);
  335.         New(ScreenGlb);
  336.       end;
  337.     end;
  338. end; { AllocateRAMScreen }
  339.  
  340. {$L GrafHGC.OBJ}
  341. procedure DC{(C : byte)}; external;
  342.  
  343. procedure DP{(X, Y : word)}; external;
  344.  
  345. procedure SwapScreen; external;
  346.  
  347. procedure InvertScreen; external;
  348.  
  349. {$F+}
  350. function WriteGrafChars(var F : TextRec) : integer;
  351. { Used to output graphics characters through the standard output channel. }
  352. const
  353.   BackSpace = #8;
  354.   LineFeed  = #10;
  355.   Return    = #13;
  356. var
  357.   I : integer;
  358. begin
  359.   with F do
  360.     if Mode = fmOutput then
  361.     begin
  362.       if BufPos > BufEnd then
  363.       begin
  364.         for I := BufEnd to Pred(BufPos) do  { Flush the output buffer }
  365.         begin
  366.           case BufPtr^[I] of
  367.             BackSpace : if XTextGlb > 1 then
  368.                           DEC(XTextGlb);
  369.  
  370.             LineFeed  : if YTextGlb < 25 then
  371.                           INC(YTextGlb);
  372.  
  373.             Return    : XTextGlb := 1;
  374.           else
  375.             DC(ORD(BufPtr^[I]));
  376.             if XTextGlb < 80 then
  377.               INC(XTextGlb);
  378.           end; { case }
  379.         end; { for }
  380.       end;
  381.       BufPos := BufEnd;
  382.     end; { if }
  383.   WriteGrafChars := 0;
  384. end; { WriteGrafChars }
  385.  
  386. function GrafCharZero(var F : TextRec) : integer;
  387. { Called when standard output is opened and closed }
  388. begin
  389.   GrafCharZero := 0;
  390. end; { GrafCharZero }
  391. {$F-}
  392.  
  393. var
  394.   OldOutput : Text; { Stores output I/O channel }
  395.  
  396. procedure GrafCharsON;
  397. { Redirects standard output to the WriteGrafChars function. }
  398. begin
  399.   Move(Output, OldOutput, SizeOf(Output));  { Save old output channel }
  400.   with TextRec(Output) do
  401.   begin
  402.     OpenFunc:=@GrafCharZero;       { no open necessary }
  403.     InOutFunc:=@WriteGrafChars;    { WriteGrafChars gets called for I/O }
  404.     FlushFunc:=@WriteGrafChars;    { WriteGrafChars flushes automatically }
  405.     CloseFunc:=@GrafCharZero;      { no close necessary }
  406.     Name[0]:=#0;
  407.   end;
  408. end; { GrafCharsON }
  409.  
  410. procedure GrafCharsOFF;
  411. { Restores original output I/O channel }
  412. begin
  413.   Move(OldOutput, Output, SizeOf(OldOutput));
  414. end; { GrafCharsOFF }
  415.  
  416. procedure LeaveGraphic;
  417. { Exit from graphics mode and clear the screen }
  418. var
  419.   Regs : Registers;
  420. begin
  421.   HerculesMode(0);
  422.   ClrScr;
  423.   GrafCharsOFF;
  424.   GrafModeGlb := false;
  425. end; { LeaveGraphic }
  426.  
  427. procedure SetForegroundColor{(Color : word)};
  428. begin
  429.   { No colors to choose }
  430. end; { SetForegroundColor }
  431.  
  432. procedure SetBackgroundColor{(Color : word)};
  433. begin
  434.   { No colors to choose }
  435. end; { SetBackgroundColor }
  436.  
  437. procedure ClearScreen;
  438. { Clear the displayed screen }
  439. begin
  440.   FillChar(Mem[GrafBase:0000], ScreenSizeGlb shl 1, 0);
  441. end; { ClearScreen }
  442.  
  443. procedure EnterGraphic;
  444. { Enter graphics mode }
  445. var
  446.   FontFile : file of GrfFont;
  447. begin
  448.   if not FontLoaded then
  449.   begin
  450.     Assign(FontFile, '14x9.FON');
  451.     {$I-} Reset(FontFile); {$I+}
  452.     if IOresult=0 then
  453.       begin
  454.         Read(FontFile, Font);
  455.         Close(FontFile);
  456.       end
  457.     else
  458.       FillChar(Font, SizeOf(Font), 0);
  459.     FontLoaded := true;
  460.   end;
  461.   HerculesMode(1);
  462.   SetForegroundColor(MaxForeground);
  463.   if not GrafModeGlb then
  464.    GrafCharsON;
  465.   ClearScreen;
  466.   GrafModeGlb := true;
  467. end; { EnterGraphic }
  468.  
  469. function PD{(X, Y : word) : boolean};
  470. { Return true if the color of the pixel at (X, Y) matches ColorGlb }
  471. begin
  472.   PD := (ColorGlb = 0) xor (Mem[GrafBase:BaseAddress(Y) + X shr 3]
  473.                        and (128 shr (X and 7)) <> 0);
  474. end; { PD }
  475.  
  476. procedure SetBackground8{(Background : BackgroundArray)};
  477. { Fills the active display with the specified bit pattern }
  478. var
  479.   I : word;
  480. begin
  481.   for I := Y1RefGlb to Y2RefGlb do
  482.     FillChar(Mem[GrafBase:BaseAddress(I) + X1RefGlb], X2RefGlb - X1RefGlb+1,
  483.              Background[I and 7]);
  484. end; { SetBackground8 }
  485.  
  486. procedure SetBackground{(Byt : byte)};
  487. { Determines the background pattern of the active window }
  488. var
  489.   Bk : BackgroundArray;
  490. begin
  491.   FillChar(Bk, 8, Byt);
  492.   SetBackground8(Bk);
  493. end; { SetBackground }
  494.  
  495. procedure DrawStraight{(X1, X2, Y : word)};
  496. { Draw a horizontal line from X1,Y to X2,Y }
  497. var
  498.   I, X : word;
  499.   DirectModeLoc : boolean;
  500. begin
  501.   if (not ((X1 < 0) or (X1 > XMaxGlb shl 3+7)) and not ((X2 < 0) or
  502.      (X2 > XMaxGlb shl 3+7)) and ((Y >= 0) and (Y <= YMaxGlb))) then
  503.   begin
  504.     DirectModeLoc := DirectModeGlb;
  505.     DirectModeGlb := true;
  506.     if X1 > X2 then
  507.     begin
  508.       X := X1;
  509.       X1 := X2;
  510.       X2 := X;
  511.     end;
  512.     if X2-X1 < 16 then
  513.       for X := X1 to X2 do
  514.         DP(X, Y)
  515.     else
  516.       begin
  517.         X1 := X1 + 8;
  518.         for I := (X1 - 8) to (X1 and -8) do
  519.           DP(I, Y);
  520.         for I := (X2 and -8) to X2 do
  521.           DP(I, Y);
  522.         FillChar(Mem[GrafBase:BaseAddress(Y) + (X1 shr 3)],
  523.                 (X2 shr 3) - (X1 shr 3), ColorGlb);
  524.       end;
  525.     DirectModeGlb := DirectModeLoc;
  526.   end
  527. end; { DrawStraight }
  528.  
  529. procedure SaveScreen{(FileName : WrkString)};
  530. { Save the current screen on disk using FileName. }
  531. type
  532.   Sector = array[0..127] of byte;
  533.   SecScreen = array[0..255] of sector;
  534. var
  535.   PictureFile : file of Sector;
  536.   Pic : ^SecScreen;
  537.   I : word;
  538.   IOErr : boolean;
  539.  
  540. procedure IOCheck;
  541. begin
  542.   IOErr := IOResult <> 0;
  543.   if IOErr then
  544.     Error(27, 5);
  545. end; { IOCheck }
  546.  
  547. begin
  548.   if FileName <> '' then
  549.     begin
  550.       IOErr := false;
  551.       Pic := Ptr(GrafBase, 0);
  552.       Assign(PictureFile, FileName);
  553.       {$I-}
  554.       Rewrite(PictureFile);
  555.       {$I+}
  556.       IOCheck;
  557.       for I := 0 to 255 do
  558.         if not IOErr then
  559.         begin
  560.           {$I-}
  561.           Write(PictureFile, Pic^[I]);
  562.           {$I+}
  563.           IOCheck;
  564.         end;
  565.       if not IOErr then
  566.       begin
  567.         {$I-}
  568.         Close(PictureFile);
  569.         {$I+}
  570.         IOCheck;
  571.       end;
  572.     end
  573.   else
  574.     Error(27, 5);
  575. end; { SaveScreen }
  576.  
  577. procedure LoadScreen{(FileName : WrkString)};
  578. { Load screen from file FileName. }
  579. type
  580.   Sector = array[0..127] of byte;
  581.   SecScreen = array[0..255] of Sector;
  582. var
  583.   PictureFile : file of Sector;
  584.   Pic : ^SecScreen;
  585.   I : word;
  586. begin
  587.   if FileName <> '' then
  588.     begin
  589.       Pic := Ptr(GrafBase, 0);
  590.       Assign(PictureFile, FileName);
  591.       {$I-}
  592.       Reset(PictureFile);
  593.       {$I+}
  594.       if IOResult <> 0 then
  595.         Error(11, 5)
  596.       else
  597.         begin
  598.           for I := 0 to 255 do
  599.             Read(PictureFile, Pic^[I]);
  600.           Close(PictureFile);
  601.         end;
  602.     end
  603.   else
  604.     Error(11, 5);
  605. end; { LoadScreen }
  606.  
  607. procedure CopyScreen;
  608. { Copies the active screen onto the inactive screen }
  609. var
  610.   ToBase : word;
  611. begin
  612.   if RamScreenGlb then
  613.   begin
  614.     if GrafBase = HardwareGrafBase then
  615.       ToBase := Seg(ScreenGlb^)
  616.     else
  617.       ToBase := HardwareGrafBase;
  618.     Move(Mem[GrafBase:0000], Mem[ToBase:0000], ScreenSizeGlb shl 1);
  619.   end;
  620. end; { CopyScreen }
  621.  
  622. end. { GDriver }