home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 07 / vga.asc < prev    next >
Text File  |  1991-06-11  |  18KB  |  535 lines

  1. _SAVING AND RESTORING VGA SCREENS_
  2. by Ben Myers
  3.  
  4. [LISTING ONE]
  5.  
  6.   PAGE 80,132
  7.   TITLE EGA/VGA screen save/restore  (Turbo Pascal 4.0+ or Quick Pascal 1.0)
  8. ; GRFSAVE.ASM -
  9. ;  (C)Copyright 1989-1990 Spirit of Performance, Inc.
  10. ;  All rights reserved. Unauthorized use or copying prohibited by law.
  11.  
  12. CODE   SEGMENT WORD PUBLIC
  13.        ASSUME  CS:CODE
  14.        PUBLIC  Write_VGA_Plane ; Write video plane from caller's memory.
  15.        PUBLIC  Read_VGA_Plane  ; Read video plane, move it to caller's memory.
  16.  
  17. ; procedure Write_VGA_Plane (Plane, Count : word; var Plane_Array );
  18. ; procedure Read_VGA_Plane  (Plane, Count : word; var Plane_Array );
  19. ; Parameters:  Plane       - Graphics plane number to move ( range 0-3 )
  20. ;           Count       - Byte count to move
  21. ;              Plane_Array - Array for video plane values
  22.  
  23. Plane_Array  EQU  DWORD PTR [bp+06h]
  24. Count          EQU  WORD  PTR [bp+0Ah]
  25. Plane          EQU  WORD  PTR [bp+0Ch]
  26.  
  27. Write_VGA_Plane PROC FAR
  28.        push  bp                 ; Save Turbo's BP
  29.        mov   bp,sp              ; Set up stack frame
  30.        mov   bx,ds              ; Save Turbo's DS
  31.        mov   di,0A000h          ; EGA/VGA buffer segment:offset, A000:0000
  32.        mov   es,di
  33.        xor   di,di              ; ES:DI is start of video buffer
  34.        mov   dx,3CEh            ; DX = Graphics Controller I/O Port
  35.        mov   ax,0005h           ; AH = 00h (Read mode 0, write mode 0)
  36.                 ; AL = Mode register number (5)
  37.        out   dx,ax              ; load Mode register
  38.        mov   ax,0001h           ; AH = 00h (mask for Enable Set/Reset),
  39.                 ; also the default for modes 12h and 10h
  40.                 ; AL = Enable Set/Reset register number (1)
  41.        out   dx,ax              ; load Enable Set/Reset register
  42.        mov   ax,0003h           ; AH = Replace bit planes with memory,
  43.                 ; no bit rotation, also the default
  44.                 ; AL = Data Rotate/Function Select register
  45.                 ; number (3)
  46.        out   dx,ax              ; load Data Rotate/Function Select register
  47.                 ; AL = Bit Mask Register (8)
  48.        mov   ax,0FF08h          ; AH = bit mask
  49.        out   dx,ax              ; Set bit mask register for all bits
  50.        mov   dx,3C4h            ; DX = Sequencer I/O Port
  51.        mov   cx,ss:Plane        ; Get Plane number from caller
  52.        and   cl,03h             ; Force it to range 0 to 3
  53.        mov   ah,1               ; Set up AH with bit number of plane to restore
  54.        shl   ah,cl              ; where bit 0 = plane 0, etc.
  55.        mov   al,02h             ; AL = Map Mask Register number (2)
  56.        out   dx,ax              ; load Map Mask register with plane number
  57.        mov   cx,ss:Count        ; byte count to move (size of plane for
  58.                 ; EGA/VGA card in current mode )
  59.        lds   si,ss:Plane_Array  ; Addr of array to restore plane values from
  60.        rep movsb                ; Move the data
  61. ; Or replace the above instruction by the slower but equivalent loop construct
  62. ; below in the event that your VGA card doesn't respond properly.
  63. @@:
  64. ;       lodsb                    ; Get a byte from save area plane
  65. ;       stosb                    ; Form a byte for current plane
  66. ;       loop  @B                 ; Do next byte, until all have been done.
  67.  
  68. ; Now reset the VGA registers used back to the defaults expected.
  69.        mov   dx,3CEh            ; DX = Graphics Controller I/O Port
  70.        mov   ax,0001            ; AH = 0 (default Enable Set/Reset Value)
  71.                 ; AL = Enable Set/Reset register number (1)
  72.        out   dx,ax              ; restore default Enable Set/Reset register
  73.        mov   dx,3C4h            ; DX = Sequencer I/O port
  74.                 ; AH = all planes enabled
  75.        mov   ax,0F02h           ; AL = Map Mask Register number (2)
  76.        out   dx,ax              ; restore Map Mask register to do all planes.
  77.        mov   ds,bx              ; Restore Turbo's DS
  78.        pop   bp                 ; Restore Turbo's BP
  79.        ret   8                  ; Remove params & return to call
  80. Write_VGA_Plane ENDP
  81.  
  82. ; procedure Read_VGA_Plane (Plane, Count : word; var Plane_Array );
  83.  
  84. Read_VGA_Plane PROC FAR
  85.        push  bp                 ; Save Turbo's BP
  86.        mov   bp,sp              ; Set up stack frame
  87.        mov   bx,ds              ; Save Turbo's DS
  88.        mov   si,0A000h          ; EGA/VGA buffer segment:offset, A000:0000
  89.        mov   ds,si
  90.        xor   si,si              ; DS:SI is start of video buffer
  91.        mov   dx,3CEh            ; DX = Graphics Controller I/O Port
  92.        mov   ax,0005h           ; AH = 00h (Read mode 0, write mode 0)
  93.                 ; AL = 5 (Mode register number)
  94.        out   dx,ax              ; load Mode register
  95. ;;     int   3            ; Enable breakpoint for debugging.
  96.        mov   ax,ss:Plane        ; Get Plane number
  97.        mov   ah,al              ; AH = color plane to get
  98.        mov   al,04h            ; AL = Read Map Select Register number (4)
  99.        out   dx,ax            ; load Read Map Select register
  100.        mov   cx,ss:Count        ; byte count to move (size of plane for
  101.                 ; EGA/VGA card in current mode )
  102.        les   di,ss:Plane_Array  ; Address of array to store plane values.
  103.        rep movsb                ; Move the data from video buffer to save area
  104. ; Or replace the above instruction by the slower but equivalent loop construct
  105. ; below in the event that your VGA card doesn't respond properly.
  106. @@:
  107. ;       lodsb                    ; Get a byte from plane of video buffer
  108. ;       stosb                    ; Save it.
  109. ;       loop  @B                 ; Do next byte, until all have been done.
  110.  
  111. ; Now reset the VGA registers used back to the defaults expected.
  112.        mov   ax,1005h           ; AH = 10h, defaults for modes 12h and 10h
  113.                 ; AL = Mode register number (5)
  114.        out   dx,ax              ; restore default mode register
  115.        mov   ax,0004h            ; AL = Read Map Select Register number (4)
  116.        out   dx,ax            ; load Read Map Select register default value
  117.        mov   ds,bx              ; Restore Turbo's DS
  118.        pop   bp                 ; Restore Turbo's BP
  119.        ret   8                  ; Remove params & return to call
  120. Read_VGA_Plane ENDP
  121. CODE   ENDS
  122.        END
  123.  
  124.  
  125.  
  126. [LISTING TWO]
  127.  
  128. {
  129.  GRFSAVE1.PAS - Unit to save and restore graphics screens
  130.  Version 1.40 (03-19-90)   --depends on MSgraph Unit for 
  131.               manifest constants indentifying graphics modes.
  132.  procedure Write_VGA_Plane (Plane, Count : word; var Plane_Array );
  133.  procedure Read_VGA_Plane  (Plane, Count : word; var Plane_Array );
  134.  Parameters:  Plane       - Graphics plane number to move ( range 0-3 )
  135.           Count       - Byte count to move
  136.           Plane_Array - Array for video plane values
  137.  (C)Copyright 1989-1990 Spirit of Performance, Inc.
  138.  All rights reserved. Unauthorized use or copying prohibited by law.
  139. }
  140.  
  141. {$R-,S-,I-,D+,F+,V-,B-,N-,L+ }
  142.  
  143. UNIT GRFSAVE;
  144.  
  145. INTERFACE
  146. USES MsGraph;
  147.  
  148. const
  149.   Max_Planes = 4;
  150.  
  151. procedure Init_Screen_Save ( Plane_Size : longint; Number_Of_Planes : word );
  152. Function  HeapFunc ( Size: word ) : integer;
  153. function  Save_Screen ( Mode : integer ) : integer;
  154. procedure Restore_Screen;
  155. procedure Write_VGA_Plane (Plane, Count : word; var Plane_Array );
  156. procedure Read_VGA_Plane  (Plane, Count : word; var Plane_Array );
  157.  
  158. IMPLEMENTATION
  159.  
  160. var
  161. { Video plane size and number of planes in bytes }
  162. { ****** When video plane size gets above 64K, need to change below }
  163.     Video_Plane_Size    : word;  { in bytes }
  164.     Number_GPlanes      : word;
  165.     Plane_Counter       : word;
  166.     Plane_Ptrs          : array [1..Max_Planes] of pointer;
  167.     Planes_Saved        : integer;
  168.     Saved_Graphics_Mode : integer;
  169.     Monochrome_Buffer   : ARRAY[ 0..$7FFF ] OF byte ABSOLUTE $B800:$0000;
  170.     VGA_Buffer          : ARRAY[ 0..$7FFF ] OF byte ABSOLUTE $A000:$0000;
  171.  
  172. procedure Init_Screen_Save;
  173.   { Initialize unit with parameters }
  174.   begin
  175.     Video_Plane_Size := Plane_Size;
  176.     Number_GPlanes := Number_Of_Planes;
  177.   end;
  178.  
  179. Function HeapFunc;
  180. { Simple heap error function overrides run time error to avoid program
  181.   abort.  }
  182. begin
  183.   HeapFunc := 1;  { return an error indicator to caller }
  184. end;
  185.  
  186. {$L d:\tpsource\GRFSAVE.obj }
  187. procedure Write_VGA_Plane; external;
  188. procedure Read_VGA_Plane;  external;
  189.  
  190. function Save_Screen;
  191. { Saves graphics planes for current graphics mode.
  192.   Returns number of planes saved, in case caller cares.
  193. }
  194.  
  195.   begin
  196.     Saved_Graphics_Mode := Mode;
  197.     Planes_Saved := 0;
  198.     HeapError := @HeapFunc;
  199.     case Saved_Graphics_Mode of
  200.       _HRes16Color,    { 640 x 200, 16 color }
  201.       _EResColor  ,    { 640 x 350, 4 or 16 color }
  202.       _VRes16Color:    { 640 x 480, 16 color }
  203.       for Plane_Counter := 1 to Number_GPlanes do
  204.     begin
  205.       { Get memory to save a plane }
  206.       GetMem( Plane_Ptrs[Plane_Counter], Video_Plane_Size);
  207.       if Plane_Ptrs[Plane_Counter] <> nil then
  208.         { Move the plane if GetMem succeeded }
  209.         begin
  210.           Read_VGA_Plane (Plane_Counter-1, Video_Plane_Size,
  211.                   Plane_Ptrs[Plane_Counter]^ );
  212.           inc ( Planes_Saved );
  213.         end;
  214.     end;
  215.       _HResBW  ,    { 640 x 200, BW }
  216.       _HercMono:    { 720 x 348, BW for HGC }
  217.     begin
  218.       GetMem( Plane_Ptrs[1], Video_Plane_Size);
  219.       if Plane_Ptrs[Plane_Counter] <> nil then
  220.         begin
  221.           Move ( Monochrome_Buffer, Plane_Ptrs[1]^, Video_Plane_Size );
  222.           Planes_Saved := 1;
  223.         end;
  224.     end;
  225.       _EResNoColor,    { 640 x 350, BW }
  226.       _VRes2Color :    { 640 x 480, BW }
  227.     begin
  228.       GetMem( Plane_Ptrs[1], Video_Plane_Size);
  229.       if Plane_Ptrs[Plane_Counter] <> nil then
  230.         begin
  231.           Move ( VGA_Buffer, Plane_Ptrs[1]^, Video_Plane_Size );
  232.           Planes_Saved := 1;
  233.         end;
  234.     end;
  235.     end; {case Saved_Graphics_Mode}
  236.     if Planes_Saved <> Number_GPlanes then
  237.       { Unsuccessful, so reset count of planes saved }
  238.       Planes_Saved := 0;
  239.     Save_Screen := Planes_Saved;
  240.   end;
  241.  
  242. procedure Restore_Screen;
  243.   begin
  244.     if Planes_Saved <> 0 then
  245.       case Saved_Graphics_Mode of
  246.     _HRes16Color,    { 640 x 200, 16 color }
  247.     _EResColor  ,    { 640 x 350, 4 or 16 color }
  248.     _VRes16Color:    { 640 x 480, 16 color }
  249.       for Plane_Counter := 1 to Number_GPlanes do
  250.       if Plane_Ptrs[Plane_Counter] <> nil then
  251.         Write_VGA_Plane (Plane_Counter-1, Video_Plane_Size,
  252.                  Plane_Ptrs[Plane_Counter]^ );
  253.     _HResBW  ,    { 640 x 200, BW }
  254.     _HercMono:    { 720 x 348, BW for HGC }
  255.       Move ( Plane_Ptrs[1]^, Monochrome_Buffer, Video_Plane_Size );
  256.     _EResNoColor,    { 640 x 350, BW }
  257.     _VRes2Color :    { 640 x 480, BW }
  258.       Move ( Plane_Ptrs[1]^, VGA_Buffer, Video_Plane_Size );
  259.       end; {case Saved_Graphics_Mode}
  260.   end;
  261. END.
  262.  
  263.  
  264.  
  265.  
  266. [LISTING THREE]
  267.  
  268. PROGRAM savedemo;
  269. { savedemo.PAS - Demonstrate EGA/VGA graphics screen save/restore
  270.   Version 1.00, 19 Mar 1990 
  271.   Uses for Microsoft Graphics Interface and selected MASM functions.
  272.   (c)Copyright 1989-1990 Spirit of Performance, Inc.
  273. }
  274.  
  275. USES
  276.     DOS, MSGraph, Crt, GrfSave1;
  277. type
  278.   TimeRec = record
  279.     Hour          : word;
  280.     Minute        : word;
  281.     Second        : word;
  282.     FracSec       : word;
  283.     Floating_Time : real;
  284. end;
  285.  
  286. var
  287.   Start_Time : TimeRec;
  288.   Stop_Time  : TimeRec;
  289.  
  290. Function Elapsed_Time ( Stop_Time, Start_Time : TimeRec ) : real;
  291.  
  292. const
  293.   R3600 : real = 3600.0;
  294.   R60   : real = 60.0;
  295.   R100  : real = 100.0;
  296.  
  297. begin   { Elapsed_Time }
  298.   with Start_Time do
  299.     begin
  300.       Floating_Time := (Hour * R3600) + (Minute * R60) + Second
  301.           + (FracSec / R100);
  302.     end;
  303.   if Stop_Time.Hour < Start_Time.Hour then inc(Stop_Time.Hour, 24);
  304.   with Stop_Time do
  305.     begin
  306.       Floating_Time := (Hour * R3600) + (Minute * R60) + Second
  307.           + (FracSec / R100);
  308.     end;
  309.   Elapsed_Time := Stop_Time.Floating_Time - Start_Time.Floating_Time;
  310. end;    { Elapsed_Time }
  311.  
  312. TYPE
  313.     ViewPortType = record
  314.     x1, y1, x2, y2 : word;
  315.     end;
  316. VAR
  317.     errorcode       : Integer;
  318.     x,y             : Integer;
  319.     maxx, maxy      : Integer; { Maximum addressable pixels }
  320.     c               : Char;
  321.     vc                 : _VideoConfig;
  322.     CurrentView     : ViewPortType;
  323.     lCount          : longint;
  324.     OldExitProc     : Pointer;  { Saves exit procedure address }
  325.     Plane_Count     : integer;
  326. var
  327.     Video_Plane_Size : longint;
  328.     Number_GPlanes   : word;
  329. CONST
  330.     Version_ID     : string = ( 'Version 1.00, 19 Mar 1990' );
  331.     Patterns       : Array [0..11] of _FillMask =
  332.     (
  333.      (0,0,0,0,0,0,0,0),
  334.      ($FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF),
  335.      ($FF, 0, $FF, 0, $FF, 0, $FF, 0),
  336.      ($44, $88, $11, $22, $44, $88, $11, $22),
  337.      ($77, $EE, $DD, $BB, $77, $EE, $DD, $BB),
  338.      ($77, $BB, $DD, $EE, $77, $BB, $DD, $EE),
  339.      ($88, $44, $22, $11, $88, $44, $22, $11),
  340.      ($11, $AA, $44, $AA, $11, $AA, $44, $AA),
  341.      ($55, $AA, $55, $AA, $55, $AA, $55, $AA),
  342.      ($F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F),
  343.      (1, 0, 0, 0, 1, 0, 0, 0),
  344.      (5, 0, 5, 0, 5, 0, 5, 0));
  345.  
  346.   CleanUp_Reqd : Boolean = TRUE;
  347.  
  348. {$F+}
  349. procedure MyExitProc;
  350. { Procedure to clean up on early program termination }
  351. begin
  352.   ExitProc := OldExitProc; { Restore exit procedure address }
  353.   if CleanUp_Reqd then
  354.   begin    { Restore original video mode. }
  355.     errorcode := _SetVideoMode( _DefaultMode );
  356.   end;
  357. end; { MyExitProc }
  358. {$F-}
  359.  
  360. Procedure GetViewSettings (var ReqView : ViewPortType);
  361. begin
  362.     ReqView := CurrentView;
  363. end;
  364.  
  365. Procedure SetView (xa, ya, xb, yb : word);
  366. begin
  367.   _SetViewPort(xa, ya, xb, yb);
  368.   _SetClipRgn (xa, ya, xb, yb);
  369.   with CurrentView do
  370.     begin
  371.       x1 := xa;  y1 := ya;
  372.       x2 := xb;  y2 := yb;
  373.     end;
  374. end;
  375.  
  376. procedure FullPort;
  377. { Set the view port to the entire screen }
  378. begin
  379.   SetView(0, 0, maxx, maxy);
  380. end; { FullPort }
  381.  
  382. procedure MainWindow(Header : string);
  383.  
  384. { Make a default window and view port for demos }
  385. begin
  386.   _SetTextColor(vc.numcolors-1);           { Reset the colors }
  387.   _SetBkColor(0);
  388.   _SetColor(vc.numcolors-1);
  389.   _ClearScreen(_GClearScreen);             { Clear the screen }
  390.   FullPort;                                { Full screen view port }
  391.   _SetTextPosition( 1, (vc.NumTextCols - length(Header)) div 2);
  392.   _OutText(Header);        { Draw the header text }
  393.   { Move the edges in to leave room for text at top and bottom }
  394.   SetView(0, vc.NumYPixels div vc.NumTextRows + 1 , maxx,
  395.            maxy-(vc.NumYPixels div vc.NumTextRows)-1);
  396. end; { MainWindow }
  397.  
  398. procedure StatusLine(Msg : string);
  399. { Display a status line at the bottom of the screen }
  400. begin
  401.   FullPort;
  402.   _SetLineStyle($FFFF);
  403.   _SetFillMask(Patterns[0]);
  404.   _SetColor(0);                   { Set the drawing color to black }
  405.   _Rectangle(_GFillInterior,
  406.         0, vc.NumYPixels-(vc.NumYPixels div vc.NumTextRows+1),
  407.         maxx, maxy);     { Erase old status line }
  408.   _SetTextPosition( vc.NumTextRows,
  409.            (vc.NumTextCols - length(Msg)) div 2);
  410.   _SetTextColor(vc.numcolors-1);  { Set the color for header }
  411.   _SetBkColor(0);
  412.   _OutText(Msg);        { Write the status message }
  413.   { Go back to the main window }
  414.    SetView(0, vc.NumYPixels div vc.NumTextRows +1 , vc.NumXPixels,
  415.           vc.NumYPixels-(vc.NumYPixels div vc.NumTextRows+1));
  416.   _SetTextPosition( 1, 1 );
  417. end; { StatusLine }
  418.  
  419. procedure WaitToGo; { Wait for user to abort program or continue }
  420. const
  421.   Esc = #27;
  422. var
  423.   Ch : char;
  424. begin
  425.   StatusLine('Esc aborts or press a key...');
  426.   with Start_Time do GetTime ( Hour, Minute, Second, FracSec );
  427.   repeat
  428.   with Stop_Time do GetTime ( Hour, Minute, Second, FracSec );
  429.   { Wait for keypress no more then 5 seconds, then go on without it }
  430.   until KeyPressed or (Elapsed_Time ( Stop_Time, Start_Time ) > 5.0);
  431.   if Keypressed then
  432.   begin
  433.     Ch := ReadKey;
  434.     if Ch = #0 then Ch := readkey;      { trap function keys }
  435.     if Ch = Esc then
  436.       Halt(0);                           { terminate program }
  437.   end;
  438. end; { WaitToGo }
  439.  
  440. procedure DrawRectangles;
  441. { Draw rectangles on the screen }
  442. var
  443.   MaxSize : word;
  444.   XCenter, YCenter : word;
  445.   ViewInfo  : ViewPortType;
  446.   YMax, XMax : word;
  447.   jCount : word;
  448.  
  449. begin  { DrawRectangles }
  450.   MainWindow('Draw Rectangles');
  451.   StatusLine('');
  452.   GetViewSettings(ViewInfo);
  453.   with ViewInfo do
  454.   begin
  455.     XMax := (x2-x1-1);
  456.     YMax := (y2-y1-1);
  457.   end;
  458.   MaxSize := XMax shr 1;
  459.   XCenter := XMax shr 1;
  460.   YCenter := YMax shr 1;
  461.   for lCount := 1 to MaxSize do
  462.   begin
  463.     _SetColor(lCount mod vc.numcolors);
  464.     _Rectangle(_GBorder, XCenter+lCount, YCenter+lCount,
  465.              XCenter-LCount, YCenter-LCount);
  466.   end;
  467.   WaitToGo;
  468. end; { DrawRectangles }
  469.  
  470. procedure DrawCircles;
  471. { Draw concentric circles on the screen }
  472. var
  473.   MaxRadius : word;
  474.   XCenter, YCenter : word;
  475.   ViewInfo  : ViewPortType;
  476.   YMax, XMax : word;
  477.  
  478. begin  { DrawCircles }
  479.   MainWindow('Draw Circles');
  480.   StatusLine('');
  481.   GetViewSettings(ViewInfo);
  482.   with ViewInfo do
  483.   begin
  484.     XMax := (x2-x1-1);
  485.     YMax := (y2-y1-1);
  486.   end;
  487.   MaxRadius := XMax shr 1;
  488.   XCenter := XMax shr 1;
  489.   YCenter := YMax shr 1;
  490.   for lCount := 1 to MaxRadius do
  491.   begin
  492.     _SetColor(lCount mod vc.numcolors);
  493.     _Ellipse(_GBorder, XCenter + lCount, YCenter + lCount,
  494.                XCenter - lCount, YCenter - lCount);
  495.   end;
  496.   WaitToGo;
  497. end; { DrawCircles }
  498.  
  499. BEGIN
  500.     OldExitProc := ExitProc;    { save previous exit proc }
  501.     ExitProc := @MyExitProc;    { insert our exit proc in chain }
  502.     _GetVideoConfig( vc );
  503.     DirectVideo := FALSE;   { No direct writes allowed in graphics modes }
  504.  
  505.     { Set graphics mode with highest resolution. }
  506.     if (_SetVideoMode( _MaxResMode) = 0) then
  507.     Halt( 1 );
  508.     _GetVideoConfig( vc );
  509.     if vc.mode <> _HercMono then
  510.       begin
  511.     Video_Plane_Size := vc.numxpixels div 8;
  512.     Video_Plane_Size := Video_Plane_Size * vc.numypixels;
  513.       end
  514.     else
  515.       Video_Plane_Size := 32768;
  516.     if vc.numcolors = 2 then
  517.       Number_GPlanes := 1   { B&W modes have 1 plane only }
  518.     else
  519.       Number_GPlanes := 4;  { Assume that color modes have 4 planes }
  520.     Init_Screen_Save (Video_Plane_Size, Number_GPlanes);
  521.  
  522.     _SetColor( vc.numcolors-1 );
  523.     DrawRectangles;
  524.     Plane_Count := Save_Screen (vc.mode); { Save the first screen }
  525.     DrawCircles;
  526.     Restore_Screen;                       { Restore the rectangles }
  527.     WaitToGo;                             { Wait before terminating }
  528.  
  529.     { Restore original video mode. }
  530.     errorcode := _SetVideoMode( _DefaultMode );
  531.     CleanUp_Reqd := FALSE;
  532. END.
  533.  
  534.  
  535.