home *** CD-ROM | disk | FTP | other *** search
- (* ┌───────────────────────────────────────────────────────────────────────┐
- │ MONITOR (v1.0) - Überwachung der Programm-Ladevorgänge │
- │ │
- │ (c) 1988 TOOLBOX & Karsten Gieselmann │
- └───────────────────────────────────────────────────────────────────────┘ *)
-
- {$R-,S-,I-,V-,B-,N-} (* keine Fehlerprüfungen, maximale Geschwindigkeit! *)
- {$M 1024,2048,2048} (* minimaler Stackbedarf reicht schon aus! *)
-
- PROGRAM Monitor;
-
- USES
- Dos; (* Mehr Units werden nicht benötigt! *)
-
- CONST
- Version : STRING[16] = 'RSM Monitor v1.0'; (* Versionsname und -nummer *)
- Name = 'MONITOR.EXE'; (* Name des MONITOR-Programms *)
- IFC = $65; (* Interruptvektor zur Adreßübergabe *)
-
- (* ------- Routinen für Umschaltung auf einen eigenen Laufzeitstapel ------- *)
-
- VAR
- SaveSS, SaveSP, (* Zwischenspeicher für alten Stapelzeiger *)
- MonSS, MonSP : WORD; (* Zeiger auf den MONITOR-Stapel *)
-
-
- PROCEDURE SwitchStack;
- (* vom aktuellen Stack auf den Monitor-Stapel umschalten *)
- INLINE(
- $8C/$16/SaveSS/ (* MOV [SaveSS],SS ;alten Stapelzeiger...*)
- $89/$26/SaveSP/ (* MOV [SaveSP],SP ;...sichern *)
- $FA/ (* CLI ;Interrupts verbieten *)
- $8E/$16/MonSS/ (* MOV SS,[MonSS] ;Zeiger auf eigenen...*)
- $8B/$26/MonSP/ (* MOV SP,[MonSP] ;..Stapel richten! *)
- $FB); (* STI ;Interrupts erlauben *)
-
-
- PROCEDURE RestoreStack;
- (* vom Monitor-Stapel zurück auf den gesicherten Stack schalten *)
- INLINE(
- $FA/ (* CLI ;Interrupts verbieten *)
- $8E/$16/SaveSS/ (* MOV SS,[SaveSS] ;Zeiger restaurieren *)
- $8B/$26/SaveSP/ (* MOV SP,[SaveSP] ; *)
- $FB); (* STI ;Interrupts erlauben *)
-
- (* ----------------- Routinen zum Aufbau der Programmliste ----------------- *)
-
- TYPE
- VectorTable = ARRAY[0..$FF] OF POINTER; (* Interrupttabelle *)
- ProgramData = RECORD
- ProgramName : ^STRING; (* Name des geladenen Programms *)
- ProgramSeg, (* Ladesegment des Programms *)
- EnvironSeg : WORD; (* Adresse des Umgebungsbereichs *)
- ProgramSize, (* Programmgröße in Bytes *)
- EnvironSize : LONGINT; (* Umgebungsgröße in Bytes *)
- TotalHooked : BYTE; (* Anzahl benutzte Vektoren *)
- HookedVectors : SET OF BYTE; (* Vektoren-Nummern *)
- SavedVectors : ^VectorTable; (* gesicherte Vektoren *)
- END;
-
- CONST
- ListSize = 100; (* maximale Größe der Programmliste *)
-
- VAR
- ProgramList : ARRAY[0..ListSize] OF ^ProgramData; (* die Liste *)
- j, k : BYTE; (* Zählvariablen *)
- c : ^CHAR;
- LastEntry, (* Index des letzten Listenelements *)
- ES_,
- CurrentPSP : WORD; (* PSP des zuletzt geladenen Programms *)
- CurrentName : STRING; (* Name des zuletzt geladenen Programms *)
- Overflow, (* ist ein Listenüberlauf aufgetreten? *)
- Released, (* letztes Programm aus Speicher entfernt? *)
- Terminated : BOOLEAN; (* Programmausführung beendet? *)
- BackupTable : VectorTable; (* gesicherte Interrupttabelle *)
- InterruptTable : VectorTable ABSOLUTE 0:0; (* aktuelle Interrupttabelle *)
-
-
- PROCEDURE InitializeList;
- (* bereitet die Liste für die Aufnahme von Daten vor *)
- VAR
- k : WORD;
- BEGIN
- Overflow := FALSE;
- BackupTable := InterruptTable;
- BackupTable[$22] := POINTER(MemL[MemW[PrefixSeg:$16]:$0A]);
- GetMem(ProgramList[0], SizeOf(Version));
- Move(Version, ProgramList[0]^, SizeOf(Version));
- SetIntVec(IFC, @ProgramList[0]); (* Zeiger auf Kennung *)
- FOR k:=1 TO ListSize DO
- ProgramList[k] := NIL; (* Ausgangszustand der Liste ist "leer"! *)
- LastEntry := 0;
- CurrentName := Name; (* den ersten Eintrag belegt MONITOR selbst! *)
- Terminated := TRUE;
- Released := FALSE;
- END;
-
-
- PROCEDURE AppendProgram;
- (* hängt das zuletzt geladene Programm an die Programmliste an *)
- BEGIN
- Inc(LastEntry); (* nächstes Listenelement *)
- New(ProgramList[LastEntry]);
- WITH ProgramList[LastEntry]^ DO BEGIN (* Listenelement mit Daten besetzen *)
- GetMem(ProgramName, Succ(Length(CurrentName)));
- Move(CurrentName, ProgramName^, Succ(Length(CurrentName)));
- ProgramSeg := CurrentPSP;
- EnvironSeg := MemW[ProgramSeg:$2C];
- ProgramSize := LONGINT(MemW[ProgramSeg-1:3])*16;
- IF (EnvironSeg <> 0) AND (MemW[EnvironSeg-1:1] = ProgramSeg) THEN
- EnvironSize := LONGINT(MemW[EnvironSeg-1:3])*16
- ELSE
- EnvironSize := 0;
- HookedVectors := [];
- TotalHooked := 0;
- FOR k:=0 TO 255 DO
- IF BackupTable[k] <> InterruptTable[k] THEN BEGIN (* Vektor benutzt? *)
- HookedVectors := HookedVectors + [k];
- Inc(TotalHooked);
- END;
- GetMem(SavedVectors, TotalHooked*SizeOf(POINTER)); (* Vektor sichern *)
- j := 0;
- FOR k:=0 TO 255 DO
- IF k IN HookedVectors THEN BEGIN
- SavedVectors^[j] := BackupTable[k];
- Inc(j);
- END;
- END;
- END;
-
-
- PROCEDURE DeleteProgram(Index : WORD);
- (* löscht alle Programme von Index..LastEntry aus der Liste heraus *)
- BEGIN
- IF (Index > 0) AND (Index <= LastEntry) THEN (* erlaubter Index? *)
- WITH ProgramList[Index]^ DO BEGIN
- FreeMem(SavedVectors, TotalHooked*SizeOf(POINTER));
- FreeMem(ProgramName, Succ(Length(ProgramName^)));
- Dispose(ProgramList[Index]);
- ProgramList[Index] := NIL;
- IF Index = LastEntry THEN (* gelöschtes Programm war das letzte... *)
- Dec(LastEntry); (* ...also Liste verkürzen *)
- END;
- END;
-
-
- PROCEDURE ExitCheck;
- (* Programm wurde normal beendet: es erfolgt keine Aufnahme in die Pro-
- grammliste bzw. Shell-Programme werden wieder aus der Liste entfernt. *)
- BEGIN
- Terminated := TRUE; (* Programmausführung wurde beendet... *)
- Released := TRUE; (* ...und Programm aus Speicher entfernt *)
- IF CurrentName = '' THEN
- DeleteProgram(LastEntry); (* war schon in Liste: wieder löschen *)
- CurrentName := '';
- END;
-
- (* ---- Interruptroutinen zur Überwachung des "DOS Process Management" ----- *)
-
- TYPE
- IntRegisters = RECORD CASE BYTE OF
- 1 : (BP,ES,DS,DI,SI,DX,CX,BX,AX,IP,CS,Flags : WORD);
- 2 : (Dummy : ARRAY[1..5] OF WORD;
- DL,DH,CL,CH,BL,BH,AL,AH : BYTE);
- END;
-
- VAR
- SaveInt20, (* alte Werte der vom MONITOR benutzten Vektoren *)
- SaveInt21,
- SaveInt27,
- ChainAddr : POINTER;
- RegsAddr : ^IntRegisters;
-
-
- FUNCTION PSP : WORD;
- (* ermittelt das Ladesegment des aktuellen Programms *)
- INLINE(
- $B4/$62/ (* MOV AH,62 ;Funktion Nr.62, "Get PSP" *)
- $9C/ (* PUSHF ;Flags für Interruptaufruf *)
- $FF/$1E/SaveInt21/ (* CALL FAR [..] ;Int-Aufruf simulieren *)
- $89/$D8); (* MOV AX,BX ;PSP nach AX zur Übergabe *)
-
-
- PROCEDURE ChainInt(Regs : IntRegisters; Address : POINTER);
- (* Weiterführen der Interrupt-Bearbeitung durch die alte Service-Routine;
- Restaurieren der CPU-Register gemäß "Regs" und Verzweigung zu "Address" *)
- INLINE(
- $5B/ (* POP BX ;"Address" nach AX:BX holen *)
- $58/ (* POP AX ; *)
- $5E/ (* POP SI ;Zeiger auf "Regs" nach DS:SI *)
- $1F/ (* POP DS ; *)
- $87/$5C/$0E/ (* XCHG BX,[SI+0E] ;Regs.BX <-> BX (=Ofs(Address)) *)
- $87/$44/$10/ (* XCHG AX,[SI+10] ;Regs.AX <-> AX (=Seg(Address)) *)
- $8B/$54/$16/ (* MOV DX,[SI+16] ;DX mit Regs.Flags besetzen... *)
- $52/ (* PUSH DX ;...und über den Stapel... *)
- $9D/ (* POPF ;...in das Statusregister laden *)
- $8C/$DA/ (* MOV DX,DS ; *)
- $FA/ (* CLI ;Interrupts unterbinden *)
- $8E/$D2/ (* MOV SS,DX ;Stapelzeiger auf "Regs" *)
- $89/$F4/ (* MOV SP,SI ; *)
- $FB/ (* STI ;Interrupts wieder erlauben *)
- $5D/ (* POP BP ;Register gemäß "Regs" besetzen *)
- $07/ (* POP ES ; *)
- $1F/ (* POP DS ; *)
- $5F/ (* POP DI ; *)
- $5E/ (* POP SI ; *)
- $5A/ (* POP DX ; *)
- $59/ (* POP CX ; *)
- $CB); (* RETF ;und Sprung zu "Address" (AX:BX) *)
-
-
- {$F+} (* alle Interruptroutinen müssen im FAR-Modell kompiliert werden!! *)
-
- PROCEDURE ISR_Int20(BP : WORD); INTERRUPT;
- (* Interrupt 20h: "Program Terminate" *)
- VAR
- Regs : IntRegisters ABSOLUTE BP; (* Register beim Interrupt-Eintritt *)
- BEGIN
- ChainAddr := SaveInt20;
- RegsAddr := @Regs;
- IF NOT Overflow THEN BEGIN
- SwitchStack; (* auf eigenen Stapel umschalten *)
- ExitCheck; (* Programm beendet und nicht resident *)
- RestoreStack; (* wieder zurück zum alten Stapel *)
- END;
- ChainInt(Regs, ChainAddr); (* weiter mit der alten Int20-Routine *)
- END;
-
-
- PROCEDURE ISR_Int23(BP : WORD); INTERRUPT;
- (* Interrupt 23h: "Control-C Handler Address" *)
- VAR
- Regs : IntRegisters ABSOLUTE BP; (* Register beim Interrupt-Eintritt *)
- BEGIN
- ChainAddr := SaveInt23;
- RegsAddr := @Regs;
- IF NOT Overflow THEN BEGIN
- SwitchStack; (* auf eigenen Stapel umschalten *)
- ExitCheck; (* Programm beendet und nicht resident *)
- RestoreStack; (* wieder zurück zum alten Stapel *)
- END;
- ChainInt(Regs, ChainAddr); (* weiter mit der alten Int27-Routine *)
- END;
-
-
- PROCEDURE ISR_Int21(BP : WORD); INTERRUPT;
- (* Interrupt 21h: DOS-Funktionsinterrupt *)
- VAR
- Regs : IntRegisters ABSOLUTE BP; (* Register beim Interrupt-Eintritt *)
- BEGIN
- ChainAddr := SaveInt21;
- RegsAddr := @Regs;
- IF NOT Overflow THEN WITH Regs DO
- CASE AH OF
- $31 : (* "Keep Process" *)
- BEGIN
- CurrentPSP := PSP; (* Programm-Segment merken *)
- Terminated := TRUE; (* beendet und resident installiert *)
- END;
-
- $49 : (* "Free Allocated Memory" *)
- BEGIN
- ES_ := ES; (* Zugriff auf Regs.ES sichern *)
- SwitchStack; (* auf eigenen Stapel umschalten *)
- IF (ES_ = CurrentPSP) AND Terminated THEN
- Released := TRUE
- ELSE (* Liste durchsuchen *)
- FOR k:=LastEntry DOWNTO 1 DO
- IF ProgramList[k]^.ProgramSeg = ES_ THEN (* in Liste? *)
- DeleteProgram(k);
- RestoreStack; (* wieder zurück zum alten Stapel *)
- END;
-
- $4B :
- IF AL = 0 THEN BEGIN (* "Load and Execute Program" *)
- c := Ptr(DS, DX); (* Zeiger auf Programmnamen holen *)
- SwitchStack; (* auf eigenen Stapel umschalten *)
- IF NOT Terminated THEN (* ein Shell-Programm! *)
- CurrentPSP := PSP;
- IF NOT Released THEN (* letztes Programm nicht entfernt: *)
- AppendProgram; (* ...dann an Liste anhängen *)
- k := 0;
- WHILE c^ <> #0 DO BEGIN (* neuen Programmnamen holen *)
- CurrentName[k+1] := Upcase(c^);
- Inc(LONGINT(c)); (* nächstes Zeichen *)
- Inc(k);
- END;
- CurrentName[0] := Chr(k); (* Zeichenlänge des Namens *)
- BackupTable := InterruptTable; (* Interrupttabelle sichern *)
- SaveInt23 := InterruptTable[$23]; (* alter ^C-Handler *)
- InterruptTable[$23] := @ISR_Int23; (* eigener ^C-Handler *)
- Terminated := FALSE;
- Released := FALSE;
- RestoreStack; (* wieder zurück zum alten Stapel *)
- END;
-
- $00, $4C : (* "Terminate Program", "End Process" *)
- BEGIN
- SwitchStack; (* auf eigenen Stapel umschalten *)
- ExitCheck; (* Programm beendet und nicht resident *)
- RestoreStack; (* wieder zurück zum alten Stapel *)
- END;
- END;
- ChainInt(Regs, ChainAddr) (* weiter mit der alten Int21-Routine *)
- END;
-
-
- PROCEDURE ISR_Int27(BP : WORD); INTERRUPT;
- (* Interrupt 27h: "Terminate and Stay Resident" *)
- VAR
- Regs : IntRegisters ABSOLUTE BP; (* Register beim Interrupt-Eintritt *)
- BEGIN
- ChainAddr := SaveInt27;
- RegsAddr := @Regs;
- IF NOT Overflow THEN BEGIN
- CurrentPSP := PSP; (* Programm-Segment merken *)
- Terminated := TRUE; (* beendet und resident installiert *)
- END;
- ChainInt(Regs, ChainAddr); (* weiter mit der alten Int27-Routine *)
- END;
-
-
- FUNCTION ReInstall(Size : WORD) : INTEGER;
- (* Abschalten des MONITORs beim Auftreten eines Heap-Überlaufs *)
- BEGIN
- Overflow := TRUE; (* MONITOR-Routinen außer Kraft setzen *)
- MemL[0:IFC*4] := $FFFFFFFF; (* Listenüberlauf anzeigen! *)
- RestoreStack; (* wieder zurück zum alten Stapel... *)
- ChainInt(RegsAddr^, ChainAddr); (* ...und weiter mit der abgebrochenen ISR *)
- END;
-
- {$F-} (* Ende der im FAR-Modell zu kompilierenden Routinen!! *)
-
- (* ------------------------- Installationsteil ----------------------------- *)
-
- FUNCTION InterfaceNotUsed : BOOLEAN;
- (* ist der Interface-Interruptvektor bereits benutzt? *)
- VAR
- Address : POINTER;
- BEGIN
- GetIntVec(IFC, Address);
- InterfaceNotUsed := (Address = Ptr($0000, $0000));
- END;
-
-
- PROCEDURE RestoreVectors;
- (* Rücksetzen der von Turbo benutzten Interruptvektoren *)
- BEGIN
- SetIntVec($00, SaveInt00);
- SetIntVec($24, SaveInt24);
- END;
-
-
- PROCEDURE Install_ISRs;
- (* Installation der Monitor-Prozeduren als Interrupt-Service-Routinen *)
- BEGIN
- GetIntVec($20, SaveInt20); (* Sichern der alten Vektoren... *)
- GetIntVec($21, SaveInt21);
- GetIntVec($27, SaveInt27);
- SetIntVec($20, @ISR_Int20); (* ...und Installieren der neuen ISR's *)
- SetIntVec($21, @ISR_Int21);
- SetIntVec($27, @ISR_Int27);
- END;
-
-
- BEGIN
- Write(^M^J, Version); (* Versionsmeldung ausgeben *)
- IF InterfaceNotUsed THEN
- WriteLn(' installiert!')
- ELSE BEGIN (* Abbruch! *)
- WriteLn(' nicht installiert,');
- WriteLn('Programm bereits resident oder Interface-Vektor benutzt!');
- Halt;
- END;
- MonSS := SSeg; (* Stapelwerte merken *)
- MonSP := SPtr;
- RestoreVectors; (* von Turbo benutzte Vektoren zurücksetzen *)
- InitializeList; (* Programmliste initialisieren *)
- HeapError := @ReInstall; (* eigene Heap-Fehlerbehandlung aktivieren *)
- Install_ISRs; (* eigene Interruptroutinen installieren *)
- Keep(0); (* resident machen!!! *)
- END.