diff --git a/tools/a7out b/tools/a7out index b349f35..4df7a66 100755 --- a/tools/a7out +++ b/tools/a7out @@ -19,6 +19,10 @@ my $AC; # Accumulator my $LINK; # Link register my $MQ; # MQ register +# Constants +my $MAXINT = 0777777; # Biggest unsigned integer +my $MAXADDR = 017777; # Largest memory address + ### Main program ### # Optional debug argument @@ -31,8 +35,8 @@ die("Usage: $0 [-d] a.outfile [arg1 arg2 ...]\n") if ( @ARGV < 1 ); # Load the a.out file into memory # and simulate it -load_code( shift(@ARGV) ); -set_arguments(@ARGV); +load_code( $ARGV[0] ); +set_arguments(); #dump_memory(); #exit(0); simulate(); @@ -43,7 +47,7 @@ sub load_code { my $filename = shift; # Fill all the 8K words in memory with zeroes - foreach my $i ( 0 .. 017777 ) { + foreach my $i ( 0 .. $MAXADDR ) { $Mem[$i] = 0; } @@ -73,58 +77,55 @@ sub load_code { ### Copy the arguments into the PDP-7 memory space, and build ### an array of pointers to these arguments. Build a pointer -### at 017777 that points at the array. -### -### At the moment, this is NOT what PDP-7 Unix uses, but it's -### a start and it will help us to grok the real thing. -### -### For now, assume abc, def and ghi are stored in memory. -### The layout of the pointers and strings would be: -### -### +-------+ -### | | -### | +-----|---------+ -### | | | | -### | | +---|---------|---------+ -### | | | | | | -### | | | V V V -### |o|o|o|0||||| argc=12 | +### +------------+ ### sub set_arguments { - # No arguments, set the 017777 pointer to 017776 which is NULL - if (@ARGV==0) { - $Mem[ 017777 ] = 017776; - dprintf("No arguments, so NULL 017777 pointer\n"); - return; - } + # Get the number of arguments including the command name + my $argc= scalar(@ARGV); - # Count the number of words to store each string and its pointer - my $wordcount=2; # 2 as we count the NULL pointer at end of array - my $argcount= @ARGV; - foreach my $arg (@ARGV) { - # +1 before /2 to allow for the NUL character at end of string - # += +1 to include the pointer to the string - $wordcount += 1 + ( (length($arg) +1)/2); - } + # We now know that argc will appear in memory + # 4*argc +1 below location $MAXADDR + # Set argc to the number of words + my $addr= $MAXADDR - (4*$argc+1); + $Mem[ $MAXADDR ] = $addr; + $Mem[ $addr++ ] = $argc*4; - my $arraybase= 017777 - $wordcount; - my $stringbase= $arraybase + $argcount + 1; # include NULL pointer - $Mem[ 017777 ] = $arraybase; + # Now start saving the arguments + foreach (@ARGV) { + # Truncate and/or space pad the argument + my $str= sprintf("%-8s", substr($_, 0, 8)); - # Now copy each string into memory and initialise the pointer - foreach my $arg (@ARGV) { - $Mem[$arraybase++]= $stringbase; - $stringbase= string2mem($arg, $stringbase); - } + # Store pairs of characters into memory + for (my $i=0; $i < length($str); $i += 2) { + my $c1= substr($str, $i, 1) || ""; + my $c2= substr($str, $i+1, 1) || ""; + #printf("Saving %06o to %05o\n", (ord($c1) << 9 ) | ord($c2), $addr); + $Mem[$addr++]= (ord($c1) << 9 ) | ord($c2); + } + } } ### Simulate the machine code loaded into memory @@ -135,6 +136,7 @@ sub simulate { oct("004") => \&dac, oct("020") => \&lac, oct("034") => \&tad, + oct("054") => \&sad, oct("060") => \&jmp, oct("070") => \&iot, oct("074") => \&special, @@ -147,12 +149,12 @@ sub simulate { my $instruction = $Mem[$PC]; my $opcode = ( $instruction >> 12 ) & 074; my $indirect = ( $instruction >> 13 ) & 1; - my $addr = $instruction & 017777; + my $addr = $instruction & $MAXADDR; # Work out what any indirect address would be - my $indaddr= ($indirect) ? $Mem[$addr] & 017777 : $addr; - dprintf( "PC %06o: instr %06o, op %03o, ind %o, addr %06o ind %06o\n", - $PC, $instruction, $opcode, $indirect, $addr, $indaddr ); + my $indaddr= ($indirect) ? $Mem[$addr] & $MAXADDR : $addr; + #dprintf( "PC %06o: instr %06o, op %03o, ind %o, addr %06o ind %06o\n", + # $PC, $instruction, $opcode, $indirect, $addr, $indaddr ); # Simulate the instruction. Each subroutine updates the $PC if ( defined( $Oplist{$opcode} ) ) { @@ -167,7 +169,7 @@ sub simulate { # Debug code: dump memory contents sub dump_memory { - foreach my $i ( 0 .. 017777 ) { + foreach my $i ( 0 .. $MAXADDR ) { printf( STDERR "%06o: %06o\n", $i, $Mem[$i] ) if ( $Mem[$i] != 0 ); } } @@ -193,12 +195,19 @@ sub dac { # Add to AC sub tad { my ( $instruction, $addr, $indaddr ) = @_; - dprintf( "PC %06o: tac AC (value %06o) from addr %05o\n", + dprintf( "PC %06o: tad AC (value %06o) from addr %05o\n", $PC, $AC, $indaddr ); - $AC+= $Mem[$indaddr]; + $AC= ($AC + $Mem[$indaddr]) & $MAXINT; $PC++; } +# Skip if AC different to Y +sub sad { + my ( $instruction, $addr, $indaddr ) = @_; + dprintf( "PC %06o: sad AC %06o cf. %06o\n", $PC, $AC, $Mem[$indaddr]); + $PC += ($AC != $Mem[$indaddr]) ? 2 : 1; +} + # Jump sub jmp { my ( $instruction, $addr, $indaddr ) = @_; @@ -301,7 +310,7 @@ sub sys_open { $PC += 4; # Convert this to a sensible ASCII filename - my $filename = mem2string($start); + my $filename = mem2arg($start); dprintf( "PC %06o: open: file %s\n", $PC, $filename ); # Open the file @@ -335,7 +344,7 @@ sub sys_read { my $fd = $AC; my $start = $Mem[ $PC + 1 ]; my $count = $Mem[ $PC + 2 ]; - my $end = ($start + $count - 1) & 017777; + my $end = ($start + $count - 1) & $MAXADDR; die("sys_read: bad start/end addresses $start $end\n") if ($end < $start); dprintf( "PC %06o: read: %d words into %o from fd %d\n", $PC, $count, $start, $fd ); @@ -388,7 +397,7 @@ sub sys_write { my $fd = $AC; my $start = $Mem[ $PC + 1 ]; my $count = $Mem[ $PC + 2 ]; - my $end = ($start + $count - 1) & 017777; + my $end = ($start + $count - 1) & $MAXADDR; die("sys_write: bad start/end addresses $start $end\n") if ($end < $start); dprintf( "PC %06o: write: %d words from %o to fd %d\n", $PC, $count, $start, $fd ); @@ -433,9 +442,29 @@ sub word2ascii { return ($result); } +# Given the address of a four word argument string in +# memory, return a copy of the string in ASCII format. +# Lose any trailing spaces as well. +sub mem2arg { + my $addr = shift; + my $result = ""; + + foreach (1 .. 4) { + # Stop if the address leave the 8K word address space + last if ( $addr > $MAXADDR ); + my $word = $Mem[$addr++]; + my $c1 = ( $word >> 9 ) & 0177; + my $c2 = $word & 0177; + $result .= chr($c1) . chr($c2); + } + $result=~ s{ *$}{}; + return($result); +} + # Given the address of a word in memory, interpret that location # and those following as a NUL-terminated ASCII string and return # a copy of this string +# XXX: not sure if I still need this. sub mem2string { my $addr = shift; my $result = ""; @@ -443,7 +472,7 @@ sub mem2string { while (1) { # Stop when the address leave the 8K word address space - return ($result) if ( $addr > 017777 ); + return ($result) if ( $addr > $MAXADDR ); # Stop when the value there is zero my $word = $Mem[$addr]; @@ -464,25 +493,6 @@ sub mem2string { } } -# Given a string and the address of a word in memory, copy -# the string into memory starting at that address and NUL -# terminate the string. Return the first address after the string. -# -# We will go off the end of the string: suppress warnings -no warnings ('substr'); -sub string2mem { - my ($str, $base)= @_; - - # <= length so we go off the end and insert a NUL - for (my $i=0; $i <= length($str); $i += 2) { - my $c1= substr($str, $i, 1) || ""; - my $c2= substr($str, $i+1, 1) || ""; - #printf("Saving %06o to %05o\n", (ord($c1) << 9 ) | ord($c2), $base); - $Mem[$base++]= (ord($c1) << 9 ) | ord($c2); - } - return($base); -} - # Print out debug messages sub dprintf { printf( STDERR @_) if ($debug); diff --git a/tools/wktcat.s b/tools/wktcat.s index ef11a35..92c7c56 100644 --- a/tools/wktcat.s +++ b/tools/wktcat.s @@ -13,33 +13,15 @@ " Also, the current coding is hideous and needs refactoring. " I'm still coming to grips with PDP-7 assembly code. - main: " Load the pointer pointer in 017777 to see if we have any arguments lac 017777 i - sza " No args, so copy stdin to stdout - jmp catfiles " Yes args, so deal with them below + sad d4 " Skip if we have more than four argument words + jmp stdinout " Only four argument words, so no arguments -" This section copies from standard input to standard output -stdinout: - " Read five words into the buffer from stdin - " Five was chosen arbitrarily - lac d0 - sys read; buf; 5 - spa " Skip if result was >= 0 - jmp error " Result was -ve, so error result - sna " Skip if result was >0 - jmp end " Result was zero, so nothing left to read - - " Save the count of words read in - dac 1f - - " Write five words from the buffer to stdout - lac d1 - sys write; buf; 1:0 - - " and loop back for more words to read - jmp stdinout + lac 017777 + tad d1 " Bump AC up by 5 so that it points at the first argument + tad d4 " This section opens files, and copies their contents to standard output catfiles: @@ -76,21 +58,44 @@ fileend: lac fd sys close - " Load and increment the 017777 pointer - lac 017777 - tad d1 - dac 017777 + " Subtract 4 from the count of argument words + lac minus4 + tad 017777 i + dac 017777 i + sad d4 " Is the value 4, i.e. no args left? + jmp end " Yes, so exit - " Load the pointer pointer in 017777 to see if we have any more arguments - lac 017777 i - sna " No args, so end the program - jmp end - jmp catfiles " Otherwise loop back to cat this file + " Still an argument, so move up to the next filename argument + lac name + tad d4 + dac name + jmp catfiles " and loop back to cat this file end: " exit sys exit +" This section copies from standard input to standard output +stdinout: + " Read five words into the buffer from stdin + " Five was chosen arbitrarily + lac d0 + sys read; buf; 5 + spa " Skip if result was >= 0 + jmp error " Result was -ve, so error result + sna " Skip if result was >0 + jmp end " Result was zero, so nothing left to read + + " Save the count of words read in + dac 1f + + " Write five words from the buffer to stdout + lac d1 + sys write; buf; 1:0 + + " and loop back for more words to read + jmp stdinout + " This code comes from the real cat.s badfile: @@ -117,7 +122,9 @@ noreadstr: fd: 0 " fd of the open file d0: 0 " Constants 0 and 1 d1: 1 +d4: 4 d8: 8 " stderr seems to have fd 8 +minus4: 0777774 " -4 constant " Input buffer for read buf: 0; 0; 0; 0; 0