home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
lan
/
spy
/
source
/
tsr.doc
< prev
next >
Wrap
Text File
|
1989-10-03
|
11KB
|
230 lines
{$A+,B-,D-,E-,F+,I-,L-,N-,O-,R-,S-,V+}
Unit Tsr;
{ TSR.PAS - Terminate and Stay Resident background processing for Turbo Pascal,
Version 5. This unit and its associated object file support unrestricted
background processes written in Pascal. This unit requires DOS 3.1 or later.
You must use the $M compiler directive in the main program or the
SetFreeHeap procedure (see below) to limit the memory occupied by the TSR
before calling BeginTSR. The default heap max limit of 640k will cause a
DOS crash immediately after the TSR is goes resident.
See TSRDEMO.PAS for example use.
For more comprehensive TSR support oriented toward pop-up utilities, please
refer to TPTSR by Turbo Power Software (ask on CompuServ BORPRO), and the
TESSERACT project (ask on CompuServ, CLM332).
Copyright 1989 by Edwin T. Floyd
All rights reserved.
Non-commercial use Ok.
Use at your own risk.
Author: Edwin T. Floyd [76067,747]
#9 Adams Park Court
Columbus, GA 31909
(404) 322-0076 }
Interface
Type
TsrRoutineType = Procedure;
KeyControlType = Record
CharCode : Char; { Character from BIOS status }
ScanCode : Byte; { Scan code from BIOS status }
FuncCode : Byte; { Function request to BIOS }
Action : (KeyUnavailable, KeySubstitute, KeyInsert, KeyFlush);
End;
KeyRoutineType = Procedure(Var Key : KeyControlType);
{ If KeyReq then KeyRoutine is called each time a BIOS Int 16h function
(00h, 01h, 10h, 11h) is requested and the current keystroke (if any) has
not been examined by KeyRoutine before. On entry, Action=KeyUnavailable
indicates no keystroke is waiting to process, CharCode and ScanCode are
invalid; Action=KeySubstitute indicates a keystroke is waiting.
Regardless of the value of FuncCode on entry, once KeyRoutine has set or
defaulted Action=KeySubstitute or KeyInsert, KeyRoutine will not be
called again until after the keystroke has been passed to the application
via a BIOS GetKey function (00h or 10h). }
Var
BackReq : Boolean; { If False, no background }
TickReq : Boolean; { If False, no clock tick call }
KeyReq : Boolean; { If False, no keyboard routine call }
Emul87 : Boolean; { If True, 80x87 emulation interrupts are swapped }
Real87 : Boolean; { If True, 80x87 registers are swapped }
ReleaseEMS : Boolean; { If True, Overlay EMS is released on exit }
{ Set ReleaseEMS := True if OvrInitEMS is successful }
Procedure BeginTSR(Background : TsrRoutineType; Code : Word);
{ Set up interrupts, Terminate and Stay Resident with exit code. If
successful, this procedure will not return. "Background" specifies the
procedure to be executed in background. Background may use any standard
Turbo Pascal facility, including DOS files, overlays and 80x87 (real or
emulated). When the Background procedure returns, the TSR is stopped.
Background should return control periodically to the foreground process by
calling TsrWait (see below). }
Procedure RemoveTSR;
{ Call from Background to remove the TSR from memory at the next opportunity.
If BeginTSR has been called, this routine will not return and Background will
not receive control again. Call RemoveTSR ONLY from Background, not from
and interrupt handler. }
Procedure TsrWait(Ticks : Word);
{ Return control to foreground process. Background is continued at the
next opportunity after "Ticks" timer ticks (55ms each) with the instruction
following the call to TsrWait. Call this procedure ONLY from Background, not
from an interrupt handler. }
Procedure WakeBackground;
{ Prematurely expire the TsrWait "Ticks" timer and set BackReq := True. The
background process will be continued at the next opportunity. WakeBackground
is safe to call from an interrupt handler. }
Function CallBackground : Boolean;
{ Use this from an interrupt handler to attempt to return control to the
background routine. Background will receive control if DOS is safe and
the TsrWait time has expired (BackReq is ignored). CallBackground returns
TRUE if background received control. CallBackground can sometimes improve
performance by restarting background without waiting for the next Int 28h
or Int 8h clock tick. CallBackground returns immediately if control could
not be returned to background, otherwise CallBackground does not return until
background calls TsrWait. }
Procedure SetTickRoutine(TickProc : TsrRoutineType);
{ Establish a timer tick routine. TickProc receives control with interrupts
disabled, once each clock tick (55ms) as long as TickReq is TRUE. DOS is NOT
safe inside TickProc; TickProc should be used only to inc/dec counters or set
flags. TickProc should be compiled with stack and error checking disabled
($S-,R-). SetTickRoutine is safe to call from an interrupt handler. }
Procedure SetKeyRoutine(KeyProc : KeyRoutineType);
{ Establish a keystroke filter routine (see above). DOS is NOT safe inside
KeyProc either. KeyProc should be compiled with stack and error checking
disabled ($S-,R-). SetKeyRoutine is safe to call from an interrupt
handler. }
Function TsrActive : Boolean;
{ Returns TRUE if BeginTSR has been called successfully. (You may not know
inside an ExitProc for instance.) TsrActive is safe to call from an
interrupt handler. }
Function IoResult : Integer;
{ Shell for System IoResult function which gets critical error info from TSR
intercept. }
Function CheckLoaded(Var Check : String) : Pointer;
{ Scans DOS Memory Control Blocks from low memory to high and returns the
address of the first string that matches Check at the same offset from the
beginning of a memory block as Check is from PrefixSeg. For a simple "I'm
already loaded" check: Declare a typed constant check string unique to your
TSR, then, If CheckLoaded(ChkStr) <> @ChkStr Then <I'm already loaded>.
If you wish to communicate with the previously loaded TSR, you may follow
the check string with data or procedure pointers and address them with the
pointer returned by CheckLoaded. }
{ Use the following procedures to optimize the use of memory }
Function ProgramSize : LongInt;
{ Returns the current size of the program block in bytes. This function
is safe to call from an interrupt handler (why would you want to?) }
Function DosFreeSpace : LongInt;
{ Returns the amount of free DOS memory immediately following the program
block. Call this function only from background, not from an interrupt
handler. }
Procedure SetFreeHeap(BytesFree : LongInt);
{ Changes the size of the free space at the end of the heap using DOS
function $4A to modify the memory block allocated to the program. The
heap may be expanded by no more than DosFreeSpace bytes. Setting heap
freespace to zero may make it impossible to Dispose/FreeMem because the
freespace table has nowhere to expand. Suggestion: Do as much heap
allocation as possible before going resident, then, just before calling
BeginTSR, call SetFreeHeap(<some small number>). This will minimize
the space the program occupies in memory. SetFreeHeap may be called
after the program is resident, but there is no way to predict how much
(if any) DosFreeSpace will be available to make the heap larger. Call
this procedure only from background, not from an interrupt handler. }
{ The following macros may be useful for interrupt handlers. }
Procedure DisableInterrupts;
{ Disable 80x86/8 interrupts }
Inline($FA);
Procedure EnableInterrupts;
{ Enable 80x86/8 interrupts }
Inline($FB);
Procedure ChainToInterrupt(IntAddr : Pointer);
Inline(
{ Chain to interrupt from Pascal interrupt handler }
$5B/ { pop bx; get interrupt address in ax:bx }
$58/ { pop ax }
$87/$5E/$0E/ { xchg [bp+$0E],bx; exchange interrupt address with old ax:bx }
$87/$46/$10/ { xchg [bp+$10],ax }
$89/$EC/ { mov sp,bp }
$FF/$76/$16/ { push [bp+$16]; restore flags }
$9D/ { popf }
$5D/ { pop bp; do most of the normal interrupt exit }
$07/ { pop es }
$1F/ { pop ds }
$5F/ { pop di }
$5E/ { pop si }
$5A/ { pop dx }
$59/ { pop cx }
$FA/ { cli; no interrupts }
$CB); { retf; bx/ax already restored, return thru old isr address }
Procedure CallInterrupt(IntAddr : Pointer);
Inline(
{ Call interrupt from Pascal interrupt handler. Uses original registers from }
{ stack and returns updated registers to stack }
$5B/ { pop bx ; save parameter}
$59/ { pop cx}
$E8/$00/$00/ { call x1 ; get IP}
$58/ {x1: pop ax}
$05/$2C/$00/ { add ax,(x2-x1) ; add offset to return}
$1E/ { push ds ; save ds}
$55/ { push bp ; and bp}
$FF/$76/$16/ { push [bp+$16] ; get original flags}
$9D/ { popf}
$9C/ { pushf ; push flags...}
$0E/ { push cs ; and our return address...}
$50/ { push ax ; so we look like an INT}
$51/ { push cx ; restore parameter (int ep)}
$53/ { push bx}
$8B/$46/$10/ { mov ax,[bp+$10] ; restore original regs}
$8B/$5E/$0E/ { mov bx,[bp+$0E]}
$8B/$4E/$0C/ { mov cx,[bp+$0C]}
$8B/$56/$0A/ { mov dx,[bp+$0A]}
$8B/$76/$08/ { mov si,[bp+$08]}
$8B/$7E/$06/ { mov di,[bp+$06]}
$8E/$5E/$04/ { mov ds,[bp+$04]}
$8E/$46/$02/ { mov es,[bp+$02]}
$8B/$6E/$00/ { mov bp,[bp+$00]}
$FA/ { cli ; enter interrupt disabled}
$CB/ { retf ; call interrupt via stack}
$9C/ {x2: pushf ; save int flags}
$55/ { push bp ; save int bp}
$89/$E5/ { mov bp,sp ; restore our bp}
$87/$6E/$04/ { xchg bp,[bp+$04]}
$89/$46/$10/ { mov [bp+$10],ax ; save int regs}
$89/$5E/$0E/ { mov [bp+$0E],bx}
$89/$4E/$0C/ { mov [bp+$0C],cx}
$89/$56/$0A/ { mov [bp+$0A],dx}
$89/$76/$08/ { mov [bp+$08],si}
$89/$7E/$06/ { mov [bp+$06],di}
$8C/$5E/$04/ { mov [bp+$04],ds}
$8C/$46/$02/ { mov [bp+$02],es}
$58/ { pop ax ; save int. bp}
$89/$46/$00/ { mov [bp+$00],ax}
$58/ { pop ax ; save int. flags}
$89/$46/$16/ { mov [bp+$16],ax ; save}
$58/ { pop ax ; was our bp, discard}
$1F); { pop ds ; restore our ds}
Implementation