SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00011 TSR UTILITIES AND ROUTINES 1 05-28-9314:09ALL SWAG SUPPORT TEAM CLKTSR.PAS IMPORT 16 /ÅWN {$A+,B-,D-,E-,F-,I-,L-,N-,O-,R-,S-,V-}π{$M 1024,0,0}πUses Dos,π clock; { My clock ISR Unit in next message. }πConstπ IRet : Word = $cf;π IDStr : String[13]='TeeCee''s Clock';πVarπ p : Pointer;πbeginπ GetIntVec($66,p); { Int 66h is reserved For user defined interrupts }π inc(LongInt(p),2);π if String(p^) = IDStr then beginπ Writeln('TeeCee''s clock is already installed - you must be blind!');π halt;π endπ else beginπ Writeln('TeeCee''s clock is now installed For demo purposes only');π SetIntVec($66,@IRet); { IRet is obviously not an interrupt! }π { What we are actually doing is storing a Pointer to the IDStr }π { in the vector table - much like the way the Video font addresses }π { are stored. }π SwapVectors;π Keep(0);π end;πend.ππ(See the next message For the Unit Clock. )ππNow that is definintely For demonstration purposes only! It will work but has πseveral serious shortcomings!ππFirstly, it hooks the user defined interrupt $66 to allow any subsequent πexecutions to determine if the Program is already installed. It does this πwithout making any checks as to whether or not something else already "owns" πthis vector. Not very smart!ππSecondly, it provides no means to uninstall itself. Mmmmm... :-(ππThirdly, Graphics mode will cause problems. Again - not terribly smart!ππFinally, TSRs are not For the faint-of-heart or beginner. They are an πextraordinarily complex and difficult part of Dos Programming and if you are πserious about getting into TSRs then get as many good books as you can find πthat authoritively discuss the subject. Buying a good commercial toolbox, such πas Turbo Power's "TSRs Made Easy" is another smart move, as studying how the πmasters do it is one of the best ways to learn.π 2 05-28-9314:09ALL TREVOR J. CARLSEN Display "ON SCREEN" ClockIMPORT 42 /Åσ≥ Unit Clock;π{π Author: Trevor J Carlsen π Purpose: Demonstrate a simple "on screen" clock.π π This demo Unit works by "hooking" the timer interrupt ($1c). Thisπ interrupt is called by the hardware interrupt ($08) approximately 18.2π times every second and normally consists of a simple return instructionπ unless some other application has already hooked it.π π Because the routine is called roughly 18 times every second it isπ important that any processing it contains is optimised as much asπ possible. Obviously the best way to do this is by assembly language butπ in this demo I have used almost pure Turbo Pascal and have set up aπ counter Variable and any processing is only done every 6 calls. This isπ more than sufficient and minimises processing. The routine is by noπ means perfect - there will be a minor irregularity For the final 10π seconds of each day and For about half a second each hour. Better thisπ than to waste valuable processing time in the interrupt by coding itπ out.π π Because Dos is not re-entrant it is also important that the routine makeπ no calls to any Procedure or Function that makes use of Dos For itsπ operation. Thus no Writeln, Write can be used. To display the time onπ screen an Array is addressed directly to screen memory. Any changes inπ this Array are thus reflected on the screen. The downside to this isπ that on older CGAs this would cause a "snow" effect and code would beπ needed to eliminate this. It also means that the TP Procedure GetTimeπ cannot be used. So the time is calculated from the value stored at theπ clock tick counter location.π π To display an on-screen clock all that is required is For a Programmerπ to include this Unit in the Uses declaration of the Program.}π πInterfaceππConstπ DisplayClock : Boolean = True;ππImplementationπ{ Everything is private to this Unit }ππUses Dos;ππConstπ line = 0; { Change as required For position of display on screen }π column = 72; { Top left corner is 0,0 }π ScreenPos = (line * 160) + (column * 2);π Colour = $1f; { White on Blue }π ZeroChar = Colour shl 8 + ord('0'); π Colon = Colour shl 8 + ord(':');πTypeπ timestr = Array[0..7] of Word;π timeptr = ^timestr;πVarπ time : timeptr;π OldInt1c : Pointer;π ExitSave : Pointer;ππ{$F+}π Procedure Int1cISR; interrupt;π { This will be called every clock tick by hardware interrupt $08 }π Constπ count : Integer = 0; { To keep track of our calls }π Varπ hr : Word Absolute $40:$6e;π ticks : Word Absolute $40:$6c; π { This location keeps the number of clock ticks since 00:00}π min,π sec : Byte;π seconds : Word;π beginπ Asm cli end;π if DisplayClock then beginπ inc(count);π if count = 6 then { ticks to update the display } beginπ count := 0; { equality check and assignment faster than mod 9 }π seconds := ticks * LongInt(10) div 182; { speed = no Reals }π min := (seconds div 60) mod 60;π sec := seconds mod 60;ππ { The following statements are what actually display the on-screen time}ππ time^[0] := ZeroChar + (hr div 10); { first Char of hours }π time^[1] := ZeroChar + (hr mod 10); { second Char of hours }π time^[2] := Colon;π time^[3] := ZeroChar + (min div 10); { first Char of minutes }π time^[4] := ZeroChar + (min mod 10); { second Char of minutes }π time^[5] := Colon;π time^[6] := ZeroChar + (sec div 10); { first Char of seconds }π time^[7] := ZeroChar + (sec mod 10); { second Char of seconds }π end; { if count = 6 }π end; { if DisplayClock }π Asm π stiπ pushf { push flags to set up For IRET }π call OldInt1c; { Call old ISR entry point }π end;π end; { Int1cISR }ππProcedure ClockExitProc;π { This Procedure is VERY important as you have hooked the timer interrupt }π { and therefore if this is omitted when the Unit is terminated your }π { system will crash in an unpredictable and possibly damaging way. }π beginπ ExitProc := ExitSave;π SetIntVec($1c,OldInt1c); { This "unhooks" the timer vector }π end;π{$F-}ππProcedure Initialise;π Varπ mode : Byte Absolute $40:$49;π beginπ if mode = 7 then { must be a mono adaptor }π time := ptr($b000,ScreenPos)π else { colour adaptor of some kind }π time := ptr($b800,ScreenPos); π GetIntVec($1c,OldInt1c); { Get old timer vector and save it }π ExitSave := ExitProc; { Save old Exit Procedure }π ExitProc := @ClockExitProc; { Setup a new Exit Procedure }π SetIntVec($1c,@Int1cISR); { Hook the timer vector to the new Procedure }π end; { Initialise } π πbegin π Initialise;πend.ππ 3 05-28-9314:09ALL BOB SWART Clock ISR unit IMPORT 29 /Å└╤ {I have made some changes in your Unit Check, in order to make ik a bit fasterπand use somewhat less code and data space (61 Bytes in all). Also, the displayπof progress on screen is now 'ticking' because I swap the colors from white onπblue to gray on blue (perhaps a nice idea, now you can see if the machineπReally crashed)...π}π{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,R-,S+,V-,X-}π{$M 8192,0,655360}π{$DEFINE COLOR}πUnit MyCheck;π{π TeeCee Bob Swart Saved:π Code size: 514 Bytes 455 Bytes 59 Bytesπ Data size: 32 Bytes 30 Bytes 2 Bytesππ Here is the $1C ISR that I will add (unless you wish to do that).ππ Some changes were made, which resulted in less code and data size, aπ little more speed, and display of the progress Variable on screen isπ made 'ticking' each second by changing the colour from white on blueπ to gray on blue and back With each update.π}πInterfaceππVar progress: LongInt Absolute $0040:$00F0;ππImplementationπ{ Everything is private to this Unit }πUses Dos;ππConstπ Line = 0; { Change as required For position of display on screen }π Column = 72; { Top left corner is 0,0 }π ScreenPos = (line * 80 * 2) + (column * 2);π Colour: Byte = $1F; { White/Gray on Blue }ππTypeπ TimeStr = Array[0..15] of Char;π TimePtr = ^TimeStr;ππVarπ {$IFDEF COLOR}π Time: TimeStr Absolute $B800:ScreenPos; { Assume colour display adaptor }π {$ELSE}π Time: TimeStr Absolute $B000:ScreenPos; { Otherwise mono display adaptor }π {$endIF}π OldInt1C: Pointer;π ExitSave: Pointer;πππ{$F+}πProcedure Int1CISR; Interrupt;π{ This will be called every clock tick by hardware interrupt $08 }πConst DisplayTickCount = 20;π TickCount: LongInt = DisplayTickCount;π HexChars: Array[$0..$F] of Char = '0123456789ABCDEF';πVar HexA: Array[0..3] of Byte Absolute progress;πbeginπ Asmπ cliπ end;π inc(TickCount);π if TickCount > DisplayTickCount then { ticks to update the display }π beginπ TickCount := 0; { equality check and assignment faster than mod }π { The following statements actually display the on-screen time }π Colour := Colour xor $08; { Swap between white and gray on blue }π FillChar(Time[1],SizeOf(Time)-1,Colour);π Time[00] := HexChars[HexA[3] SHR 4];π Time[02] := HexChars[HexA[3] and $F];π Time[04] := HexChars[HexA[2] SHR 4];π Time[06] := HexChars[HexA[2] and $F];π Time[08] := HexChars[HexA[1] SHR 4];π Time[10] := HexChars[HexA[1] and $F];π Time[12] := HexChars[HexA[0] SHR 4];π Time[14] := HexChars[HexA[0] and $F]π end { if TickCount > DisplayTickCount };π Asmπ stiπ pushf { push flags to set up For IRET }π call OldInt1C { Call old ISR entry point }π endπend {Int1CISR};π{$F-}πππProcedure ClockExitProc; Far;π{ This Procedure is VERY important as you have hooked the timer interrupt }π{ and therefore if this is omitted when the Unit is terminated your }π{ system will crash in an unpredictable and possibly damaging way. }πbeginπ ExitProc := ExitSave;π SetIntVec($1C,OldInt1C); { This "unhooks" the timer vector }πend {ClockExitProc};πππbeginπ progress := 0;π ExitSave := ExitProc; { Save old Exit Procedure }π ExitProc := @ClockExitProc; { Setup a new Exit Procedure }π GetIntVec($1C,OldInt1C); { Get old timer vector and save it }π SetIntVec($1C,@Int1CISR); { Hook the timer vector to the new Procedure }πend.ππ 4 05-28-9314:09ALL SWAG SUPPORT TEAM Prevent Ctl/Alt/Del Keys IMPORT 8 /Å│å PROGRAM NoBoot ;π{$M 1024, 0, 0 } { TSR : reserve 1K stack no heap }π{$S-} { Needed in a TSR }ππUsesπ Crt, { Sound }π Dos,π KeyIntr ;ππVarπ OldInt09 : Pointer ;ππ{$F+}πProcedure NewInt09 ; Interrupt ;πBeginπ EnableInterrupts ; { Delete key }π If ControlPressed and AltPressed and (ReadScanCode = $53) thenπ Beginπ ResetKeyboard ; { Ignore key }π EOI ;ππ Sound( 880 ) ; { optional }π Delay( 100 ) ;π Sound( 440 ) ;π Delay( 100 ) ;π NoSound ;ππ Endπ Elseπ CallInterrupt( OldInt09 ) ;πEnd ;ππBEGINπ GetIntVec( $09, OldInt09 ) ;π SetIntVec( $09, Addr(NewInt09) ) ;π Keep( 0 ) ; { make this a TSR }πEND.π 5 05-28-9314:09ALL SWAG SUPPORT TEAM Screen Saver TSR IMPORT 16 /Å┐┤ { I'm sorry that my reply Sounded rude, it wasn't meant as such. Probablyπthe best way to make a screen saver TSR is to latch onto inT $8, which isπcalled once a second to update the clock, using GetIntVec and SetIntVec.πSince your other TSR code is probably a normal Procedure For whatever otherπinterrupts you are using, just put the screen blanker Procedure inside theπother Procedure, and hopefully when you use Keep Dos will retain both yourπnormal TSR code and the screen saver code.π}π{$M 4096,0,0}π{$N-,S-}πProgram TSRplusSaver;πUses Dos;ππProcedure MyTSR (Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : Word);πinTERRUPT;πConst Maximum = 120; {2 minutes}πVar Elapsed : Word;πVar Saving : Boolean;ππProcedure ResetSvr (Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : Word);πInterrupt;πbeginπ if Saving then beginπ Saving := False;π Port[984] := 41; {Enable 6845 video}π end;π Elapsed := 0;π end;ππProcedure MyScreenSaver (Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : Word);πInterrupt;πbeginπ Inc (Elapsed);π if Elapsed=Maximum thenπ Port[984] := 33; {Disable 6845 video}π Saving := True;π end;π end;ππbegin {MyTSR}π MemW[$b800:$0000] := 3585; {Happy face}π end;ππbeginπ SetIntVec( $09, @ResetSvr); {Reset screen saver on Keypress}π SetIntVec( $08, @MyScreenSaver); {Increment elapsed every second,π activate when ready}π SetIntVec( $1C, @MyTSR); {Set up your TSR code}π Keep(0);πend.ππ{ I'm pretty sure something like this will work, but I haven't tried itπmyself yet. of course you'll have to add CLI instructions at theπbeginning of each of the interrupt Procedure and a restore interrupts afterπit, so nothing can occur during them except NMI. You may have some troubleπthere, since on the PCjr the NMI includes keyboard input (pretty stupid,πhuh?)π} 6 05-28-9314:09ALL SWAG SUPPORT TEAM General Purpose TSR Unit IMPORT 300 /ż8 Unit TSRUnit; {Create TSR Programs With Turbo Pascal 5.0 & TSRUnit}ππ{$B-,F-,I+,R-,S+} {Set Compiler directives to normal values.}ππInterface {=======================================================}π{πThe author and any distributor of this software assume no responsi-πbility For damages resulting from this software or its use due toπerrors, omissions, inCompatibility With other software or withπhardware, or misuse; and specifically disclaim any implied warrantyπof fitness For any particular purpose or application.π}πUses Dos, Crt;πConstπ{*** Shift key combination codes. }π AltKey = 8; CtrlKey = 4; LeftKey = 2; RightKey = 1;ππ TSRVersion : Word = $0204; {Low Byte.High Byte = 2.04 }ππTypeπ String80 = String[80];π ChrWords = Record Case Integer ofπ 1: ( W: Word );π 2: ( C: Char; A: Byte );π end;π LineWords = Array[1..80] of ChrWords;π WordFuncs = Function : Word;ππVarπ TSRScrPtr : Pointer; {Pointer to saved screen image. }π TSRChrPtr : Pointer; {Pointer to first Character to insert. }π TSRMode : Byte; {Video mode --------- beFore TSR popped up.}π TSRWidth : Byte; {Number of screen columns-- " " " " .}π TSRPage : Byte; {Active video page number-- " " " " .}π TSRColumn : Byte; {Cursor column number ----- " " " " .}π TSRRow : Byte; {Cursor row number -------- " " " " .}π{π** Procedure For installing the TSR Program. }πProcedure TSRInstall( TSRName : String; {Name or title For TSR. }π TSRFunc : WordFuncs;{Ptr to Function to call}π ShiftComb: Byte; {Hot key--shift key comb}π KeyChr : Char ); {Hot Key--Character key.}π{π ShiftComb and KeyChr specify the default hot keys For the TSR.π ShiftComb may be created by adding or oring the Constants AltKey,π CtrlKey, LeftKey, and RightKey together. KeyChr may beπ Characters 0-9 and A-Z.ππ The default hot keys may be overridden when the TSR is installedπ by specifying optional parameters on the command line. Theπ parameter Format is:π [/A] [/C] [/R] [/L] [/"[K["]]]π The square brackets surround optional items--do not include them.π Any Characters between parameters are ignored. The order of theπ Characters does not matter; however, the shift keys specified areπ cummulative and the last Character key "K" specified is the used.π}π{π** Functions For checking status of Printer LPT1. }πFunction PrinterOkay: Boolean; {Returns True if Printer is okay.}πFunction PrinterStatus: Byte; {Returns status of Printer.π Definition of status Byte bits (1 & 2 are not used), if set then:π Bit: -- 7 --- ---- 6 ---- -- 5 --- -- 4 --- -- 3 -- --- 0 ---π not busy Acknowledge No paper Selected I/O Err. Timed-outπ}π{π** Routines For obtaining one row of screen Characters. }πFunction ScreenLineStr( Row: Byte ): String80; {Returns Char. str.}πProcedure ScreenLine( Row: Byte; Var Line: LineWords; {Returns }π Var Words: Byte ); {chr & color}ππImplementation {==================================================}πVarπ BuffSize, InitCMode : Word;π NpxFlag : Boolean;π Buffer : Array[0..8191] of Word;π NpxState : Array[0..93] of Byte;π RetrnVal, InitVideo : Byte;π TheirFunc : WordFuncs;ππConst {offsets to items contained in Procedure Asm. }π UnSafe = 0; Flg = 1; Key = 2; Shft = 3;π Stkofs = 4; StkSs = 6; DosSp = 8; DosSs = 10;π Prev = 12; Flg9 = 13; InsNumb = 14;π Dos21 = $10; Dos25 = Dos21+4; Dos26 = Dos25+4;π Bios9 = Dos26+4; Bios16 = Bios9+4; DosTab = Bios16+4;π Our21 = DosTab+99; Our25 = Our21+51; Our26 = Our25+27;π Our09 = Our26+27; Our16 = Our09+127+8; InsChr = Our16+180-8;π PopUp = InsChr+4;ππProcedure Asm1; {Inline code--data storage and intercept routines. }πINTERRUPT;πbeginπInline(π{*** Storage For interrupt vectors. }π {Dos21: } >0/>0/ {Dos func. intr vector. }π {Dos25: } >0/>0/ {Dos abs. disk read intr. vector. }π {Dos26: } >0/>0/ {Dos abs. sector Write intr.vector. }π {Bios9: } >0/>0/ {BIOS key stroke intr. vector. }π {Bios16: } >0/>0/ {BIOS buffered keybd. input intr.vect.}ππ {DosTab: Array[0..98] of Byte = {Non-reetrant Dos Functions.}π 0/0/0/0/0/0/0/0/ 0/0/0/0/0/1/1/1/ 1/1/1/1/1/1/1/1/π 1/1/1/1/1/1/1/1/ 1/1/1/1/1/1/0/1/ 1/1/1/1/1/1/1/0/π 1/0/0/0/0/0/1/1/ 1/1/1/1/1/1/1/1/ 1/1/1/1/1/1/1/1/π 0/0/0/0/0/0/1/1/ 0/0/0/0/1/0/1/1/ 0/1/1/1/1/0/0/0/ 0/0/0/ππ{*** OurIntr21 ******* Intercept routine For Dos Function Intr.***}π{ 0} $9C/ { PUSHF ;Save flags. }π{ 1} $FB/ { STI ;Enable interrupts. }π{ 2} $80/$FC/$63/ { CMP AH,63H ;Assume unsafe if new }π{ 5} $73/<22-7/ { JNB IncF ;Function--skip table.}π{ 7} $50/ { PUSH AX ;Save Registers. }π{ 8} $53/ { PUSH BX ;Load offset to table.}π{ 9} $BB/>DosTab/ { MOV BX,[DosTab] }π{ 12} $8A/$C4/ { MOV AL,AH ;Load table entry }π{ 14} $2E/ { CS: ;index. }π{ 15} $D7/ { XLAT ;Get value from table.}π{ 16} $3C/$00/ { CMP AL,0 ;if True then set flag}π{ 18} $5B/ { POP BX ;Restore Registers. }π{ 19} $58/ { POP AX ; }π{ 20} $74/$17/ { JZ JmpDos21 ;Jump to orig. intr. }π{ 22} $2E/ {IncF: CS: ; }π{ 23} $FE/$06/>UnSafe/ { inC [UnSafe] ;Set UnSafe flag. }π{ 27} $9D/ { POPF ;Restore flags. }π{ 28} $9C/ { PUSHF ; }π{ 29} $2E/ { CS: ; }π{ 30} $FF/$1E/>Dos21/ { CALL Far [Dos21] ;Call orig. intr. }π{ 34} $FB/ { STI ;Enable interrupts. }π{ 35} $9C/ { PUSHF ;Save flags. }π{ 36} $2E/ { CS: ; }π{ 37} $FE/$0E/>UnSafe/ { DEC [UnSafe] ;Clear UnSafe flag. }π{ 41} $9D/ { POPF ;Restore flags. }π{ 42} $CA/$02/$00/ { RETF 2 ;Return & remove flag.}ππ{ 45} $9D/ {JmpDos21: POPF ;Restore flags. }π{ 46} $2E/ { CS: ; }π{ 47} $FF/$2E/>Dos21/ { JMP Far [Dos21] ;Jump to orig. intr. }π{ 51}π{*** OurIntr25 ********** Intercept routine For Dos Abs. Read *** }π{ 0} $9C/ { PUSHF ;Save flags. }π{ 1} $2E/ { CS: ; }π{ 2} $FE/$06/>UnSafe/ { inC [UnSafe] ;Set UnSafe flag. }π{ 6} $9D/ { POPF ;Restore flags. }π{ 7} $9C/ { PUSHF ; }π{ 8} $2E/ { CS: ; }π{ 9} $FF/$1E/>Dos25/ { CALL Far [Dos25] ;Call Dos abs. read. }π{ 13} $68/>Our25+19/ { PUSH Our25+19 ;Clean up stack with- }π{ 16} $C2/$02/$00/ { RET 2 ;out changing flags. }π{ 19} $9C/ { PUSHF ;Save flags. }π{ 20} $2E/ { CS: ; }π{ 21} $FE/$0E/>UnSafe/ { DEC [UnSafe] ;Clear UnSafe flag. }π{ 25} $9D/ { POPF ;Restore flags. Leave}π{ 26} $CB/ { RETF ;old flags on the stk.}π{ 27}π{*** OurIntr26 ********** Intercept routine For Dos Abs. Write ***}π{ 0} $9C/ { PUSHF ;Save flags. }π{ 1} $2E/ { CS: ; }π{ 2} $FE/$06/>UnSafe/ { inC [UnSafe] ;Set UnSafe flag. }π{ 6} $9D/ { POPF ;Restore flags. }π{ 7} $9C/ { PUSHF ; }π{ 8} $2E/ { CS: ; }π{ 9} $FF/$1E/>Dos26/ { CALL Far [Dos26] ;Call Dos abs. Write. }π{ 13} $68/>Our26+19/ { PUSH Our26+19 ;Clean up stack with- }π{ 16} $C2/$02/$00/ { RET 2 ;out changing flags. }π{ 19} $9C/ { PUSHF ;Save flags. }π{ 20} $2E/ { CS: ; }π{ 21} $FE/$0E/>UnSafe/ { DEC [UnSafe] ;Clear UnSafe flag. }π{ 25} $9D/ { POPF ;Restore flags. Leave}π{ 26} $CB/ { RETF ;old flags on the stk.}π{ 27}ππ{*** OurIntr9 ********** Intercept For BIOS Hardware Keyboard Intr}π{ 0} $9C/ { PUSHF ;Entry point. }π{ 1} $FB/ { STI ;Enable interrupts. }π{ 2} $1E/ { PUSH DS ; }π{ 3} $0E/ { PUSH CS ;DS := CS; }π{ 4} $1F/ { POP DS ; }π{ 5} $50/ { PUSH AX ;Preserve AX on stack.}π{ 6} $31/$C0/ { xor AX,AX ;Set AH to 0. }π{ 8} $E4/$60/ { in AL,60h ;Read Byte from keybd }π{ 10} $3C/$E0/ { CMP AL,0E0h ;if multi-Byte codes, }π{ 12} $74/<75-14/ { JE Sfx ;then jump and set }π{ 14} $3C/$F0/ { CMP AL,0F0h ;multi-Byte flag, Flg9}π{ 16} $74/<75-18/ { JE Sfx ; }π{ 18} $80/$3E/>Flg9/$00/ { CMP [Flg9],0 ;Exit if part of }π{ 23} $75/<77-25/ { JNZ Cfx ;multi-Byte code. }π{ 25} $3A/$06/>Key/ { CMP AL,[Key] ;Exit if key pressed }π{ 29} $75/<88-31/ { JNE PreExit ;is not hot key. }ππ{ 31} $50/ { PUSH AX ;Hot key was pressed, }π{ 32} $06/ { PUSH ES ;check shift key }π{ 33} $B8/$40/$00/ { MOV AX,0040h ;status Byte. First }π{ 36} $8E/$C0/ { MOV ES,AX ;load BIOS segment. }π{ 38} $26/ { ES: ; }π{ 39} $A0/>$0017/ { MOV AL,[0017h] ;AL:= Shift key status}π{ 42} $07/ { POP ES ;Restore ES register. }π{ 43} $24/$0F/ { and AL,0Fh ;Clear unwanted bits. }π{ 45} $3A/$06/>Shft/ { CMP AL,[Shft] ;Exit if not hot key }π{ 49} $58/ { POP AX ;shift key combination}π{ 50} $75/<88-52/ { JNE PreExit ;(Restore AX first). }ππ { ;Hot Keys encountered.}π{ 52} $3A/$06/>Prev/ { CMP AL,[Prev] ;Discard Repeated hot }π{ 56} $74/<107-58/ { JE Discard ;key codes. }π{ 58} $A2/>Prev/ { MOV [Prev],AL ;Update Prev. }π{ 61} $F6/$06/>Flg/3/ { TEST [Flg],3 ;if Flg set, keep key }π{ 66} $75/<99-68/ { JNZ JmpBios9 ;& Exit to orig. BIOS }π{ 68} $80/$0E/>Flg/1/ { or [Flg],1 ;9. else set flag and}π{ 73} $EB/<107-75/ { JMP SHorT Discard;discard key stroke. }ππ{ 75} $B4/$01/ {Sfx: MOV AH,1 ;Load AH With set flag}π{ 77} $88/$26/>Flg9/ {Cfx: MOV [Flg9],AH ;Save multi-Byte flag.}π{ 81} $C6/$06/>Prev/$FF/ { MOV [Prev],0FFh ;Change prev key Byte.}π{ 86} $EB/<99-88/ { JMP SHorT JmpBios9 }ππ{ 88} $3C/$FF/ {PreExit: CMP AL,0FFh ;Update previous key }π{ 90} $74/<99-92/ { JE JmpBios9 ;unless key is buffer-}π{ 92} $3C/$00/ { CMP AL,0 ;full code--a 00h }π{ 94} $74/<99-96/ { JZ JmpBios9 ;0FFh }π{ 96} $A2/>Prev/ { MOV [Prev],AL ;Update previous key. }ππ{ 99} $58/ {JmpBios9: POP AX ;Restore Registers and}π{100} $1F/ { POP DS ;flags. }π{101} $9D/ { POPF ; }π{102} $2E/ { CS: ; }π{103} $FF/$2E/>Bios9/ { JMP [Bios9] ;Exit to orig. intr 9.}ππ{107} $E4/$61/ {Discard: in AL,61h ;Clear key from buffer}π{109} $8A/$E0/ { MOV AH,AL ;by resetting keyboard}π{111} $0C/$80/ { or AL,80h ;port and sending EOI }π{113} $E6/$61/ { OUT 61h,AL ;to intr. handler }π{115} $86/$E0/ { XCHG AH,AL ;telling it that the }π{117} $E6/$61/ { OUT 61h,AL ;key has been }π{119} $B0/$20/ { MOV AL,20h ;processed. }π{121} $E6/$20/ { OUT 20h,AL ; }π{123} $58/ { POP AX ;Restore Registers and}π{124} $1F/ { POP DS ;flags. }π{125} $9D/ { POPF ; }π{126} $CF/ { IRET ;Return from interrupt}π{127}ππ{*** OurIntr16 ***** Intercept routine For Buffered Keyboard Input}π{ 0} $58/ {JmpBios16: POP AX ;Restore AX, DS, and }π{ 1} $1F/ { POP DS ;FLAGS Registers then }π{ 2} $9D/ { POPF ;exit to orig. BIOS }π{ 3} $2E/ { CS: ;intr. 16h routine. }π{ 4} $FF/$2E/>Bios16/ { JMP [Bios16] ; }ππ{ 8} $9C/ {OurIntr16: PUSHF ;Preserve FLAGS. }π{ 9} $FB/ { STI ;Enable interrupts. }π{ 10} $1E/ { PUSH DS ;Preserve DS and AX }π{ 11} $50/ { PUSH AX ;Registers. }π{ 12} $0E/ { PUSH CS ;DS := CS; }π{ 13} $1F/ { POP DS ; }π{ 14} $F6/$C4/$EF/ { TEST AH,EFh ;Jmp if not read Char.}π{ 17} $75/<48-19/ { JNZ C3 ;request. }ππ {*** Intercept loop For Read Key service.}π{ 19} $F6/$06/>Flg/1/ {C1: TEST [Flg],1 ;if pop up Flg bit is }π{ 24} $74/<29-26/ { JZ C2 ;set then call Inline }π{ 26} $E8/>122-29/ { CALL toPopUp ;pop up routine. }π{ 29} $F6/$06/>Flg/16/{C2: TEST [Flg],10h ;Jmp if insert flg set}π{ 34} $75/<48-36/ { JNZ C3 ; }π{ 36} $FE/$C4/ { inC AH ;Use orig. BIOS }π{ 38} $9C/ { PUSHF ;service to check For }π{ 39} $FA/ { CLI ;Character ready. }π{ 40} $FF/$1E/>Bios16/ { CALL Far [Bios16];Disable interrupts. }π{ 44} $58/ { POP AX ;Restore AX and save }π{ 45} $50/ { PUSH AX ;it again. }π{ 46} $74/<19-48/ { JZ C1 ;Loop Until chr. ready}ππ{ 48} $F6/$06/>Flg/17/{C3: TEST [Flg],11h ;Exit if neither bit }π{ 53} $74/<-55/ { JZ JmpBios16 ;of Flg is set. }π{ 55} $F6/$06/>Flg/$01/ { TEST [Flg],1 ;if pop up Flg bit is }π{ 60} $74/<65-62/ { JZ C4 ;set then call Inline }π{ 62} $E8/>122-65/ { CALL toPopUp ;pop up routine. }π{ 65} $F6/$06/>Flg/$10/{C4:TEST [Flg],10h ;Exit unless have }π{ 70} $74/<-72/ { JZ JmpBios16 ;Characters to insert.}π{ 72} $F6/$C4/$EE/ { TEST AH,0EEh ;if request is not a }π{ 75} $75/<-77/ { JNZ JmpBios16 ;chr. request, Exit. }ππ {*** Insert a Character. }π{ 77} $58/ { POP AX ;AX := BIOS service no}π{ 78} $53/ { PUSH BX ;Save BX and ES. }π{ 79} $06/ { PUSH ES ; }π{ 80} $C4/$1E/>InsChr/ { LES BX,[InsChr] ;PTR(ES,BX) := InsChr;}π{ 84} $26/ { ES: ;AL := InsChr^; }π{ 85} $8A/$07/ { MOV AL,[BX] ; }π{ 87} $07/ { POP ES ;Restore ES and BX. }π{ 88} $5B/ { POP BX ; }π{ 89} $F6/$C4/$01/ { TEST AH,01h ;if AH in [$01,$11] }π{ 92} $B4/$00/ { MOV AH,00h ; then ReportOnly; }π{ 94} $75/<114-96/ { JNZ ReportOnly ;Set Scan code to 0. }π{ 96} $FE/$06/>InsChr/ { inC [InsChr] ;Inc( InsChr ); }π{100} $FF/$0E/>InsNumb/ { DEC [InsNumb] ;Dec( InsNumb ); }π{104} $75/<111-106/ { JNZ SkipReset ;if InsNumb = 0 then }π{106} $80/$26/>Flg/$EF/ { and [Flg],0EFh ; Clear insert chr flg}π{111} $1F/ {SkipReset: POP DS ;Restore BX, DS, and }π{112} $9D/ { POPF ;FLAGS, then return }π{113} $CF/ { IRET ;from interrupt. }ππ{114} $1F/ {ReportOnly: POP DS ;Report Char. ready. }π{115} $9D/ { POPF ;Restore DS and FLAGS.}π{116} $50/ { PUSH AX ;Clear zero flag bit }π{117} $40/ { inC AX ;to indicate a }π{118} $58/ { POP AX ;Character ready. }π{119} $CA/>0002/ { RETF 2 ;Exit & discard FLAGS }ππ {*** Interface to PopUpCode Routine. }π{122} $50/ {toPopUp: PUSH AX ;Save AX. }π{123} $FA/ { CLI ;Disable interrupts. }π{124} $F6/$06/>UnSafe/$FF/{TEST [UnSafe],0FFh ;if UnSafe <> 0 }π{129} $75/<177-131/ { JNZ PP2 ; then Return. }π{131} $A0/>Flg/ { MOV AL,[Flg] ;Set in-use bit; clear}π{134} $24/$FE/ { and AL,0FEh ;pop up bit of Flg. }π{136} $0C/$02/ { or AL,2 ;Flg := (Flg and $FE) }π{138} $A2/>Flg/ { MOV [Flg],AL ; or 2; }π { ;**Switch to our stack}π{141} $A1/>Stkofs/ { MOV AX,[Stkofs] ;Load top of our stack}π{144} $87/$C4/ { XCHG AX,SP ;Exchange it With }π{146} $A3/>DosSp/ { MOV [DosSp],AX ;stk.ptr, save old SP.}π{149} $8C/$16/>DosSs/ { MOV [DosSs],SS ;Save old SS. }π{153} $8E/$16/>StkSs/ { MOV SS,[StkSs] ;Replace SS With our }π{157} $FB/ { STI ;SS. Enable interrupts}ππ{158} $9C/ { PUSHF ;Interrupt call to pop}π{159} $FF/$1E/>PopUp/ { CALL Far [PopUp] ;up TSR routine. }ππ{163} $FA/ { CLI ;Disable interrupts. }π{164} $8B/$26/>DosSp/ { MOV SP,[DosSp] ;Restore stack ptr }π{168} $8E/$16/>DosSs/ { MOV SS,[DosSs] ;SS:SP. Clear in-use }π{172} $80/$26/>Flg/$FD/ { and [Flg],0FDh ;bit of Flg. }ππ{177} $FB/ {PP2: STI ;Enable interrupts. }π{178} $58/ { POP AX ;Restore AX. }π{179} $C3 ); { RET ;Return. }π{180}πend; {Asm.} {end corresponds to 12 Bytes of code--used For storage}ππProcedure PopUpCode; {Interface between the BIOS intercept }πINTERRUPT; {routines and your TSR Function. }πConst BSeg = $0040; VBiosofs = $49;πTypeπ VideoRecs = Recordπ VideoMode : Byte;π NumbCol, ScreenSize, Memoryofs : Word;π CursorArea : Array[0..7] of Word;π CursorMode : Word;π CurrentPage : Byte;π VideoBoardAddr : Word;π CurrentMode, CurrentColor : Byte;π end;πVarπ Regs : Registers;π VideoRec : VideoRecs;π KeyLock : Byte;π ScrnSeg, NumbChr : Word;πbeginπ SwapVectors; {Set T.P. intr. vectors.}π Move( Ptr(BSeg,VBiosofs)^, VideoRec, {Get Video BIOS info. }π Sizeof(VideoRec) );π With VideoRec, Regs do beginπ if (VideoMode > 7) or {Abort pop up if unable}π (ScreenSize > BuffSize) then begin {to save screen image. }π SwapVectors; {Restore intr. vectors.}π Exit;π end;π KeyLock := Mem[BSeg:$0017]; {Save lock key states. }π if VideoMode = 7 then ScrnSeg := $B000 {Save screen--supports }π else ScrnSeg := $B800; {Text, MGA & CGA modes.}π Move( PTR( ScrnSeg, Memoryofs )^, Buffer, ScreenSize );π AX := InitVideo; {if in Graphics mode, }π if (VideoMode >=4) {switch to Text mode. }π and (VideoMode <= 6) then Intr( $10, Regs );π AX := $0500; {Select display page 0.}π Intr( $10, Regs );π CX := InitCMode; {Set cursor size. }π AH := 1;π Intr( $10, Regs );ππ TSRMode := VideoMode; {Fill global Variables }π TSRWidth := NumbCol; {with current inFormation}π TSRPage := CurrentPage;π TSRColumn := Succ( Lo( CursorArea[CurrentPage] ) );π TSRRow := Succ( Hi( CursorArea[CurrentPage] ) );ππ if NpxFlag then {Save co-processor state.}π Inline( $98/ $DD/$36/>NpxState ); {WAIT FSAVE [NpxState] }π{π*** Call user's Program and save return code--no. Char. to insert.π}π NumbChr := TheirFunc;π MemW[CSeg:InsNumb] := NumbChr;π if NumbChr > 0 then begin {Have Char. to insert.}π MemL[CSeg:InsChr] := LongInt( TSRChrPtr );π Mem[CSeg:Flg] := Mem[CSeg:Flg] or $10;π end;π{π*** Pop TSR back down--Restore Computer to previous state.π}π if NpxFlag then {Restore co-prcssr state.}π Inline( $98/ $DD/$36/>NpxState ); {WAIT FSAVE [NpxState] }ππ Mem[BSeg:$17] := {Restore key lock status.}π (Mem[BSeg:$17] and $0F) or (KeyLock and $F0);ππ if Mem[BSeg:VBiosofs] <> VideoMode then beginπ AX := VideoMode; {Restore video mode. }π Intr( $10, Regs );π end;π AH := 1; CX := CursorMode; {Restore cursor size. }π Intr( $10, Regs );π AH := 5; AL := CurrentPage; {Restore active page. }π Intr( $10, Regs );π AH := 2; BH := CurrentPage; {Restore cursor positon. }π DX := CursorArea[CurrentPage];π Intr( $10, Regs ); {Restore screen image. }π Move( Buffer, PTR( ScrnSeg, Memoryofs )^, ScreenSize );ππ SwapVectors; {Restore non-T.P. vectors.}π end;πend; {PopUp.}π{π***** Printer Functions:π}πFunction PrinterStatus: Byte; {Returns status of LPT1.}π{ Definition of status Byte bits (1 & 2 are not used), if set then:π Bit: -- 7 --- ---- 6 ---- -- 5 --- -- 4 --- -- 3 -- --- 0 ---π not busy Acknowledge No paper Selected I/O Err. Timed-outπ}πVar Regs : Registers;πbeginπ With Regs do beginπ AH := 2; DX := 0; {Load BIOS Function and Printer number. }π Intr( $17, Regs ); {Call BIOS Printer services. }π PrinterStatus := AH; {Return With Printer status Byte. }π end;πend; {PrinterStatus.}ππFunction PrinterOkay: Boolean; {Returns True if Printer is okay. }πVar S : Byte;πbeginπ S := PrinterStatus;π if ((S and $10) <> 0) and ((S and $29) = 0) thenπ PrinterOkay := Trueπ else PrinterOkay := False;πend; {PrinterOkay.}π{π***** Procedures to obtain contents of saved screen image.π}πProcedure ScreenLine( Row: Byte; Var Line: LineWords;π Var Words: Byte );πbeginπ Words := 40; {Determine screen line size.}π if TSRMode > 1 then Words := Words*2; {Get line's }π Move( Buffer[Pred(Row)*Words], Line, Words*2 ); {Characters and }πend; {ScreenLine.} {colors. }ππFunction ScreenLineStr( Row: Byte ): String80; {Returns just Chars}πVarπ Words, i : Byte;π LineWord : LineWords;π Line : String80;πbeginπ ScreenLine( Row, LineWord, Words ); {Get Chars & attributes. }π Line := ''; {Move Characters to String}π For i := 1 to Words do Insert( LineWord[i].C, Line, i );π ScreenLineStr := Line;πend; {ScreenString.}π{π***** TSR Installation Procedure.π}πProcedure TSRInstall( TSRName: String; TSRFunc: WordFuncs;π ShiftComb: Byte; KeyChr: Char );πConstπ ScanChr = '+1234567890++++QWERTYUIOP++++ASDFGHJKL+++++ZXCVBNM';π CombChr = 'RLCA"';πVarπ PlistPtr : ^String;π i, j, k : Word;π Regs : Registers;π Comb, ScanCode : Byte;πbeginπ if ofs( Asm1 ) <> 0 then Exit; {offset of Asm must be 0}π MemW[CSeg:StkSs] := SSeg; {Save Pointer to top of }π MemW[CSeg:Stkofs] := Sptr + 562; {TSR's stack. }π MemL[CSeg:PopUp] := LongInt(@PopUpCode); {Save PopUpCode addr. }π TheirFunc := TSRFunc; {& their TSR func. addr.}π Writeln('Installing Stay-Resident Program: ',TSRName );π{π***** Save intercepted interrupt vectors: $09, $16, $21, $25, $26.π}π GetIntVec( $09, Pointer( MemL[CSeg:Bios9] ) );π GetIntVec( $16, Pointer( MemL[CSeg:Bios16] ) );π GetIntVec( $21, Pointer( MemL[CSeg:Dos21] ) );π GetIntVec( $25, Pointer( MemL[CSeg:Dos25] ) );π GetIntVec( $26, Pointer( MemL[CSeg:Dos26] ) );π{π***** Get equipment list and video mode.π}π With Regs do beginπ Intr( $11, Regs ); {Check equipment list For }π NpxFlag := (AL and 2) = 2; {math co-processor. }π AH := 15; {Get current video mode }π Intr( $10, Regs ); {and save it For when TSR }π InitVideo := AL; {is activated. }π AH := 3; BH := 0; {Get current cursor size }π Intr( $10, Regs ); {and save it For when TSR }π InitCMode := CX; {is activated. }π end; {WITH Regs}π{π***** Get info. on buffer For saving screen image.π}π BuffSize := Sizeof( Buffer );π TSRScrPtr := @Buffer;π{π*** Determine activation key combination.π}π Comb := 0; i := 1; {Create ptr to }π PlistPtr := Ptr( PrefixSeg, $80 ); {parameter list. }π While i < Length( PlistPtr^ ) do begin {Check For parameters.}π if PlistPtr^[i] = '/' then begin {Process parameter. }π Inc( i );π j := Pos( UpCase( PlistPtr^[i] ), CombChr );π if (j > 0) and (j < 5) then Comb := Comb or (1 SHL Pred(j))π else if j <> 0 then begin {New activation Char. }π Inc( i ); k := Succ( i );π if i > Length(PlistPtr^) then KeyChr := #0π else beginπ if ((k <= Length(PlistPtr^)) and (PlistPtr^[k] = '"'))π or (PlistPtr^[i] <> '"') then KeyChr := PlistPtr^[i]π else KeyChr := #0;π end; {else begin}π end; {else if ... begin}π end; {if PlistPtr^[i] = '/'}π Inc( i );π end; {While ...}π if Comb = 0 then Comb := ShiftComb; {Use default combination. }π if Comb = 0 then Comb := AltKey; {No default, use [Alt] key.}π ScanCode := Pos( UpCase( KeyChr ), ScanChr ); {Convert Char. to}π if ScanCode < 2 then begin {scan code. }π ScanCode := 2; KeyChr := '1';π end;π Mem[CSeg:Shft] := Comb; {Store shift key combination}π Mem[CSeg:Key] := ScanCode; {and scan code. }π{π*** Output an installation message: Memory used & activation code.π}π {Writeln( 'Memory used is approximately ',π ( ($1000 + Seg(FreePtr^) - PrefixSeg)/64.0):7:1,' K (K=1024).');π }Writeln(π'Activate Program by pressing the following keys simultaneously:');π if (Comb and 1) <> 0 then Write(' [Right Shift]');π if (Comb and 2) <> 0 then Write(' [Left Shift]');π if (Comb and 4) <> 0 then Write(' [Ctrl]');π if (Comb and 8) <> 0 then Write(' [Alt]');π Writeln(' and "', KeyChr, '".');π{π*** Intercept orig. interrupt vectors; then Exit and stay-resident.π}π SetIntVec( $21, Ptr( CSeg, Our21 ) );π SetIntVec( $25, Ptr( CSeg, Our25 ) );π SetIntVec( $26, Ptr( CSeg, Our26 ) );π SetIntVec( $16, Ptr( CSeg, Our16 ) );π SetIntVec( $09, Ptr( CSeg, Our09 ) );π SwapVectors; {Save turbo intr.vectors.}π MemW[CSeg:UnSafe] := 0; {Allow TSR to pop up. }π Keep( 0 ); {Exit and stay-resident. }πend; {TSRInstall.}πend. {TSRUnit.}πππProgram TSRDemo; {An example TSR Program created using TSRUnit. }ππ{$M $0800,0,0} {Set stack and heap size For demo Program. }ππUses Crt, Dos, TSRUnit; {Specify the TSRUnit in the Uses statement.}π {Do not use the Printer Unit, instead treat}π {the Printer like a File; i.e. use the }π {Assign, ReWrite, and Close Procedures. }ππConst DemoPgmName : String[16] = 'TSR Demo Program';ππVarπ Lst : Text; {Define Variable name For the Printer. }π TextFile : Text; { " " " " a data File. }π InsStr : String; {Storage For Characters to be inserted into}π {keyboard input stream--must be a gobal or }π {heap Variable. }ππFunction IOError: Boolean; {Provides a message when an I/O error}πVar i : Word; {occurs. }πbeginπ i := Ioresult;π IOError := False;π if i <> 0 then beginπ Writeln('I/O Error No. ',i);π IOError := True;π end;πend; {OurIoresult.}π{π***** Demo routine to be called when TSRDemo is popped up.π be Compiled as a Far Function that returns a Word containingπ the number of Characters to insert into the keyboard inputπ stream.π}π{$F+} Function DemoTasks: Word; {$F-}πConstπ FileName : String[13] = ' :TSRDemo.Dat';π endPos = 40;π Wx1 = 15; Wy1 = 2; Wx2 = 65; Wy2 = 23;πVarπ Key, Drv : Char;π Done, IOErr : Boolean;π InputPos, RowNumb : Integer;π DosVer : Word;π InputString : String;ππ Procedure ClearLine; {Clears current line and resets line Pointer}π beginπ InputString := ''; InputPos := 1;π GotoXY( 1, WhereY ); ClrEol;π end;ππbeginπ DemoTasks := 0; {Default to 0 Characters to insert.}π Window( Wx1, Wy1, Wx2, Wy2 ); {Set up the screen display. }π TextColor( Black );π TextBackground( LightGray );π LowVideo;π ClrScr; {Display initial messages. }π Writeln;π Writeln(' Example Terminate & Stay-Resident (TSR) Program');π Writeln(' --written With Turbo Pascal 5.0 and Uses TSRUnit.');π Window( Wx1+1, Wy1+4, Wx2-1, Wy1+12);π TextColor( LightGray );π TextBackground( Black );π ClrScr; {Display Function key definitions. }π Writeln;π Writeln(' Function key definitions:');π Writeln(' [F1] Write message to TSRDEMO.DAT');π Writeln(' [F2] " " to Printer.');π Writeln(' [F3] Read from saved screen.');π Writeln(' [F8] Exit and insert Text.');π Writeln(' [F10] Exit TSR and keep it.');π Write( ' or simply echo your input.');ππ {Create active display Window. }π Window( Wx1+1, Wy1+14, Wx2-1, Wy2-1 );π ClrScr;π {Display system inFormation. }π Writeln('TSRUnit Version: ', Hi(TSRVersion):8, '.',π Lo(TSRVersion):2 );π Writeln('Video Mode, Page:', TSRMode:4, TSRPage:4 );π Writeln('Cursor Row, Col.:', TSRRow:4, TSRColumn:4 );ππ DosVer := DosVersion;π Writeln('Dos Version: ', Lo(DosVer):8, '.', Hi(DosVer):2 );ππ InputString := ''; {Initialize Variables. }π InputPos := 1;π Done := False;ππ Repeat {Loop For processing keystrokes. }π GotoXY( InputPos, WhereY ); {Move cursor to input position. }π Key := ReadKey; {Wait For a key to be pressed. }π if Key = #0 then begin {Check For a special key. }π Key := ReadKey; {if a special key, get auxiliary}π Case Key of {Byte to identify key pressed. }ππ{Cursor Keys and simple editor.}π{Home} #71: InputPos := 1;π{Right} #75: if InputPos > 1 then Dec( InputPos );π{Left} #77: if (InputPos < Length( InputString ))π or ((InputPos = Length( InputString ))π and (InputPos < endPos )) then Inc( InputPos );π{end} #79: beginπ InputPos := Succ( Length( InputString ) );π if InputPos > endPos then InputPos := endPos;π end;π{Del} #83: beginπ Delete( InputString, InputPos, 1 );π Write( Copy( InputString, InputPos, endPos ), ' ');π end;ππ{Function Keys--TSRDemo's special features.}π{F1} #59: begin {Write short message to a File. }π ClearLine;π Repeatπ Write('Enter disk drive: ',FileName[1] );π Drv := UpCase( ReadKey ); Writeln;π if Drv <> #13 then FileName[1] := Drv;π Writeln('Specifying an invalid drive will cause your');π Write('system to crash. Use drive ',π FileName[1], ': ? [y/N] ');π Key := UpCase( ReadKey ); Writeln( Key );π Until Key = 'Y';π Writeln('Writing to ',FileName );π {$I-} {Disable I/O checking.}π Assign( TextFile, 'TSRDemo.Dat' );π if not IOError then begin {Check For error. }π ReWrite( TextFile );π if not IOError then beginπ Writeln(TextFile,'File was written by TSRDemo.');π IOErr := IOError;π Close( TextFile );π IOErr := IOError;π end;π end;π {$I+} {Enable standard I/O checking.}π Writeln('Completed File operation.');π end; {F1}ππ{F2} #60: begin {Print a message, use TSRUnit's auxiliary }π {Function PrinterOkay to check Printer status. }π ClearLine;π Writeln('Check Printer status, then print if okay.');π if PrinterOkay then begin {Check if Printer is okay}π Assign( Lst, 'LPT1' ); {Define Printer device. }π ReWrite( Lst ); {Open Printer. }π Writeln( Lst, 'Printing perFormed from TSRDemo');π Close( Lst ); {Close Printer. }π endπ else Writeln('Printer is not ready.');π Writeln( 'Completed print operation.' );π end; {F2}ππ{F3} #61: begin {Display a line from the saved screen image--not}π {valid if the TSR was popped up While the }π {display was in a Graphics mode. }π ClearLine;π Case TSRMode of {Check video mode of saved image.}π 0..3,π 7: beginπ {$I-}π Repeatπ Writeln('Enter row number [1-25] from ');π Write('which to copy Characters: ');π Readln( RowNumb );π Until not IOError;π {$I+}π if RowNumb <= 0 then RowNumb := 1;π if RowNumb > 25 then RowNumb := 25;π Writeln( ScreenLineStr( RowNumb ) );π end;π else Writeln('not valid For Graphics modes.');π end; {Case TSRMode}π end; {F3}π{F8} #66: begin {Exit and insert String into keyboard buffer.}π ClearLine;π Writeln('Enter Characters to insert;');π Writeln('Up to 255 Character may be inserted.');π Writeln('Terminate input String by pressing [F8].');π InsStr := '';π Repeat {Insert Characters into a}π Key := ReadKey; {Until [F8] is pressed. }π if Key = #0 then begin {Check For special key.}π Key := ReadKey; {Check if key is [F8]. }π if Key = #66 then Done := True; {[F8] so done. }π endπ else begin {not special key, add it to the String.}π if Length(InsStr) < Pred(Sizeof(InsStr)) thenπ beginπ if Key = #13 then Writelnπ else Write( Key );π InsStr := InsStr + Key;π endπ else Done := True; {Exceeded Character limit. }π end;π Until Done;π DemoTasks := Length( InsStr ); {Return no. of chr. }π TSRChrPtr := @InsStr[1]; {Set ptr to 1st chr.}π end; {F8}ππ{F10} #68: Done := True; {Exit and Stay-Resident. }ππ end; {Case Key}π end {if Key = #0}π else begin {Key pressed was not a special key--just echo it. }π Case Key ofπ{BS} #08: begin {Backspace}π if InputPos > 1 then beginπ Dec( InputPos );π Delete( InputString, InputPos, 1 );π GotoXY( InputPos, WhereY );π Write( Copy( InputString, InputPos, endPos ), ' ');π end;π end; {BS}π{CR} #13: begin {Enter}π Writeln;π InputString := '';π InputPos := 1;π end; {CR}π{Esc} #27: ClearLine;π elseπ if Length( InputString ) >= endPos thenπ Delete( InputString, endPos, 1 );π Insert( Key, InputString, InputPos );π Write( Copy( InputString, InputPos, endPos ) );π if InputPos < endPos thenπ Inc( InputPos );π end; {Case...}π end; {else begin--Key <> #0}π Until Done;πend; {DemoTasks.}ππbeginπ TSRInstall( DemoPgmName, DemoTasks, AltKey, 'E' );πend. {TSRDemo.}πππ 7 05-28-9314:09ALL LARRY HADLEY Remove TSR IMPORT 30 /Å█« {ok i would like some info on how to remove a tsrππFollow these steps:ππ I tested out some TSR code today and came up With this. It's beenπ debugged and Functions as advertised. not as clean as I'd like,π but it works.π}ππ{**********************************************π * CLICK.PAS by Larry Hadley 2-02-1993 *π * donated to the public domain. if you use *π * this code or derive from it, credit would *π * be appreciated. *π ********************************************** }ππ{$S-,N-}π{$M 1024, 0, 0}πProgram CLICK;ππUsesπ Crt,Dos;ππVarπ SavedInt09h,π SavedInt66h :Pointer;ππProcedure keyClick;πbeginπ Sound(50);π Delay(1);π NoSound;πend;ππProcedure Int09h; interrupt;πbeginπ keyClick; { Sound click everytime called -π this is clumsy because key releases asπ well as keypresses are signalled. Goodπ thing this is For demo only! :-) }π Asmπ pushf { push flags to simulate "int" call }π call SavedInt09h { pass control to original int09 handler -π necessary to allow keyboard use. Alsoπ demo's chaining of interrupts. }π end;πend;ππProcedure Int66h(AX, BX, CX, DX, SI, DI, DS, ES, BP:Word); interrupt;πVarπ int09new :Pointer;πbeginπ if AX<>$FFFF thenπ Exit; { not our call, leave }ππ GetIntVec($09, int09new);π if int09new<>@int09h thenπ Exit; { interrupt vectors have been changed. }ππ SetIntVec($09, SavedInt09h); { restore interrupt vectors }π SetIntVec($66, SavedInt66h);ππ MemW[PrefixSeg:$16] := BX; { modify PSP to return to calling }π MemW[PrefixSeg:$0A] := DI; { Program... }π MemW[PrefixSeg:$0C] := ES;ππ Asmπ mov ah, $50π mov bx, PrefixSegπ push dsπ int $21 { set conText }π pop dsπ end;π AX := 0; { tell caller "no error" }πend;ππbegin { main - t.s.r. init code }π GetIntVec($09, SavedInt09h);π GetIntVec($66, SavedInt66h);ππ SetIntVec($09, #Int09h);π SetIntVec($66, @Int66h);ππ Writeln('Click TSR installed.');ππ Keep(0);πend.ππ{************************************************π * CLICKU.PAS by Larry Hadley 2-02-1993 *π * CLICK T.S.R. removal Program *π * released into the public domain. if you use *π * this code or derive from it, credit would be *π * appreciated. *π ************************************************}ππ{$S-,N-}πProgram CLICKU;ππUsesπ Dos;ππVarπ rtn_seg,π rtn_ofs : Word;π return : Pointer;ππLabel int66_error;ππProcedure Exit_Label; { ...to provide an address For Dos return to }πbeginπ Halt(0); { I haven't been able to establish For sure thatπ this code regains control here. BTW, Brian I haveπ code to save DS and restore upon return to thisπ Program if you're interested. This would allowπ using global Variables to save SS:SP. Int 21h funcπ $4C destroys DS (and just about everything else)π on Exit...}πend;ππbeginπ return := @exit_Label;π rtn_seg := SEG(return^);π rtn_ofs := ofS(return^);π Asmπ mov ax, $FFFFπ mov bx, PrefixSegπ mov es, rtn_segπ mov di, rtn_ofs { pass parms in Registers ax, bx, es, di}π int $66 { call i.s.r. uninstall Function }π cmp ax, 0π jne int66_error { i.s.r. has returned error }π end;π Writeln('Click TSR uninstalled.');π Asmπ mov ah, $4Cπ int $21 { Dos terminate }π end;ππ int66_error:π Writeln('Error removing TSR.');π Halt(1);πend.π 8 07-16-9306:03ALL STEVE MULLIGAN Write to DISK in a TSR IMPORT 50 /Å ===========================================================================π BBS: Canada Remote SystemsπDate: 06-23-93 (10:24) Number: 27349πFrom: STEVE MULLIGAN Refer#: NONEπ To: EDWARD WALKER Recvd: NO πSubj: TSRS THAT WRITE TO DISK.. Conf: (1221) F-PASCALπ---------------------------------------------------------------------------πTuesday June 22 1993 02:38, Edward Walker wrote to All:ππ EW> What do I need to set up in the code to write to disk in a TSR?ππHere's a TSR called BootRes. It opens a file and writes to disk every xπseconds :ππ=-=-=-=-=-=-=-=-= PART 1 =-=-=-=-=-=-=-=-=-=πprogram BootRes;ππ{$M 2048,0,0}π{$F+}ππUses BootVars, Crt, Dos;ππconstπ OLDSTACKSS : WORD = 0;π OLDSTACKSP : WORD = 0;π STACKSW : INTEGER = - 1;π OurStackSeg : word = 0;π OurStackSp : word = 0;π DosDelimSet : set of Char = ['\', ':', #0];ππvarπ R : registers;π DosSeg, DosBusy : word;π Tick, WaitBuf : integer;π NeedPop : boolean;ππPROCEDURE BEGINint;πINLINE($FF/$06/STACKSW/π $75/$10/π $8C/$16/OLDSTACKSS/π $89/$26/OLDSTACKSP/π $8E/$16/OURSTACKSEG/π $8B/$26/OURSTACKSP);ππPROCEDURE ENDint;πINLINE($FF/$0E/STACKSW/π $7D/$08/π $8E/$16/OLDSTACKSS/π $8B/$26/OLDSTACKSP);ππPROCEDURE CALLPOP(SUB:POINTER);πBEGINπINLINE($FF/$5E/$06);πEND;ππPROCEDURE CLI; INLINE($FA);πPROCEDURE STI; INLINE($FB);ππfunction Exist(fname : string) : boolean;πvarπ f1 : file; err : integer;πbeginπ {$I-}π assign(f1,fname); reset(f1); err := ioresult;π {$I+}π if err = 0 then close(f1); exist := err = 0;πend;ππ function AddBackSlash(DirName : string) : string;π {-Add a default backslash to a directory name}π beginπ if DirName[Length(DirName)] in DosDelimSet thenπ AddBackSlash := DirNameπ elseπ AddBackSlash := DirName+'\';π end;ππprocedure TsrCrap;πbeginπ CLI;π BEGINint;π STI;ππ NeedPop := False;ππ GetDate(h, m, s, hund);π TimeLoad.Year := h;π TimeLoad.Month := m;π TimeLoad.Day := s;π GetTime(h, m, s, hund);π TimeLoad.Hour := h;π TimeLoad.Min := m;π TimeLoad.Sec := s;ππ DoDate;π DoDate2;ππ if not exist(LogName) then beginπ assign(LogFile, LogName);π rewrite(LogFile);π write(LogFile, LogRec);π close(LogFile);π end;ππ assign(LogFile, LogName);π reset(LogFile);π if FileSize(LogFile) = 0 then beginπ close(LogFile);π assign(LogFile, LogName);π rewrite(LogFile);π write(LogFile, LogRec);π close(LogFile);π assign(LogFile, LogName);π reset(LogFile);π end;π seek(LogFile, FileSize(LogFile) - 1);π read(LogFile, LogRec);π DoDate2;π seek(LogFile, FileSize(LogFile) - 1);π write(LogFile, LogRec);π close(LogFile);π Tick := 0;ππ CLI;π ENDint;π STI;πend;π=-=-=-=-=-=-=-=-= PART 1 =-=-=-=-=-=-=-=-=-=ππ--- GoldED 2.41π * Origin: Ask me about SubMove * Carp, Ontario (1:163/307.30)π===========================================================================π BBS: Canada Remote SystemsπDate: 06-23-93 (10:25) Number: 27350πFrom: STEVE MULLIGAN Refer#: NONEπ To: EDWARD WALKER Recvd: NO πSubj: TSRS THAT WRITE TO DISK.. Conf: (1221) F-PASCALπ---------------------------------------------------------------------------π=-=-=-=-=-=-=-=-= PART 2 =-=-=-=-=-=-=-=-=-=πprocedure RunTSR; Interrupt;πbeginπ CLI;π BEGINint;π STI;π inc(Tick);π if Tick > 18.2 * WaitBuf then beginπ NeedPop := True;π if MEM[DosSeg:DosBusy] = 0 then beginπ NeedPop := False;π PORT[$20] := $20;π TsrCrap;π end;π end;π CLI;π ENDint;π STI;πend;ππprocedure Int28TSR; Interrupt;πbeginπ CLI;π BEGINint;π STI;π if NeedPop = True then TsrCrap;π CLI;π ENDint;π STI;πend;ππprocedure InitTSR;πbeginπ OurStackSeg := SSEG;π InLine($89/$26/OurStackSp);π R.Ah := $34;π MSDOS(R);π DosSeg := R.ES;π DosBusy := R.BX;πend;ππprocedure ShowHelp;πbeginπ writeln('Usage : BOOTRES <command line options>');π writeln;π writeln('Valid Options : # Number of seconds to wait before writing currentπtime');π writeln(' /? This screen');πend;ππbeginπ InitTSR;ππ GetDir(0, LogName);π LogName := AddBackSlash(LogName) + 'BOOTLOG.DAT';π WaitBuf := 60;ππ writeln;ππ if ParamCount > 0 then beginπ if ParamStr(1) = '/?' then beginπ ShowHelp;π halt(0);π end;π val(ParamStr(1), WaitBuf, Tick);π if (Tick <> 0) or ((WaitBuf > 60 * 10) or (WaitBuf < 5)) then beginπ writeln('Must be an integer between 5 and ', 60 * 10);π halt(1);π end;π end else beginπ writeln('Type BOOTRES /? for help');π writeln;π end;ππ Tick := 0;ππ SetIntVec($28,@Int28TSR);π SetIntVec($1C,@RunTSR);ππ writeln('BootRes installed');ππ keep(0);πend. =-=-=-=-=-=-=-=-= PART 2 =-=-=-=-=-=-=-=-=-=ππ--- GoldED 2.41π * Origin: Ask me about VoteFix * Carp, Ontario (1:163/307.30)π===========================================================================π BBS: Canada Remote SystemsπDate: 06-23-93 (10:26) Number: 27351πFrom: STEVE MULLIGAN Refer#: NONEπ To: EDWARD WALKER Recvd: NO πSubj: TSRS THAT WRITE TO DISK.. Conf: (1221) F-PASCALπ---------------------------------------------------------------------------π=-=-=-=-=-=-=-=-= PART 3 =-=-=-=-=-=-=-=-=-=πunit BootVars;ππinterfaceππuses Dos;ππconstπ Version = '1.00';π ProgName = 'BootLog';π CopYear = '1992 - 1993';ππtypeπ LogType = recordπ TimeLoad : DateTime;π TimeOff : DateTime;π end;ππvarπ LogFile : file of LogType;π LogRec : LogType;π h, m, s, hund : word;π TimeLoad, TimeOff : DateTime;π LogName : string;ππprocedure DoDate;πprocedure DoDate2;ππimplementationππprocedure DoDate;πbeginπ LogRec.TimeLoad.Year := TimeLoad.Year;π LogRec.TimeLoad.Month := TimeLoad.Month;π LogRec.TimeLoad.Day := TimeLoad.Day;π LogRec.TimeLoad.Hour := TimeLoad.Hour;π LogRec.TimeLoad.Min := TimeLoad.Min;π LogRec.TimeLoad.Sec := TimeLoad.Sec;πend;ππprocedure DoDate2;πbeginπ LogRec.TimeOff.Year := TimeLoad.Year;π LogRec.TimeOff.Month := TimeLoad.Month;π LogRec.TimeOff.Day := TimeLoad.Day;π LogRec.TimeOff.Hour := TimeLoad.Hour;π LogRec.TimeOff.Min := TimeLoad.Min;π LogRec.TimeOff.Sec := TimeLoad.Sec;πend;ππend.π=-=-=-=-=-=-=-=-= PART 3 =-=-=-=-=-=-=-=-=-=ππ--- GoldED 2.41π * Origin: Ask me about SubMove * Carp, Ontario (1:163/307.30)π 9 08-27-9321:31ALL SEAN PALMER Learning about TSR's IMPORT 15 /Å {πSEAN PALMERππ>I don't know if he is or not...but I'd like to see a simple and verboseπ>explanation on how to make a TSR...pref. using the KEEP Procedure...ππOK. I'll Write up one right quick. This isn't gonna be tested (hard toπtest a TSR While keeping your mail Program in memory...)π}ππProgram TSRTest;ππUsesπ Dos;ππVarπ oldInt : Procedure; {hook For old keyboard interrupt handler}ππProcedure newInt; interrupt; { interrupt keyWord makes Procedure far }π { also saves/restores all regs and }π { ends With an iRet instruction }π { sets up DS correctly also but }π { Uses caller's stack }πVarπ i : Word;π b : Boolean;πbeginπ b := port[$60] < $80; {see if it's a press}ππ oldInt; {call old interrupt handler For keystrokes (BIOS)}ππ if b thenπ For i := 0 to $3FFF do {change screen colors as example}π mem[$B800 : i * 2] := succ(mem[$B800 : i * 2]) and $EF;πend;ππbeginπ getIntVec(9, @oldInt); { keep previous keyboard hooks }π setIntVec(9, @newInt); { patch in our keyboard interrupt handler }π keep(0); { returns Exit code of 0 (normal termination) }π { and stays resident }πend.ππ{π All it does is sit in memory, and every time you press a key,π it gets called, and it changes the screen colors.ππ That's about as simple as you're gonna get, now verbosity was never myπ strong point. if you don't understand something, ask.ππ DJ Murdoch's TPU2TPS For TP 6.0 lets you make VERY small tsr's, but thisπ will link in about 1k worth of the system Unit plus some stuff from theπ Dos Unit which will make it about 1.5k.ππ If you wanna Write TSR's the best bet is to learn assembly.π}π 10 08-27-9321:56ALL STEVE CONNET Screen Save TSR IMPORT 28 /Å {πSTEVE CONNETππ>Have you written a screen saver before (or has ANYONE ELSE on thisπ>echo)? Please post some code I could modify/study/adapt...ππI have written a screen saver TSR. Here's some source if you're interested.π}ππProgram Save20;π{ SAVE v2.0 by Steve Connet -- Sunday, Jan. 17, 1993π This is a simple TSR screen saver. Numeric keypad 5 is the hot key. }ππ{$M 1024,0,0} { reserve 1k of stack space }πUsesπ DOS; { needed to set int vectors }ππ{$F+}πVARπ KbdIntVec : Procedure; { used to get ISR }ππProcedure GoSave; Interrupt; { this is our baby }πBegin { gosave }π If Port[$60] = 76 then { Numeric Keypad 5 pressed? }π Begin { our baby }π Asmπ cli { ;clear interrupts }π mov ah, 0fh { ;get video mode, al=mode, bh=page }π int 10h { ;call interrupt }π mov ah, 03h { ;get cursor position, dl=x, dh=y }π int 10h { ;call interrupt }π push dx { ;store cursor position on stack }π push bx { ;store page number on stack }π push ax { ;store video mode on stack }π End;π Repeatπ Port[$3c2] := 0; { wierd video mode }π Port[$3c2] := 9; { wierd video mode }π Port[$3c2] := 247 { wierd video mode }π Until Port[$60] in [0..75, 77..128]; { wait for keypress }ππ Port[$60] := 1; { stuff left shift key }π { to disable right ctrl,alt,shift keys }π { so they don't mess up keyboard input }π Asmπ pop ax { ;restore video mode from stack }π or al,80h { ;set bit 7, prevent screen clearing }π mov ah,00h { ;set video mode }π int 10h { ;call interrupt }π pop bx { ;restore page number from stack }π mov ah,05h { ;set display page }π mov al,bh { ;use saved page number }π int 10h { ;call interrupt }π pop dx { ;restore cursor position from stack }π mov ah,02h { ;set cursor position }π int 10h { ;call interrupt }π sti { ;restore interrupts }π End;π End; { our baby }π Inline($9c); { PUSHF push flags }π KbdIntVec; { call old ISR using saved vector }πEnd;π{$F-}ππBeginπ Writeln(#13#10, 'SAVE 2.0 by Steve Connet', #13#10, 'Installed.');π GetIntVec($9, @KbdIntVec); { define a procedure for ISR }π SetIntVec($9, @GoSave); { insert ISR into keyboard chain }π Keep(0) { terminate and stay resident }πEnd.ππ{π The GoSave procedure has two statements that you may want to take outπ at the very beginning and at the end. CLI and STI are assemblyπ statements that prevent interrupts from happening during our procedure.π The downfall of these statements is that it prevents the internal clockπ from being updated while the procedure is executing.π} 11 08-27-9322:07ALL CHRIS PRIEDE Sample TSR Routine IMPORT 19 /Å {πCHRIS PRIEDEππ> Can anyone give me any samples of TSR routines?ππ My old example of a generic keyboard TSR has mysteriouslyπdisappeared, so I had to write a new one. This is as simple TSR as itπcan be: no stack switching, no DOS reentrancy check. In the formπpresented here it simply beeps when you press Shift-Esc. Set your ownπhotkey and rewrite TsrMain to turn it into something useful.ππ Use Crt unit for screen writes and don't try to access files. Sinceπit uses foreground program's stack, you shouldn't use deeply nested orπrecursive function calls, or declare large local variables. This is moreπon demo side, but will get you started.π}ππprogram GenericKeyboardTSR;π{$M 0, 0, 512} { reduce memory size }π{$S-,R-} { can't use stack or range checking in TSR }πππusesπ Dos, Crt;ππConstπ RtShift = $01;π LtShift = $02;π AnyShift = RtShift + LtShift;π Ctrl = $04;π Alt = $08;ππ HotKey = $01; { Hotkey scan code, Esc }π HotShiftState = AnyShift; { Hotkey shift state }π FakeFlags = 0; { Fake flags for interrupt call }ππtypeπ IntProc = procedure(Flags : word);ππvarπ OldInt09 : IntProc;π Popped : boolean;πππprocedure Enable; Inline($FB); { inline macro -- STI }ππprocedure TsrMain; { TSR main procedure, executed on hotkey }πbeginπ Sound(400); { Make noise (replace with something useful) }π Delay(100);π NoSound;πend;ππprocedure NewInt09; interrupt;πbeginπ Enable; { Allow other interrupts }π if (not Popped) and (Port[$60] = HotKey) and { if not in TSR already }π (Mem[$40 : $17] and $0F = HotShiftState) then { and hotkey detected }π beginπ Popped := true; { set Popped to avoid re-entry }π Port[$61] := Port[$61] or $80; { reset keyboard }π Port[$61] := Port[$61] and not $80;π Port[$20] := $20; { signal end of interrupt }π TsrMain; { run TSR main procedure }π Popped := false; { clear Popped and return }π endπ elseπ OldInt09(FakeFlags); { call old handler }πend;πππbegin { installation }π Popped := false;π GetIntVec($09, pointer(@OldInt09)); { Install int. handler}π SetIntVec($09, @NewInt09);π Keep(0); { stay resident }πend.π