diff --git a/tools/a7out b/tools/a7out index ddb1457..4dec18c 100755 --- a/tools/a7out +++ b/tools/a7out @@ -14,8 +14,11 @@ use Data::Dumper; my $debug = 0; # Debug flag my $singlestep = 0; # Are we running in single-step mode? my $coverage = 0; # Print out code coverage +my $namelist = undef; # Namelist filename my %Breakpoint; # Hash of defined breakpoints my @Mem; # 8K 18-bit words of main memory +my %Addr2Name; # Map addresses to names +my %Name2Addr; # Map names to addresses my @CC; # Code coverage: what addrs executed instructions my @FD; # Array of open filehandles my @ISBINARY; # Array of filehandle flags: ASCII or binary files? @@ -35,6 +38,8 @@ use constant EAESTEP => 077; # EAE step count mask use constant EAEIMASK => 0777700; # EAE instruction mask use constant SIGN => 0400000; # Sign bit +use constant EPSILON => 99; # max delta for symbol display + ### Main program ### # Get any optional arguments @@ -49,7 +54,7 @@ while ( defined( $ARGV[0] ) && ( $ARGV[0] =~ m{^-} ) ) { # -b: set a breakpoint if ( $ARGV[0] eq "-b" ) { shift(@ARGV); - $Breakpoint{ oct( shift(@ARGV) ) } = 1; + $Breakpoint{ lookup( shift(@ARGV) ) } = 1; } # -c: print out code coverage @@ -57,10 +62,16 @@ while ( defined( $ARGV[0] ) && ( $ARGV[0] =~ m{^-} ) ) { shift(@ARGV); $coverage= 1; } + + # -n: read a namelist file + if ( $ARGV[0] eq "-n" ) { + shift(@ARGV); + load_names( shift(@ARGV) ); + } } # Check the arguments -die("Usage: $0 [-c] [-d] [-b breakpoint] a.outfile [arg1 arg2 ...]\n") +die("Usage: $0 [-c] [-d] [-b breakpoint] [-n namelist] a.outfile [arg1 arg2 ...]\n") if ( @ARGV < 1 ); # Load the a.out file into memory @@ -189,12 +200,55 @@ sub set_arguments { my $c1 = substr( $str, $i, 1 ) || ""; my $c2 = substr( $str, $i + 1, 1 ) || ""; - #printf("Saving %06o to %06o\n", (ord($c1) << 9 ) | ord($c2), $addr); + #printf("Saving %06o to %s\n", (ord($c1) << 9 ) | ord($c2), addr($addr)); $Mem[ $addr++ ] = ( ord($c1) << 9 ) | ord($c2); } } } +### Load a namelist file +sub load_names { + my $filename = shift; + open( my $IN, "<", $filename ) || die("Unable to open $filename: $!\n"); + while (<$IN>) { + chomp; + if ($_ =~ m{([a-z0-9.]+)\s+([0-7]+)}) { + my $i = oct($2); + $Name2Addr{$1} = $i; + $Addr2Name{$i} = $1; + } + } + close ($IN); +} + +### Format an address +sub addr { + my $addr = shift; + my $oct = sprintf("%06o", $addr); + if (%Addr2Name) { + return "$oct ($Addr2Name{$addr})" if ($Addr2Name{$addr}); + # XXX keep Addr2Name as a sorted array? + # prefer after to before + for (my $epsilon = 1; $epsilon <= EPSILON; $epsilon++) { + my $n = $Addr2Name{$addr-$epsilon}; + return "$oct ($n+$epsilon)" if (defined($n)); + } + for (my $epsilon = 1; $epsilon <= EPSILON; $epsilon++) { + my $n = $Addr2Name{$addr+$epsilon}; + return "$oct ($n-$epsilon)" if (defined($n)); + } + } + return $oct; +} + +### convert string (symbol or octal) to address +sub lookup { + my $x = shift; + return oct($x) if ($x =~ m/^[0-7]+$/); + return $Name2Addr{$x} if (defined($Name2Addr{$x})); + return 0; +} + ### Simulate the machine code loaded into memory sub simulate { @@ -251,10 +305,11 @@ sub simulate { # If this is a breakpoint, stop now and get a user command if ( defined( $Breakpoint{$PC} ) ) { $singlestep = 1; - dprintf( "break at PC %06o\n", $PC ); + dprintf( "break at PC %s\n", addr($PC) ); } get_user_command() if ($singlestep); - dprintf( "PC %06o: ", $PC ); + dprintf( "PC %-20.20s L.AC %d.%06o MQ %06o: ", addr($PC), + ($LINK ? 1 : 0), $AC & 0777777, $MQ ); #dprintf("PC %06o: instr %06o, op %03o, in %o, addr %06o indaddr %06o\n", # $PC, $instruction, $opcode, $indirect, $addr, $indaddr ); @@ -264,8 +319,8 @@ sub simulate { $Oplist{$opcode}->( $instruction, $addr, $indaddr ); } else { - printf( STDERR "Unknown instruction 0%06o at location 0%06o\n", - $instruction, $PC ); + printf( STDERR "Unknown instruction 0%06o at location %s\n", + $instruction, addr($PC) ); exit(1); } } @@ -291,7 +346,7 @@ sub dump_memory { # Load AC sub lac { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "lac %06o (value %06o) into AC\n", $indaddr, $Mem[$indaddr] ); + dprintf( "lac %s (value %06o) into AC\n", addr($indaddr), $Mem[$indaddr] ); $AC = $Mem[$indaddr]; $PC++; } @@ -299,7 +354,8 @@ sub lac { # Deposit AC sub dac { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "dac AC (value %06o) into %06o\n", $AC, $indaddr ); + dprintf( "dac AC (value %06o) into %s\n", $AC, addr($indaddr) ); + dprintf("(****WRITE TO LOW MEMORY****)\n") if ($indaddr < 010000 && !($indaddr >= 010 && $indaddr <= 020) ) ; $Mem[$indaddr] = $AC; $PC++; } @@ -307,8 +363,8 @@ sub dac { # Add to AC, twos complement sub tad { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "tad AC (value %06o) with addr %06o (%06o)\n", - $AC, $indaddr, $Mem[$indaddr] ); + dprintf( "tad AC (value %06o) with addr %s (%06o)\n", + $AC, addr($indaddr), $Mem[$indaddr] ); $AC = $AC + $Mem[$indaddr]; $LINK = ( $LINK ^ $AC ) & LINKMASK; $AC = $AC & MAXINT; @@ -318,8 +374,8 @@ sub tad { # Add to AC, ones complement sub add { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "add AC (value %06o) with addr %06o (%06o)\n", - $AC, $indaddr, $Mem[$indaddr] ); + dprintf( "add AC (value %06o) with addr %s (%06o)\n", + $AC, addr($indaddr), $Mem[$indaddr] ); # This logic shamelessly borrowed from SimH # https://github.com/simh/simh/blob/master/PDP18B/pdp18b_cpu.c @@ -337,8 +393,8 @@ sub add { # And AC and Y sub and { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "and AC (value %06o) with addr %06o (%06o)\n", - $AC, $indaddr, $Mem[$indaddr] ); + dprintf( "and AC (value %06o) with addr %s (%06o)\n", + $AC, addr($indaddr), $Mem[$indaddr] ); $AC &= $Mem[$indaddr]; $PC++; } @@ -346,8 +402,8 @@ sub and { # Xor AC and Y sub xor { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "xor AC (value %06o) with addr %06o (%06o)\n", - $AC, $indaddr, $Mem[$indaddr] ); + dprintf( "xor %s (%06o)\n", + addr($indaddr), $Mem[$indaddr] ); $AC ^= $Mem[$indaddr]; $PC++; } @@ -355,14 +411,14 @@ sub xor { # Skip if AC different to Y sub sad { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "sad AC %06o cf. %06o\n", $AC, $Mem[$indaddr] ); + dprintf( "sad %s (%06o)\n", addr($indaddr), $Mem[$indaddr] ); $PC += ( $AC != $Mem[$indaddr] ) ? 2 : 1; } # Deposit zero in memory sub dzm { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "dzm into %06o\n", $indaddr ); + dprintf( "dzm %s\n", addr($indaddr) ); $Mem[$indaddr] = 0; $PC++; } @@ -370,7 +426,7 @@ sub dzm { # Index and skip if zero sub isz { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "isz %06o (value %06o)\n", $indaddr, $Mem[$indaddr] ); + dprintf( "isz %s (value %06o)\n", addr($indaddr), $Mem[$indaddr] ); $Mem[$indaddr]++; $Mem[$indaddr] &= MAXINT; $PC += ( $Mem[$indaddr] == 0 ) ? 2 : 1; @@ -379,14 +435,14 @@ sub isz { # Jump sub jmp { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "jmp %06o\n", $indaddr ); + dprintf( "jmp %s\n", addr($indaddr) ); $PC = $indaddr; } # Jump to subroutine sub jms { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "jms %06o\n", $indaddr ); + dprintf( "jms %s\n", addr($indaddr) ); # Save the LINK and current PC into the $indaddr location $Mem[ $indaddr++ ] = $PC + 1 | ( ($LINK) ? 0400000 : 0 ); @@ -399,7 +455,7 @@ sub opr { # hlt: halt simulation if ( $instruction == 0740040 ) { - printf( STDERR "PC %06o: program halted\n", $PC ); + printf( STDERR "PC %s: program halted\n", addr($PC) ); dump_memory( 0, MAXADDR, 0 ) if ($debug); exit(1); } @@ -425,7 +481,7 @@ sub opr { # https://github.com/simh/simh/blob/master/PDP18B/pdp18b_cpu.c my $skip = 0; my $i = ( $instruction >> 6 ) & 017; # decode IR<8:11> - dprintf( "L.AC %d.%06o %s", ($LINK) ? 1 : 0, $AC, $skipop[$i] ); + dprintf( "%s", $skipop[$i] ); $skip = 1 if ( ( $i == 1 ) && ( $AC & SIGN ) != 0 ); # sma $skip = 1 if ( ( $i == 2 ) && ( $AC & MAXINT ) == 0 ); # sza @@ -530,8 +586,8 @@ sub eae { if ( $instruction == 0653323 ) { # idiv: integer division my $divisor= $Mem[ $PC+1 ]; - dprintf( "idiv AC %06o by %06o (decimal %d by %d)\n", - $AC, $divisor, $AC, $divisor ); + dprintf( "idiv %06o by %06o (decimal %d by %d)\n", + $AC, $divisor, $AC, $divisor ); # Prevent division by zero :-) my $quotient = ($divisor) ? $AC / $divisor : 0; my $remainder = ($divisor) ? $AC % $divisor : 0; @@ -542,7 +598,7 @@ sub eae { } if ( $instruction == 0640323 ) { # div: 36-bit unsigned integer division my $divisor= $Mem[ $PC+1 ]; - dprintf( "div MQ.AC %06o.%06o AC by %06o (decimal %d)\n", + dprintf( "div MQ.AC %06o.%06o by %06o (decimal %d)\n", $MQ, $AC, $divisor, $divisor ); # http://www.perlmonks.org/?node_id=718414 says that we won't # lose accuracy before 2^53 @@ -569,7 +625,7 @@ sub eae { return; } if ( $maskedinstr == 0660700 ) { # alss: AC left shift, signed - dprintf( "alss AC %06o step %d\n", $AC, $step ); + dprintf( "alss step %d\n", $step ); $AC = ( $AC << $step ) & MAXINT; $LINK = ( $AC << 1 ) & LINKMASK; $PC++; @@ -578,7 +634,7 @@ sub eae { if ( $maskedinstr == 0660600 ) { # llss: long left shift, signed # Set the link to be the AC sign bit $LINK= ($AC & SIGN) ? LINKMASK : 0; - dprintf( "llss AC %06o step %d\n", $AC, $step ); + dprintf( "llss step %d\n", $step ); foreach my $i ( 1 .. $step ) { my $MQmsb = ( $MQ & SIGN ) ? 1 : 0; $AC = ( ( $AC << 1 ) | $MQmsb ) & MAXINT; @@ -588,7 +644,7 @@ sub eae { return; } if ( $maskedinstr == 0640600 ) { # lls: long left shift - dprintf( "lls AC %06o step %d\n", $AC, $step ); + dprintf( "lls step %d\n", $step ); foreach my $i ( 1 .. $step ) { my $MQmsb = ( $MQ & SIGN ) ? 1 : 0; $AC = ( ( $AC << 1 ) | $MQmsb ) & MAXINT; @@ -598,7 +654,7 @@ sub eae { return; } if ( $maskedinstr == 0640700 ) { # als: AC left shift - dprintf( "als AC %06o step %d\n", $AC, $step ); + dprintf( "als AC step %d\n", $step ); $AC = ( $AC << $step ) & MAXINT; $PC++; return; @@ -655,8 +711,8 @@ sub eae { $PC+=2; return; } - printf( STDERR "PC %06o: Unknown eae instruction %06o\n", - $PC, $instruction ); + printf( STDERR "PC %s: Unknown eae instruction %06o\n", + addr($PC), $instruction ); exit(1); } @@ -703,7 +759,7 @@ sub cal { $Syscallist{$addr}->(); } else { - printf( STDERR "PC %06o: Unknown syscall %d\n", $PC, $addr ); + printf( STDERR "PC %s: Unknown syscall %d\n", addr($PC), $addr ); exit(1); } } @@ -960,7 +1016,7 @@ sub sys_read { my $count = $Mem[ $PC + 2 ]; my $end = ( $start + $count - 1 ) & MAXADDR; die("sys_read: bad start/end addresses $start $end\n") if ( $end < $start ); - dprintf( "read: %d words into %06o from fd %d\n", $count, $start, $fd ); + dprintf( "read: %d words into %s from fd %d\n", $count, addr($start), $fd ); # Bump up the PC $PC += 3; @@ -1025,7 +1081,7 @@ sub sys_write { my $end = ( $start + $count - 1 ) & MAXADDR; die("sys_write: bad start/end addresses $start $end\n") if ( $end < $start ); - dprintf( "write: %d words from %06o to fd %d\n", $count, $start, $fd ); + dprintf( "write: %d words from %s to fd %d\n", $count, addr($start), $fd ); # Bump up the PC $PC += 3; @@ -1386,8 +1442,9 @@ sub get_user_command { print("a7out> "); chomp( my $line = ); my ( $cmd, $addr, $endaddr ) = split( /\s+/, $line ); - $addr = oct($addr) if ( defined($addr) ); - $endaddr = oct($endaddr) if ( defined($endaddr) ); + + $addr = lookup($addr) if ( defined($addr) ); + $endaddr = lookup($endaddr) if ( defined($endaddr) ); # Run the command my $leave; @@ -1451,7 +1508,7 @@ sub cmd_help { sub cmd_showregs { my $link = ($LINK) ? 1 : 0; - printf( "PC: %06o, L.AC %d.%06o, MQ: %06o\n", $PC, $link, $AC, $MQ ); + printf( "PC: %s, L.AC %d.%06o, MQ: %06o\n", addr($PC), $link, $AC, $MQ ); return (0); }