del@pilchuck.Data-IO.COM (Erik Lindberg) (11/21/86)
The requests for the sources to trace DOS memory block allocations have been pouring in, so I decided to post. I will not be mailing to the individuals that requested it. Sorry, but .... well, you know... # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # clrtsr.doc # mapmem.pas # mark.asm # oldmark.asm # read.me # release.pas # This archive created: Thu Nov 20 18:23:42 1986 echo shar: extracting clrtsr.doc sed 's/^XX//' << \SHAR_EOF > clrtsr.doc XX{************************************************************************** XX* Copyright (c) 1986 Kim Kokkonen, TurboPower Software. * XX* Released to the public domain for personal, non-commercial use only. * XX*************************************************************************** XX* written 2/8/86 * XX*************************************************************************** XX* telephone: 408-378-3672, CompuServe: 72457,2131. * XX***************************************************************************} XX XXClrTSR is a small system of two programs that can be used XXto manage memory-resident programs. TSR stands for "Terminate XXand Stay Resident". The two programs are are used simply XXas follows: XX XX1) Call the program MARK.COM before installing any memory- XX resident program that you may wish to deinstall later. XX This marks the current position in memory and stores the XX DOS interrupt vector table (all interrupts from 0 to FFH). XX XX2) Install whatever TSRs that you want to use in the normal XX way that you install them. XX XX3) When you want to deinstall all TSRs above the last MARK, XX call the program RELEASE.COM. This will release all of the XX memory above (and including) the last MARK, and restore XX all interrupt vectors taken over by the memory resident XX programs. XX XXMARK and RELEASE can be "stacked" as many times as desired. XXRELEASE always releases the memory above the last MARK called. XX XXMARK and RELEASE should work on any system running PCDOS or XXMSDOS 2.0 or later. They were developed on a Compaq Deskpro XX286 running Compaq DOS 3.0. XX XXGet the program MAPMEM.COM (or MAPMEM.PAS) to display the XXcurrent DOS memory map at any time. Get the program EATMEM.COM XXor EATMEM.ASM for development work where you want to test XXsoftware in an environment with a desired amount of available XXmemory. XX XXWritten by Kim Kokkonen, TurboPower Software, XXVersion 1.0 - 2/8/86. XXTelephone: 408-378-3672, Compuserve: 72457,2131 SHAR_EOF if test 2024 -ne "`wc -c clrtsr.doc`" then echo shar: error transmitting clrtsr.doc '(should have been 2024 characters)' fi echo shar: extracting mapmem.pas sed 's/^XX//' << \SHAR_EOF > mapmem.pas XX{************************************************************************** XX* Maps system memory blocks for MS/PCDOS 2.0 and higher. * XX* Copyright (c) 1986 Kim Kokkonen, TurboPower Software. * XX* Released to the public domain for personal, non-commercial use only. * XX*************************************************************************** XX* written 1/2/86 * XX* revised 1/10/86 for * XX* running under DOS 2.X, where block owner names are unknown * XX* revised 1/22/86 for * XX* a bug in parsing the owner name of the block * XX* a quirk in the way that the DOS PRINT buffer installs itself * XX* minor cosmetic changes * XX* revised 2/6/86 for (version 1.3) * XX* smarter filtering for processes that deallocate their environment * XX*************************************************************************** XX* telephone: 408-378-3672, CompuServe: 72457,2131. * XX* requires Turbo version 3 to compile. * XX* Compile with mAx dynamic memory = A000. * XX* limited to environment sizes of 255 bytes (default is 128 bytes) * XX***************************************************************************} XX XX{$P128} XX XXPROGRAM MapMem; XX {-look at the system memory map using DOS memory control blocks} XXCONST XX MaxBlocks = 100; XX Version = '1.3'; XXTYPE XX Block = RECORD {store info about each memory block as it is found} XX idbyte : Byte; XX mcb : Integer; XX psp : Integer; XX len : Integer; XX psplen : Integer; XX env : Integer; XX cnt : Integer; XX END; XX BlockType = 0..MaxBlocks; XX BlockArray = ARRAY[BlockType] OF Block; XX XXVAR XX Blocks : BlockArray; XX BlockNum : BlockType; XX XX PROCEDURE FindTheBlocks; XX {-scan memory for the allocated memory blocks} XX CONST XX MidBlockID = $4D; {byte DOS uses to identify part of MCB chain} XX EndBlockID = $5A; {byte DOS uses to identify last block of MCB chain} XX VAR XX mcbSeg : Integer; {potential segment address of an MCB} XX nextSeg : Integer; {computed segment address for the next MCB} XX gotFirst : Boolean; {true after first MCB is found} XX gotLast : Boolean; {true after last MCB is found} XX idbyte : Byte; {byte that DOS uses to identify an MCB} XX XX PROCEDURE StoreTheBlock(VAR mcbSeg, nextSeg : Integer; XX VAR gotFirst, gotLast : Boolean); XX {-store information regarding the memory block} XX VAR XX nextID : Byte; XX pspAdd : Integer; {segment address of the current PSP} XX mcbLen : Integer; {size of the current memory block in paragraphs} XX BEGIN XX XX mcbLen := MemW[mcbSeg:3]; {size of the MCB in paragraphs} XX nextSeg := Succ(mcbSeg+mcbLen); {where the next MCB should be} XX pspAdd := MemW[mcbSeg:1]; {address of program segment prefix for MCB} XX nextID := Mem[nextSeg:0]; XX XX IF gotLast OR (nextID = EndBlockID) OR (nextID = MidBlockID) THEN BEGIN XX BlockNum := Succ(BlockNum); XX gotFirst := True; XX WITH Blocks[BlockNum] DO BEGIN XX idbyte := Mem[mcbSeg:0]; XX mcb := mcbSeg; XX psp := pspAdd; XX env := MemW[pspAdd:$2C]; XX len := mcbLen; XX psplen := 0; XX cnt := 1; XX END; XX END; XX XX END {storetheblock} ; XX XX BEGIN XX {start above the Basic work area, could probably start even higher} XX {there must be a magic address to start from, but it is not documented} XX mcbSeg := $50; XX gotFirst := False; XX gotLast := False; XX BlockNum := 0; XX XX {scan all memory until the last block is found} XX REPEAT XX idbyte := Mem[mcbSeg:0]; XX IF idbyte = MidBlockID THEN BEGIN XX StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast); XX IF gotFirst THEN mcbSeg := nextSeg ELSE mcbSeg := Succ(mcbSeg); XX END ELSE IF gotFirst AND (idbyte = EndBlockID) THEN BEGIN XX gotLast := True; XX StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast); XX END ELSE XX {still looking for first block, try every paragraph boundary} XX mcbSeg := Succ(mcbSeg); XX UNTIL gotLast; XX XX END {findtheblocks} ; XX XX XX PROCEDURE ShowTheBlocks; XX {-analyze and display the blocks found} XX CONST XX MaxVector = $40; {highest interrupt vector checked for trapping} XX TYPE XX Pathname = STRING[64]; XX HexString = STRING[4]; XX Address = RECORD XX offset, segment : Integer; XX END; XX VectorType = 0..MaxVector; XX VAR XX st : Pathname; XX b : BlockType; XX dosV : Byte; XX Vectors : ARRAY[VectorType] OF Address ABSOLUTE 0 : 0; XX vTable : ARRAY[VectorType] OF Real; XX SumBlocks : BlockType; XX Sum : BlockArray; XX XX FUNCTION Hex(i : Integer) : HexString; XX {-return hex representation of integer} XX CONST XX hc : ARRAY[0..15] OF Char = '0123456789ABCDEF'; XX VAR XX l, h : Byte; XX BEGIN XX l := Lo(i); h := Hi(i); XX Hex := hc[h SHR 4]+hc[h AND $F]+hc[l SHR 4]+hc[l AND $F]; XX END {hex} ; XX XX FUNCTION DOSversion : Byte; XX {-return the major version number of DOS} XX VAR XX reg : RECORD XX CASE Byte OF XX 1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer); XX 2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte); XX END; XX BEGIN XX reg.ah := $30; XX MsDos(reg); XX DOSversion := reg.al; XX END {dosversion} ; XX XX FUNCTION Cardinal(i : Integer) : Real; XX {-return an unsigned integer 0..65535} XX BEGIN XX Cardinal := 256.0*Hi(i)+Lo(i); XX END {cardinal} ; XX XX FUNCTION Owner(startadd : Integer) : Pathname; XX {-return the name of the owner program of an MCB} XX VAR XX e : STRING[255]; XX i : Integer; XX t : Pathname; XX XX PROCEDURE StripNonAscii(VAR t : Pathname); XX {-return an empty string if t contains any non-printable characters} XX VAR XX ipos : Byte; XX goodname : Boolean; XX BEGIN XX goodname := True; XX FOR ipos := 1 TO Length(t) DO XX IF (t[ipos] < ' ') OR (t[ipos] > '}') THEN XX goodname := False; XX IF NOT(goodname) THEN t := ''; XX END {stripnonascii} ; XX XX PROCEDURE StripPathname(VAR pname : Pathname); XX {-remove leading drive or path name from the input} XX VAR XX spos, cpos, rpos : Byte; XX BEGIN XX spos := Pos('\', pname); XX cpos := Pos(':', pname); XX IF spos+cpos = 0 THEN Exit; XX IF spos <> 0 THEN BEGIN XX {find the last slash in the pathname} XX rpos := Length(pname); XX WHILE (rpos > 0) AND (pname[rpos] <> '\') DO rpos := Pred(rpos); XX END ELSE XX rpos := cpos; XX Delete(pname, 1, rpos); XX END {strippathname} ; XX XX BEGIN XX {get the environment string to scan} XX e[0] := #255; XX Move(Mem[startadd:0], e[1], 255); XX XX {find end of the standard environment} XX i := Pos(#0#0, e); XX IF i = 0 THEN BEGIN XX {something's wrong, exit gracefully} XX Owner := ''; XX Exit; XX END; XX XX {end of environment found, get the program name that follows it} XX t := ''; XX i := i+3; {skip over #0#0#args} XX REPEAT XX t := t+Chr(Mem[startadd:i]); XX i := Succ(i); XX UNTIL (Length(t) > 64) OR (Mem[startadd:i] = 0); XX XX StripNonAscii(t); XX IF Length(t) = 0 THEN XX Owner := 'N/A' XX ELSE BEGIN XX StripPathname(t); XX IF t = '' THEN t := 'N/A'; XX Owner := t; XX END; XX XX END {owner} ; XX XX PROCEDURE InitVectorTable; XX {-build real equivalent of vector addresses} XX VAR XX v : VectorType; XX XX FUNCTION RealAdd(a : Address) : Real; XX {-return the real equivalent of an address (pointer)} XX BEGIN XX WITH a DO XX RealAdd := 16.0*Cardinal(segment)+Cardinal(offset); XX END {realadd} ; XX XX BEGIN XX FOR v := 0 TO MaxVector DO XX vTable[v] := RealAdd(Vectors[v]); XX END {initvectortable} ; XX XX PROCEDURE WriteHooks(start, stop : Integer); XX {-show the trapped interrupt vectors} XX VAR XX v : VectorType; XX sadd, eadd : Real; XX BEGIN XX sadd := 16.0*Cardinal(start); XX eadd := 16.0*Cardinal(stop); XX FOR v := 0 TO MaxVector DO BEGIN XX IF (vTable[v] >= sadd) AND (vTable[v] <= eadd) THEN XX Write(Copy(Hex(v), 3, 2), ' '); XX END; XX END {writehooks} ; XX XX PROCEDURE SortByPSP(VAR Blocks : BlockArray; BlockNum : BlockType); XX {-sort in order of ascending PSP} XX VAR XX i, j : BlockType; XX temp : Block; XX BEGIN XX FOR i := 1 TO Pred(BlockNum) DO XX FOR j := BlockNum DOWNTO Succ(i) DO XX IF Cardinal(Blocks[j].psp) < Cardinal(Blocks[Pred(j)].psp) THEN BEGIN XX temp := Blocks[j]; XX Blocks[j] := Blocks[Pred(j)]; XX Blocks[Pred(j)] := temp; XX END; XX END {SortByPSP} ; XX XX PROCEDURE SumTheBlocks(VAR Blocks : BlockArray; XX BlockNum : BlockType; XX VAR Sum : BlockArray; XX VAR SumBlocks : BlockType); XX {-combine the blocks with equivalent PSPs} XX VAR XX prevpsp : Integer; XX b : BlockType; XX BEGIN XX SumBlocks := 0; XX prevpsp := $FFFF; XX FOR b := 1 TO BlockNum DO BEGIN XX IF Blocks[b].psp <> prevpsp THEN BEGIN XX SumBlocks := Succ(SumBlocks); XX Sum[SumBlocks] := Blocks[b]; XX prevpsp := Blocks[b].psp; XX END ELSE XX WITH Sum[SumBlocks] DO BEGIN XX cnt := Succ(cnt); XX len := len+Blocks[b].len; XX END; XX {get length of the block which owns the executable program} XX {for checking vector trapping next} XX IF Succ(Blocks[b].mcb) = Blocks[b].psp THEN XX Sum[SumBlocks].psplen := Blocks[b].len; XX END; XX END {sumblocks} ; XX XX BEGIN XX WriteLn; XX WriteLn(' Allocated Memory Map - by TurboPower Software - Version ', Version); XX WriteLn; XX WriteLn('PSP adr MCB adr paras bytes owner hooked vectors'); XX WriteLn('------- ------- ------- ------- ---------- ------------------------------'); XX XX dosV := DOSversion; XX InitVectorTable; XX SortByPSP(Blocks, BlockNum); XX SumTheBlocks(Blocks, BlockNum, Sum, SumBlocks); XX XX FOR b := 1 TO SumBlocks DO WITH Sum[b] DO BEGIN XX Write(' ', XX Hex(psp), ' ', {PSP address} XX Hex(mcb), ' ', {MCB address} XX Hex(len), ' ', {size of block in paragraphs} XX 16.0*Cardinal(len):6:0, ' '); {size of block in bytes} XX XX {get the program owning this block by scanning the environment} XX IF (dosV >= 3) AND (cnt > 1) THEN XX st := Owner(env) XX ELSE XX st := 'N/A'; XX WHILE Length(st) < 13 DO st := st+' '; XX Write(st); XX WriteHooks(psp, psp+psplen); XX WriteLn; XX END; XX XX END {showtheblocks} ; XX XXBEGIN XX FindTheBlocks; XX ShowTheBlocks; XXEND. SHAR_EOF if test 11160 -ne "`wc -c mapmem.pas`" then echo shar: error transmitting mapmem.pas '(should have been 11160 characters)' fi echo shar: extracting mark.asm sed 's/^XX//' << \SHAR_EOF > mark.asm XX;_ mark.asm Sun Jul 13 1986 */ XX;MARK.ASM - mark a position in memory, XX; above which TSRs will later be cleared by RELEASE.PAS/COM XX; MARK can be called multiple times, each RELEASE will clear XX; above the last MARK called XX; XX; written for CHASM (CHeap ASseMbler) XX; by Kim Kokkonen, TurboPower Software XX; telephone: 408-378-3672, Compuserve 72457,2131 XX; XXcseg segment XXassume cs:cseg,ds:cseg XXorg 100h XXmark proc near XX jmp install XX XXidstr db "MARK PARAMETER BLOCK FOLLOWS" ;used to find this TSR XXdummy db 0 ;puts vector table on an even paragraph boundary XXvector db 400H dup(0) ;holds vector table (0..FF)*4 at invocation XX XX;store the interrupt vector table XXinstall: XX push ds XX cli ;interrupts of XX cld ;copy up XX mov cx,200H ;512 integers to store XX xor ax,ax XX mov ds,ax ;source address segment 0 XX xor si,si ;offset 0 XX mov di,offset vector ;destination offset, es=cs already XX rep movsw ;copy vectors to our table XX sti ;interrupts on XX XX;print message and TSR XX pop ds XX mov dx,offset didit ;get end of code XX mov ah,9 XX int 21H ;write success message XX mov cx,4 XX shr dx,cl ;convert to paragraphs XX inc dx ;round up XX mov ax,3100H XX int 21H ;terminate and stay resident XX XX;used to mark end of this TSR XXdidit db 13,10,'Marked current memory position',13,10,36 XXmark endp XXcseg ends XX end mark XX SHAR_EOF if test 1615 -ne "`wc -c mark.asm`" then echo shar: error transmitting mark.asm '(should have been 1615 characters)' fi echo shar: extracting oldmark.asm sed 's/^XX//' << \SHAR_EOF > oldmark.asm XX;MARK.ASM - mark a position in memory, XX; above which TSRs will later be cleared by RELEASE.PAS/COM XX; MARK can be called multiple times, each RELEASE will clear XX; above the last MARK called XX; XX; written for CHASM (CHeap ASseMbler) XX; by Kim Kokkonen, TurboPower Software XX; telephone: 408-378-3672, Compuserve 72457,2131 XX; XXmark proc near XX jmp install XX XXidstr db 'MARK PARAMETER BLOCK FOLLOWS' ;used to find this TSR XXdummy db 0 ;puts vector table on an even paragraph boundary XXvector ds 400H,0 ;holds vector table (0..FF)*4 at invocation XX XX;store the interrupt vector table XXinstall XX push ds XX cli ;interrupts of XX cld ;copy up XX mov cx,200H ;512 integers to store XX xor ax,ax XX mov ds,ax ;source address segment 0 XX xor si,si ;offset 0 XX mov di,offset(vector) ;destination offset, es=cs already XX rep XX movsw ;copy vectors to our table XX sti ;interrupts on XX XX;print message and TSR XX pop ds XX mov dx,offset(didit) ;get end of code XX mov ah,9 XX int 21H ;write success message XX mov cx,4 XX shr dx,cl ;convert to paragraphs XX inc dx ;round up XX mov ax,3100H XX int 21H ;terminate and stay resident XX XX;used to mark end of this TSR XXdidit db 13,10,'Marked current memory position',13,10,36 XX endp SHAR_EOF if test 1590 -ne "`wc -c oldmark.asm`" then echo shar: error transmitting oldmark.asm '(should have been 1590 characters)' fi echo shar: extracting read.me sed 's/^XX//' << \SHAR_EOF > read.me XXThis distribution contains a modified version of MARK.ASM. It has been XXmodified to run with the Microsoft MASM assembler, since the chasm XXassembler is distributed in non-ASCII basic format. Some of us poor XXsouls do not have IBM machines, and have no interest in buying a ^&$(%^&@ XXbasic interpreter!!!!! XX XXNot wanting to cause problems for anyone, the original CHASM compatible XXversion is still in the archive, under the name OLDMARK.ASM. XX XXI have not tried this code on DOS 2.0. XX XXThis stuff works very well on DOS 3.1, but seems to have some trouble XXunder *some* conditions in DOS 3.2. There are newer versions of this stuff XXavailable, but the author seems to be no longer distributing source, so I XXcan't give you the latest and greatest. Incidently, the binaries for this XXwere posted recently. XX XXdel (Erik Lindberg) XXuw-beaver!tikal!pilchuck!del SHAR_EOF if test 844 -ne "`wc -c read.me`" then echo shar: error transmitting read.me '(should have been 844 characters)' fi echo shar: extracting release.pas sed 's/^XX//' << \SHAR_EOF > release.pas XX{************************************************************************** XX* Releases memory above the last MARK call made. * XX* Copyright (c) 1986 Kim Kokkonen, TurboPower Software. * XX* Released to the public domain for personal, non-commercial use only. * XX*************************************************************************** XX* written 2/8/86 * XX*************************************************************************** XX* telephone: 408-378-3672, CompuServe: 72457,2131. * XX* requires Turbo version 3 to compile. * XX* Compile with mAx dynamic memory = A000. * XX***************************************************************************} XX XX{$P128} XX XXPROGRAM ReleaseTSR; XX {-release system memory above the last mark call} XXCONST XX MaxBlocks = 100; XX Version = '1.0'; XX markID = 'MARK PARAMETER BLOCK FOLLOWS'; {marking string for TSR} XX markOffset = $103; {offset into MARK.COM where markID is found in TSR} XX vectoroffset = $120; {offset into MARK.COM where vector table is stored} XXTYPE XX Block = RECORD {store info about each memory block as it is found} XX mcb : Integer; XX psp : Integer; XX END; XX BlockType = 0..MaxBlocks; XX BlockArray = ARRAY[BlockType] OF Block; XX allstrings = STRING[255]; XX XXVAR XX Blocks : BlockArray; XX BottomBlock, BlockNum : BlockType; XX XX PROCEDURE FindTheBlocks; XX {-scan memory for the allocated memory blocks} XX CONST XX MidBlockID = $4D; {byte DOS uses to identify part of MCB chain} XX EndBlockID = $5A; {byte DOS uses to identify last block of MCB chain} XX VAR XX mcbSeg : Integer; {potential segment address of an MCB} XX nextSeg : Integer; {computed segment address for the next MCB} XX gotFirst : Boolean; {true after first MCB is found} XX gotLast : Boolean; {true after last MCB is found} XX idbyte : Byte; {byte that DOS uses to identify an MCB} XX XX PROCEDURE StoreTheBlock(VAR mcbSeg, nextSeg : Integer; XX VAR gotFirst, gotLast : Boolean); XX {-store information regarding the memory block} XX VAR XX nextID : Byte; XX pspAdd : Integer; {segment address of the current PSP} XX mcbLen : Integer; {size of the current memory block in paragraphs} XX BEGIN XX XX mcbLen := MemW[mcbSeg:3]; {size of the MCB in paragraphs} XX nextSeg := Succ(mcbSeg+mcbLen); {where the next MCB should be} XX pspAdd := MemW[mcbSeg:1]; {address of program segment prefix for MCB} XX nextID := Mem[nextSeg:0]; XX XX IF gotLast OR (nextID = EndBlockID) OR (nextID = MidBlockID) THEN BEGIN XX BlockNum := Succ(BlockNum); XX gotFirst := True; XX WITH Blocks[BlockNum] DO BEGIN XX mcb := mcbSeg; XX psp := pspAdd; XX END; XX END; XX XX END {storetheblock} ; XX XX BEGIN XX {start above the Basic work area, could probably start even higher} XX {there must be a magic address to start from, but it is not documented} XX mcbSeg := $50; XX gotFirst := False; XX gotLast := False; XX BlockNum := 0; XX XX {scan all memory until the last block is found} XX REPEAT XX idbyte := Mem[mcbSeg:0]; XX IF idbyte = MidBlockID THEN BEGIN XX StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast); XX IF gotFirst THEN mcbSeg := nextSeg ELSE mcbSeg := Succ(mcbSeg); XX END ELSE IF gotFirst AND (idbyte = EndBlockID) THEN BEGIN XX gotLast := True; XX StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast); XX END ELSE XX {still looking for first block, try every paragraph boundary} XX mcbSeg := Succ(mcbSeg); XX UNTIL gotLast; XX XX END {findtheblocks} ; XX XX FUNCTION findmark(idstring : allstrings; idoffset : Integer) : Integer; XX {-find the last memory block matching idstring at offset idoffset} XX VAR XX b : BlockType; XX foundit : Boolean; XX XX FUNCTION MatchString(segment : Integer; idstring : allstrings; idoffset : Integer) XX : Boolean; XX {-return true if idstring is found at segment:idoffset} XX VAR XX tstring : allstrings; XX len : Byte; XX BEGIN XX len := Length(idstring); XX tstring[0] := Chr(len); XX Move(Mem[segment:idoffset], tstring[1], len); XX MatchString := (tstring = idstring); XX END {matchstring}; XX XX BEGIN XX {scan from the last block-1 down to find the last MARK TSR} XX b := Pred(BlockNum); XX REPEAT XX foundit := MatchString(Blocks[b].psp, idstring, idoffset); XX IF NOT(foundit) THEN XX b := Pred(b); XX UNTIL (b < 1) OR foundit; XX IF NOT(foundit) THEN BEGIN XX WriteLn('No memory marker found. Mark memory by calling MARK.COM'); XX Halt(1); XX END; XX findmark := b; XX END {findmark} ; XX XX PROCEDURE CopyVectors(BottomBlock : BlockType; vectoroffset : Integer); XX {-put interrupt vectors back into table} XX BEGIN XX {interrupts off} XX INLINE($FA); XX {replace vectors} XX Move(Mem[Blocks[BottomBlock].psp:vectoroffset], Mem[0:0], 1024); XX {interrupts on} XX INLINE($FB); XX END {copyvectors} ; XX XX PROCEDURE ReleaseMem(BottomBlock : BlockType); XX {release memory starting at block b, up to but not including this program} XX TYPE XX hexstring = STRING[4]; XX VAR XX b : BlockType; XX regs : RECORD XX CASE Byte OF XX 1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer); XX 2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte); XX END; XX XX FUNCTION Hex(i : Integer) : hexstring; XX {-return hex representation of integer} XX CONST XX hc : ARRAY[0..15] OF Char = '0123456789ABCDEF'; XX VAR XX l, h : Byte; XX BEGIN XX l := Lo(i); h := Hi(i); XX Hex := hc[h SHR 4]+hc[h AND $F]+hc[l SHR 4]+hc[l AND $F]; XX END {hex} ; XX XX BEGIN XX WITH regs DO XX FOR b := BottomBlock TO BlockNum DO XX IF Blocks[b].psp <> CSeg THEN BEGIN XX ah := $49; XX {the block is always 1 paragraph above the MCB} XX es := Succ(Blocks[b].mcb); XX MsDos(regs); XX IF Odd(flags) THEN BEGIN XX WriteLn('Could not release block at segment ', Hex(es)); XX WriteLn('Memory is now a mess... Please reboot'); XX Halt(1); XX END; XX END; XX END {releasemem} ; XX XXBEGIN XX WriteLn; XX {get all allocated memory blocks} XX FindTheBlocks; XX {find the last one marked with the MARK idstring} XX BottomBlock := findmark(markID, markOffset); XX {copy the vector table from the MARK resident} XX CopyVectors(BottomBlock, vectoroffset); XX {release memory at and above the mark resident} XX ReleaseMem(Pred(BottomBlock)); XX {DOS will release this program's memory when it exits} XX {write success message} XX WriteLn('Memory released above last MARK'); XXEND. SHAR_EOF if test 6785 -ne "`wc -c release.pas`" then echo shar: error transmitting release.pas '(should have been 6785 characters)' fi # End of shell archive exit 0 -- del (Erik Lindberg) aka Hugable uw-beaver!tikal!pilchuck!del Hugs: One of the few good things in life that are still free.