Output of file : TSR.SWG contained in archive :
ALLSWAG4.ZIP
SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00024 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 /Qc ===========================================================================ã 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
');ã 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 /T {ã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.ã 12 11-02-9305:34ALL CEES BINKHORST TSR in DPMI Mode IMPORT 85 /Ç {ãCEES BINKHORSTãã> I have a Turbo Pascal program running in DPMI mode that needs toã> interface to a real mode TSR program. The TSR program issuesã> INT$61 when it has data that needs to be serviced. I've installedã> an interupt service routine that works ok in real mode, but not DPMI.ããHave a look at the following. With some amendments it will do what you want.ãã;from c't 1/1992 #196ã.286p ;generate protected mode code for 286 or higherããdpmitsr segment public ;dpmitsr: name of programã ;segment: indicates start of programcode forã ; 'dpmitsr'. see also end of code: 'dpmitsr ends'ã ;public (without addition (name)): instructionã ; for linker to put all of it in one segmentã assume cs:dpmitsr, ds:dpmitsr ;as soon as program starts 'cs' andã ; 'ds' cpu-registers (code and data segmentã ; segment registers) are filled with memoryã ; position of start of program 'dpmitsr'ã ;there are no seperate code en dataã ; segmentsãolduserint label word ;ã dw ?, ?ãreadmessage db 'This text is in TSR and is be shown through a pointer.', 0ãwritemessage db 'This text is copied from TSR', 0ãwritedata equ $-offset writemessage ;calculate length 'writemessage' andã ; use value later in programã;--------- procedure 'userint' is comparable with a pascal instruction if:ã; case ah of 0: execute instruction 'message'ã; 1: excute instruction 'read'ã; 2: excute instruction 'write'ãuserint proc far ;new int 61hã ;userint: name has only measning within thsi text forã ; compiler - see also end 'userint endp'ã ;proc far: instruktion for compiler to generate codeã ; to push a segment:offset return address on the stackã ; (near proc pushes only offset)ã ; this procedure is called from anotherã ; code segment (dos through int. in dpmiwin!)ã pushf ;save flagsã cmp ah, 00h ;message instruction - see dpmiwin dcs.eax:=$00000000ã je messageã cmp ah, 01h ;read instruction = dpmiwin dcs.eax:=$00000100ã je readã cmp ah, 02h ;write instruction = dpmiwin dcs.eax:=$00000200ã je writeã popf ;put flags back if ah is not 00, 01 or 02 in ah andã jmp dword ptr cs:[olduserint] ; continue with old interruptã;---------- procedure 'message'ãmessage: mov ax,0affeh ;affe hex is in-memory mark of this programã popf ;put flags backã iret ;interrupt ends here and has only putã ; affe hex in cpu register axã ;program dpmiwin will see it there and know thenã ; that 'dpmitsr' is loaded in memoryã;---------- procedure 'read'ãread: mov ax, seg dpmitsr ;make registers es:di together pint toã mov es, ax ; string readmessage. this will then be usedã ; by 'dpmiwin' to put it on the screenã mov di, offset readmessageã popf ;put flags backã iret ;interrupt ends now hereã;---------- procedure 'write'ãwrite: push cx ;save registersã push siã push dsã cld ;direction flag = 0ã mov cx, seg dpmitsrã mov ds, cx ;make ds:si point to string writemessageã mov cx, writedata ;get calculated length of string writemessageã mov si, offset writemessageã rep movsb ; and copy string from ds:si to es:di.ã ; es:di are put in dpmicallstruc by dpmiwinã pop ds ;put registers backã pop siã pop cxã popf ;put flags backã iret ;interrupt now ends hereãuserint endp ;end of code for procedure 'userint'ã ; van gehele echte interrupt dusã;---------- this code does not remain in memoryãinstall: mov ax, seg dpmitsr ;make ds:dx point to string hello$ã mov ds, axã mov dx, offset hello$ ;offset of message that it is installedã ; as a memory-resident programã mov ah, 09h ;send string hello$ to (dos) screenã int 21h ; to signal installation of 'dpmitsr'ã mov ax, 03561h ;what is old address of int. 61hã int 21hã mov [olduserint], bx ; and save it in two stepsã mov [olduserint+2], es ;ã mov ax, 2561h ;subfunction 25 of int. 21: pu new addressã mov dx, offset userint; int. 61h (procedure 'userint') in memoryã int 21hã mov dx, offset install ;how many bytes (convert to paragraphs by:ãshr 4)ã shr dx, 4 ; of program have toã add dx, 011h ; remain in memoryã mov ax, 03100h ;subfunction 31 of int. 21 with 'returnãcode' 0ã int 21h ;makes part of program residentãhello$ db 13,10,'DPMITSR-example installed.',13,10,'$'ãdpmitsr endsã end install ;end of installation procedureãã}ãprogram dpmiwin; {from c't 1/1992 # 197}ããusesã winprocs,ã wintypes,ã win31,ã wincrt;ããtypeã tDPMICallStruc = Record {for use by RMInterrupt}ã EDI, ESI, EBP, Reserved,ã EBX, EDX, ECX, EAX : longint;ã Flags, ES, DS, FS,ã GS, IP, CS, SP, SS : word;ã end;ããfunction RMInterrupt(IntNo, flags, copywords : byte;ã var DPMICallStruc : tDPMICallStruc) : boolean;ãbeginã asmã push es {save es en di from protected mode on stack}ã push diã mov bh, flags {if bit 0 is zero interrupt controller ...}ã {... and A20-line will be reset. other bits must be zero}ã mov bl, intno {put interrupt nummer to be executed in register bl}ã mov cx, word ptr copywords {cx = number of words that are to be copied...}ã { from... prot. mode to real mode stack}ã mov ax, 0300h {put DPMI simulate real mode interrupt nummer in register ax}ã les di, dpmiCallStruc {16-bits pointer to record - 32 bits uses edi}ã {les di, ...: load segment (2 bytes) dpmicallstruc in}ã { register di en offset (ook 2 bytes) }ã { in register es. in short load pointer to dpmicallstruc}ã { in registers di:es }ã int 31h {excute interrupt nummer in bl in real-mode after filling }ã { cpu-registers with values from dpmicallstruc and return in}ã { protected mode with contents of cpu-registers at end of real-mode}ã { interrupt in dpmicallstruc. i.o.w. act as if dpmicallstruc }ã { are the cpu-registers at the end of excuting the real-mode int.}ã jc @errorã mov ax, 1 {function succesfull}ã jmp @doneã @error:ã xor ax, ax {make ax=0, function not succesfull}ã @done:ã pop di {put es and di back}ã pop esã end;ãend;ããvarã selector : word;ã segment : word;ã selseg : longint;ã dcs : tdpmicallstruc;ã printstrg : pchar;ããbeginã fillchar(dcs, sizeof(dcs), 0); {zero dcs}ã {------- verify presence of dpmitsr in memory}ã dcs.eax := $00000000; {just for clarity that ax is called with function 0}ã { as contents is already zero because of use}ã { of function filchar() on previous line. }ã rminterrupt($61, 0, 0, dcs);ã if (dcs.eax and $ffff = $affe) thenã writeln('DPMItsr in memory')ã elseã writeln('Something went wrong!');ã {this part needs improvement. }ã {if dpmitsr is not in memory then pc may crash,}ã { which is not strange as then an interrupt }ã { is called that most likely is 0000:0000 in }ã { memory. }ã {this is to be substituted with a routine that first checks}ã { that pointer of int. 61 is not 0000:0000. }ã {------- read string through pointer}ã dcs.eax := $00000100; {call int. 61 (=dpmitsr) with ah = 1}ã rminterrupt($61, 0, 0, dcs);ã selector := allocselector(word(nil)); {make new selector and fill with values:}ã setselectorbase(selector, longint(dcs.es) * 16);ã { base: es is put in by 'dpmitsr'}ã setselectorlimit(selector, longint($ffff));ã { and limit: $ffff is maximum value. this}ã { does not give problems because we put a}ã { 'zero-terminated' string on the screen.}ã printstrg := ptr(selector, word(dcs.edi)); {also di is put in by 'dpmitsr' }ã writeln(printstrg);ã freeselector(selector);ã {------- read string by making a copy from real-mode memory toã Windows-memory in low 640k-area}ã selseg := globaldosalloc(100); {allocate 100 bytes Windows-memory below 640k.}ã {high word of longint 'selseg' is segment for }ã { use in real-mode and low word is selector }ã { for use in protected mode. }ã if selseg <> 0 thenã beginã selector := word(selseg and $ffff); {determine selector}ã segment := word(selseg shr 16); {determine segment}ã dcs.eax := $00000200; {call int. 61 (=dpmitsr) with ah = 2 }ã dcs.es := segment; {use segment for int. 61 in real-mode}ã dcs.edi := 0; {offset is 0 }ã rminterrupt($61, 0, 0, dcs);ã printstrg := ptr(selector, 0);ã writeln(printstrg);ã globaldosfree(selector);ã end;ãend.ãã{ãTo excute the program, dpmitsr.exe has to be executed before starting Windows.ãDpmitsr will remain permanently in memory.ããBoth DPMITSR.ASM and DPMIWIN.PAS were nicely running programs in early 1992ãwith TPW. Now, under BPW an error is reported from the SYSTEM unit.ãHowever, as I now don't have the time to trace the error herewith the programs,ãas it will surely point the way for you to go.ã}ã 13 11-02-9306:32ALL LOU DUCHEZ Dealing with TSR's IMPORT 12 /½ (*ãLOU DUCHEZãã>I need to write a TSR, but the books I have really don't go into much detailã>about them. Anyone know any good books that explain about them?ããMy recommendation:ãã"Turbo Pascal 6.0: The Complete Reference" by Stephen O'BrienããTaught me about TSRs. The basic deal with a TSR is these things:ãã1) A $M directive to reduce the amount of memory used.ã2) A "Keep" procedure to make it TSR.ã3) (the tricky part) A new interrupt handler. Actually it's not so tricky.ã What your handler should do is react to the hardware, then call the oldã interrupt handler. In parts here:ãã A) Determine old handler address with getintvec. Assign it to aã "procedure" variable like so:ãã var oldkbdhandler: procedure; { for a keyboard handler }ãã getintvec($09, @oldkbdhandler);ããã B) Create a new handler that reads the hardware: like so:ãã var port60h: byte; { global variable }ããã procedure newkeyboardhandler; interrupt;ã beginã port60h := port[$60]; { store keyboard port status }ã asmã pushf { PUSHF instruction is crucial before calling old ISR }ã end;ã oldkbdhandler; { run the old keyboard handler }ã end;ããã C) To hook up the new handler, it's:ãã setintvec($09, @newkeyboardhandler);ãã*)ã 14 11-02-9306:32ALL MARC BIR TSR template IMPORT 7 /¿Í {ãMARC BIRãã>I'm looking For a template to build TSR Program.ã}ãã{$M 2048, 0, 5120}ãUsesã Dos;ããVarã OldKbdIntVec : Procedure;ããProcedure DoWhatever;ãbeginã if Mem[$B800:0] <> 32 Thenã FillChar(Mem[$B800:0], 80 * 2, 32)ã elseã FillChar(Mem[$B800:0], 80 * 2, 23);ãend;ãã{$F+}ãProcedure NewKbdIntVec; Interrupt;ãVarã Input : Byte;ãbeginã Input := port[$60];ã if Input = $3B then { F1 }ã DoWhatever;ã Inline ($9C);ã OldKbdIntVec;ãend;ã{$F-}ããbeginã GetIntVec($9,@OldKbdIntVec);ã SetIntVec($9,@NewKbdIntVec);ã Keep(0);ãend.ãã{ãThis works, but you will most likely want a better TSR initiater thanãKEEP, there are some PD/Shareware ones out. if you still need code,ãNETMAIL me, the code I have For TSR's is a couple hundred lines...ã}ã 15 11-02-9306:32ALL LOU DUCHEZ More TSR Stuff IMPORT 18 /í {ãLOU DUCHEZãã>I recently wrote a short utility in TP. I want to make it a TSRã>which can be activated by a hotkey (like ALT-R). Do I need toã>redirect the Keyboard INT to my Program?ããRight on the nose.ãã>if so, then where does my Program direct the INT after that?ããTo the OLD keyboard interrupt. You can use the GetIntVec to findãwhere the interrupt originally pointed; and trust me, it's a royalãpain in the keister to Program your own. (Note: you'll want toãexecute a PUSHF instruction before calling the "old" interrupt;ãeasily done With the built-in Assembler: Asm PUSHF end.)ããNow, For reading the Alt-R: you can get the "Alt" key fromãmemory location $0040:$0017. It Records the Alt key, shift keys,ãcaps lock, etc. Each bit sets/reports whether the key is active orãinactive ("1" = "active"). Like so:ããConst insByte = $80; capsByte = $40; numByte = $20; scrollByte = $10;ã altByte = $08; ctrlByte = $04; lshftByte = $02; rshftByte = $01;ããVar keyboardstat: Byte Absolute $0040:$0017;ããTo test if Alt is on, see if this expression evaluates to "True":ãã keyboardstat and altByte = altByteããAs For the "R", check port $60 (the keyboard port) For scan code $13.ã(Maybe ya oughtta find a complete list of the scan codes.)ãã>Also, I want my Window to disappear when my Programã>is finished (and the previous screen to come back).ã>How can I do this?ããStore the old screen into memory. Hint: on Mono systems, it's theã4000 Bytes starting at b000:0000; on color, it's the 4000 startingãat b800:0000. Use the "Move" Procedure first to move the 4000 Bytesãto an Array of 4000 Characters, then use "Move" to move the 4000 Bytesãback to the video location.ãã> (BTW, I could do all this on the Commodore 64 back in the good 'olã>days when the 64 was king. Life was much simpler then).ããYeah, I can hear ya now: "Oh you spoiled kids. When I started inãcomputers, we had only 64k to work With, and we LIKED it! And weãdidn't waste our money on a separate 'monitor', oh no! we just hookedãour computers up to the TV. Damn kids these days."ã}ã 16 11-02-9317:03ALL ANDREW KEY Screen Saver TSR IMPORT 56 /ÊÔ {ãFrom: ANDREW KEYãSubj: Screen Saveã}ãã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.ãã 17 01-27-9411:55ALL WILBERT VAN LIEJEN TSR Clock IMPORT 31 /{Ù {ã> I would like to include a clock in my current project which will beã> updated once a minute. Instead of constantly checking the computer's clockã> and waiting for it to change, I would like to use an interrupt.ããThis one has even a hot key handler. If you want to update it once perãminute, bump a counter within the interrupt 1Ch handler till it reaches theãvalue 60*18.2. Then refresh the screen.ã}ããProgram Clock;ãã{$G+,R-,S-,M 1024, 0, 0 }ããusesã Dos;ããConstã x = 71; { x location on screen }ã y = 1; { y location on screen }ã Keyboard = 9; { Hardware keyboard interrupt }ã TimerTick = $1C; { Gets called 18.2 / second }ã VideoOffset = 160 * (y - 1) + 2 * x;{ Offset in display memory }ã yellow = 14;ã blue = 1;ã attribute = blue * 16 + yellow; { Clock colours }ã VideoBase : Word = $B800; { Segment of display memory }ã ActiveFlag : ShortInt = -1; { 0: on, -1: off }ããVarã OrgInt9, { Saved interrupt 9 vector }ã OrgInt1Ch : Pointer; { Saved interrupt 1Ch vector }ã VideoMode : Byte absolute $0000:$0449;ãã{ Display a string using Dos services (avoid WriteLn, save memory) }ããProcedure DisplayString(s : String); Assembler;ããASMã PUSH DSã XOR CX, CXã LDS SI, sã LODSBã MOV CL, ALã JCXZ @EmptyStringã CLDã @NextChar:ã LODSBã XCHG AX, DXã MOV AH, 2ã INT 21hã LOOP @NextCharã @EmptyString:ã POP DSãend;ãã{ Returns True if a real time clock could be found }ãFunction HasRTClock : Boolean; Assembler;ããASMã XOR AL, ALã MOV AH, 2ã INT 1Ahã JC @NoRTClockã INC AXã @NoRTCLock:ãend;ãã{ Release Dos environment }ãProcedure ReleaseEnvironment; Assembler;ãASMã MOV ES, [PrefixSeg]ã MOV ES, ES:[002Ch]ã MOV AH, 49hã INT 21hãend;ãã{ INT 9 handler intercepting Alt-F11 }ãProcedure ToggleClock; Interrupt; Assembler;ãConstã F11 = $57; { 'F11' make code }ã BiosSeg = $40; { Segment of BIOS data area }ã AltMask = $08; { Bitmask of Alt key }ã KbdFlags = $17; { Byte showing keyboard status }ããASMã STIã IN AL, 60hãã { F11 pressed? }ã CMP AL, F11ã JNE @PassThruãã { Alt-key pressed? }ã PUSH BiosSegã POP ESã MOV AL, ES:[KbdFlags]ã AND AL, AltMaskã CMP AL, AltMaskã JNE @PassThruãã { Flip status flag, force EOI and leave routine }ã NOT [ActiveFlag]ã IN AL, 61hã MOV AH, ALã OR AL, 80hã OUT 61h, ALã MOV AL, AHã OUT 61h, ALã CLIã MOV AL, 20hã OUT 20h, ALã STIã JMP @Exitãã @PassThru:ã CLIã PUSHFã CALL DWord Ptr [OrgInt9]ã @Exit:ãend; { ToggleClock }ãã{ Convert a packed BCD byte to ASCII character }ãProcedure Digit; Assembler;ãASMã PUSH AXã CALL @HiNibbleã POP AXã CALL @LoNibbleã RETNãã @HiNibble:ã SHR AL, 4ã JMP @MakeAsciiã @LoNibble:ã AND AL, 0Fhã @MakeAscii:ã OR AL, '0'ã STOSWãend;ãã{ INT 1Ch handler that displays a clock on the right hand side of the screen }ãProcedure DisplayClock; Interrupt; Assembler;ãASMã CMP [ActiveFlag], 0ã JNE @Exitã CLDã MOV AH, 2ã INT 1Ahã MOV ES, [VideoBase]ã MOV DI, VideoOffsetã MOV AH, attributeã MOV AL, CHã CALL Digitã MOV AL, ':'ã STOSWã MOV AL, CLã CALL Digitã MOV AL, ':'ã STOSWã MOV AL, DHã CALL Digitã PUSHFã CALL DWord Ptr [OrgInt1Ch]ã @Exit:ãend;ããBeginã If VideoMode = 7 Thenã VideoBase := $B000;ã GetIntVec(TimerTick, OrgInt1Ch);ã SetIntVec(TimerTick, @DisplayClock);ã GetIntVec(Keyboard, OrgInt9);ã SetIntVec(Keyboard, @ToggleClock);ã SwapVectors;ã ReleaseEnvironment;ã DisplayString('CLOCK installed. toggles on/off');ã Keep(0);ãend.ã 18 01-27-9411:59ALL DAVID BRAATEN TSR Disk Writes IMPORT 17 /ã {ã>Does anybody know how to write to disk inside a TSR usingã>turbo pascal? I know all about how to write a simple TSR,ã>cannot call Dos functions from within a hardware interrupt.ããHere is parts of a tsr to write to disk when a hotkey is pressedã(Leftshift,left alt,right alt)ã}ããuses dos,crt;ãconstã hotkey : byte = 5;ã writekey : byte = 10; {write to disk when this combo comes up }ãvarã dat : file of word; {keep file definition on globals}ããprocedure diskit;ãvarã x,y : word;ãbeginã if not cracking thenã beginã cracking := true; {disable checking for hotkey while writing}ã assign(dat,'a:\dump.scr');ã rewrite(Dat);ã display;ã for y := 0 to 24 doã for x := 0 to 79 doã write(dat,wind[X,y]);ã close(dat);ã current := 1; {Reset current to 0}ã cracking := false;ã end;ãend;ã{------------------------------------------------------------}ãprocedure calloldint(sub:pointer);ãbegin {calloldint}ãinline($9C/$FF/$5E/$06); {Assembly to pop pointer off stack and call it}ãend; {calloldint}ã{-------------------------------------------------------------}ããprocedure tick(flags,cs,ip,ax,bx,cx,dx,si,di,ds,es,bp:word); interrupt;ãvar regs:registers;ãbeginãcalloldint(oldvec);ã regs.ah := $12;ã intr($16, regs);ã statflags := (regs.al and regs.ah) and hotkey;ã regflags := (regs.al and regs.ah) and writekey;ãif (statflags = hotkey) and (cnt =0) thenã beginã cnt := 1;ã display;ã cnt := 0;ã endãelse if (regflags = writekey) and (cnt = 0) thenã beginã cnt := 1;ã diskit; {write to disk if hotkey}ã cnt := 0;ã endãelse inline($FB);ãend; {tick}ã{-----------------------------------------------------}ããbegin {MAIN}ãwriteln('Saving screens function activated');ãcurrent := 1;ãgetintvec($08,oldvec);ãsetintvec($08,@tick);ãgetintvec($09,oldkbdvec);ãsetintvec($09,@keyboard);ãcnt := 0;ãCracking := false;ãkeep(0);ãend. {MAIN}ãã{ãThis will work for writing to disk as long as no other disk activity is beingãperformed.ã} 19 01-27-9412:21ALL JAY A. KEY Screen Saver IMPORT 54 /`ê 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.ãã 20 01-27-9412:23ALL AMIR FRENKEL Clock 2 IMPORT 21 /f½ {ã> H E L P!!! I need help with the timer Interrupt... I guess? Hereã> is what I would like to do, I want to have the Time in the upper rightã> hand corner updated by the second. I was told that I need to "hook inã> to the timmer interrupt" but, how do I do that? Any help would beã> appreciated. And if possible make it as non technical as possable.ããHere is just the program your looking for!ã}ãProgram Clock_demo;ã{$M $400 ,0 ,0} { Stack Size $400 , No Heap }ã{$F+}ãUses Dos ,Crt;ãVarã Count:Byte; { Counts Seconds }ã Time:Longint Absolute $40:$6C; { Bios Keeps Clock Time Here }ã Old1Cint:Procedure; { Linkage To Old 1C Interrupt }ããã{ Every 18 Pulses Shows Time At The Left Corner Of Screen }ãProcedure Get_Time;Interrupt;ãVar X ,Y:Byte;ã Hour ,Minute ,Sec:Word;ãBeginã Inc(Count);ã If Count =18 Then { Every Second 18.2 Pulses }ã Beginã Count:=0;ã X:=Wherex; { Save Cursor Place }ã Y:=Wherey;ã Hour:=Time Div 65520; { Calculate Hours. In Each Hour }ã { 18.2 * 60 * 60 Pulses }ã Minute:=Time Mod 65520 Div 1092; { Calculate Minutes. In Each }ã { Minute 18.2 * 60 Pulses }ã Sec:=Round((Time Mod 65520 Mod 1092) / 18.2) Mod 60; { Seconds }ã Gotoxy(70 ,1); { Left Corner Of Screen }ã { Write time }ã If Hour<10 Thenã Write(0,Hour,':')ã Elseã Write(Hour,':');ã If Minute<10 Thenã Write(0,Minute,':')ã Elseã Write(Minute,':');ã If Sec<10 Thenã Write(0,Sec)ã Elseã Write(Sec);ã Gotoxy(X ,Y); { Restore Cursor Position }ã End;ã Inline($9C); { Pushf - Push Flags }ã Old1Cint; { Link Old 1C Procedure }ãEnd;ããBegin { Of Main Program }ã Count:=0; { Clock Pulses Counter }ã Getintvec($1C ,@Old1Cint); { Save Old 1C Interrupt Vector }ã Setintvec($1C ,@Get_Time); { Insert Current Interurupt Procedure }ã Keep(0); { Terminate And Stay Resident - Tsr }ãEnd.ãã 21 01-27-9412:23ALL ROB PERELMAN TSR Skeleton IMPORT 13 /< {ã>Thanks for the procedure. I don't want to use WRITE OR WRITELN causeã>they are slow and used a lot of mem. I copy one from the book but itã>makes the file even bigger!!!ããWell, I hope mine worked decently...it just didn't mod the currentãcursor position.ãã>You help certainly clear up something about TSR programming. Likeã>why I need to interrupt hooking....but I still don't know how toã>detect hotkey and check to see if the program has been loaded.ã>Anyway, I used a skeleton named TSR_TPU.PAS of an unkown author toã>write my TSR and it ran fine though not very good.ããGood...I'm glad you understand this. I don't have TSR_TPU, but I doãhave some source that shows how to detect if a TSR is already loaded andãhow to unload a TSR. The hotkey part you can do your self. You canãput in this program like the one I have below which will tell you what valuesãto look for in Port[$60] for keypresses. Just run it, and hit your key combo.ãFor example, if you wanted ALT-A, you'd run this, and hit ALT-A, and you'dãsee it would exit with 30 on the screen. So in your TSR, you say:ãIf Port[$60]=30 then...ãSee? If you want the uninstall/detect TSR program, please tell me...ã}ããProgram HotKey;ããUsesã Crt, Dos;ããVarã Old : Procedure;ãã{$F+}ãProcedure New; Interrupt;ãBeginã Writeln(Port[$60]);ã InLine($9C);ã Old;ãEnd;ã{$F-}ããBeginã GetIntVec($9, @Old);ã SetIntVec($9, @New);ã Repeat Until Keypressed;ãEnd.ã 22 02-03-9407:07ALL MIKE CHAMBERS A Working TSR IMPORT 171 /:ß {ã---------------------------------------------------------------------------ã MD> How do you write something so it is interup driven?ãã MD> Can anyone post some simple code to perhaps count a numberã MD> up by one and display it to the screen while at the sameã MD> time allowing another part of the program do what ever it'sã MD> suppose to.?ããMarek:ããThe features to which you refer constitute writing a TSR program.ãThese programs are executed and terminate, but stay resident inãsystem memory. Prior to termination they insert themselves intoãthe Interrupt Service Routine chain of a known interrupt vector.ããThis programming was simplified somewhat in turbo Pascal Releaseã4 or 5 with the addition of the Keep, GetIntVec and SetIntVecãprocedures of the DOS unit. However, these procedures are onlyãa small fraction of the coding needed to write reliable TSR's.ãThis fact explains the lack of simple code examples.ããTo write good TSR's you need to read about them and play withãsomeone else's code for a while. (I've coded pascal for 17 years,ãbut my first TSR took about 3 days to debug). For a solidãPascal reference, try Tom Swan's 'Mastering Turbo Pascal'.ãTo figure out what the interrupt functions are doing, I recommendãRalf Brown's & Jim Kyle's 'PC Interrupts'. The book is derivedãfrom their well-known interrupt list (INTERnn.ZIP) availableãon many programming oriented BBS systems. The index of the bookãis well worth the money. Finally, I recommend Ed Mitchell'sã"Borland Pascal Developer's Guide". Ed's TSR code is thoroughãand documented with education in mind. Since I'm quotingãED's book here, please buy a copy if you find the code useful.ããGood luckãã-------------------------------------------------------------------------ã TSR Code Exampleã-------------------------------------------------------------------------ã}ãã{ TSR.PASã Sample TSR application written in Turbo Pascal.ã IMPORTANT!ã This TSR operates only in TEXT mode, and, asã written, supports only 80 x 25 sized screensã (not 43- or 50- line display modes).ãã This TSR will only operate on DOS 3.x or newerã versions of DOS.ãã Use this code as an example to write your own TSR code.ã Modify the $M compiler directive, below, to specifyã the maximum stack size, minimum heap and maximum heapã required for your TSR application.ãã Please see the text and other source commentsã for important restrictions.ãã TSRs can be DANGEROUS, so be careful.ã}ãã{$S-}ã{$M 3072, 0, 512}ãusesã Crt, Dos;ããtypeã { Defines an array to store the screen image.ã For greater efficiency, your code may want to save onlyã the portion of the screen that is changed by your TSRã code. This implementation saves the entire screen image,ã at popup time, for the greatest flexibility. }ã TSavedVideo=Array[0..24, 0..79] of Word;ã PSavedVideo=^TSavedVideo;ããã{ The following items define the TSR's identificationãstring. }ãtypeã String8=String[8];ãconstã IdStr1:String8='TP7RTSR';ã IdStr2:String8='TSRInUse';ããvarã CPURegisters: Registers;ã { General register structure for Intr calls. }ã CursorStartLine: Byte;ã { Stores cursor shape information. }ã CursorEndLine: Byte;ã { Stores cursor shape information. }ã DiskInUse: Word;ã { Tracks INT 13 calls in progress. }ã MadeActive: Boolean;ã { TRUE if this TSR has been asked to pop up. }ã OurSP: Word;ã OurSS: Word;ã { Saved copies of our SS and SP registers. }ã PInt09: Pointer;ã { Saved address of keyboard handler. }ã PInt12: Pointer;ã { Saved address of GetMemorySize interrupt. }ã PInt1B: Pointer;ã { Ctrl-Break interrupt address. }ã PInt24: Pointer;ã { DOS Critical error handler. }ã PInt28: Pointer;ã { Saved address of background task scheduler. }ã PInt1C: Pointer;ã { Saved address of timer handler. }ã PInDosFlag: ^Word;ã { Points to DOS's InDos flag. }ã VideoMem: PSavedVideo;ã { Points to actual video memory area. }ã SavedVideo:TSavedVideo;ã { Stores the video memory when TSR is popped up. }ã SavedWindMin: Word;ã { Holds saved copy of WindMin for restoring window. }ã SavedWindMax: Word;ã { Holds saved copy of WindMax for restoring window. }ã SavedSS,ã SavedSP: Word;ã { Saves caller stack registers; must be globalã to store in fixed memory location, not on localã stack of interrupted process. }ã SavedX,ã SavedY: Word;ã { Stores X, Y cursor prior to TSR popup }ã { for restoration when TSR goes away. }ã TempPtr: Pointer;ã { Used internally to DoUnInstall. }ã TSRInUse: Boolean;ã { Set TRUE during processing to avoid doubleã activation. }ããããprocedure SaveDisplay;ã{ Copies the content of video memory to an internalã array structure. Saves the cursor location and cursorã shape definitions. }ãvarã CursorLines: Byte;ãbeginã { Save cursor location. }ã SavedX := WhereX;ã SavedY := WhereY;ã { Saved existing window values. }ã SavedWindMin := WindMin;ã SavedWindMax := WindMax;ãã { Get and save current cursor shape. }ã with CPURegisters doã beginã AH := $03;ã BH := 0;ã Intr($10, CPURegisters);ã CursorStartLine := CH;ã CursorEndLine:= CL;ã end;ãã { Get equipment-type information. If Monochromeã adapter in use, then point to $B000; otherwise use theã color memory area. }ã Intr( $11, CPURegisters );ã if ((CPURegisters.AX shr 4) and 7) = 3 thenã beginã VideoMem := Ptr( $B000, 0 );ã CursorLines := 15;ã endã elseã beginã VideoMem := Ptr( $B800, 0 );ã CursorLines := 7;ã end;ã SavedVideo := VideoMem^;ãã { Change cursor shape to block cursor. }ã with CPURegisters doã beginã AH := $01;ã CH := 0;ã CL := CursorLines;ã Intr($10, CPURegisters);ã end;ããend; { SaveDisplay }ãããprocedure RestoreDisplay;ã{ Always called sometime after calling SaveDisplay.ã Restores the video display to its state prior to theã TSR popping up. }ãbeginã { Restore screen content. }ã VideoMem^ := SavedVideo;ãã { Restore cursor shape. }ã with CPURegisters doã beginã AH := $01;ã CH := CursorStartLine;ã CL := CursorEndLine;ã Intr($10, CPURegisters);ã end;ãã { Resize window so the GotoXY (below) will work. }ã Window (Lo(SavedWindMin)+1, Hi(SavedWindMin)+1,ã Lo(SavedWindMax)+1, Hi(SavedWindMax)+1);ã Gotoxy ( SavedX, SavedY );ããend; { Restore Display }ãããprocedure TrapCriticalErrors;ãassembler;ã{ INT 24H }ã{ãThis handler is enabled only while the TSR is poppedãup on-screen.ããThis handler exists solely to catch any DOSãcritical errors and is a crude method of doing so. Sinceãthis routine does nothing, any critical errors thatãoccur while the TSR is popped up are ignored--whichãcould be very dangerous. Also, if another TSR or ISRãpops up after this one, it may get the criticalãerror that was intended for this TSR.ãNOTE: Normally, this could be an "interrupt" typeãprocedure. However, Turbo Pascal pushes and then popsãall registers prior to the IRET instruction. By writingãthis as an assembler routine, this generates the IRETãdirectly, followed by one superfluous RET instructionãgenerated by the assembler, resulting in substantialãcode savings.ã}ãasmã IRETãend; { TrapCriticalErrors }ãããããprocedure CBreakCheck;ãassembler;ã{ INT 1BH }ãã{ This routine results in a no operation; when hooked toãthe INT 1B Ctrl-Break interrupt handler, it causesãnothing to happen when Ctrl-Break or Ctrl-C are pressed.ãThis routine is hooked only when the TSR is popped up. }ãasmã IRETãend; { CBreakCheck }ãããããfunction GetKey : Integer;ã{ Pauses for input of aãsingle keystroke from the keyboard and returns the ASCIIãvalue. In the case where an Extended keyboard key isãpressed, GetKey returns the ScanCode + 256. The TurboãPascal ReadKey function is called to perform theãkeystroke input. This routine returns a 0 when an Extendedãkey has been typed (for example, left or right arrow)ãand we must read the next byte to determine the Scanãcode.ã}ãvarã Ch : Char;ãbeginã { While waiting for a key to be pressed, callã the INT $28 DOS Idle interrupt to allow backgroundã tasks a chance to run. }ã repeatã asmã int $28ã end;ã until KeyPressed;ã Ch := ReadKey;ã If Ord(Ch) <> 0 thenã GetKey := Ord(Ch) { Return normal ASCII value. }ã elseã { Read the DOS Extended SCAN code that follows. }ã GetKey := Ord(ReadKey) + 256;ãend;{GetKey}ãããããprocedure DoPopUpFunction;ã{ This procedure is the "guts" of the popupã application. You can code your own application here, ifã you want. Be sure to read the text for importantã restrictions on what can be written in a TSR.ãã As implemented here, this popup displays a tableã of ASCII values.ã}ããconstã UpperLeftX=10;ã UpperLeftY=5;ã { Define upper-left corner of TSR's popup window. }ãã LowerLeftX=70;ã LowerRightY=20;ã { Define lower-right corner of TSR's popup window. }ãã Width=LowerLeftX - UpperLeftX + 1;ã Height=LowerRightY - UpperLeftY + 1;ã { Calculated width and height of popup window. }ã MinX=6;ã { Distance from left edge to display ASCII table. }ã RightEdge=8;ã { Marks the right edge (Width-RightEdge) of table. }ã MinY=4;ã { Distance from top of window to start ASCII Table. }ã ValuesPerLine=Width-8 - MinX;ã { Computed number of ASCII values in each line. }ã NumLines=255 div ValuesPerLine;ã { Computed number of lines in the ASCII table. }ã KEY_LEFTARROW = 331;ã { Keystroke values for extended keyboard codes. }ã KEY_RIGHTARROW = 333;ã KEY_DOWNARROW = 336;ã KEY_UPARROW = 328;ã KEY_ESCAPE = 27;ã KEY_ENTER = 13;ãããprocedure PutChar( X, Y: Integer; ChCode: Char );ã{ Writes the single character ChCode to the screenãat (X, Y), where (X, Y) is relative to the TSR popupãwindow. This routine is used for displaying the ASCIIãtable because the usual Pascal Write() translates ASCIIã7 to a bell ring, and ASCII 13 and 10 to carriage returnãand line feed. By using the PC BIOS routine directly, weãbypass Pascal's translation of these characters. }ãbeginã with CPURegisters doã beginã { Move the cursor to adjusted (X, Y). }ã AH := $02;ã BH := 0;ã DH := UpperLeftY + Y - 2;ã DL := UpperLeftX + X - 2;ã Intr($10, CPURegisters);ãã { Output the character to the current cursor location. }ã AH := $09;ã AL := byte(ChCode);ã BH := 0;ã BL := 3 shl 4 + 14;ã { Background=color 3; Foreground=color 14 }ã CX := 1;ã Intr($10, CPURegisters);ã end;ãend; {PutChar}ãããvarã ASCIICode: Integer;ã { Computed from X, Y location in table. }ã I: Integer;ã { For loop index variable. }ã TextLine: String[Width];ã { Buffer to hold width of window's text. }ã X, Y: Integer;ã { Tracks cursor location in ASCII table. }ã Ch : Integer;ã { Holds the keystroke typed. }ããbegin {DoPopUpFunction}ã { Select White text on Cyan background forã { Set up a viewing window; makes calculationã of X, Y easier. }ã Window(UpperLeftX, UpperLeftY, LowerLeftX, LowerRightY);ãã { Enclose the window by drawing a border aroundã it and filling the interior with blanks. }ã FillChar( TextLine[1], Width, ' ');ã TextLine[0] := Chr( Width );ã TextLine[1] := chr( 179 );ã TextLine[Width] := chr( 179 );ãã for I := 2 to Height - 2 doã beginã Gotoxy(1, I);ã Write( TextLine );ã end;ãã FillChar( TextLine[1], Width, Chr(196));ã TextLine[1] := Chr( 218 );ã TextLine[Width] := Chr( 191 );ã Gotoxy( 1 , 1 );ã Write( TextLine );ãã TextLine[1] := Chr( 192 );ã TextLine[Width] := Chr( 217 );ã Gotoxy ( 1, Height - 1 );ã Write( TextLine );ãã { Display window title }ã Gotoxy ( Width div 2 -10, 2 );ã Write( 'Table of ASCII Values' );ãã { Draw the ASCII table on the display }ã X := MinX;ã Y := MinY;ã for I := 0 to 255 doã beginã PutChar( X, Y, Chr(I) );ã Inc(X);ã If X = (Width-RightEdge) thenã beginã Inc( Y );ã X := MinX;ã end;ã end;ãã Gotoxy ( Width div 2 - 20 , 11 );ã Write('Use arrow keys to navigate; Esc when done');ã X := MinX; Y := MinY;ã repeatã { Compute ASCII code and value at X, Y }ã ASCIICode := (X-MinX + (Y-MinY)*ValuesPerLine);ã Gotoxy (Width div 2 - 11 , 13);ã { NOTE: This allows display ofã "ASCII codes" greater than 256 if cursorã moves into blank area in table. }ã Write('Character= ', ' ASCII=',ASCIICode:3);ã PutChar(Width div 2, 13, Chr(ASCIICode));ã { Display that value, below }ã Gotoxy ( X, Y );ã Ch := GetKey;ã Case Ch Ofã KEY_LEFTARROW: if X > MinX then Dec(X);ã KEY_RIGHTARROW:ã if X < (Width - RightEdge - 1) then Inc(X);ã KEY_DOWNARROW: if Y < (MinY+NumLines) then Inc(Y);ã KEY_UPARROW: if Y > MinY then Dec(Y);ã end;ã until Ch = 27;ãã { End of TSR popup code. }ãend; {DoPopUpFunction}ããããprocedure RunPopUp;ã{ Switches from system stack to TSR's stack. CallsãDoPopUpFunction to run the actual TSR application. Thisãkeeps all the ugly details separate from theãapplication. Note that while the TSR is up on theãscreen, and only while the TSR is up, we trap theãCtrl-Break and DOS critical errors interrupt. We doãnothing when we see them except return, thereby ignoringãthe interrupts. }ãbeginã { Switch stacks. }ã asmã CLIã end;ã SavedSS := SSeg;ã SavedSP := SPtr;ã asmã MOV SS, OurSSã MOV SP, OurSPã STIã end;ã GetIntVec( $1B, PInt1B );ã { Disable Ctrl-Break checking. }ã SetIntVec( $1B, @CBreakCheck );ã GetIntVec( $24, PInt24 );ã { Trap DOS critical errors. }ã SetIntVec( $24, @TrapCriticalErrors );ã SaveDisplay;ãã DoPopUpFunction;ãã RestoreDisplay;ã SetIntVec( $24, PInt24 );ã { Reenable DOS critical error trapping. }ã SetIntVec( $1B, PInt1B );ã { Reenable Ctrl-C trapping. }ã { Restore stacks. }ã asmã CLIã MOV SS, SavedSSã MOV SP, SavedSPã STIã end;ãend; {RunPopUp}ãããprocedure BackgroundInt;ãinterrupt;ã{ INT 28H }ã{ This routine is hooked in the DOS INT 28H chain,ãknown variously as the DOSOK or DOSIdle interrupt. Theãidea is that when applications are doing nothing exceptãwaiting for a keystroke, they can call INT 28ãrepeatedly. INT 28 runs through a chain of applicationsãthat each get a crack at running.ããThe keyboard interrupt handler watches for theãmagic TSR popup key. Some of the time it runs the TSRãpopup directly; when DOS is doing something, however, itãcan't run the TSR. So, it sets a flag saying "Hey, INTã28, if you see this flag set, then do the TSR." So theãINT 28 code, here, examines the flag. If the flag is set, INT 28ãknows that the TSR was activated, so it calls it now.ãWe can do this because DOS calls INT 28 only if it's safeãfor something else to run.ã}ãbeginã { Call saved INT 28H handler. }ã asmã PUSHFã CALL PInt28ã end;ã if MadeActive thenã beginã TSRInUse := True;ã MadeActive := False;ã RunPopUp;ã TSRInUse := False;ã end;ãend; {BackgroundInt}ããããprocedure KeyboardInt;ãinterrupt;ã{ INT 09H }ã{ Examines all keyboard interrupts. First calls theãexisting interrupt handler. If our TSR is NOT currentlyãrunning, then it checks for the magic pop keystrokes. Ifãthe TSR is already running, then we do not want toãactivate it again, so we ignore keystroke checking whenãthe TSR is already alive and on-screen.ããSeveral bytes in low memory contain keyboard statusãinformation. By checking the values in these bytes,ãvarious "not normal" key combinations can be detected.ãAs implemented here, the TSR is made active by pressingãthe left Alt key, plus the SysRq key (Print Screen on myãPC). You can change these keystrokes to something else.ã}ãconstã CallTSRMask = 6;ã { ACTIVATE TSR = left Alt key + SysRq key }ãvarã ScanCode: byte absolute $40:$18;ã { One of the keyboard status bytes. }ãbeginã { Call existing keyboard interrupt handler. }ã asmã PUSHFã CALL PINT09ã end;ãã if not TSRInUse thenã if (ScanCode and CallTSRMask) = CallTSRMask thenã beginã { The TSR has been activated. }ã TSRInUse := True;ã { Set to TRUE to prevent reactivation of this TSR. }ã if (PInDosFlag^ = 0) thenã beginã { If in "Safe" DOS area, then pop up now. }ã MadeActive := False; { So INT $28 won't call us. }ã RunPopUp;ã TSRInUse := False;ã endã elseã MadeActive := True;ã { o/w, set flag let INT 28 call us when ok. }ã end;ãend; {KeyboardInt}ãããããprocedure DoUnInstall ( var Removed: Boolean ); forward;ããã{ These are "local" to OurInt12; it is safer to store them in theãTSR's global data area than to place them on the stack asãlocal variables. }ãvarã IdStr : ^String8;ã MessageNum : Integer;ããprocedure OurInt12ã (_AX, BX, CX, DX, SI, DI, DS, ES, BP:Word);ãinterrupt;ã{ INT 12H }ã{ Intercepts INT 12H calls. If the ES:BX registerãjust happens to point to IdStr1, then the command-lineãTSR program just called in. If this is the case, theãother registers could be used to pass a message to theãrunning TSR. Here, it's used by the running TSR to returnãa pointer to another string, confirming that the TSR isãindeed running.ã}ããvarã DeInstallOk: Boolean;ãbeginã IdStr := Ptr( ES, BX );ã { Check to see if ES:BX points to the }ã { magic ID string }ã If IdStr^ = IdStr1 thenã MessageNum := CXã elseã beginã MessageNum := 0;ã { No message rcvd; this is normal DOS call. }ã asmã pushfã call PInt12ã mov _AX, axã { AX returns the memory size value. }ã end;ã end;ã if MessageNum > 0 thenã { Process a message directed to this TSR. }ã beginã case MessageNum ofã 1: beginã { Returns pointer to IdStr2 indicating thisã TSR is here. }ã ES := Seg(IdStr2);ã BX := Ofs(IdStr2);ã end;ã 2: begin { Performs request to uninstall the TSR. }ã DoUnInstall (DeInstallOk);ã if DeInstallOk thenã CX := 0 { Report success. }ã elseã CX := 1; { Report failure. }ã end;ã end;ã end;ãend; {OurInt12}ããããprocedure DoUnInstall ( var Removed: Boolean );ãbeginã { See if any TSRs have loaded in memory after us.ã If another TSR has loaded after us, then we reallyã cannot safely terminate this TSR. Why? Because when theyã terminate, they may reset their interrupts to point backã to this TSR. And if this TSR is no longer in memory,ã uh-oh... }ãã Removed := True;ã GetIntVec( $28, TempPtr );ã if TempPtr <> @BackgroundInt thenã Removed := False;ã GetIntVec( $12, TempPtr );ã If TempPtr <> @OurInt12 thenã Removed := False;ãã GetIntVec( $09, TempPtr );ã if TempPtr <> @KeyBoardInt thenã Removed := False;ãã if Removed thenã beginã { Restore interrupts }ã SetIntVec( $28, PInt28 );ã SetIntVec( $12, PInt12 );ã SetIntVec( $09, PInt09 );ãã { Free up memory allocated to this program usingã INT 21 Func=49H "Release memory". }ã CPURegisters.AH := $49;ã CPURegisters.ES := PrefixSeg;{ Current program's PSP }ã Intr( $21, CPURegisters );ã end;ãend; {DoUnInstall}ããããããprocedure InstallTSR (var AlreadyInstalled: Boolean );ã{ Installs all interrupt handlers for this TSR. }ããvarã PSPPtr : ^Word;ã IdStr : ^String8;ãbeginãã { Check to see if the TSR is already running byã executing INT 12 with ES:BX pointing to IdStr1. If,ã after calling INT 12, ES:BX points to IdStr2, then theã TSR is running since it must have intercepted INT 12 andã set ES:BX to those return values. }ãã with CPURegisters doã beginã ES := Seg(IdStr1);ã BX := Ofs(IdStr1);ã CX := 1;ã Intr( $12, CPURegisters );ã IdStr := Ptr( ES, BX );ã if IdStr^ = IdStr2 thenã beginã AlreadyInstalled := True;ã Exit;ã end;ã { TSR hasn't been installed, so install ourã INT 12 driver. }ã asmã cliã end;ã GetIntVec( $12, PInt12 );ã SetIntVec( $12, @OurInt12 );ã asmã stiã end;ã end;ã AlreadyInstalled := False;ã MadeActive := False;ã DiskInUse := 0;ãã { Check to see if TSR is already installed. }ã TSRInUse := False;ãã { Deallocate DOS Environment block if not needed. }ã { Comment out this code if you need to accessã environment strings. }ã PSPPtr := Ptr( PrefixSeg, $2C );ã CPURegisters.AX := $4900;ã CPURegisters.ES := PSPPtr^;ã Intr( $21, CPURegisters);ãã asmã cliã end;ãã { Save and set INT 28H, background process interrupt. }ã GetIntVec( $28, PInt28 );ã SetIntVec( $28, @BackgroundInt );ãã { Save and set INT 09H, the keyboard interrupt handler. }ã GetIntVec( $09, PInt09 );ã SetIntVec( $09, @KeyboardInt );ã asmã stiã end;ãã { Initialize pointer to DOS's InDos flag. }ã { Uses INT 21H Function 34H to retrieve a pointer to theã InDos flag. The result is returned in the ES:BXã registers. }ã CPURegisters.AH := $34;ã Intr( $21, CPURegisters );ã PInDosFlag := Ptr( CPURegisters.ES, CPURegisters.BX );ãã asmã CLIã end;ã { Save our SS:SP for later use. }ã OurSS := SSeg;ã OurSP := SPtr;ã asmã STIã end;ãend; {InstallTSR}ããvarã InstallError: Boolean;ããbegin {program}ã InstallTSR( InstallError );ã if InstallError thenã beginã { This means that the TSR is already running.ã In that case, see if the command line requestsã an uninstall. }ã if (ParamStr(1) = '/u') or (ParamStr(1) = '/U') thenã { Send an Uninstall message to the TSR }ã with CPURegisters doã beginã ES := Seg(IdStr1);ã BX := Ofs(IdStr1);ã CX := 2;ã Intr( $12, CPURegisters );ã if CX = 0 thenã Writeln('TSR is now uninstalled.')ã elseã Writeln('Unable to uninstall.');ã Exit;ã endã elseã Writeln('!!!TSR is already installed!!!');ã endã elseã beginã Writeln('TSR is now resident.');ã Keep( 0 );ã { Exit to DOS, leaving program memory-resident. }ã end;ãend. {program}ã 23 02-05-9407:56ALL LOU DUCHEZ Disable ctrl-alt-del IMPORT 17 /M ã{ãHmm ... My initial attempt was to disable the Int 19h handler, which isãcalled when you hit ctrl-alt-del; but it didn't work. So here's a TSRãthat "cheats": if you hit "ctrl" and "alt" and then "del", it flagsã"ctrl" and "alt" as "not down" and so your system never sees the rebootãcondition. }ãã{$M $0400, $0000, $0000}ã{$F+}ããprogram noreboot;ãuses dos;ããconst ctrlbyte = $04; { Memory location $0040:$0017 governs the statuses of }ã altbyte = $08; { the Ctrl key, Alt, Shifts, etc. the "$04" bit }ã { handles "Ctrl"; "$08" handles "Alt". }ããvar old09h: procedure; { original keyboard handler }ã ctrldown, altdown, deldown: boolean;ã keyboardstat: byte absolute $0040:$0017; { the aforementioned location }ãã{-----------------------------------------------------------------------------}ããprocedure new09h; interrupt; { new keyboard handler: it checks if you'veã pressed "Ctrl", "Alt" and "Delete"; if youã have, it changes "Ctrl" and "Alt" toã "undepressed". Then it calls theã "old" keyboard handler. }ãbeginã if port[$60] and $1d = $1d then ctrldown := (port[$60] < 128);ã if port[$60] and $38 = $38 then altdown := (port[$60] < 128);ã if port[$60] and $53 = $53 then deldown := (port[$60] < 128);ã if ctrldown and altdown and deldown then beginã keyboardstat := keyboardstat and not ctrlbyte; { By killing the "Ctrl" }ã keyboardstat := keyboardstat and not altbyte; { and "Alt" bits, the }ã end; { "reboot" never runs }ã asmã pushfã end;ã old09h;ã end;ãã{-----------------------------------------------------------------------------}ããbeginã getintvec($09, @old09h);ã setintvec($09, @new09h); { set up new keyboard handler }ã ctrldown := false;ã altdown := false; { Set "Ctrl", "Alt", "Delete" to "False" }ã deldown := false;ã keep(0);ã end.ã 24 05-25-9408:24ALL ALEX KARIPIDIS Listing TSR's SWAG9405 26 / {ãWell, here it is, a program that lists TSRs loaded.ãAnybody willing to enhance it so that it checks the HMA for TSRs too?ããOh, were do I send this so that it gets into the next SWAGS release? I wasãunable to find such a program in the all the SWAGS until (and inclusive) theãlatest February '94 release...ãã{ ------------ Cut Here ----------- }ããProgram ListTSRs;ãã{ã Written by Alex Karipidis on the 8th of March 1994.ã Donated to the public domain. Use this code freely.ãã You can contact me at:ã Fidonet : 2:410/204.4ã Hellasnet: 7:2000/50.4ã SBCnet : 14:2100/201.4ã Zyxelnet : 16:800/108.4ã Pascalnet: 115:3005/1.4ãã If you enhance/improve this code in any way, I would appreciate it if youã sent me a copy of your version.ãã I will not be responsible for any damage caused by this code.ãã This program will print a list of all programs currently loaded inã memory.ã}ããTypeãã pMCB_Rec = ^MCB_Rec;ã MCB_Rec = Recordã ChainID : Byte; { 77 if part of MCB chain, 90 if last MCB allocated }ã Owner : Word; { PSP segment address of the MCB's owner }ã Paragraphs : Word; { Paragraphs related to this MCB }ã end;ããVarã MCB : pMCB_Rec;ã InVarsSeg, InVarsOfs : Word;ã EnvSeg, Counter : Word;ããbeginã { Dos service 52h returns the address of the DOS "invars" table in ES:BX }ã { !!! This is an undocumented DOS function !!! }ã asmã MOV AH,52hã INT 21hã MOV InVarsSeg,ESã MOV InVarsOfs,BXã end;ãã {ã The word before the "invars" table is the segment of the first MCBã allocated by DOS.ã }ã MCB := Ptr (MemW [InVarsSeg:InVarsOfs-2], 0);ãã While MCB^.ChainID <> 90 do { While valid MCBs exist... }ã beginãã If MCB^.Owner = Seg (MCB^) + 1 then { If MCB owns itself, then... }ã beginã Write ('In memory: '); { We've found a program in memory }ãã {ã The word at offset 2Ch of the program's PSP contains the value ofã the program's environment data area. That's were the program's nameã is located.ã }ã EnvSeg := MemW [MCB^.Owner:$2C];ãã {ã The environment also contains the environment variables as ASCIIZã (null-terminated) strings. Two consecutive null (0) bytes mean thatã the environment variables have ended and the program name followsã after 4 bytes. That is also an ASCIIZ string.ã }ã Counter := 0;ã While (Mem [EnvSeg:Counter ] <> 0) or { Find 2 consecutive }ã (Mem [EnvSeg:Counter+1] <> 0) do { null bytes. }ã inc (counter);ãã inc (counter,4); { Program name follows after 4 bytes }ãã While Mem [EnvSeg:Counter] <> 0 do { Print program name }ã beginã Write (Char (Mem [EnvSeg : Counter]));ã Inc (counter);ã end;ãã WriteLn;ãã end;ãã { Point to next MCB }ã MCB := Ptr (Seg (MCB^) + MCB^.Paragraphs + 1, 0);ã end;ãã {ã Note: The last MCB is not processed!ã It is assumed that it is this program's MCB.ã In your programs, this may or may not be the case.ã }ããend.ã
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/