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