home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / pascal / tsrsrc.zip / MAPMEM.PAS < prev    next >
Pascal/Delphi Source File  |  1992-02-14  |  31KB  |  1,032 lines

  1. {**************************************************************************
  2. *   MAPMEM - Reports system memory blocks.                                *
  3. *   Copyright (c) 1986,1991 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   version 1.0 1/2/86                                                    *
  7. *   :                                                                     *
  8. *   long intervening history                                              *
  9. *   :                                                                     *
  10. *   version 3.0 9/24/91                                                   *
  11. *     completely rewritten for DOS 5 compatibility                        *
  12. *     add upper memory reporting                                          *
  13. *     add XMS reporting                                                   *
  14. *     add free memory report                                              *
  15. *     report on EMS handle names                                          *
  16. *     change command line switches                                        *
  17. *     add check for TSR feature                                           *
  18. *     add Quiet option (useful with "check for" option only)              *
  19. *     add summary report                                                  *
  20. *   version 3.1 11/4/91                                                   *
  21. *     fix bug in EMS handle reporting                                     *
  22. *     fix problem in getting name of TSR that shrinks environment (FSP)   *
  23. *     prevent from keeping interrupt 0                                    *
  24. *     fix source naming of WriteChained vs WriteHooked                    *
  25. *     show command line and vectors even if lower part of PSP is          *
  26. *       overwritten (DATAPATH)                                            *
  27. *     wouldn't find (using /C) a program whose name was stored in         *
  28. *       lowercase in the environment (Windows 3.0)                        *
  29. *   version 3.2 11/22/91                                                  *
  30. *     generalize high memory support                                      *
  31. *     handle some DRDOS 6.0 conventions                                   *
  32. *     fix indentation problem in raw extended memory report               *
  33. *   version 3.3 1/8/92                                                    *
  34. *     /C getname wasn't finding TSRs in high memory                       *
  35. *     increase stack space                                                *
  36. *     new features for parsing and getting command line options           *
  37. *   version 3.4 2/14/92                                                   *
  38. *     fix bug in memory reported for device memory blocks                 *
  39. *     add /L option to turn off low memory reporting                      *
  40. *     change /C to find TSRS only in low memory unless /U specified       *
  41. *     add a new test to validate command line strings of mcbs             *
  42. ***************************************************************************
  43. *   telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  44. *   requires Turbo Pascal 6 to compile.                                   *
  45. ***************************************************************************}
  46.  
  47. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  48. {$M 4096,2048,655360}
  49. {.$DEFINE MeasureStack}  {Activate to measure stack usage}
  50.  
  51. program MapMem;
  52.  
  53. uses
  54.   Dos,
  55.   MemU,
  56.   Xms,
  57.   Ems;
  58.  
  59. const
  60.   CheckTSR : Boolean = False;          {'C'}
  61.   ShowEmsMem : Boolean = False;        {'E'}
  62.   ShowFree : Boolean = False;          {'F'}
  63.   UseWatch : Boolean = True;           {'H'}
  64.   UseLoMem : Boolean = True;           {'L'}
  65.   Quiet : Boolean = False;             {'Q'}
  66.   ShowSummary : Boolean = False;       {'S'}
  67.   UseHiMem : Boolean = False;          {'U'}
  68.   Verbose : Boolean = False;           {'V'}
  69.   ShowExtMem : Boolean = False;        {'X'}
  70.  
  71. var
  72.   TotalMem : LongInt;
  73.   TopSeg : Word;
  74.   HiMemSeg : Word;
  75.   WatchPsp : Word;
  76.   ShowDevices : Boolean;
  77.   ShowSegments : Boolean;
  78.   ShowBlocks : Boolean;
  79.   ShowFiles : Boolean;
  80.   ShowVectors : Boolean;
  81.   GotXms : Boolean;
  82.   SizeLen : Byte;
  83.   NameLen : Byte;
  84.   CmdLen : Byte;
  85.   UmbLinkStatus : Boolean;
  86.   SaveExit : Pointer;
  87.   TsrName : string[79];
  88.   {$IFDEF MeasureStack}
  89.   I : Word;
  90.   {$ENDIF}
  91.  
  92. const
  93.   FreeName  : string[10] = '---free---';
  94.   TotalName : string[10] = '---total--';
  95.  
  96. const
  97.   VerboseIndent = 5;
  98.   NoShowVecSeg = $FFFE;
  99.   ShowVecSeg   = $FFFF;
  100.  
  101.   procedure SafeExit; far;
  102.   begin
  103.     ExitProc := SaveExit;
  104.     SwapVectors;
  105.   end;
  106.  
  107.   function GetName(M : McbPtr; var Devices : Boolean) : String;
  108.     {-Return a name for Mcb M}
  109.   const
  110.     EnvName : array[boolean] of string[4] = ('', 'env');
  111.     DatName : array[boolean] of string[4] = ('', 'data');
  112.   var
  113.     PspSeg : Word;
  114.     IsCmd : Boolean;
  115.   begin
  116.     Devices := False;
  117.     PspSeg := M^.Psp;
  118.  
  119.     if (PspSeg = 0) or (PspSeg = PrefixSeg) then
  120.       GetName := FreeName
  121.     else if PspSeg = 8 then begin
  122.       GetName := 'sys data';
  123.       if DosV = 5 then
  124.         if (M^.Name[1] = 'S') and (M^.Name[2] = 'D') then begin
  125.           GetName := 'cfg info';
  126.           Devices := True;
  127.         end;
  128.     end else if (PspSeg < 8) or (PspSeg >= $FFF0) then
  129.       GetName := 'unknown'
  130.     else if PspSeg = OS(M).S+1 then begin
  131.       {program block}
  132.       IsCmd := (PspSeg = MemW[PspSeg:$16]);
  133.       if (not IsCmd) and (DosV > 2) and HasEnvironment(HiMemSeg, M) then
  134.         GetName := NameFromEnv(M)
  135.       else if DosV >= 4 then
  136.         GetName := NameFromMcb(M)
  137.       else if IsCmd then
  138.         GetName := 'command'
  139.       else if DosVT >= $031E then
  140.         GetName := NameFromMcb(M)
  141.       else
  142.         GetName := 'n/a';
  143.     end else if MemW[PspSeg:$2C] = OS(M).S+1 then
  144.       GetName := EnvName[Verbose]+'('+GetName(Ptr(PspSeg-1, 0), Devices)+')'
  145.     else
  146.       GetName := DatName[Verbose]+'('+GetName(Ptr(PspSeg-1, 0), Devices)+')';
  147.   end;
  148.  
  149.   function ValidPsp(PspSeg : Word) : Boolean;
  150.     {-Return True if PspSeg is a valid Psp}
  151.   begin
  152.     if ((PspSeg >= 0) and (PspSeg <= 8)) or
  153.        (PspSeg = PrefixSeg) or
  154.        (PspSeg >= $FFF0) then
  155.       ValidPsp := False
  156.     else
  157.       ValidPsp := True;
  158.   end;
  159.  
  160.   function GetFiles(M : McbPtr) : Word;
  161.     {-Return number of open files for given Mcb's Psp}
  162.   type
  163.     HandleTable = array[0..65520] of Byte;
  164.   var
  165.     PspSeg : Word;
  166.     O : Word;
  167.     Files : Word;
  168.     FileMax : Word;
  169.     TablePtr : ^HandleTable;
  170.   begin
  171.     PspSeg := M^.Psp;
  172.     if (PspSeg <> OS(M).S+1) or not ValidPsp(PspSeg) or
  173.        (MemW[PspSeg:$50] <> $21CD) then begin
  174.       GetFiles := 0;
  175.       Exit;
  176.     end;
  177.     {Deal with expanded handle tables in DOS 3.0 and later}
  178.     if DosV >= 3 then begin
  179.       FileMax := MemW[M^.Psp:$32];
  180.       TablePtr := Pointer(MemL[M^.Psp:$34]);
  181.     end else begin
  182.       FileMax := 20;
  183.       TablePtr := Ptr(M^.Psp, $18);
  184.     end;
  185.  
  186.     Files := 0;
  187.     for O := 0 to FileMax-1 do
  188.       case TablePtr^[O] of
  189.         0, 1, 2, $FF : {standard handle or not open} ;
  190.       else
  191.         Inc(Files);
  192.       end;
  193.     GetFiles := Files;
  194.   end;
  195.  
  196.   function GetCmdLine(M : McbPtr) : String;
  197.     {-Return command line for program}
  198.   var
  199.     PspSeg : Word;
  200.     Len : Byte;
  201.     S : String[127];
  202.   begin
  203.     PspSeg := M^.Psp;
  204.     if (PspSeg <> OS(M).S+1) or not ValidPsp(PspSeg) then begin
  205.       GetCmdLine := '';
  206.       Exit;
  207.     end;
  208.     Move(Mem[PspSeg:$80], S, 127);
  209.     if S <> '' then begin
  210.       Len := Length(S);
  211.       if (Len > 127) or (S[Len+1] <> ^M) then
  212.         S := ''
  213.       else
  214.         StripNonAscii(S);
  215.       if S = '' then
  216.         S := 'n/a';
  217.     end;
  218.     while (Length(S) > 0) and (S[1] = ' ') do
  219.       Delete(S, 1, 1);
  220.     GetCmdLine := S;
  221.   end;
  222.  
  223.   procedure WriteHooked(LowSeg, HighSeg : Word; StartCol, WrapCol : Byte);
  224.     {-Write vectors that point into specified region of memory}
  225.   var
  226.     Vectors : array[0..255] of Pointer absolute 0:0;
  227.     Vec : Pointer;
  228.     LoL : LongInt;
  229.     HiL : LongInt;
  230.     VeL : LongInt;
  231.     V : Byte;
  232.     Col : Byte;
  233.   begin
  234.     LoL := LongInt(LowSeg) shl 4;
  235.     HiL := LongInt(HighSeg) shl 4;
  236.     Col := StartCol;
  237.     for V := 0 to 255 do begin
  238.       Vec := Vectors[V];
  239.       VeL := (LongInt(OS(Vec).S) shl 4)+OS(Vec).O;
  240.       if (VeL >= LoL) and (VeL < HiL) then begin
  241.         if Col+3 > WrapCol then begin
  242.           {wrap to next line}
  243.           Write(^M^J, '':StartCol-1);
  244.           Col := StartCol;
  245.         end;
  246.         Write(HexB(V), ' ');
  247.         inc(Col, 3);
  248.       end;
  249.     end;
  250.   end;
  251.  
  252.   procedure WriteChained(PspSeg : Word; StartCol, WrapCol : Byte);
  253.     {-Write vectors that WATCH found taken over by a block}
  254.   var
  255.     P : ^ChangeBlock;
  256.     I, MaxChg, Col : Word;
  257.     Found : Boolean;
  258.   begin
  259.     {initialize}
  260.     MaxChg := MemW[WatchPsp:NextChange];
  261.     Col := StartCol;
  262.     Found := False;
  263.     I := 0;
  264.  
  265.     while I < MaxChg do begin
  266.       P := Ptr(WatchPsp, ChangeVectors+I);
  267.       with P^ do
  268.         case ID of
  269.           $00 :           {ChangeBlock describes an active vector takeover}
  270.             if Found then begin
  271.               if Col+3 > WrapCol then begin
  272.                 {wrap to next line}
  273.                 Write(^M^J, '':StartCol-1);
  274.                 Col := StartCol;
  275.               end;
  276.               Write(HexB(Lo(VecNum)), ' ');
  277.               inc(Col, 3);
  278.             end;
  279.           $01 :           {ChangeBlock specifies a disabled takeover}
  280.             if Found then begin
  281.               Write('disabled');
  282.               {Don't write this more than once}
  283.               Exit;
  284.             end;
  285.           $FF :           {ChangeBlock starts a new PSP}
  286.             Found := (PspSeg = PspAdd);
  287.         end;
  288.       inc(I, SizeOf(ChangeBlock));
  289.     end;
  290.   end;
  291.  
  292.   procedure WriteVectors(LowSeg, HighSeg : Word; StartCol, WrapCol : Byte);
  293.     {-Write interrupt vectors either hooked or chained}
  294.   begin
  295.     if UseWatch then
  296.       WriteChained(LowSeg, StartCol, WrapCol)
  297.     else
  298.       WriteHooked(LowSeg, HighSeg, StartCol, WrapCol);
  299.   end;
  300.  
  301.   procedure WriteMcb(McbSeg, PspSeg, Paras, Blocks, Files : Word;
  302.                      Name : String; CmdLine : String);
  303.     {-Write information about one Mcb or group of mcbs}
  304.   var
  305.     Col : Byte;
  306.   begin
  307.     Col := 1;
  308.  
  309.     if ShowSegments then begin
  310.       case McbSeg of
  311.         NoShowVecSeg, ShowVecSeg : ;
  312.       else
  313.         Write(HexW(McbSeg), ' ');
  314.         inc(Col, 5);
  315.       end;
  316.  
  317.       if (PspSeg = 0) or (PspSeg = 8) then
  318.         Write('    ')
  319.       else
  320.         Write(HexW(PspSeg));
  321.       inc(Col, 4);
  322.     end else
  323.       Write('  ');
  324.  
  325.     if ShowBlocks then begin
  326.       Write(' ', Blocks:2);
  327.       inc(Col, 3);
  328.     end;
  329.  
  330.     if ShowFiles then begin
  331.       if Files = 0 then
  332.         Write('   ')
  333.       else
  334.         Write(' ', Files:2);
  335.       inc(Col, 3);
  336.     end;
  337.  
  338.     Write(' ', CommaIze(LongInt(Paras) shl 4, SizeLen),
  339.           ' ', Extend(Name, NameLen),
  340.           ' ', SmartExtend(CmdLine, CmdLen));
  341.     inc(Col, 3+SizeLen+NameLen+CmdLen);
  342.  
  343.     if ShowVectors then
  344.       if (PspSeg = McbSeg+1) or (McbSeg = ShowVecSeg) then
  345.         if ValidPsp(PspSeg) then begin
  346.           Write(' ');
  347.           WriteVectors(PspSeg, PspSeg+Paras, Col+1, 79);
  348.         end;
  349.  
  350.     WriteLn;
  351.  
  352.     {keep track of total reported memory}
  353.     Inc(TotalMem, Paras);
  354.     Inc(TotalMem, Blocks);        {for the mcbs themselves}
  355.   end;
  356.  
  357.   procedure WriteDevices(DevSeg, NextSeg : Word);
  358.     {-Write the DOS 5 device list}
  359.   var
  360.     D : McbPtr;
  361.     Name : String[79];
  362.   begin
  363.     D := Ptr(DevSeg, 0);
  364.     while OS(D).S < NextSeg do begin
  365.       case D^.Id of
  366.         'B' : Name := 'buffers';
  367.         'C' : Name := 'ems buffers';
  368.         'D' : Name := 'device='+Asc2Str(D^.Name);
  369.         'E' : Name := 'device ext';
  370.         'F' : Name := 'files';
  371.         'I' : Name := 'ifs='+Asc2Str(D^.Name);
  372.         'L' : Name := 'lastdrive';
  373.         'S' : Name := 'stacks';
  374.         'X' : Name := 'fcbs';
  375.       else
  376.         Name := '';
  377.       end;
  378.       if Name <> '' then
  379.         WriteLn('':20, CommaIze(LongInt(D^.Len+1) shl 4, 6), ' ', Name);
  380.       D := Ptr(OS(D).S+D^.Len+1, 0);
  381.     end;
  382.   end;
  383.  
  384.   procedure WriteTotalMem;
  385.     {-Write total reported memory with leading space PreSpace}
  386.   var
  387.     PreSpace : Word;
  388.   begin
  389.     if TotalMem <> 0 then begin
  390.       PreSpace := 7;
  391.       if Verbose then
  392.         inc(PreSpace, VerboseIndent);
  393.       WriteLn('':PreSpace, CommaIze(LongInt(TotalMem) shl 4, 8), ' ', TotalName);
  394.       TotalMem := 0;
  395.     end;
  396.   end;
  397.  
  398.   procedure FindTSR;
  399.     {-Find TSRName, report if appropriate, and halt}
  400.  
  401.     procedure FindOne(Start : McbPtr);
  402.     var
  403.       M : McbPtr;
  404.       PspSeg : Word;
  405.       Done : Boolean;
  406.       IsCmd : Boolean;
  407.       Name : String[79];
  408.     begin
  409.       M := Start;
  410.       repeat
  411.         PspSeg := M^.Psp;
  412.         if OS(M).S+1 = PspSeg then begin
  413.           IsCmd := (PspSeg = MemW[PspSeg:$16]);
  414.           if (not IsCmd) and (DosV > 2) and HasEnvironment(HiMemSeg, M) then
  415.             Name := NameFromEnv(M)
  416.           else if DosV >= 4 then
  417.             Name := NameFromMcb(M)
  418.           else if (not IsCmd) and (DosVT >= $031E) then
  419.             Name := NameFromMcb(M)
  420.           else
  421.             Name := '';
  422.           if StUpcase(Name) = TsrName then begin
  423.             if not Quiet then
  424.               WriteLn('Found ', TsrName, ' at ', HexW(PspSeg));
  425.             Halt(0);
  426.           end;
  427.         end;
  428.         Done := (M^.Id = 'Z');
  429.         M := Ptr(OS(M).S+M^.Len+1, 0);
  430.       until Done;
  431.     end;
  432.  
  433.   begin
  434.     if UseLoMem then
  435.       FindOne(Mcb1);
  436.     if UseHiMem then
  437.       FindOne(Ptr(HiMemSeg, 0));
  438.     {Not found if we get here}
  439.     if not Quiet then
  440.       WriteLn('Did not find ', TsrName);
  441.     Halt(2);
  442.   end;
  443.  
  444.   procedure ShowChain(M : McbPtr);
  445.     {-Show chain of blocks starting at M}
  446.   var
  447.     Done : Boolean;
  448.   begin
  449.     repeat
  450.       WriteMcb(OS(M).S, M^.Psp, M^.Len, 1,
  451.                GetFiles(M), GetName(M, ShowDevices), GetCmdLine(M));
  452.       if ShowDevices then
  453.         WriteDevices(OS(M).S+1, OS(M).S+M^.Len+1);
  454.       Done := (M^.Id = 'Z');
  455.       M := Ptr(OS(M).S+M^.Len+1, 0);
  456.     until Done;
  457.     WriteTotalMem;
  458.   end;
  459.  
  460.   procedure WriteVerbose;
  461.     {-Report on each Mcb individually}
  462.   var
  463.     M : McbPtr;
  464.   begin
  465.     Write('Mcb  Psp  Hdl   Size Name           Command Line        ');
  466.     if UseWatch then
  467.       Write('Chained')
  468.     else
  469.       Write('Hooked');
  470.     WriteLn(' Vectors');
  471.     WriteLn('---- ---- --- ------ -------------- ------------------- -----------------------');
  472.  
  473.     if UseLoMem then begin
  474.       {fake Mcb's used by dos itself}
  475.       WriteMcb($0000, $0000, $0040, 0, 0, 'vectors', '');
  476.       WriteMcb($0040, $0000, $0010, 0, 0, 'BIOS data', '');
  477.       WriteMcb($0050, $0000, $0020, 0, 0, 'DOS data', '');
  478.       WriteMcb($0070, $0000, OS(DosList).S-$70, 0, 0, 'sys data', '');
  479.       WriteMcb(OS(DosList).S, $0000, OS(Mcb1).S-OS(DosList).S, 0, 0, 'sys code', '');
  480.       M := Mcb1;
  481.       ShowChain(Mcb1);
  482.     end;
  483.  
  484.     if UseHiMem then begin
  485.       if UseLoMem then
  486.         WriteLn(^M^J'High Memory');
  487.       ShowChain(Ptr(HiMemSeg, 0));
  488.     end;
  489.   end;
  490.  
  491.   procedure SummarizePsp(TPsp, LoMcb, HiMcb : Word);
  492.     {-Write info about all Mcbs in range LoMcb..HiMcb with the specified Psp}
  493.   var
  494.     TM : McbPtr;
  495.     M : McbPtr;
  496.     Size : Word;
  497.     Blocks : Word;
  498.     FakeSeg : Word;
  499.     MPsp : Word;
  500.     Done : Boolean;
  501.     HaveCodeBlock : Boolean;
  502.   begin
  503.     Size := 0;
  504.     Blocks := 0;
  505.     M := Ptr(LoMcb, 0);
  506.     TM := nil;
  507.     HaveCodeBlock := False;
  508.     repeat
  509.       MPsp := M^.Psp;
  510.       if MPsp = 0 then
  511.         MPsp := OS(M).S;
  512.       if MPsp = TPsp then begin
  513.         if (OS(M).S >= LoMcb) and (OS(M).S < HiMcb) then begin
  514.           Inc(Size, M^.Len);
  515.           Inc(Blocks);
  516.           if OS(M).S+1 = TPsp then
  517.             HaveCodeBlock := True;
  518.         end;
  519.         if TM = nil then
  520.           TM := M
  521.         else if M^.Psp = OS(M).S+1 then
  522.           TM := M;
  523.       end;
  524.       Done := (M^.Id = 'Z');
  525.       M := Ptr(OS(M).S+M^.Len+1, 0);
  526.     until Done;
  527.  
  528.     if Blocks > 0 then begin
  529.       if HaveCodeBlock then
  530.         FakeSeg := ShowVecSeg
  531.       else
  532.         FakeSeg := NoShowVecSeg;
  533.       WriteMcb(FakeSeg, TM^.Psp, Size, Blocks, 0,
  534.                GetName(TM, ShowDevices), GetCmdLine(TM));
  535.     end;
  536.   end;
  537.  
  538.   procedure SummarizeRange(LoMcb, HiMcb : Word);
  539.     {-Summarize Psps in the range LoMcb..HiMcb,
  540.       for Psp > 8, Psp < $FFF0, and Psp <> PrefixSeg}
  541.   var
  542.     M : McbPtr;
  543.     MinPsp : Word;
  544.     TPsp : Word;
  545.     PrvPsp : Word;
  546.     Done : Boolean;
  547.   begin
  548.     PrvPsp := 8;
  549.     repeat
  550.       {find the smallest Psp not yet summarized}
  551.       MinPsp := $FFFF;
  552.       M := Ptr(LoMcb, 0);
  553.       repeat
  554.         TPsp := M^.Psp;
  555.         if TPsp = 0 then
  556.           TPsp := OS(M).S;
  557.         if TPsp < MinPsp then
  558.           if (TPsp > PrvPsp) and (TPsp < $FFF0) and (TPsp <> PrefixSeg) then
  559.             MinPsp := TPsp;
  560.         Done := (M^.Id = 'Z');
  561.         M := Ptr(OS(M).S+M^.Len+1, 0);
  562.       until Done;
  563.  
  564.       if MinPsp <> $FFFF then begin
  565.         {add up info about this Psp}
  566.         SummarizePsp(MinPsp, LoMcb, HiMcb);
  567.         {"mark out" this Psp}
  568.         PrvPsp := MinPsp;
  569.       end;
  570.     until MinPsp = $FFFF;
  571.   end;
  572.  
  573.   procedure SummarizeDos(LoMcb, HiMcb : Word);
  574.     {-Sum up memory attributed to DOS}
  575.   var
  576.     M : McbPtr;
  577.     Size : Word;
  578.     Blocks : Word;
  579.     FakeSeg : Word;
  580.     Done : Boolean;
  581.   begin
  582.     M := Ptr(LoMcb, 0);
  583.     Size := 0;
  584.     Blocks := 0;
  585.     repeat
  586.       if M^.Psp = 8 then
  587.         if (OS(M).S >= LoMcb) and (OS(M).S < HiMcb) then begin
  588.           Inc(Size, M^.Len);
  589.           Inc(Blocks);
  590.         end;
  591.       Done := (M^.Id = 'Z');
  592.       M := Ptr(OS(M).S+M^.Len+1, 0);
  593.     until Done;
  594.     if Blocks > 0 then begin
  595.       if HiMcb > TopSeg then
  596.         FakeSeg := NoShowVecSeg
  597.       else
  598.         FakeSeg := ShowVecSeg;
  599.       WriteMcb(FakeSeg, $00, OS(Mcb1).S+Size, Blocks, 0, 'DOS', '');
  600.     end;
  601.   end;
  602.  
  603.   procedure SummarizeFree(LoMcb, HiMcb : Word);
  604.     {-Write the free memory blocks in specified range of Mcbs}
  605.   var
  606.     M : McbPtr;
  607.     Done : Boolean;
  608.   begin
  609.     M := Ptr(LoMcb, 0); {!!}
  610.     {M := Mcb1;}        {!!}
  611.     repeat
  612.       if (M^.Psp = 0) and (M^.Len > 0) and
  613.          (OS(M).S >= LoMcb) and (OS(M).S < HiMcb) then
  614.         WriteMcb(NoShowVecSeg, $0000, M^.Len, 1, 0, FreeName, '');
  615.       Done := (M^.Id = 'Z');
  616.       M := Ptr(OS(M).S+M^.Len+1, 0);
  617.     until Done;
  618.   end;
  619.  
  620.   procedure WriteCondensed;
  621.     {-Report on Mcb's by Psp}
  622.   begin
  623.     Write('Psp  Cnt   Size Name       Command Line        ');
  624.     if UseWatch then
  625.       Write('Chained')
  626.     else
  627.       Write('Hooked');
  628.     WriteLn(' Vectors');
  629.     WriteLn('---- --- ------ ---------- ------------------- --------------------------------');
  630.  
  631.     if UseLoMem then begin
  632.       SummarizeDos(OS(Mcb1).S, TopSeg-1);  {DOS memory usage}
  633.       SummarizeRange(OS(Mcb1).S, TopSeg-1);{programs loaded in low memory}
  634.       SummarizePsp(PrefixSeg, OS(Mcb1).S, $FFFF);   {current program free space}
  635.       WriteTotalMem;                       {sum of memory so far}
  636.     end;
  637.  
  638.     if UseHiMem then begin
  639.       if UseLoMem then
  640.         WriteLn(^M^J'High Memory');
  641.       SummarizeDos(HiMemSeg, $FFFF);
  642.       SummarizeRange(HiMemSeg, $FFFF);
  643.       WriteTotalMem;
  644.     end;
  645.   end;
  646.  
  647.   procedure WriteFree;
  648.     {-Show just the free blocks in conventional memory}
  649.   begin
  650.     if UseLoMem then begin
  651.       WriteLn('Normal Memory');
  652.       SummarizeFree(OS(Mcb1).S, TopSeg-1);         {free blocks in low memory}
  653.       SummarizePsp(PrefixSeg, OS(Mcb1).S, $FFFF);  {current program free space}
  654.     end;
  655.     if UseHiMem then begin
  656.       if UseLoMem then
  657.         WriteLn(^M^J'High Memory');
  658.       SummarizeFree(HiMemSeg, $FFFF);
  659.     end;
  660.   end;
  661.  
  662.   procedure WriteSummary;
  663.     {-Write "summary" report for conventional memory}
  664.   begin
  665.     WriteLn('      Size Name       Command Line');
  666.     WriteLn('---------- ---------- --------------------------------------------------------');
  667.  
  668.     if UseLoMem then begin
  669.       SummarizeDos(OS(Mcb1).S, TopSeg-1);            {DOS memory usage}
  670.       SummarizeRange(OS(Mcb1).S, TopSeg-1);          {programs loaded in low memory}
  671.       SummarizePsp(PrefixSeg, OS(Mcb1).S, $FFFF);    {current program free space}
  672.     end;
  673.     if UseHiMem then begin
  674.       if UseLoMem then
  675.         WriteLn(^M^J'High Memory');
  676.       SummarizeDos(HiMemSeg, $FFFF);
  677.       SummarizeRange(HiMemSeg, $FFFF);
  678.     end;
  679.   end;
  680.  
  681.   procedure ShowConventionalMem;
  682.     {-Report on conventional memory, low and high}
  683.   begin
  684.     {Default values for display}
  685.     ShowSegments := True;
  686.     ShowBlocks := False;
  687.     ShowFiles := False;
  688.     ShowVectors := True;
  689.     SizeLen := 7;
  690.     NameLen := 10;
  691.     CmdLen := 19;
  692.  
  693.     if ShowFree then begin
  694.       ShowSegments := False;
  695.       ShowVectors := False;
  696.       WriteFree;
  697.     end else if ShowSummary then begin
  698.       ShowSegments := False;
  699.       ShowVectors := False;
  700.       CmdLen := 56;
  701.       WriteSummary;
  702.     end else if Verbose then begin
  703.       ShowFiles := True;
  704.       NameLen := 14;
  705.       WriteVerbose;
  706.     end else begin
  707.       ShowBlocks := True;
  708.       WriteCondensed;
  709.     end;
  710.   end;
  711.  
  712.   procedure ShowTheEmsMem;
  713.   var
  714.     Handles : Word;
  715.     H : Word;
  716.     P : Word;
  717.     Pages : LongInt;
  718.     EmsV : Byte;
  719.     PreSpace : Byte;
  720.     Name : string[9];
  721.     PageMap : PageArray;
  722.   begin
  723.     if not EmsPresent then
  724.       Exit;
  725.     WriteLn;
  726.     WriteLn('EMS Memory');
  727.     if not(ShowFree or ShowSummary) then begin
  728.       EmsV := EmsVersion;
  729.       Handles := EmsHandles(PageMap);
  730.       if Handles > 0 then
  731.         for H := 1 to Handles do begin {!!}
  732.           P := PageMap[H].NumPages;
  733.           if P <> 0 then begin
  734.             Write(HexW(H), ' ');
  735.             if Verbose then
  736.               Write('':VerboseIndent);
  737.             Write(CommaIze(LongInt(P) shl 14, 10));
  738.             if EmsV >= $40 then begin
  739.               GetHandleName(PageMap[H].Handle, Name);
  740.               if Name = '' then
  741.                 Name := 'n/a';
  742.             end else
  743.               Name := 'n/a';
  744.             WriteLn(' ', Name);
  745.           end;
  746.         end;
  747.     end;
  748.     Pages := EmsPagesAvailable;
  749.     if ShowFree or ShowSummary then
  750.       PreSpace := 0
  751.     else
  752.       PreSpace := 5;
  753.     if Verbose then
  754.       inc(PreSpace, VerboseIndent);
  755.     WriteLn('':PreSpace, CommaIze(LongInt(OS(Pages).O) shl 14, 10), ' ', FreeName);
  756.     if ShowSummary or (not ShowFree) then
  757.       WriteLn('':PreSpace, CommaIze(LongInt(OS(Pages).S) shl 14, 10), ' ', TotalName);
  758.   end;
  759.  
  760.   procedure ShowTheXmsMem;
  761.     {-Show what we can about XMS}
  762.   label
  763.     ExitPoint;
  764.   var
  765.     FMem : Word;
  766.     FMax : Word;
  767.     XHandles : Word;
  768.     H : Word;
  769.     HMem : Word;
  770.     Total : Word;
  771.     XmsPages : XmsHandlesPtr;
  772.     Status : Byte;
  773.     PreSpace : Byte;
  774.   begin
  775.     if not XmsInstalled then
  776.       Exit;
  777.     Status := QueryFreeExtMem(FMem, FMax);
  778.     if Status = $A0 then begin
  779.       FMem := 0;
  780.       FMax := 0;
  781.     end else if Status <> 0 then
  782.       Exit;
  783.  
  784.     {Total will count total XMS memory}
  785.     Total := 0;
  786.  
  787.     WriteLn(^M^J'XMS Memory');
  788.     GotXms := not Verbose;
  789.  
  790.     if ShowFree then
  791.       goto ExitPoint;
  792.  
  793.     {Get an array containing handles}
  794.     XHandles := GetXmsHandles(XmsPages);
  795.  
  796.     {Report all the handles}
  797.     for H := 1 to XHandles do begin
  798.       HMem := XmsPages^[H].NumPages;
  799.       if not ShowSummary then begin
  800.         Write(HexW(H), ' ');
  801.         if Verbose then
  802.           Write('':VerboseIndent);
  803.         WriteLn(CommaIze(LongInt(HMem) shl 10, 10), ' n/a');
  804.       end;
  805.       inc(Total, HMem);
  806.     end;
  807.  
  808.     {Add the free memory to the total}
  809.     inc(Total, FMem);
  810.  
  811. ExitPoint:
  812.     if ShowFree or ShowSummary then
  813.       PreSpace := 0
  814.     else
  815.       PreSpace := 5;
  816.     if Verbose then
  817.       inc(PreSpace, VerboseIndent);
  818.     WriteLn('':PreSpace, CommaIze(LongInt(FMem) shl 10, 10), ' ', FreeName);
  819.     if Total <> 0 then
  820.       WriteLn('':PreSpace, CommaIze(LongInt(Total) shl 10, 10), ' ', TotalName);
  821.   end;
  822.  
  823.   procedure ShowTheExtendedMem;
  824.   var
  825.     Total : LongInt;
  826.     PreSpace : Byte;
  827.   begin
  828.     if GotXms or ShowFree then
  829.       Exit;
  830.     if ExtMemPossible then
  831.       Total := ExtMemTotalPrim
  832.     else
  833.       Total := 0;
  834.     if Total = 0 then
  835.       Exit;
  836.  
  837.     WriteLn(^M^J'Raw Extended Memory');
  838.     if ShowSummary then
  839.       PreSpace := 0
  840.     else
  841.       PreSpace := 5;
  842.     if Verbose then
  843.       inc(PreSpace, VerboseIndent);
  844.     WriteLn('':PreSpace, CommaIze(Total, 10), ' ', TotalName);
  845.   end;
  846.  
  847.   procedure WriteCopyright;
  848.     {-Write a copyright message}
  849.   begin
  850.     Write('MAPMEM ', Version, ', Copyright 1991 TurboPower Software'^M^J);
  851.   end;
  852.  
  853.   procedure Initialize;
  854.     {-Initialize various global variables}
  855.   begin
  856.     GotXms := False;
  857.     TotalMem := 0;
  858.     TopSeg := TopOfMemSeg;
  859.   end;
  860.  
  861.   procedure GetOptions;
  862.     {-Parse command line and set options}
  863.   var
  864.     Arg : String[127];
  865.  
  866.     procedure WriteHelp;
  867.     begin
  868.       WriteCopyright;
  869.       WriteLn;
  870.       WriteLn('MAPMEM produces a report showing what memory resident programs are installed,');
  871.       WriteLn('how much memory each uses, and what interrupt vectors are taken over.');
  872.       WriteLn;
  873.       WriteLn('MAPMEM accepts the following command line syntax:');
  874.       WriteLn;
  875.       WriteLn('  MAPMEM [Options]');
  876.       WriteLn;
  877.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  878.       WriteLn;
  879.       WriteLn('     /C name  check whether TSR "name" is loaded.');
  880.       WriteLn('     /E       report expanded (EMS) memory.');
  881.       WriteLn('     /F       report free areas only.');
  882.       WriteLn('     /H       do not use WATCH information for vectors.');
  883.       WriteLn('     /L       do not report low memory blocks (<640K).');
  884.       WriteLn('     /Q       write no screen output with /C option.');
  885.       WriteLn('     /S       show summary of all memory areas.');
  886.       WriteLn('     /U       report upper memory blocks if available.');
  887.       WriteLn('     /V       verbose report.');
  888.       WriteLn('     /X       report extended (XMS) memory.');
  889.       WriteLn('     /?       write this help screen.');
  890.       Halt(1);
  891.     end;
  892.  
  893.     procedure UnknownOption;
  894.     begin
  895.       WriteCopyright;
  896.       WriteLn('Unknown command line option: ', Arg);
  897.       Halt(1);
  898.     end;
  899.  
  900.     procedure BadOption;
  901.     begin
  902.       WriteCopyright;
  903.       WriteLn('Invalid command line option: ', Arg);
  904.       Halt(1);
  905.     end;
  906.  
  907.     procedure GetArgs(S : String);
  908.     var
  909.       SPos : Word;
  910.     begin
  911.       SPos := 1;
  912.       repeat
  913.         Arg := NextArg(S, SPos);
  914.         if Arg = '' then
  915.           Exit;
  916.         if Arg = '?' then
  917.           WriteHelp
  918.         else
  919.           case Arg[1] of
  920.             '-', '/' :
  921.               case Length(Arg) of
  922.                 1 : BadOption;
  923.                 2 : case Upcase(Arg[2]) of
  924.                       '?' : WriteHelp;
  925.                       'C' : begin
  926.                               CheckTSR := not CheckTSR;
  927.                               if CheckTSR then begin
  928.                                 TSRName := StUpcase(NextArg(S, SPos));
  929.                                 if TSRName = '' then begin
  930.                                   WriteCopyright;
  931.                                   WriteLn('TSR name to check for is missing');
  932.                                   Halt(1);
  933.                                 end;
  934.                               end;
  935.                             end;
  936.                       'E' : ShowEmsMem := not ShowEmsMem;
  937.                       'F' : ShowFree := not ShowFree;
  938.                       'H' : UseWatch := not UseWatch;
  939.                       'L' : UseLoMem := not UseLoMem;
  940.                       'Q' : Quiet := not Quiet;
  941.                       'S' : ShowSummary := not ShowSummary;
  942.                       'U' : UseHiMem := not UseHiMem;
  943.                       'V' : Verbose := not Verbose;
  944.                       'X' : ShowExtMem := not ShowExtMem;
  945.                     else
  946.                       BadOption;
  947.                     end;
  948.               else
  949.                 UnknownOption;
  950.               end;
  951.           else
  952.             UnknownOption;
  953.           end;
  954.       until False;
  955.     end;
  956.  
  957.   begin
  958.     TsrName := '';
  959.  
  960.     {Get arguments from the command line and the environment}
  961.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  962.     GetArgs(GetEnv('MAPMEM'));
  963.  
  964.     {Account for related options}
  965.     if ShowFree then
  966.       ShowSummary := False;
  967.     if not UseLoMem then
  968.       UseHiMem := True;
  969.     if ShowFree or ShowSummary then begin
  970.       UseLoMem := True;
  971.       UseHiMem := True;
  972.       ShowEmsMem := True;
  973.       ShowExtMem := True;
  974.       Verbose := False;
  975.     end;
  976.     if not CheckTSR then
  977.       Quiet := False;
  978.  
  979.     {Initialize for high memory access}
  980.     HiMemSeg := FindHiMemStart;
  981.     if HiMemSeg = 0 then
  982.       UseHiMem := False;
  983.  
  984.     {Don't report any vectors normally taken over by SYSTEM}
  985.     SwapVectors;
  986.  
  987.     {ExitProc will undo swap and restore high memory access}
  988.     SaveExit := ExitProc;
  989.     ExitProc := @SafeExit;
  990.  
  991.     {Find WATCH in memory if requested}
  992.     if UseWatch then begin
  993.       WatchPsp := WatchPspSeg;
  994.       if WatchPsp = 0 then
  995.         UseWatch := False;
  996.     end;
  997.  
  998.     if not Quiet then
  999.       WriteCopyright;
  1000.   end;
  1001.  
  1002. begin
  1003.   {$IFDEF MeasureStack}
  1004.   FillChar(Mem[SSeg:0], SPtr-16, $AA);
  1005.   {$ENDIF}
  1006.  
  1007.   Initialize;
  1008.   GetOptions;
  1009.   if CheckTSR then
  1010.     FindTSR
  1011.   else begin
  1012.     WriteLn;
  1013.     ShowConventionalMem;
  1014.     if ShowEmsMem then
  1015.       ShowTheEmsMem;
  1016.     if ShowExtMem then begin
  1017.       ShowTheXmsMem;
  1018.       ShowTheExtendedMem;
  1019.     end;
  1020.   end;
  1021.  
  1022.   {$IFDEF MeasureStack}
  1023.   I := 0;
  1024.   while I < SPtr-16 do
  1025.     if Mem[SSeg:i] <> $AA then begin
  1026.       writeln('Unused stack ', i, ' bytes');
  1027.       I := SPtr;
  1028.     end else
  1029.       inc(I);
  1030.   {$ENDIF}
  1031. end.
  1032.