_SAVING AND RESTORING VGA SCREENS_ by Ben Myers [LISTING ONE] PAGE 80,132 TITLE EGA/VGA screen save/restore (Turbo Pascal 4.0+ or Quick Pascal 1.0) ; GRFSAVE.ASM - ; (C)Copyright 1989-1990 Spirit of Performance, Inc. ; All rights reserved. Unauthorized use or copying prohibited by law. CODE SEGMENT WORD PUBLIC ASSUME CS:CODE PUBLIC Write_VGA_Plane ; Write video plane from caller's memory. PUBLIC Read_VGA_Plane ; Read video plane, move it to caller's memory. ; procedure Write_VGA_Plane (Plane, Count : word; var Plane_Array ); ; procedure Read_VGA_Plane (Plane, Count : word; var Plane_Array ); ; Parameters: Plane - Graphics plane number to move ( range 0-3 ) ; Count - Byte count to move ; Plane_Array - Array for video plane values Plane_Array EQU DWORD PTR [bp+06h] Count EQU WORD PTR [bp+0Ah] Plane EQU WORD PTR [bp+0Ch] Write_VGA_Plane PROC FAR push bp ; Save Turbo's BP mov bp,sp ; Set up stack frame mov bx,ds ; Save Turbo's DS mov di,0A000h ; EGA/VGA buffer segment:offset, A000:0000 mov es,di xor di,di ; ES:DI is start of video buffer mov dx,3CEh ; DX = Graphics Controller I/O Port mov ax,0005h ; AH = 00h (Read mode 0, write mode 0) ; AL = Mode register number (5) out dx,ax ; load Mode register mov ax,0001h ; AH = 00h (mask for Enable Set/Reset), ; also the default for modes 12h and 10h ; AL = Enable Set/Reset register number (1) out dx,ax ; load Enable Set/Reset register mov ax,0003h ; AH = Replace bit planes with memory, ; no bit rotation, also the default ; AL = Data Rotate/Function Select register ; number (3) out dx,ax ; load Data Rotate/Function Select register ; AL = Bit Mask Register (8) mov ax,0FF08h ; AH = bit mask out dx,ax ; Set bit mask register for all bits mov dx,3C4h ; DX = Sequencer I/O Port mov cx,ss:Plane ; Get Plane number from caller and cl,03h ; Force it to range 0 to 3 mov ah,1 ; Set up AH with bit number of plane to restore shl ah,cl ; where bit 0 = plane 0, etc. mov al,02h ; AL = Map Mask Register number (2) out dx,ax ; load Map Mask register with plane number mov cx,ss:Count ; byte count to move (size of plane for ; EGA/VGA card in current mode ) lds si,ss:Plane_Array ; Addr of array to restore plane values from rep movsb ; Move the data ; Or replace the above instruction by the slower but equivalent loop construct ; below in the event that your VGA card doesn't respond properly. @@: ; lodsb ; Get a byte from save area plane ; stosb ; Form a byte for current plane ; loop @B ; Do next byte, until all have been done. ; Now reset the VGA registers used back to the defaults expected. mov dx,3CEh ; DX = Graphics Controller I/O Port mov ax,0001 ; AH = 0 (default Enable Set/Reset Value) ; AL = Enable Set/Reset register number (1) out dx,ax ; restore default Enable Set/Reset register mov dx,3C4h ; DX = Sequencer I/O port ; AH = all planes enabled mov ax,0F02h ; AL = Map Mask Register number (2) out dx,ax ; restore Map Mask register to do all planes. mov ds,bx ; Restore Turbo's DS pop bp ; Restore Turbo's BP ret 8 ; Remove params & return to call Write_VGA_Plane ENDP ; procedure Read_VGA_Plane (Plane, Count : word; var Plane_Array ); Read_VGA_Plane PROC FAR push bp ; Save Turbo's BP mov bp,sp ; Set up stack frame mov bx,ds ; Save Turbo's DS mov si,0A000h ; EGA/VGA buffer segment:offset, A000:0000 mov ds,si xor si,si ; DS:SI is start of video buffer mov dx,3CEh ; DX = Graphics Controller I/O Port mov ax,0005h ; AH = 00h (Read mode 0, write mode 0) ; AL = 5 (Mode register number) out dx,ax ; load Mode register ;; int 3 ; Enable breakpoint for debugging. mov ax,ss:Plane ; Get Plane number mov ah,al ; AH = color plane to get mov al,04h ; AL = Read Map Select Register number (4) out dx,ax ; load Read Map Select register mov cx,ss:Count ; byte count to move (size of plane for ; EGA/VGA card in current mode ) les di,ss:Plane_Array ; Address of array to store plane values. rep movsb ; Move the data from video buffer to save area ; Or replace the above instruction by the slower but equivalent loop construct ; below in the event that your VGA card doesn't respond properly. @@: ; lodsb ; Get a byte from plane of video buffer ; stosb ; Save it. ; loop @B ; Do next byte, until all have been done. ; Now reset the VGA registers used back to the defaults expected. mov ax,1005h ; AH = 10h, defaults for modes 12h and 10h ; AL = Mode register number (5) out dx,ax ; restore default mode register mov ax,0004h ; AL = Read Map Select Register number (4) out dx,ax ; load Read Map Select register default value mov ds,bx ; Restore Turbo's DS pop bp ; Restore Turbo's BP ret 8 ; Remove params & return to call Read_VGA_Plane ENDP CODE ENDS END [LISTING TWO] { GRFSAVE1.PAS - Unit to save and restore graphics screens Version 1.40 (03-19-90) --depends on MSgraph Unit for manifest constants indentifying graphics modes. procedure Write_VGA_Plane (Plane, Count : word; var Plane_Array ); procedure Read_VGA_Plane (Plane, Count : word; var Plane_Array ); Parameters: Plane - Graphics plane number to move ( range 0-3 ) Count - Byte count to move Plane_Array - Array for video plane values (C)Copyright 1989-1990 Spirit of Performance, Inc. All rights reserved. Unauthorized use or copying prohibited by law. } {$R-,S-,I-,D+,F+,V-,B-,N-,L+ } UNIT GRFSAVE; INTERFACE USES MsGraph; const Max_Planes = 4; procedure Init_Screen_Save ( Plane_Size : longint; Number_Of_Planes : word ); Function HeapFunc ( Size: word ) : integer; function Save_Screen ( Mode : integer ) : integer; procedure Restore_Screen; procedure Write_VGA_Plane (Plane, Count : word; var Plane_Array ); procedure Read_VGA_Plane (Plane, Count : word; var Plane_Array ); IMPLEMENTATION var { Video plane size and number of planes in bytes } { ****** When video plane size gets above 64K, need to change below } Video_Plane_Size : word; { in bytes } Number_GPlanes : word; Plane_Counter : word; Plane_Ptrs : array [1..Max_Planes] of pointer; Planes_Saved : integer; Saved_Graphics_Mode : integer; Monochrome_Buffer : ARRAY[ 0..$7FFF ] OF byte ABSOLUTE $B800:$0000; VGA_Buffer : ARRAY[ 0..$7FFF ] OF byte ABSOLUTE $A000:$0000; procedure Init_Screen_Save; { Initialize unit with parameters } begin Video_Plane_Size := Plane_Size; Number_GPlanes := Number_Of_Planes; end; Function HeapFunc; { Simple heap error function overrides run time error to avoid program abort. } begin HeapFunc := 1; { return an error indicator to caller } end; {$L d:\tpsource\GRFSAVE.obj } procedure Write_VGA_Plane; external; procedure Read_VGA_Plane; external; function Save_Screen; { Saves graphics planes for current graphics mode. Returns number of planes saved, in case caller cares. } begin Saved_Graphics_Mode := Mode; Planes_Saved := 0; HeapError := @HeapFunc; case Saved_Graphics_Mode of _HRes16Color, { 640 x 200, 16 color } _EResColor , { 640 x 350, 4 or 16 color } _VRes16Color: { 640 x 480, 16 color } for Plane_Counter := 1 to Number_GPlanes do begin { Get memory to save a plane } GetMem( Plane_Ptrs[Plane_Counter], Video_Plane_Size); if Plane_Ptrs[Plane_Counter] <> nil then { Move the plane if GetMem succeeded } begin Read_VGA_Plane (Plane_Counter-1, Video_Plane_Size, Plane_Ptrs[Plane_Counter]^ ); inc ( Planes_Saved ); end; end; _HResBW , { 640 x 200, BW } _HercMono: { 720 x 348, BW for HGC } begin GetMem( Plane_Ptrs[1], Video_Plane_Size); if Plane_Ptrs[Plane_Counter] <> nil then begin Move ( Monochrome_Buffer, Plane_Ptrs[1]^, Video_Plane_Size ); Planes_Saved := 1; end; end; _EResNoColor, { 640 x 350, BW } _VRes2Color : { 640 x 480, BW } begin GetMem( Plane_Ptrs[1], Video_Plane_Size); if Plane_Ptrs[Plane_Counter] <> nil then begin Move ( VGA_Buffer, Plane_Ptrs[1]^, Video_Plane_Size ); Planes_Saved := 1; end; end; end; {case Saved_Graphics_Mode} if Planes_Saved <> Number_GPlanes then { Unsuccessful, so reset count of planes saved } Planes_Saved := 0; Save_Screen := Planes_Saved; end; procedure Restore_Screen; begin if Planes_Saved <> 0 then case Saved_Graphics_Mode of _HRes16Color, { 640 x 200, 16 color } _EResColor , { 640 x 350, 4 or 16 color } _VRes16Color: { 640 x 480, 16 color } for Plane_Counter := 1 to Number_GPlanes do if Plane_Ptrs[Plane_Counter] <> nil then Write_VGA_Plane (Plane_Counter-1, Video_Plane_Size, Plane_Ptrs[Plane_Counter]^ ); _HResBW , { 640 x 200, BW } _HercMono: { 720 x 348, BW for HGC } Move ( Plane_Ptrs[1]^, Monochrome_Buffer, Video_Plane_Size ); _EResNoColor, { 640 x 350, BW } _VRes2Color : { 640 x 480, BW } Move ( Plane_Ptrs[1]^, VGA_Buffer, Video_Plane_Size ); end; {case Saved_Graphics_Mode} end; END. [LISTING THREE] PROGRAM savedemo; { savedemo.PAS - Demonstrate EGA/VGA graphics screen save/restore Version 1.00, 19 Mar 1990 Uses for Microsoft Graphics Interface and selected MASM functions. (c)Copyright 1989-1990 Spirit of Performance, Inc. } USES DOS, MSGraph, Crt, GrfSave1; type TimeRec = record Hour : word; Minute : word; Second : word; FracSec : word; Floating_Time : real; end; var Start_Time : TimeRec; Stop_Time : TimeRec; Function Elapsed_Time ( Stop_Time, Start_Time : TimeRec ) : real; const R3600 : real = 3600.0; R60 : real = 60.0; R100 : real = 100.0; begin { Elapsed_Time } with Start_Time do begin Floating_Time := (Hour * R3600) + (Minute * R60) + Second + (FracSec / R100); end; if Stop_Time.Hour < Start_Time.Hour then inc(Stop_Time.Hour, 24); with Stop_Time do begin Floating_Time := (Hour * R3600) + (Minute * R60) + Second + (FracSec / R100); end; Elapsed_Time := Stop_Time.Floating_Time - Start_Time.Floating_Time; end; { Elapsed_Time } TYPE ViewPortType = record x1, y1, x2, y2 : word; end; VAR errorcode : Integer; x,y : Integer; maxx, maxy : Integer; { Maximum addressable pixels } c : Char; vc : _VideoConfig; CurrentView : ViewPortType; lCount : longint; OldExitProc : Pointer; { Saves exit procedure address } Plane_Count : integer; var Video_Plane_Size : longint; Number_GPlanes : word; CONST Version_ID : string = ( 'Version 1.00, 19 Mar 1990' ); Patterns : Array [0..11] of _FillMask = ( (0,0,0,0,0,0,0,0), ($FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF), ($FF, 0, $FF, 0, $FF, 0, $FF, 0), ($44, $88, $11, $22, $44, $88, $11, $22), ($77, $EE, $DD, $BB, $77, $EE, $DD, $BB), ($77, $BB, $DD, $EE, $77, $BB, $DD, $EE), ($88, $44, $22, $11, $88, $44, $22, $11), ($11, $AA, $44, $AA, $11, $AA, $44, $AA), ($55, $AA, $55, $AA, $55, $AA, $55, $AA), ($F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F), (1, 0, 0, 0, 1, 0, 0, 0), (5, 0, 5, 0, 5, 0, 5, 0)); CleanUp_Reqd : Boolean = TRUE; {$F+} procedure MyExitProc; { Procedure to clean up on early program termination } begin ExitProc := OldExitProc; { Restore exit procedure address } if CleanUp_Reqd then begin { Restore original video mode. } errorcode := _SetVideoMode( _DefaultMode ); end; end; { MyExitProc } {$F-} Procedure GetViewSettings (var ReqView : ViewPortType); begin ReqView := CurrentView; end; Procedure SetView (xa, ya, xb, yb : word); begin _SetViewPort(xa, ya, xb, yb); _SetClipRgn (xa, ya, xb, yb); with CurrentView do begin x1 := xa; y1 := ya; x2 := xb; y2 := yb; end; end; procedure FullPort; { Set the view port to the entire screen } begin SetView(0, 0, maxx, maxy); end; { FullPort } procedure MainWindow(Header : string); { Make a default window and view port for demos } begin _SetTextColor(vc.numcolors-1); { Reset the colors } _SetBkColor(0); _SetColor(vc.numcolors-1); _ClearScreen(_GClearScreen); { Clear the screen } FullPort; { Full screen view port } _SetTextPosition( 1, (vc.NumTextCols - length(Header)) div 2); _OutText(Header); { Draw the header text } { Move the edges in to leave room for text at top and bottom } SetView(0, vc.NumYPixels div vc.NumTextRows + 1 , maxx, maxy-(vc.NumYPixels div vc.NumTextRows)-1); end; { MainWindow } procedure StatusLine(Msg : string); { Display a status line at the bottom of the screen } begin FullPort; _SetLineStyle($FFFF); _SetFillMask(Patterns[0]); _SetColor(0); { Set the drawing color to black } _Rectangle(_GFillInterior, 0, vc.NumYPixels-(vc.NumYPixels div vc.NumTextRows+1), maxx, maxy); { Erase old status line } _SetTextPosition( vc.NumTextRows, (vc.NumTextCols - length(Msg)) div 2); _SetTextColor(vc.numcolors-1); { Set the color for header } _SetBkColor(0); _OutText(Msg); { Write the status message } { Go back to the main window } SetView(0, vc.NumYPixels div vc.NumTextRows +1 , vc.NumXPixels, vc.NumYPixels-(vc.NumYPixels div vc.NumTextRows+1)); _SetTextPosition( 1, 1 ); end; { StatusLine } procedure WaitToGo; { Wait for user to abort program or continue } const Esc = #27; var Ch : char; begin StatusLine('Esc aborts or press a key...'); with Start_Time do GetTime ( Hour, Minute, Second, FracSec ); repeat with Stop_Time do GetTime ( Hour, Minute, Second, FracSec ); { Wait for keypress no more then 5 seconds, then go on without it } until KeyPressed or (Elapsed_Time ( Stop_Time, Start_Time ) > 5.0); if Keypressed then begin Ch := ReadKey; if Ch = #0 then Ch := readkey; { trap function keys } if Ch = Esc then Halt(0); { terminate program } end; end; { WaitToGo } procedure DrawRectangles; { Draw rectangles on the screen } var MaxSize : word; XCenter, YCenter : word; ViewInfo : ViewPortType; YMax, XMax : word; jCount : word; begin { DrawRectangles } MainWindow('Draw Rectangles'); StatusLine(''); GetViewSettings(ViewInfo); with ViewInfo do begin XMax := (x2-x1-1); YMax := (y2-y1-1); end; MaxSize := XMax shr 1; XCenter := XMax shr 1; YCenter := YMax shr 1; for lCount := 1 to MaxSize do begin _SetColor(lCount mod vc.numcolors); _Rectangle(_GBorder, XCenter+lCount, YCenter+lCount, XCenter-LCount, YCenter-LCount); end; WaitToGo; end; { DrawRectangles } procedure DrawCircles; { Draw concentric circles on the screen } var MaxRadius : word; XCenter, YCenter : word; ViewInfo : ViewPortType; YMax, XMax : word; begin { DrawCircles } MainWindow('Draw Circles'); StatusLine(''); GetViewSettings(ViewInfo); with ViewInfo do begin XMax := (x2-x1-1); YMax := (y2-y1-1); end; MaxRadius := XMax shr 1; XCenter := XMax shr 1; YCenter := YMax shr 1; for lCount := 1 to MaxRadius do begin _SetColor(lCount mod vc.numcolors); _Ellipse(_GBorder, XCenter + lCount, YCenter + lCount, XCenter - lCount, YCenter - lCount); end; WaitToGo; end; { DrawCircles } BEGIN OldExitProc := ExitProc; { save previous exit proc } ExitProc := @MyExitProc; { insert our exit proc in chain } _GetVideoConfig( vc ); DirectVideo := FALSE; { No direct writes allowed in graphics modes } { Set graphics mode with highest resolution. } if (_SetVideoMode( _MaxResMode) = 0) then Halt( 1 ); _GetVideoConfig( vc ); if vc.mode <> _HercMono then begin Video_Plane_Size := vc.numxpixels div 8; Video_Plane_Size := Video_Plane_Size * vc.numypixels; end else Video_Plane_Size := 32768; if vc.numcolors = 2 then Number_GPlanes := 1 { B&W modes have 1 plane only } else Number_GPlanes := 4; { Assume that color modes have 4 planes } Init_Screen_Save (Video_Plane_Size, Number_GPlanes); _SetColor( vc.numcolors-1 ); DrawRectangles; Plane_Count := Save_Screen (vc.mode); { Save the first screen } DrawCircles; Restore_Screen; { Restore the rectangles } WaitToGo; { Wait before terminating } { Restore original video mode. } errorcode := _SetVideoMode( _DefaultMode ); CleanUp_Reqd := FALSE; END.