#! /usr/local/bin/perl
#
#  Convert AVR GNU linker .map file into symbol table
#

$debug = 0;
$emulate = 0;

$dups_ok = 1;

$DATA = 'data';
$TEXT = 'text';
$EEPROM = 'eeprom';
$SEEPROM = 'seeprom';

$MAX_BITS = 16;

sub usage {
    $ego = $0; $ego =~ s/^.*[\\\/]//;
    print STDERR "usage: $ego [-d] mapfile.map > symbol_table_file\n";
}

while ($ARGV[0] =~ /^-(.*)$/) {
    $option = $1; shift;
    if ($option eq 'd') {
        $debug = 1;
    } else {
        &usage;
        die "unknown option -$option!\n";
    }
}

if ($ARGV[0] eq '') { &usage; exit(1); }

&add_symbol($DATA, 0, '');
&add_symbol($TEXT, 0, '');
&add_symbol($EEPROM, 0, '');
&add_symbol($SEEPROM, 0, '');

$linenum = 0;
$section = '';
while (<>) {
    $line = $_;
    $line =~ s/[\r\n\s]+$//;
    $line =~ s/^\s+//;
    ++$linenum;
    next if ($line eq ''); 
    ##print $line."\n" if ($debug);
    #
    # .text           0x00000000     0x1a48
    #  *(vectors)
    #  vectors        0x00000000       0x1a x:\avrfirm\lib\kernel.o
    #  *(imports)
    #  imports        0x0000001a        0x2 x:\avrfirm\lib\kernel.o
    #                 0x0000001a                import_user_debug
    #                 0x0000001a                import_unknown_arp
    #                 0x0000001a                import_unknown_icmp
    #                 0x0000001a                import_udp
    #  *(.text.*)
    #  .text.libgcc   0x00001e82       0x4c C:\avrfirm\lib\libgcc.a(divhi3.o)
    #                 0x00001e82                _divhi3
    #                 0x00001e9a                _divhi_raw
    #
    if ($line =~ /^(\S+)/) {
        $s = $1;
        if ($s =~ /^.text/) {
            $section = $TEXT 
        } elsif ($s =~ /^\.txts*/) {
            $section = $SEEPROM 
        } elsif ($s eq '.data') {
            $section = $DATA 
        } elsif ($s eq '.bss') {
            $section = $DATA 
        } elsif ($s eq 'COMMON') {
            $section = $DATA 
        } elsif ($s eq '.eeprom') {
            $section = $EEPROM 
        } elsif ($s eq 'seeprom') {
            $section = $SEEPROM 
        } elsif ($s =~ /^[.]/) {
            $section = '';
        }
    }
    print "'$section'\t$line\n" if ($debug);
    next if ($section eq '');

    if ($s = /^                (0x[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])\s+(.*)$/i) {
        $loc = $1;
        $label = $2;
    } else {
        next;
    }
    $label =~ s/[,]\s+/,/g;
    $label =~ s/^\s+//;
    $label =~ s/\s+$//;
    $n = @F = split(/\s+/, $label);
    $label = $F[$n-1] if ($n > 1);
    next if ($label =~ /^[.]=ALIGN/);
    if ($label =~ /[(]([^)]+)[)]/) {
        $label = $1;
    }
    $label =~ s/[=,][.]$//;
    #print "$section\t$loc\t$label\n" if ($debug);
    if (!$dups_ok) {
        if (++$locs[hex($loc)] > 1) {
		print STDERR "skipping duplicate: $section $label $loc\n" if ($debug);
		next;
	}
    }
    &add_symbol($section, $loc, $label);
}

foreach $key (sort(keys %symbols)) {
    print "$symbols{$key}\n";
}

sub add_symbol {
    local($section, $loc, $label) = @_;
    local($key, $symbol);

    $label = ".$section" if ($label eq '');
    $key = sprintf("%-10s 0x%08x  %s", $section, hex($loc), $label);
    $symbol = sprintf("%s\t%x\t%s", $section, hex($loc), $label);
    ##print "$key\n";
    $symbols{$key} = $symbol;
}

