unit Scrnsavr; {$F+} (*************************************************************************) (* Screen Saver *) (* *) (* Written by Jay A. Key -- Oct 1993 *) (* Code may be modified and used freely. Please mention my name *) (* somewhere in your docs or in the program itself. *) (* *) (* Self contained unit to install a text-mode screen saver in Turbo *) (* Pascal programs. Simply include the following line in your code. *) (* uses ScrnSavr; *) (* *) (* It will initialize itself automatically, and will remove itself *) (* upon exit from your program, graceful exit or not. Functions *) (* SetTimeOut and SetDelay are included if you wish to modify the *) (* default values. *) (* *) (* Warning: will not properly save and restore screens while running *) (* under the Turbo Pascal IDE. Runs great from DOS. *) (*************************************************************************) interface uses Dos, Crt; function NumRows : byte; {Returns number of rows in current screen} function ColorAdaptor : boolean; {TRUE if color video card installed} procedure SetTimeOut(T : integer); {Delay(seconds) before activation} procedure SetDelay(T : integer); {Interval between iterations} implementation type VideoArray = array [1..2000] of word; {buffer to save video screen} var Timer : word; Waiting : boolean; OldInt15, {Keyboard interrupt} OldInt1C, {Timer interrupt} OldInt23, {Cntl-C/Cntl-Break handler} ExitSave : pointer; Position, Cursor : integer; {save and restore cursor positions} VideoSave : VideoArray; VideoMem : ^VideoArray; TimeOut, Delay : integer; procedure JumpToPriorIsr(p : pointer); {Originally written by Brook Monroe, "An ISR Clock", pg. 64, PC Techniques Aug/Sep 1992} inline($5b/$58/$87/$5e/$0e/$87/$46/$10/$89/$ec/$5d/$07/$1f/ $5f/$5e/$5a/$59/$cb); function ColorAdaptor : boolean; assembler; asm int 11 {BIOS call - get equipment list} and al,$0010 {mask off all but bit 4} xor al,$0010 {flip bit 4 - return val is in al} end; function NumRows : byte; assembler; {returns number of displayable rows} asm mov ax,$40 mov es,ax mov ax,$84 mov di,ax mov al,[es:di] {byte at [$40:$84] is number of rows in display} end; procedure HideCursor; assembler; asm mov ah,$03 xor bh,bh int $10 {video interrupt} mov Position,dx {save cursor position} mov Cursor,cx {and type} mov ah,$01 mov ch,$20 int $10 {video interrupt - hide cursor} end; procedure RestoreCursor; assembler; asm mov ah,$02 xor bh,bh mov dx,Position {get old position} int $10 {video interrupt - restore cursor position} mov cx,Cursor {get old cursor type} mov ah,$01 int $10 {video interrupt - restore cursor type} end; procedure RestoreScreen; begin VideoMem^ := VideoSave; {Copy saved image back onto video memory} RestoreCursor; end; procedure SaveScreen; begin VideoSave := VideoMem^; {Copy video memory to array} HideCursor; end; procedure DispMsg; {simple stub-out for displaying YOUR message(s), pictures, etc...use your imagination!!!} begin ClrScr; GotoXY(random(50), random(23)); writeln('This would normally be something witty!'); end; procedure NewInt15(Flags,CS,IP,AX,BX,CX,DX, SI,DI,DS,ES,BP:WORD); interrupt; {keyboard handler} begin Timer := 0; {Reset timer} if Waiting then {Screen saver activated?} begin RestoreScreen; {Restore saved screen image} Waiting := FALSE; {De-activate screen saver} Flags := (Flags and $FFFE); {Tell BIOS to ignore current keystroke} end else JumpToPriorISR(OldInt15); {call original int 15} end; procedure NewInt1C; interrupt; {timer interrupt} begin Inc(Timer); {Increment timer} if Timer > TimeOut then {No key hit for TimeOut seconds?} begin Waiting := TRUE; {Activate screen saver} SaveScreen; {Save image of video memory} DispMsg; {Display your own message} Timer := 0; {Reset timer} end; if waiting then {Is saver already active?} begin if Timer > Delay then {Time for next message?} begin Timer := 0; {Reset timer} DispMsg; {Display next message} end; end; JumpToPriorISR(OldInt1C); {Chain to old timer interrupt} end; procedure ResetIntVectors; {Restores Intrrupt vectors to orig. values} begin SetIntVec($15, OldInt15); SetIntVec($1C, OldInt1C); SetIntVec($23, OldInt23); end; procedure NewInt23; interrupt;{Called to handle cntl-c/brk} begin ResetIntVectors; {Restore old interrupt vectors} JumpToPriorISR(OldInt23); {Chain to original int 23h} end; procedure MyExit; far; {exit code for unit} begin ResetIntVectors; {Restore old interrupt vectors} ExitProc := ExitSave; {Restore old exit code} end; procedure SetVideoAddress; {Returns pointer to text video memory} begin if ColorAdaptor then VideoMem := ptr($B000, $0000) else VideoMem := ptr($B800, $0000); end; procedure SetTimeOut(T : integer); {Set delay(seconds) before activation} begin TimeOut := Round(T * 18.2); end; procedure SetDelay(T : integer); {Set interval between iterations} begin Delay := Round(T * 18.2); end; {Initialize unit} begin SetVideoAddress; {Set up address for video memory} Waiting := FALSE; {Screen saver initially OFF} Timer := 0; {Reset timer} ExitSave := ExitProc; {Save old exit routine} ExitProc := @MyExit; {Install own exit routine} {Install user defined int vectors} GetIntVec($15, OldInt15); {Keyboard handler} SetIntVec($15, @NewInt15); GetIntVec($1c, OldInt1C); {Timer int} SetIntVec($1c, @NewInt1C); GetIntVec($23, OldInt23); {Cntl-C/Brk handler} SetIntVec($23, @NewInt23); SetTimeOut(120); SetDelay(15); end.