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