#!/usr/bin/perl -w

use Device::SerialPort;

$chunk_size=32;
$elffile = "vems.elf";

if (@ARGV < 1) {
    print "Usage:\n\t$0 idx\n";
    print "\tWhere 'idx' is the name of the variable, or the address \@xxx\n";
    exit;
}

$PORT = "/dev/ttyS0";
$ob = Device::SerialPort->new ($PORT) || die "Can't open $PORT: $!";
$ob->baudrate(9600)   || die "failed setting baudrate";
$ob->parity("none")    || die "failed setting parity";
$ob->databits(8)       || die "failed setting databits";
$ob->handshake("none") || die "failed setting handshake";
$ob->write_settings    || die "no settings";
$tmout = 1000;

get_elf();

# determine if address or symbolic name was given as starting address
$ARGV[0] =~ /(^\@)?(\w+)/;
if (defined($1) && defined($2)) {
    $start_addr = $2;
} else {
    my $var = $2;
    if (defined($offset{$var})) {
	$start_addr = $offset{$var};
    } else {
	print "Variable '$var' not found\n";
	undef $ob if defined $ob;
	exit;
    }
    
}

# address is multiplied by 16 in the AVR
$packed_addr = int($start_addr / 16);
$start_addr = $packed_addr * 16;

printf("start_addr = %d (0x%x), ", $start_addr, $start_addr);
printf("packed_addr = %d (0x%x)\n", $packed_addr, $packed_addr);

##### Insert rs232 code here instead of this: #####
# send 'mdx${packed_addr}' to avr and pickup the result
#for (my $i=0; $i<$chunk_size; $i++) {
#    $data_from_avr[$i] = 0;
#}

$ob->lookclear();
comm_send('Man'); 
srl_wait(8);
$ob->lookclear();
my $str = sprintf("mdx%02x", $packed_addr);
comm_send($str);
my $data = srl_wait($chunk_size);
@data_from_avr = split //, $data;

###################################

print "data\taddr\tvariable\n";
for (my $i=0; $i<$chunk_size; $i++) {
    my $addr = $start_addr+$i;
    my $varname = get_varname($addr);
    my $val = ord($data_from_avr[$i]);
    $val = sprintf("%02x(%d)", $val, $val);
    print "$val\t$addr\t$varname\n";
}

undef $ob if defined $ob;


sub get_varname {
    my $addr = shift;
    my $name = "unknown";

    # Find the variable/array that $i belongs to.
    # Start at address $addr and backtrack until variable is found
    for (my $i=$addr; $i>0; $i--) {
	if (defined($addr_idx{$i})) {
	    # got it!
	    my $name = $addr_idx{$i};
	    my $len = $size{$name};

	    if (($i + $len) >= $addr) { # check if i+len >= addr
		# then calculate position in array
		my $idx = $addr - $i;
		if ($len > 1) {  # dont want +0 for single byte variables
		    $name .= "+$idx"
		}
		return $name;
	    }
	}
    }
    return "<unknown>";  # cannot find the name
};

# ParseStruct.pm could be used to lookup the name of the variable
# in structs...

#%structs = ( "benchstat" => "benchmark.h",
#	     "etime" => "eventqueue.h", );
	     

sub get_elf {
# *.map could also be used, although length of variable needs to be 
# manually computed
    $elf = `avr-readelf -a $elffile`;

    foreach (split /\n/, $elf) {
#   1101: 0080086f     1 OBJECT  GLOBAL DEFAULT    3 ego_idx
	/\d+: 0080(\w+)\s+(\d+)\s+OBJECT.+DEFAULT\s+\d+\s+(\w+)/;
	my $addr = $1;
	my $len = $2;
	my $var = $3;
	if (defined($addr) && defined($var)) {
	    $offset{$var} = hex($addr);
	    $size{$var} = $len;
	}
	# we'll need a reverse hash later on
	%addr_idx = reverse %offset;
    }
};



#######################
sub comm_send
{
    my $str = shift ;
    $count_out = $ob->write($str);
    warn "write failed\n" unless ($count_out);
    warn "write incomplete\n" if ($count_out != length($str));
    return $count_out;
}

#######################
sub comm_recv
{
    my $b = shift;
    ($count_in, $string_in) = $ob->read($b);
    warn "read unsuccessful\n" unless ($count_in == $b);
    return ($count_in, $string_in);
}

#################################################
# what's the reply???
sub srl_wait
{
	my $realcharcnt = shift;
	if (defined($realcharcnt)) {
	    $charcnt = $realcharcnt;
	} else {
	    $charcnt = 1;
	}
# TODO: if no reply comes within 2 sec, we should start saying 'v' -s or sg.
# finally report the problem (or let the user ctrl-c)
	# $ob->read($in_);
	$ob->read_const_time($tmout);
	my ($cnt, $in_) = comm_recv($charcnt);
	#sysread (SRL, $in_, 1, 0) or die "cannot readback from SRL $!";
	
	if(defined $in_){
		#print "got back $cnt:$in_ :".ord(substr($in_, 0, 1));
		#print ' '.ord(substr($in_, 1, 1)) if(length($in_) > 1);
		#print "\n";
	} else {
		print "nothing\n";
	}
	
	return $in_ if defined($realcharcnt) && ($realcharcnt > 0);
	return undef if $in_ eq "\r";	# success 
	return 1;					# error
}
