
(*
 * fmap - find symbols related to an address in a .MAP load
 *        map generated by LINK or TMAP
 *
 * S.H.Smith, 27-jan-86
 *
 *)

{$g512,p512,c-}

const
   version = 'FMAP 1.0 (1/26/87 SHS)';

type
   anystring = string[80];

var
   line:    anystring;
   fd:      text[10240];
   target:  anystring;
   mapname: anystring;


procedure abort_check;
begin
   if keypressed then
   begin
      writeln('aborted');
      halt;
   end;
end;


procedure parse_segments;
begin
   writeln('Segments');
   repeat
      readln(fd,line);
   until length(line) < 20;
end;


procedure parse_by_name;
begin
   writeln('Names');
   readln(fd,line);

   repeat
      readln(fd,line);
      abort_check;
   until length(line) < 17;
end;


procedure parse_by_value;
var
   pr:      anystring;
   ad:      anystring;
   ppr:     anystring;
   pad:     anystring;
   pline:   anystring;

begin
   writeln('Values');
   readln(fd,line);
   pad := '0000';
   ppr := '';

   repeat
      ad := copy(line,7,4);
      pr := copy(line,18,99);
      if (ppr <> '') and (target >= pad) and (target < ad) then
         writeln(pad,'-',ad,' ',pline);

      pad := ad;
      ppr := pr;
      pline := line;

      readln(fd,line);
      abort_check;
   until length(line) < 17;
end;


procedure output_lines(name: anystring; first, last: integer);
var
   fd: text[1024];
   n:  integer;
   b:  anystring;

begin
   writeln('Output lines ',first,'-',last,' from ',name);
   assign(fd,name);
{$i-}
   reset(fd);
{$i+}
   if ioresult <> 0 then
   begin
      writeln('can''t find source file: ',name);
      writeln('need lines ',first,'-',last);
      halt;
   end;

{$i-}
   for n := 1 to first-1 do
      readln(fd,b);

   for n := first to last+1 do
   begin
      writeln(n:6,'| ',b);
      readln(fd,b);
      abort_check;
   end;
{$i+}

   close(fd);
end;


var
   name:    anystring;
   ln:      integer;
   ad:      anystring;
   pln:     integer;
   pad:     anystring;
   first:   boolean;

   procedure check_match;
   begin
      writeln('   check match, ',pad,'-',ad,'  lines ',pln,'-',ln);

      if (pln <> 0) and (target >= pad) and (target < ad) then
      begin
         if first then
         begin
            writeln;
            writeln('==============================');
            writeln(name);
            first := false;
         end;

         if (ln-pln) < 20 then
         begin
            writeln('---------');
            writeln(pad,'-',ad);
            output_lines(name,pln,ln);
         end
         else
         begin
            writeln('---------');
            writeln(pad,'-',ad,'  lines ',pln,'-',ln);
         end;
      end;
   end;

procedure parse_line_numbers;
var
   i:       integer;
   code:    integer;
   buf:     anystring;

begin
   writeln('Line numbers: ',line);

   i := pos('(',line) + 1;
   name := '';
   while line[i] <> ')' do
   begin
      name := name + line[i];
      i := i + 1;
   end;

   readln(fd,line);
   writeln('name=[',name,']');

   pln := 0;
   pad := '0000';
   first := true;

   repeat
      abort_check;

      while length(line) > 6 do
      begin

         {extract the line number}
         buf := copy(line,1,5);
         while copy(buf,1,1) = ' ' do
            delete(buf,1,1);
         val(buf,ln,code);

         {extract the code address}
         ad := copy(line,12,4);

         {remove the processed part of the line}
         delete(line,1,17);

         {if target is between two lines, then print it out}
         check_match;

         pad := ad;
         pln := ln;
      end;

      readln(fd,line);
   until length(line) < 6;

   check_match;   {process the last line}
end;


procedure parse_others;
begin
   writeln('Other: ',line);
   readln(fd,line);
end;


procedure parse_mapfile;
begin
   writeln('Scanning mapfile ',mapname);
   writeln('for address ',target,':');
   writeln;

   readln(fd,line);

   while not eof(fd) do
   begin
      if copy(line,1,30) = ' Start  Stop   Length Name    ' then
         parse_segments
      else
      if copy(line,1,30) = '  Address         Publics by N' then
         parse_by_name
      else
      if copy(line,1,30) = '  Address         Publics by V' then
         parse_by_value
      else
      if copy(line,1,17) = 'Line numbers for ' then
         parse_line_numbers
      else
         parse_others;

      abort_check;
   end;

   close(fd);
end;


var
   i: integer;

begin
   writeln;
   writeln(version);
   writeln;

   if paramcount <> 2 then
   begin
      writeln('Usage: fmap MAPFILE TARGET_ADDRESS');
      writeln('Finds references to TARGET_ADDRESS in MAPFILE.');
      halt(1);
   end;

   mapname := paramstr(1);
   if pos('.',mapname) = 0 then
      mapname := mapname + '.MAP';

   assign(fd,mapname);
{$i-}
   reset(fd);
{$i+}
   if ioresult <> 0 then
   begin
      writeln('can''t open mapfile: ',mapname);
      halt;
   end;

   target := paramstr(2);
   for i := 1 to length(target) do
      target[i] := upcase(target[i]);

   if length(target) <> 4 then
   begin
      writeln('TARGET_ADDRESS must be 4 hex digits');
      halt;
   end;

   parse_mapfile;
   writeln;
end.

