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