home *** CD-ROM | disk | FTP | other *** search
- {**************************************************************************
- * RELNET - releases memory above the last MARKNET call made. *
- * Copyright (c) 1986,1989 Kim Kokkonen, TurboPower Software. *
- * May be distributed freely, but not for a profit except with written *
- * permission from TurboPower Software. *
- ***************************************************************************
- * Version 2.7 3/4/89 *
- * first public release *
- * (based on RELEASE 2.6) *
- * Version 2.8 3/10/89 *
- * restore the DOS environment *
- * restore the async ports *
- * Version 2.9 5/4/89 *
- * ignore file marks *
- ***************************************************************************
- * Telephone: 408-438-8608, CompuServe: 72457,2131. *
- * Requires Turbo version 5 to compile. *
- ***************************************************************************}
-
- {$R-,S-,I-}
-
- program ReleaseNet;
-
- uses
- {$IFDEF ReleaseSocket}
- NetWare, {This unit is part of TurboPower's
- commercial package B-Tree Filer.
- It isn't really needed here.}
- {$ENDIF}
- Dos;
-
- const
- Version = '2.9';
- NmarkID = 'MN2.9 TSR'; {Marking string for TSR file mark}
- NetMarkID = 'MN29'; {ID at start of net mark file}
- MarkID = 'M2.9 PARAMETER BLOCK FOLLOWS'; {Marking string for TSR MARK,
- just for WATCH here}
- FmarkID = 'FM2.9 TSR'; {Marking string for TSR file mark}
-
- ProtectChar = '!'; {Marks whose name begins with this will be
- released ONLY if an exact name match occurs}
- MaxBlocks = 128; {Max number of DOS allocation blocks supported}
- MaxHandles = 32; {Max number of EMS allocation blocks supported}
- EMSinterrupt = $67; {The vector used by the expanded memory manager}
-
- {Offsets into resident copy of MARK.COM for data storage}
- MarkOffset = $103; {Where markID is found in MARK TSR}
- NmarkOffset = $60; {Where NmarkID is found in FMARK TSR}
- FmarkOffset = $60; {Where FmarkID is found in FMARK TSR}
-
- WatchID = 'TSR WATCHER'; {Marking string for WATCH}
-
- {Offsets into resident copy of WATCH.COM for data storage}
- WatchOffset = $81;
- NextChange = $104;
- ChangeVectors = $220;
- OrigVectors = $620;
- CurrVectors = $A20;
- MaxChanges = 128; {Maximum number of vector deltas in WATCH}
-
- MarkFOpen : Boolean = False; {True while mark file is open}
- VectorsRestored : Boolean = False; {True after old vector table restored}
-
- Digits : array[0..$F] of Char = '0123456789ABCDEF';
-
- RBR = 0; {Receiver buffer register offset}
- THR = 0; {Transmitter buffer register offset}
- BRL = 0; {Baud rate low}
- BRH = 1; {Baud rate high}
- IER = 1; {Interrupt enable register}
- IIR = 2; {Interrupt identification register}
- LCR = 3; {Line control register}
- MCR = 4; {Modem control register}
- LSR = 5; {Line status register}
- MSR = 6; {Modem status register}
-
- type
- HandlePageRecord =
- record
- Handle : Word;
- NumPages : Word;
- end;
-
- PageArray = array[1..MaxHandles] of HandlePageRecord;
- PageArrayPtr = ^PageArray;
-
- Block =
- record {Store info about each memory block}
- Mcb : Word;
- Psp : Word;
- ReleaseIt : Boolean;
- end;
-
- BlockType = 0..MaxBlocks;
- BlockArray = array[BlockType] of Block;
-
- {Structure of a device driver header}
- DeviceHeader =
- record
- NextHeaderOffset : Word; {Offset address of next device in chain}
- NextHeaderSegment : Word; {Segment address of next device in chain}
- Attributes : Word; {Device attributes}
- StrategyEntPt : Word; {Offset in current segment - strategy}
- InterruptEntPt : Word; {Offset in current segment - interrupt}
- DeviceName : array[1..8] of Char; {Name of the device}
- end;
- DeviceHeaderPtr = ^DeviceHeader;
- DeviceArray = array[1..256] of DeviceHeaderPtr;
-
- SO =
- record
- O, S : Word;
- end;
-
- FileRec =
- record
- OpenCnt : Word;
- OpenMode : Word;
- Attribute : Byte;
- Unknown1 : Word;
- DCB : Pointer;
- InitCluster : Word;
- Time : Word;
- Date : Word;
- Size : LongInt;
- Pos : LongInt;
- BeginCluster : Word;
- CurCluster : Word;
- Block : Word;
- Unknown2 : Byte; {Varies with DOS version beyond here}
- Name : array[0..7] of Char;
- Ext : array[0..2] of Char;
- Unknown3 : array[0..5] of Byte;
- Owner : Word;
- Unknown4 : Word;
- end;
-
- SftRecPtr = ^SftRec;
- SftRec =
- record
- Next : SftRecPtr;
- Count : Word;
- Files : array[1..255] of FileRec;
- end;
-
- DosRec =
- record
- McbSeg : Word;
- FirstDPB : Pointer;
- FirstSFT : SftRecPtr;
- ClockDriver : Pointer;
- ConDriver : Pointer;
- MaxBlockBytes : Word;
- CachePtr : Pointer;
- DriveTable : Pointer;
- Unknown2 : Pointer;
- Unknown3 : Word;
- BlockDevices : Byte;
- LastDrive : Byte;
- NullDevice : DeviceHeader;
- end;
-
- ComRec = {State of the communications system}
- record
- Base : Word;
- IERReg : Byte;
- LCRReg : Byte;
- MCRReg : Byte;
- BRLReg : Byte;
- BRHReg : Byte;
- end;
- ComArray = array[1..2] of ComRec;
-
- var
- Blocks : BlockArray;
- WatchBlock : BlockType;
- BottomBlock : BlockType;
- BlockNum : BlockType;
-
- MarkName : String[79];
-
- Regs : Registers;
-
- ReturnCode : Word;
- StartMCB : Word;
- StoredHandles : Word;
- EMShandles : Word;
-
- UseWatch : Boolean;
- Revector8259 : Boolean;
- DealWithEMS : Boolean;
- KeepMark : Boolean;
- RestoreEnvir : Boolean;
- ResetTimer : Boolean;
- RestoreComm : Boolean;
- MemMark : Boolean;
- FilMark : Boolean;
- Junk : Boolean;
- Verbose : Boolean;
- Keys : string[16];
-
- FilMarkPageMap : PageArrayPtr;
- Map : PageArrayPtr;
- StoredMap : PageArrayPtr;
- TrappedBytes : LongInt;
-
- {Save areas read in from file mark}
- Vectors : array[0..1023] of Byte;
- EGAsavTable : array[0..7] of Byte;
- IntComTable : array[0..15] of Byte;
- ParentTable : array[0..1] of Byte;
- FilMarkHandles : Word;
- DevA : DeviceArray; {Temporary array of device headers}
- DevCnt : Word; {Number of device headers}
- CommandPsp : array[1..$100] of Byte; {Buffer for COMMAND.COM PSP}
- DosData : array[1..$200] of Byte; {Buffer for DOS data area}
- DosTableSize : Word;
- DosTable : Pointer; {Dos internal variables}
- FileTableA : array[1..5] of SftRecPtr; {Points to system file table buffers}
- FileTableCnt : Word; {Number of system file table blocks}
- FileRecSize : Word; {Bytes in internal DOS file record}
- PatchOfst : Word; {Address of COMMAND.COM patch}
- PatchSegm : Word;
- EnvLen : Word; {Bytes in DOS environment}
- EnvPtr : Pointer; {Pointer to copy of DOS environment}
- PicMask : Byte; {8259 interrupt mask}
- ComData : ComArray; {Communications data array}
-
- TestPtr : DeviceHeaderPtr; {Test pointer while getting started on chain}
- DevicePtr : DeviceHeaderPtr; {Pointer to the next device header}
- DeviceSegment : Word; {Current device segment}
- DeviceOffset : Word; {Current device offset}
- MarkF : file; {Saved system information file}
- DosPtr : ^DosRec; {Pointer to internal DOS variable table}
- CommandSeg : Word; {Segment of primary COMMAND.COM}
-
- procedure Halt(ReturnCode : Word);
- {-Replace Turbo halt with one that doesn't restore any interrupts}
- begin
- if VectorsRestored then begin
- Close(Output);
- with Regs do begin
- AH := $4C;
- AL := Lo(ReturnCode);
- MsDos(Regs);
- end;
- end else
- System.Halt(ReturnCode);
- end;
-
- procedure RemoveMarkFile;
- {-Close and remove the mark file}
- begin
- Close(MarkF);
- if IoResult = 0 then
- if not KeepMark then begin
- Erase(MarkF);
- if IoResult = 0 then ;
- end;
- MarkFOpen := False;
- end;
-
- procedure Abort(Msg : String);
- {-Halt in case of error}
- begin
- if MarkFOpen then
- RemoveMarkFile;
- WriteLn(Msg);
- Halt(255);
- end;
-
- function HexW(W : Word) : string;
- {-Return hex string for word}
- begin
- HexW[0] := #4;
- HexW[1] := Digits[hi(W) shr 4];
- HexW[2] := Digits[hi(W) and $F];
- HexW[3] := Digits[lo(W) shr 4];
- HexW[4] := Digits[lo(W) and $F];
- end;
-
- function HexPtr(P : Pointer) : string;
- {-Return hex string for pointer}
- begin
- HexPtr := HexW(SO(P).S)+':'+HexW(SO(P).O);
- end;
-
- function StUpcase(S : String) : string;
- {-Return uppercase for string}
- var
- I : Integer;
- begin
- for I := 1 to Length(S) do
- S[I] := Upcase(S[I]);
- StUpcase := S;
- end;
-
- procedure FindTheBlocks;
- {-Scan memory for the allocated memory blocks}
- const
- MidBlockID = $4D; {Byte DOS uses to identify part of MCB chain}
- EndBlockID = $5A; {Byte DOS uses to identify last block of MCB chain}
- var
- McbSeg : Word; {Segment address of current MCB}
- NextSeg : Word; {Computed segment address for the next MCB}
- GotFirst : Boolean; {True after first MCB is found}
- GotLast : Boolean; {True after last MCB is found}
- IdByte : Byte; {Byte that DOS uses to identify an MCB}
-
- procedure StoreTheBlock(var McbSeg, NextSeg : Word;
- var GotFirst, GotLast : Boolean);
- {-Store information regarding the memory block}
- var
- NextID : Byte;
- PspAdd : Word; {Segment address of the current PSP}
- McbLen : Word; {Size of the current memory block in paragraphs}
- begin
- McbLen := MemW[McbSeg:3]; {Size of the MCB in paragraphs}
- NextSeg := Succ(McbSeg+McbLen); {Where the next MCB should be}
- PspAdd := MemW[McbSeg:1]; {Address of program segment prefix for MCB}
- NextID := Mem[NextSeg:0];
-
- if GotLast or (NextID = EndBlockID) or (NextID = MidBlockID) then begin
- Inc(BlockNum);
- GotFirst := True;
- with Blocks[BlockNum] do begin
- Mcb := McbSeg;
- Psp := PspAdd;
- end;
- end;
- end;
-
- begin
- StartMCB := DosPtr^.McbSeg;
- McbSeg := StartMCB;
- GotFirst := False;
- GotLast := False;
- BlockNum := 0;
-
- {Scan all memory until the last block is found}
- repeat
- IdByte := Mem[McbSeg:0];
- if IdByte = MidBlockID then begin
- StoreTheBlock(McbSeg, NextSeg, GotFirst, GotLast);
- if GotFirst then
- McbSeg := NextSeg
- else
- Inc(McbSeg);
- end else if GotFirst and (IdByte = EndBlockID) then begin
- GotLast := True;
- StoreTheBlock(McbSeg, NextSeg, GotFirst, GotLast);
- end else
- {Start block was invalid}
- Abort('Corrupted allocation chain or program error....');
- until GotLast;
- end;
-
- function FindMark(MarkName, MarkID : String;
- MarkOffset : Word;
- var MemMark, FilMark : Boolean;
- var B : BlockType) : Boolean;
- {-Find the last memory block matching idstring at offset idoffset}
-
- function HasIDstring(Segment : Word;
- IdString : String;
- IdOffset : Word) : Boolean;
- {-Return true if idstring is found at segment:idoffset}
- var
- Tstring : String;
- Len : Byte;
- begin
- Len := Length(IdString);
- Tstring[0] := Chr(Len);
- Move(Mem[Segment:IdOffset], Tstring[1], Len);
- HasIDstring := (Tstring = IdString);
- end;
-
- function GetMarkName(Segment : Word) : String;
- {-Return a cleaned up mark name from the segment's PSP}
- var
- Tstring : String;
- Tlen : Byte absolute Tstring;
- begin
- Move(Mem[Segment:$80], Tstring[0], 128);
- while (Tlen > 0) and ((Tstring[1] = ' ') or (Tstring[1] = ^I)) do
- Delete(Tstring, 1, 1);
- while (Tlen > 0) and ((Tstring[Tlen] = ' ') or (Tstring[Tlen] = ^I)) do
- Dec(Tlen);
- GetMarkName := StUpcase(Tstring);
- end;
-
- function MatchMemMark(Segment : Word;
- MarkName : String;
- var B : BlockType) : Boolean;
- {-Return true if MemMark is unnamed or matches current name}
- var
- Tstring : String;
- FoundIt : Boolean;
- begin
- {Check the mark name stored in the PSP of the mark block}
- Tstring := GetMarkName(Segment);
- if (MarkName <> '') then begin
- FoundIt := (Tstring = MarkName);
- if not(FoundIt) then
- if (Tstring <> '') and (Tstring[1] = ProtectChar) then
- {Current mark is protected, stop searching}
- B := 1;
- end else if (Tstring <> '') and (Tstring[1] = ProtectChar) then begin
- {Stored mark name is protected}
- FoundIt := False;
- {Stop checking}
- B := 1;
- end else
- {Match any mark}
- FoundIt := True;
- if not(FoundIt) then
- Dec(B);
- MatchMemMark := FoundIt;
- end;
-
- function MatchFilMark(Segment : Word;
- MarkName : String;
- var B : BlockType) : Boolean;
- {-Return true if FilMark is unnamed or matches current name}
- var
- Tstring : String;
- FoundIt : Boolean;
-
- function ExistFile(Path : String) : Boolean;
- {-Return true if file exists}
- var
- F : file;
- begin
- Assign(F, Path);
- Reset(F);
- if IoResult = 0 then begin
- ExistFile := True;
- Close(F);
- end else
- ExistFile := False;
- end;
-
- begin
- {Check the mark name stored in the PSP of the mark block}
- Tstring := GetMarkName(Segment);
- if (MarkName <> '') then begin
- MarkName := MarkName;
- FoundIt := (Tstring = MarkName);
- if FoundIt then begin
- {Assure named file exists}
- if Verbose then
- WriteLn('Finding mark file ', MarkName);
- FoundIt := ExistFile(MarkName);
- end;
- end else
- {File marks must be named on RELEASE command line}
- FoundIt := False;
- if not(FoundIt) then
- {Net marks are protected marks}
- {Stop checking if a non-matching net mark is found}
- B := 0;
- MatchFilMark := FoundIt;
- end;
-
- begin
- {Scan from the last block down to find the last MARK TSR}
- B := BlockNum;
- MemMark := False;
- FilMark := False;
- repeat
- if Blocks[B].Psp = PrefixSeg then
- {Assure this program's command line is not matched}
- Dec(B)
- (* ignore file marks !!!!!!!
- else if HasIDstring(Blocks[b].psp, FmarkID, FmarkOffset) then
- {A file mark, can't release it here. Stop looking}
- b := 0
- *)
- else if HasIDstring(Blocks[B].Psp, MarkID, MarkOffset) then
- {An in-memory mark}
- MemMark := MatchMemMark(Blocks[B].Psp, MarkName, B)
- else if HasIDstring(Blocks[B].Psp, NmarkID, NmarkOffset) then
- {A net mark}
- FilMark := MatchFilMark(Blocks[B].Psp, MarkName, B)
- else
- {Not a mark}
- Dec(B);
- until (B < 1) or MemMark or FilMark;
- FindMark := MemMark or FilMark;
- end;
-
- procedure CheckReadError;
- {-Check previous I/O operation}
- begin
- if IoResult = 0 then
- Exit;
- Abort('Error reading '+MarkName);
- end;
-
- function PhysicalAddress(P : Pointer) : LongInt;
- begin
- PhysicalAddress := LongInt(SO(P).S) shl 4+SO(P).O;
- end;
-
- procedure ValidateMarkFile;
- {-Open mark file and assure it's valid}
- type
- IDArray = array[1..4] of Char;
- var
- ID : IDArray;
- ExpectedID : IDArray;
- begin
- Assign(MarkF, MarkName);
- Reset(MarkF, 1);
- if IoResult <> 0 then
- Abort('Mark file '+MarkName+' not found');
- MarkFOpen := True;
-
- {Check the ID at the start of the file}
- ExpectedID := NetMarkID;
- BlockRead(MarkF, ID, SizeOf(IDArray));
- CheckReadError;
- if ID <> ExpectedID then
- Abort(MarkName+' is not a valid net mark file');
-
- {Read the NUL device address}
- BlockRead(MarkF, TestPtr, SizeOf(Pointer));
- CheckReadError;
- if PhysicalAddress(TestPtr) <> PhysicalAddress(DevicePtr) then begin
- if Verbose then
- WriteLn('Old NUL addr:', HexPtr(TestPtr),
- ' Current NUL addr:', HexPtr(DevicePtr));
- Abort('Unexpected error. NUL device moved');
- end;
- end;
-
- procedure BufferFileTable;
- {-Read the file table from the mark file into memory}
- type
- SftRecStub =
- record
- Next : SftRecPtr;
- Count : Word;
- end;
- var
- I : Word;
- Size : Word;
- P : Pointer;
- S : SftRecStub;
- begin
- BlockRead(MarkF, FileTableCnt, SizeOf(Word));
- for I := 1 to FileTableCnt do begin
- BlockRead(MarkF, S, SizeOf(SftRecStub));
- Size := 6+S.Count*FileRecSize;
- GetMem(FileTableA[I], Size);
- P := FileTableA[I];
- Move(S, P^, SizeOf(SftRecStub));
- Inc(SO(P).O, SizeOf(SftRecStub));
- BlockRead(MarkF, P^, Size-SizeOf(SftRecStub));
- end;
- CheckReadError;
- end;
-
- procedure ReadReg(var B : Byte);
- {-Read a communications register from the mark file}
- begin
- BlockRead(MarkF, B, SizeOf(Byte));
- CheckReadError;
- end;
-
- procedure ReadMarkFile;
- {-Read the mark file info into memory}
- var
- DevPtr : DeviceHeaderPtr;
- Com : Byte;
- begin
- {Read the vector table from the mark file, into a temporary memory area}
- BlockRead(MarkF, Vectors, 1024);
- CheckReadError;
-
- {Read the BIOS miscellaneous save areas into temporary tables}
- BlockRead(MarkF, EGAsavTable, 8);
- BlockRead(MarkF, IntComTable, 16);
- BlockRead(MarkF, ParentTable, 2);
- CheckReadError;
-
- {Read the number of EMS handles stored}
- BlockRead(MarkF, FilMarkHandles, 2);
-
- {Get a page map area and read the page map into it}
- GetMem(FilMarkPageMap, 4*FilMarkHandles);
- BlockRead(MarkF, FilMarkPageMap^, 4*FilMarkHandles);
- CheckReadError;
-
- {Read the device driver chain}
- DevPtr := DevicePtr;
- DevCnt := 0;
- while SO(DevPtr).O <> $FFFF do begin
- Inc(DevCnt);
- GetMem(DevA[DevCnt], SizeOf(DeviceHeader));
- BlockRead(MarkF, DevA[DevCnt]^, SizeOf(DeviceHeader));
- CheckReadError;
- with DevA[DevCnt]^ do
- DevPtr := Ptr(NextHeaderSegment, NextHeaderOffset);
- end;
-
- {Read the DOS data area table}
- BlockRead(MarkF, DosData, $200);
- CheckReadError;
-
- {Read the DOS internal variables table}
- BlockRead(MarkF, DosTableSize, SizeOf(Word));
- if DosTableSize <> 0 then begin
- GetMem(DosTable, DosTableSize);
- BlockRead(MarkF, DosTable^, DosTableSize);
- end;
- CheckReadError;
-
- {Read the internal file table}
- BufferFileTable;
-
- {Read in the copy of COMMAND.COM's PSP}
- BlockRead(MarkF, CommandPsp, $100);
- CheckReadError;
-
- {Read in the address used for COMMAND.COM patching by NetWare}
- BlockRead(MarkF, PatchOfst, SizeOf(Word));
- BlockRead(MarkF, PatchSegm, SizeOf(Word));
- CheckReadError;
-
- {Read in the DOS master environment}
- BlockRead(MarkF, EnvLen, SizeOf(Word));
- GetMem(EnvPtr, EnvLen);
- BlockRead(MarkF, EnvPtr^, EnvLen);
- CheckReadError;
-
- {Read in the communications data area}
- BlockRead(MarkF, PicMask, SizeOf(Byte));
- CheckReadError;
- for Com := 1 to 2 do
- with ComData[Com] do begin
- BlockRead(MarkF, Base, SizeOf(Word));
- CheckReadError;
- if Base <> 0 then begin
- ReadReg(IERReg);
- ReadReg(LCRReg);
- ReadReg(MCRReg);
- ReadReg(BRLReg);
- ReadReg(BRHreg);
- end;
- end;
-
- {Close and possibly erase mark file}
- RemoveMarkFile;
- end;
-
- procedure IntsOff;
- {-Turn off CPU interrupts}
- inline($FA);
-
- procedure IntsOn;
- {-Turn on CPU interrupts}
- inline($FB);
-
- procedure NullJump;
- {-Slight delay}
- inline($EB/$00);
-
- procedure RestoreCommState;
- {-Restore the communications chips to their previous state}
- var
- Com : Byte;
- begin
- for Com := 1 to 2 do
- with ComData[Com] do
- if Base <> 0 then begin
- Port[Base+IER] := IERReg; {Interrupt enable register}
- NullJump;
- Port[Base+MCR] := MCRReg; {Modem control register}
- NullJump;
- Port[Base+LCR] := LCRReg or $80; {Enable baud rate divisor registers}
- NullJump;
- Port[Base+BRL] := BRLReg; {Baud rate low}
- NullJump;
- Port[Base+BRH] := BRHReg; {Baud rate high}
- NullJump;
- Port[Base+LCR] := LCRReg; {Line control register}
- NullJump;
- end;
- {Restore the interrupt mask}
- Port[$21] := PicMask;
- end;
-
- procedure CopyVectors;
- {-Put interrupt vectors back into table}
-
- procedure Reset8259;
- {-Reset the 8259 interrupt controller to its powerup state}
- {-Interrupts assumed OFF prior to calling this routine}
-
- function ATmachine : Boolean;
- {-Return true if machine is AT class}
- var
- MachType : Byte absolute $FFFF : $000E;
- begin
- case MachType of
- $F8, $FC : ATmachine := True;
- else
- ATmachine := False;
- end;
- end;
-
- procedure Reset8259PC;
- {-Reset the 8259 on a PC class machine}
- begin
- inline(
- $E4/$21/ { in al,$21}
- $88/$C4/ { mov ah,al}
- $B0/$13/ { mov al,$13}
- $E6/$20/ { out $20,al}
- $B0/$08/ { mov al,8}
- $E6/$21/ { out $21,al}
- $B0/$09/ { mov al,9}
- $E6/$21/ { out $21,al}
- $88/$E0/ { mov al,ah}
- $E6/$21 { out $21,al}
- );
- end;
-
- procedure Reset8259AT;
- {-Reset the 8259 interrupt controllers on an AT machine}
- begin
- inline(
- $32/$C0/ { xor al,al }
- $E6/$F1/ { out 0f1h,al ; Switch off an 80287 if necessary}
- {Set up master 8259 }
- $E4/$21/ { in al,21h ; Get current interrupt mask }
- $8A/$E0/ { mov ah,al ; save it }
- $B0/$11/ { mov al,11h }
- $E6/$20/ { out 20h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$08/ { mov al,8 ; Set up main interrupt vector number}
- $E6/$21/ { out 21h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$04/ { mov al,4 }
- $E6/$21/ { out 21h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$01/ { mov al,1 }
- $E6/$21/ { out 21h,al }
- $EB/$00/ { jmp short $+2 }
- $8A/$C4/ { mov al,ah }
- $E6/$21/ { out 21h,al }
- {Set up slave 8259 }
- $E4/$A1/ { in al,0a1h ; Get current interrupt mask }
- $8A/$E0/ { mov ah,al ; save it }
- $B0/$11/ { mov al,11h }
- $E6/$A0/ { out 0a0h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$70/ { mov al,70h }
- $E6/$A1/ { out 0a1h,al }
- $B0/$02/ { mov al,2 }
- $EB/$00/ { jmp short $+2 }
- $E6/$A1/ { out 0a1h,al }
- $EB/$00/ { jmp short $+2 }
- $B0/$01/ { mov al,1 }
- $E6/$A1/ { out 0a1h,al }
- $EB/$00/ { jmp short $+2 }
- $8A/$C4/ { mov al,ah ; Reset previous interrupt state }
- $E6/$A1 { out 0a1h,al }
- );
- end;
-
- begin
- if ATmachine then
- Reset8259AT
- else
- Reset8259PC;
- end;
-
- begin
- {Interrupts off}
- IntsOff;
-
- {Reset 8259 if requested}
- if Revector8259 then
- Reset8259;
-
- {Reset the communications state if requested}
- if RestoreComm then
- RestoreCommState;
-
- {Restore the main interrupt vector table and the misc save areas}
- Move(Vectors, Mem[0:0], 1024);
-
- {Interrupts on}
- IntsOn;
-
- {Flag that we don't want system restoring vectors for us}
- VectorsRestored := True;
-
- Move(EGAsavTable, Mem[$40:$A8], 8); {EGA table}
- Move(IntComTable, Mem[$40:$F0], 16); {Interapplications communication area}
- Move(ParentTable, Mem[PrefixSeg:$16], 2); {Parent address}
- Move(Mem[0:$88], Mem[PrefixSeg:$0A], 12); {Int 22,23,24 addresses}
- end;
-
- procedure MarkBlocks(BottomBlock : BlockType);
- {-Mark those blocks to be released}
- var
- B : BlockType;
- CommandPsp, MarkPsp : Word;
-
- procedure BatchWarning(B : BlockType);
- {-Warn about the trapping effect of batch files}
- var
- T : BlockType;
- begin
- ReturnCode := 1;
- {Accumulate number of bytes temporarily trapped}
- for T := 1 to B do
- if Blocks[T].ReleaseIt then
- Inc(TrappedBytes, LongInt(MemW[Blocks[T].Mcb:3]) shl 4);
- end;
-
- begin
- CommandPsp := Blocks[2].Psp;
- MarkPsp := Blocks[BottomBlock].Psp;
-
- for B := 1 to BlockNum do
- with Blocks[B] do
- if (B < BottomBlock) then begin
- {Release any trapped environment block}
- if KeepMark then
- releaseIt := (psp <> PrefixSeg) and (psp > markPsp)
- else
- releaseIt := (psp <> PrefixSeg) and (psp >= markPsp);
- end else if (Psp = CommandPsp) then begin
- {Don't release blocks owned by COMMAND.COM}
- ReleaseIt := False;
- BatchWarning(B);
- end else if KeepMark then
- {Release all but RELNET and the mark}
- releaseIt := (psp <> PrefixSeg) and (psp <> markPsp)
- else
- {Release all but RELNET itself}
- ReleaseIt := (Psp <> PrefixSeg);
-
- {$IFDEF Debug}
- for B := 1 to BlockNum do
- with Blocks[B] do
- WriteLn(B:3, ' ', HexW(Psp), ' ', HexW(Mcb), ' ', ReleaseIt);
- {$ENDIF}
- end;
-
- procedure ReleaseMem;
- {-Release DOS memory marked for release}
- var
- B : BlockType;
- begin
- if Verbose then
- WriteLn('Releasing DOS memory');
- with Regs do
- for B := 1 to BlockNum do
- with Blocks[B] do
- if ReleaseIt then begin
- AH := $49;
- {The block is always 1 paragraph above the MCB}
- ES := Mcb+1;
- MsDos(Regs);
- if Odd(Flags) then begin
- WriteLn('Could not release block at segment ', HexW(ES));
- Abort('Memory may be a mess.... Please reboot');
- end;
- end;
- end;
-
- procedure UpdateWatch(WatchBlock : BlockType);
- {-Write a new watch data area based on the release and the original watch}
- type
- ChangeBlock =
- record
- VecID : Word;
- VecOfs : Word;
- VecSeg : Word;
- PatchWord : Word;
- end;
- var
- Changes : array[0..MaxChanges] of ChangeBlock;
- P : ^ChangeBlock;
- WatchSeg, C, O, I, ActualMax : Word;
- KeepPSP : Boolean;
-
- function WillKeepPSP(PspAdd : Word) : Boolean;
- {-Return true if this psp address will be kept}
- var
- B : BlockType;
- begin
- for B := 1 to BlockNum do
- with Blocks[B] do
- if Psp = PspAdd then begin
- WillKeepPSP := not(ReleaseIt);
- Exit;
- end;
- end;
-
- begin
- WatchSeg := Blocks[WatchBlock].Psp;
- ActualMax := MemW[WatchSeg:NextChange];
-
- {Transfer changes from WATCH into a buffer array}
- I := 0;
- O := 0;
- while I < ActualMax do begin
- P := Ptr(WatchSeg, ChangeVectors+I);
- Move(P^, Changes[O], SizeOf(ChangeBlock));
- Inc(I, SizeOf(ChangeBlock));
- Inc(O);
- end;
-
- {Determine which change records to keep and transfer them back to WATCH}
- KeepPSP := True;
- I := 0;
- for C := 0 to Pred(O) do begin
- with Changes[C] do
- if VecID = $FFFF then
- {This record starts a new PSP. See if PSP is kept in memory}
- KeepPSP := WillKeepPSP(VecOfs);
- if KeepPSP then begin
- P := Ptr(WatchSeg, ChangeVectors+I);
- Move(Changes[C], P^, SizeOf(ChangeBlock));
- I := I+SizeOf(ChangeBlock);
- end;
- end;
- MemW[WatchSeg:NextChange] := I;
-
- {Update the WATCH image of the vector table to whatever's current}
- Move(Mem[0:0], Mem[WatchSeg:CurrVectors], 1024);
- end;
-
- function EMSpresent : Boolean;
- {-Return true if EMS memory manager is present}
- var
- F : file;
- begin
- {"file handle" defined by the expanded memory manager at installation}
- Assign(F, 'EMMXXXX0');
- Reset(F);
- if IoResult = 0 then begin
- EMSpresent := True;
- Close(F);
- end else
- EMSpresent := False;
- end;
-
- procedure RestoreEMSmap;
- {-Restore EMS to state at time of mark}
-
- procedure EMSpageMap(var PageMap : PageArray; var EMShandles : Word);
- {-return an array of the allocated memory blocks}
- begin
- Regs.AH := $4D;
- Regs.ES := Seg(PageMap);
- Regs.DI := Ofs(PageMap);
- Regs.BX := 0;
- Intr(EMSinterrupt, Regs);
- if Regs.AH <> 0 then
- EMShandles := 0
- else
- EMShandles := Regs.BX;
- end;
-
- procedure ReleaseEMSblocks(var OldMap, NewMap : PageArray);
- {-Release those EMS blocks allocated since MARK was installed}
- var
- O, N, NHandle : Word;
-
- procedure EMSdeallocate(EMShandle : Word);
- {-Release the allocated expanded memory}
- begin
- Regs.AH := $45;
- Regs.DX := EMShandle;
- Intr(EMSinterrupt, Regs);
- if Regs.AH <> 0 then begin
- WriteLn('Program error or EMS device not responding');
- Abort('EMS memory may be a mess... Please reboot');
- end;
- end;
-
- begin
- for N := 1 to EMShandles do begin
- {Scan all current handles}
- NHandle := NewMap[N].Handle;
- if StoredHandles > 0 then begin
- {See if current handle matches one stored by MARK}
- O := 1;
- while (OldMap[O].Handle <> NHandle) and (O <= StoredHandles) do
- Inc(O);
- {If not, deallocate the current handle}
- if (O > StoredHandles) then
- EMSdeallocate(NHandle);
- end else
- {No handles stored by MARK, deallocate all current handles}
- EMSdeallocate(NHandle);
- end;
- end;
-
- begin
- {Get the existing EMS page map}
- GetMem(Map, 2048);
- EMSpageMap(Map^, EMShandles);
- if EMShandles > MaxHandles then
- WriteLn('EMS handle count exceeds capacity of RELNET -- no action taken')
- else if EMShandles <> 0 then begin
- {See how many handles were active when MARK was installed}
- if Verbose then
- WriteLn('Releasing EMS memory allocated since mark');
- StoredHandles := FilMarkHandles;
- {Get the stored page map}
- StoredMap := FilMarkPageMap;
- {Compare the two maps and deallocate pages not in the stored map}
- ReleaseEMSblocks(StoredMap^, Map^);
- end;
- end;
-
- procedure GetOptions;
- {-Analyze command line for options}
- var
- Arg : String;
- ArgLen : Byte absolute Arg;
- I : Word;
-
- procedure WriteHelp;
- {-Show the options}
- begin
- WriteLn;
- WriteLn('RELNET removes memory-resident programs from memory, particularly network');
- WriteLn('shells like Novell''s NetWare, although it will also release normal memory');
- WriteLn('resident programs. In combination with MARKNET it thoroughly restores the');
- WriteLn('system to its state at the time MARKNET was called.');
- WriteLn;
- WriteLn('RELNET accepts the following command line syntax:');
- WriteLn;
- WriteLn(' RELNET NetMarkFile [Options]');
- WriteLn;
- WriteLn('Options may be preceded by either / or -. Valid options are:');
- WriteLn;
- WriteLn(' /C do NOT restore communications state.');
- WriteLn(' /E do NOT access EMS memory.');
- WriteLn(' /K release memory, but keep the mark in place.');
- WriteLn(' /P do NOT restore DOS environment.');
- WriteLn(' /R revector 8259 interrupt controller to powerup state.');
- WriteLn(' /S chars stuff string (<16 chars) into keyboard buffer on exit.');
- WriteLn(' /T do NOT reset system timer chip to default rate.');
- WriteLn(' /V verbose: show each step of the restore.');
- WriteLn(' /? write this help screen.');
- Halt(1);
- end;
-
- begin
- {Initialize defaults}
- MarkName := '';
- Keys := '';
-
- Revector8259 := False;
- KeepMark := False;
- DealWithEMS := True;
- ResetTimer := True;
- Verbose := False;
- RestoreEnvir := True;
- RestoreComm := True;
-
- ReturnCode := 0;
- TrappedBytes := 00;
-
- I := 1;
- while I <= ParamCount do begin
- Arg := ParamStr(I);
- if (Arg[1] = '?') then
- WriteHelp
- else if (Arg[1] = '-') or (Arg[1] = '/') then
- case ArgLen of
- 1 : Abort('Missing command option following '+Arg);
- 2 : case Upcase(Arg[2]) of
- 'C' : RestoreComm := False;
- 'E' : DealWithEMS := False;
- 'K' : KeepMark := True;
- 'P' : RestoreEnvir := False;
- 'R' : Revector8259 := True;
- 'S' : begin
- if I >= ParamCount then
- Abort('Key string missing');
- inc(I);
- Arg := ParamStr(I);
- if ArgLen > 15 then
- Abort('No more than 15 keys may be stuffed');
- Keys := Arg+^M;
- end;
- 'T' : ResetTimer := False;
- 'V' : Verbose := True;
- '?' : WriteHelp;
- else
- Abort('Unknown command option: '+Arg);
- end;
- else
- Abort('Unknown command option: '+Arg);
- end
- else if Length(MarkName) = 0 then
- {Mark file}
- MarkName := StUpcase(Arg)
- else
- Abort('Too many mark files specified');
- Inc(I);
- end;
-
- if Length(MarkName) = 0 then begin
- WriteLn('No mark file specified');
- WriteHelp;
- end;
- end;
-
- {$IFDEF ReleaseSocket}
- procedure CloseSockets;
- {-Close IPX sockets. Specific to Novell NetWare}
- var
- Socket : Word;
- Status : Byte;
- begin
- if Verbose then
- Write('Closing sockets ');
- for Socket := 0 to $1000 do begin
- Status := IPXOpenSocket(Socket, False);
- case Status of
- 0 : {Socket wasn't open}
- IPXCloseSocket(Socket);
- $FF : {Socket was already open}
- begin
- if Verbose then
- Write(HexW(Socket), ' ');
- IPXCloseSocket(Socket);
- end;
- end;
- end;
- if Verbose then
- WriteLn;
- end;
- {$ENDIF}
-
- procedure GetDosPtr;
- {-Return pointer to DOS internal variables table}
- begin
- with Regs do begin
- AH := $52;
- MsDos(Regs);
- Dec(BX, 2);
- DosPtr := Ptr(ES, BX);
- end;
- end;
-
- procedure FindDevChain;
- {-Return segment, offset and pointer to NUL device}
- begin
- GetDosPtr;
- DevicePtr := @DosPtr^.NullDevice;
- DeviceSegment := SO(DevicePtr).S;
- DeviceOffset := SO(DevicePtr).O;
- end;
-
- procedure RestoreDosTable;
- {-Restore the DOS variables table, except for the buffer pointer}
- var
- DosBase : Pointer;
- CPtr : Pointer;
- begin
- if Verbose then
- WriteLn('Restoring DOS data area at 0050:0000');
- CPtr := Ptr($50, 0);
- Move(DosData, CPtr^, $200);
- DosBase := Ptr(SO(DosPtr).S, 0);
- if Verbose then
- WriteLn('Restoring DOS variables table at ', HexPtr(DosBase));
- CPtr := DosPtr^.CachePtr;
- move(DosTable^, DosBase^, DosTableSize);
- DosPtr^.CachePtr := CPtr;
- end;
-
- procedure RestoreFileTable;
- {-Copy the internal file table from our memory buffer to its DOS location}
- var
- S : SftRecPtr;
- I : Word;
- begin
- S := DosPtr^.FirstSFT;
- if Verbose then
- WriteLn('Restoring DOS file table at ', HexPtr(S));
- for I := 1 to FileTableCnt do begin
- Move(FileTableA[I]^, S^, 6+FileTableA[I]^.Count*FileRecSize);
- S := S^.Next;
- end;
- end;
-
- procedure RestoreDeviceDrivers;
- {-Restore the device driver chain to its original state}
- var
- D : Word;
- DevPtr : DeviceHeaderPtr;
- begin
- if Verbose then
- WriteLn('Restoring device driver chain');
- DevPtr := DevicePtr;
- for D := 1 to DevCnt do begin
- DevPtr^ := DevA[D]^;
- with DevA[D]^ do
- DevPtr := Ptr(NextHeaderSegment, NextHeaderOffset);
- end;
- end;
-
- procedure RestoreCommandPSP;
- {-Copy COMMAND.COM's PSP back into place}
- type
- McbRec =
- record
- ID : Char;
- PSPSeg : Word;
- Len : Word;
- end;
- var
- McbPtr : ^McbRec;
- PspPtr : Pointer;
- begin
- {First block}
- McbPtr := Ptr(DosPtr^.McbSeg, 0);
- {Next block, which is owned by COMMAND.COM}
- McbPtr := Ptr(SO(McbPtr).S+McbPtr^.Len+1, 0);
- CommandSeg := McbPtr^.PSPSeg;
- PspPtr := Ptr(CommandSeg, 0);
- if Verbose then
- WriteLn('Restoring COMMAND.COM PSP at ', HexPtr(PspPtr));
- Move(CommandPsp, PspPtr^, $100);
- end;
-
- procedure RestoreCommandPatch;
- {-Restore the patch that NetWare applies to COMMAND.COM}
- begin
- if (PatchSegm <> 0) or (PatchOfst <> 0) then
- if (Mem[PatchSegm:PatchOfst+$01] <> Byte('/')) or
- (Mem[PatchSegm:PatchOfst+$11] <> Byte('/')) then begin
- if Verbose then
- WriteLn('Removing patch at ', HexW(PatchSegm), ':', HexW(PatchOfst));
- Mem[PatchSegm:PatchOfst+$01] := Byte('/');
- Mem[PatchSegm:PatchOfst+$11] := Byte('/');
- end;
- end;
-
- procedure FindEnv(CommandSeg : Word; var EnvSeg, EnvLen : Word);
- {-Return the segment and length of the master environment}
- var
- Mcb : Word;
- begin
- Mcb := CommandSeg-1;
- EnvSeg := MemW[CommandSeg:$2C];
- if EnvSeg = 0 then
- {Master environment is next block past COMMAND}
- EnvSeg := Commandseg+MemW[Mcb:3]+1;
- EnvLen := MemW[(EnvSeg-1):3] shl 4;
- end;
-
- procedure RestoreDosEnvironment;
- {-Restore the master copy of the DOS environment}
- var
- EnvSeg : Word;
- CurLen : Word;
- P : Pointer;
- begin
- if RestoreEnvir then begin
- FindEnv(CommandSeg, EnvSeg, CurLen);
- if CurLen <> EnvLen then
- Abort('Environment length changed');
- if Verbose then
- WriteLn('Restoring DOS environment, ', EnvLen, ' bytes at ', HexW(EnvSeg), ':0000');
- P := Ptr(EnvSeg, 0);
- move(EnvPtr^, P^, EnvLen);
- end;
- end;
-
- procedure SetTimerRate(Rate : Word);
- {-Program system 8253 timer number 0 to run at specified rate}
- begin
- IntsOff;
- Port[$43] := $36;
- NullJump;
- Port[$40] := Lo(Rate);
- NullJump;
- Port[$40] := Hi(Rate);
- IntsOn;
- end;
-
- procedure RestoreTimer;
- {-Set the system timer to its normal rate}
- begin
- if Verbose then
- WriteLn('Restoring system timer to normal rate');
- SetTimerRate(0);
- end;
-
- function CompaqDOS30 : Boolean;
- {-Return true if Compaq DOS 3.0}
- begin
- with Regs do begin
- AH := $34;
- MsDos(Regs);
- CompaqDOS30 := (BX = $19C);
- end;
- end;
-
- procedure ValidateDosVersion;
- {-Assure supported version of DOS and compute size of DOS internal filerec}
- var
- DosVer : Word;
- begin
- DosVer := DosVersion;
- case Lo(DosVer) of
- 3 : if (Hi(DosVer) < $0A) and not CompaqDOS30 then
- {IBM DOS 3.0}
- FileRecSize := 56
- else
- {DOS 3.1+ or Compaq DOS 3.0}
- FileRecSize := 53;
- 4 : FileRecSize := 59;
- else
- Abort('Requires DOS 3.x or 4.x');
- end;
- end;
-
- const
- KbdStart = $1E;
- KbdEnd = $3C;
- var
- KbdHead : Word absolute $40 : $1A;
- KbdTail : Word absolute $40 : $1C;
-
- procedure StuffKey(W : Word);
- {-Stuff one key into the keyboard buffer}
- var
- SaveKbdTail : Word;
- begin
- SaveKbdTail := KbdTail;
- if KbdTail = KbdEnd then
- KbdTail := KbdStart
- else
- Inc(KbdTail, 2);
- if KbdTail = KbdHead then
- KbdTail := SaveKbdTail
- else
- MemW[$40:SaveKbdTail] := W;
- end;
-
- procedure StuffKeys(Keys : string; ClearFirst : Boolean);
- {-Stuff up to 16 keys into keyboard buffer}
- var
- Len : Byte;
- I : Byte;
- begin
- if ClearFirst then
- KbdTail := KbdHead;
- Len := Length(Keys);
- if Len > 16 then
- Len := 16;
- for I := 1 to Length(Keys) do
- StuffKey(Ord(Keys[I]));
- end;
-
- begin
- WriteLn('RELNET ', Version, ', by TurboPower Software');
-
- {Assure supported version of DOS}
- ValidateDosVersion;
-
- {Analyze command line for options}
- GetOptions;
-
- {Find the start of the device driver chain via the NUL device}
- FindDevChain;
-
- {Get all allocated memory blocks in normal memory}
- FindTheBlocks;
-
- {Find the block marked with the MARK idstring, and MarkName if specified}
- if not(FindMark(MarkName, MarkID, MarkOffset, MemMark, FilMark, BottomBlock)) then
- Abort('No matching marker found, or protected marker encountered.');
- if MemMark then
- Abort('Marker must have been placed by NETMARK');
-
- {Find the watch block, if any}
- UseWatch := FindMark('', WatchID, WatchOffset, Junk, Junk, WatchBlock);
-
- {Mark those blocks to be released}
- MarkBlocks(BottomBlock);
-
- {Open and validate the mark file}
- ValidateMarkFile;
-
- {$IFDEF ReleaseSocket}
- {Close IPX sockets}
- if IPXServicesAvail then
- CloseSockets;
- {$ENDIF}
-
- {Get file mark information into memory}
- ReadMarkFile;
-
- {Copy the vector table from the MARK copy}
- CopyVectors;
-
- {Restore the device driver chain}
- RestoreDeviceDrivers;
-
- {Restore the COMMAND.COM patch possibly made by NetWare}
- RestoreCommandPatch;
-
- {Restore the DOS variables table}
- RestoreDosTable;
-
- {Restore the DOS file table}
- RestoreFileTable;
-
- {Restore the COMMAND.COM PSP}
- RestoreCommandPSP;
-
- {Restore the master DOS environment}
- RestoreDosEnvironment;
-
- {Set the timer to normal rate}
- if ResetTimer then
- RestoreTimer;
-
- {Update the watch block if requested}
- if UseWatch then
- if not Blocks[WatchBlock].ReleaseIt then
- {Watch itself won't be released}
- UpdateWatch(WatchBlock);
-
- {Release normal memory}
- ReleaseMem;
-
- {Deal with expanded memory}
- if DealWithEMS then
- if EMSpresent then
- RestoreEMSmap;
-
- {Write success message}
- WriteLn('Memory released above ', StUpcase(MarkName));
-
- if (ReturnCode <> 0) and Verbose then
- WriteLn(TrappedBytes, ' bytes temporarily trapped until batch file completes');
-
- {Stuff keyboard buffer if requested}
- if Length(Keys) > 0 then
- StuffKeys(Keys, True);
-
- Halt(ReturnCode);
- end.