diff --git a/src/cmd/cat.s b/src/cmd/cat.s index f40fd6c..76b8d45 100644 --- a/src/cmd/cat.s +++ b/src/cmd/cat.s @@ -1,4 +1,4 @@ -" cat: cat [arg1 arg2 ...] +" cat: cat arg1 [arg2 ...] " Load the pointer pointer in 017777 to see if we have any arguments lac 017777 i diff --git a/src/cmd/chmod.s b/src/cmd/chmod.s index 6748186..bf0b102 100644 --- a/src/cmd/chmod.s +++ b/src/cmd/chmod.s @@ -1,77 +1,83 @@ -" chmode +" chmod: chmod mode file [file file ...] +" +" mode is an octal number - lac 017777 i + lac 017777 i " Have we got any arguments? sad d4 - jmp error - + jmp error " No, give an error lac 017777 - tad d4 - dac 8 - tad d1 - dac name - dzm octal - dzm nchar - -8 - dac c1 + tad d4 " Skip past argc + dac 8 " and save the ptr to the octal value at location 8 + tad d1 " Why only 1 here? + dac name " Save the filename pointer + dzm octal " Zero the octal value and + dzm nchar " the number of characters + -8 " Set c1 to -8 so we can increment up to zero + dac c1 " and thus count the number of chars in the argument + 1: - lac nchar - dzm nchar - sza - jmp 2f - lac 8 i - lmq - and o177 - dac nchar - lacq - lrss 9 + lac nchar " Get any left-over character from the last loop + dzm nchar " and set nchar to empty now + sza " It was already empty, so get a new word + jmp 2f " Not empty, it has the second ASCII value, goto 2f + lac 8 i " Read the next word with two chars in it + lmq " Copy it to MQ + and o177 " Trim off all but the second ASCII value + dac nchar " Save it into nchar + lacq " Bring it back from MQ + lrss 9 " and shift down the first ASCII value + 2: - sad o40 - jmp 3f - tad om60 - lmq - lac octal - cll; als 3 - omq - dac octal + sad o40 " If the character is a space + jmp 3f " don't try to make it part of the octal value + tad om60 " Not a space, subtract 060 i.e. '0' + lmq " Move it into MQ + lac octal " Load the octal value up to here + cll; als 3 " Shift it left 3 bits + omq " OR in the digit from MQ + dac octal " and save back in the octal value + 3: - isz c1 - jmp 1b + isz c1 " Is that the last character? + jmp 1b " No, go back and get another one loop: - lac 017777 i + lac 017777 i " How many arguments left? sad d8 - sys exit - tad dm4 - dac 017777 i - lac name + sys exit " None, exit + tad dm4 " Subtract 4 to indicate one less argument + dac 017777 i " and save for next time + lac name " Also move to the next filename tad d4 dac name - lac octal - sys chmode; name:0 + lac octal " Set AC to have the new file mode + sys chmod; name:0 " and change the file's mode sma - jmp loop - lac name - dac 1f - lac d1 - sys write; 1:0; 4 + jmp loop " Loop if no error + lac name " We got back -1, error + dac 1f " Write out the filename on stdout + lac d1 " followed by the " ?\n" string + sys write; 1:0; 4 " and loop back lac d1 sys write; 1f; 2 jmp loop + 1: - 040;077012 + 040;077012 " String literal " ?\n" + error: - lac d1 + lac d1 " Write " "\n" to stdout and exit sys write; 1b+1; 1 sys exit -om60: -060 -o40: 040 -d1: 1 +d1: 1 " Numeric constants +d4: 4 d8: 8 dm4: -4 -d4: 4 +o40: 040 o177: 0177 +om60: -060 -nchar: .=.+1 +nchar: .=.+1 " Number of characters in the value entered c1: .=.+1 -octal: .=.+1 \ No newline at end of file +octal: .=.+1 " The resulting octal value diff --git a/src/cmd/chown.s b/src/cmd/chown.s index b9d334b..d1a2bc8 100644 --- a/src/cmd/chown.s +++ b/src/cmd/chown.s @@ -1,76 +1,83 @@ -" chown - lac 017777 i - sad d4 - jmp error +" chown: chown uid file [file file ...] +" +" uid is an octal number + lac 017777 i " Have we got any arguments? + sad d4 + jmp error " No, give an error lac 017777 - tad d4 - dac 8 - tad d1 - dac name - dzm octal - dzm nochar - -8 - dac c1 + tad d4 " Skip past argc + dac 8 " and save the ptr to the octal value at location 8 + tad d1 " Why only 1 here? + dac name " Save the filename pointer + dzm octal " Zero the octal value and + dzm nchar " the number of characters + -8 " Set c1 to -8 so we can increment up to zero + dac c1 " and thus count the number of chars in the argument + 1: - lac nchar - dzm nchar - sza - jmp 2f - lac 8 i - lmq - and o177 - dac nchar - lacq - lrss 9 + lac nchar " Get any left-over character from the last loop + dzm nchar " and set nchar to empty now + sza " It was already empty, so get a new word + jmp 2f " Not empty, it has the second ASCII value, goto 2f + lac 8 i " Read the next word with two chars in it + lmq " Copy it to MQ + and o177 " Trim off all but the second ASCII value + dac nchar " Save it into nchar + lacq " Bring it back from MQ + lrss 9 " and shift down the first ASCII value + 2: - sad o40 - jmp 3f - tad om60 - lmq - lac octal - cll; als 3 - omq - dac octal + sad o40 " If the character is a space + jmp 3f " don't try to make it part of the octal value + tad om60 " Not a space, subtract 060 i.e. '0' + lmq " Move it into MQ + lac octal " Load the octal value up to here + cll; als 3 " Shift it left 3 bits + omq " OR in the digit from MQ + dac octal " and save back in the octal value + 3: - isz c1 - jmp 1b + isz c1 " Is that the last character? + jmp 1b " No, go back and get another one loop: - lac 017777 i + lac 017777 i " How many arguments left? sad d8 - sys exit - tad dm4 - dac 017777 i - lac name + sys exit " None, exit + tad dm4 " Subtract 4 to indicate one less argument + dac 017777 i " and save for next time + lac name " Also move to the next filename tad d4 dac name - lac octal - sys chowner; name:0 + lac octal " Set AC to have the new file mode + sys chown; name:0 " and change the file's owner sma - jmp loop - lac name - dac 1f - lac d1 - sys write; 1:0; 4 + jmp loop " Loop if no error + lac name " We got back -1, error + dac 1f " Write out the filename on stdout + lac d1 " followed by the " ?\n" string + sys write; 1:0; 4 " and loop back lac d1 sys write; 1f; 2 jmp loop + 1: - 040;077012 + 040;077012 " String literal " ?\n" + error: - lac d1 - sys write; 1b+1; 1 + lac d1 " Write " "\n" to stdout and exit + sys write; 1b+1; 1 sys exit -om60: -060 -o40: 040 -d1: 1 +d1: 1 " Numeric constants +d4: 4 d8: 8 dm4: -4 -d4: 4 +o40: 040 o177: 0177 +om60: -060 -nchar: .=.+1 +nchar: .=.+1 " Number of characters in the value entered c1: .=.+1 -octal: .=.+1 +octal: .=.+1 " The resulting octal value diff --git a/src/cmd/chrm.s b/src/cmd/chrm.s index 5f5cab3..bec4e08 100644 --- a/src/cmd/chrm.s +++ b/src/cmd/chrm.s @@ -1,41 +1,46 @@ -" chrm +" chrm: chrm dir file [file file ...] +" +" chdir into the named directory and unlink the files that are in there +" +" The code depends on a "dd" directory existing which holds the named dir - lac 017777 - tad d5 - dac 1f - dac 2f - lac 017777 i + lac 017777 " Go to the argc + tad d5 " Skip past the argc and argv[0] + dac 1f " Save argv[1] in the chdir arg below + dac 2f " and in the unlink as well? Yes, we skip it + lac 017777 i " How many arguments do we have? sad d4 - sys exit - tad dm4 - dac 017777 i - sys chdir; dd - sys chdir; 1;0 + sys exit " None, so exit + tad dm4 " Subtract 1 + dac 017777 i " and save in the argc + + sys chdir; dd " chdir to dd + sys chdir; 1;0 " and then into the first argument 1: - lac 017777 i + lac 017777 i " Any arguments left? sad d4 - sys exit - tad dm4 + sys exit " No, exit the program + tad dm4 " Subtract 4 from the argc and update it dac 017777 i - lac 2f + lac 2f " Move up to the next filename tad d4 - dac 2f - sys unlink; 2:0 + dac 2f " and save it in the unlink arg + sys unlink; 2:0 " Unlink the file sma - jmp 1b - lac 2b + jmp 1b " Loop back if the unlink was OK, or issue err + lac 2b " Copy the filename pointer below dac 2f - lac d1 + lac d1 " Write the filename on stdout sys write; 2:0; 4 lac d1 - sys write; 1f; 2 - jmp 1b -1: - 040077;012000 + sys write; 1f; 2 " Write " ?\n" on stdout + jmp 1b " and loop back +1: + 040077;012000 " String literal " ?\n" dd: -
;040040;040040;040040 +
;040040;040040;040040 " Filename dd d1: 1 d4: 4 d5: 5 -dm4: -4 \ No newline at end of file +dm4: -4 diff --git a/src/cmd/cp.s b/src/cmd/cp.s index a77f91b..0943415 100644 --- a/src/cmd/cp.s +++ b/src/cmd/cp.s @@ -1,81 +1,89 @@ -" cp +" cp: cp file1 file2 [file3 file4 [file5 file6] ...] +" +" Copies in pairs: file1 to file2, file3 to file4 etc. + + lac 017777 " Skip past argc and save + tad d1 " argv[0] (our name) into name2 + dac name2 " We will skip past it later - lac 017777 - tad d1 - dac name2 loop: - lac 017777 i + lac 017777 i " Any arguments left? sad d4 - sys exit - sad d8 - jmp unbal - tad dm8 - dac 017777 i - lac name2 + sys exit " 4 words = no args left, exit + sad d8 " Do we have 2 args? + jmp unbal " No, an unbalanced set of arguments + tad dm8 " Subtract 8 (two args) from the argc + dac 017777 i " and save it + lac name2 tad d4 - dac name1 + dac name1 " Skipping pairs of filenames? not sure tad d4 dac name2 - sys open; name1: 0; 0 + sys open; name1: 0; 0 " Open the input file spa - jmp error - lac o17 - sys creat; name2: 0 + jmp error " File open error + lac o17 " Why load 15 (017) into AC? + sys creat; name2: 0 " Create the output file spa - jmp error - dzm nin + jmp error " File create error + dzm nin " Set the number of input words to zero 1: - lac bufp - tad nin - dac 0f + lac bufp " Set up the base of the upcoming read + tad nin " to be the buffer + nin so we skip + dac 0f " the existing words in the buffer -1 - tad nin - cma - tad d1024 + tad nin " Calculate 1024 - nin, i.e. the number + cma " of empty words yet to be filled in the + tad d1024 " buffer, and use it as the read count dac 0f+1 - lac d2 + lac d2 " Read from fd 2: hard-wired in fd! sys read; 0:..;.. sna - jmp 2f - tad nin - dac nin + jmp 2f " No words were read in, go to 2f + tad nin " Add the number of words read in + dac nin " to the existing number of words sad d1024 - jmp 2f - jmp 1b + jmp 2f " We do have 1,024 words, go to 2f + jmp 1b " Loop back if we don't have 1,024 words + 2: - lac nin - dac 2f - lac d3 - sys write; buf; 2: 0 - dzm nin - lac 2b - sad d1024 - jmp 1b - lac d2 - sys close + lac nin " Load the number of words in the input buffer + dac 2f " Save in the write word count argument + lac d3 " Write to fd 3: hard-wired out fd! + sys write; buf; 2:0 + dzm nin " Set nin back to zero + lac 2b " Get the write count (updated by sys write) + sad d1024 " Did we write the buffer out? + jmp 1b " Yes, we wrote 1,024 words, so loop back + " to read another buffer's worth + lac d2 + sys close " Close fd 2 and fd 3 lac d3 sys close - jmp loop -error: - lac name1 + jmp loop " Loop back to deal with the next arguments + +error: " File error, print out the name1 on + lac name1 " standard output, fd 1 followed by " ?\n" dac 1f - lac d1 - sys write; 1: 0; 4 + lac d1 + sys write; 1:0; 4 lac d1 sys write; mes; 1 - lac name2 + lac name2 " Then do the same with name 2 dac 1f lac d1 sys write; 1: 0; 4 lac d1 sys write; mes; 2 - jmp loop + jmp loop " Loop back to deal with the next arguments + mes: - 040000;077012 -unbal: - lac name2 - tad d4 + 040000;077012 " String literal: " ?\n" + +unbal: " We had an unbalanced set of arguments + lac name2 " so print out the name after name2 + tad d4 " on standard output followed by " ?\n" dac 1f lac d1 sys write; 1: 0; 4 @@ -83,15 +91,14 @@ unbal: sys write; mes; 2 sys exit -d1: 1 +d1: 1 " Numeric constants +d2: 2 +d3: 3 d4: 4 d8: 8 o17: 017 dm8: -8 -d3: 3 d1024: 1024 -nin: 0 -bufp: buf -d2: 2 - -buf: \ No newline at end of file +nin: 0 " Number of words in the input buffer +bufp: buf " Pointer to the buffer +buf: .=.+1024 " Buffer of 1,024 words diff --git a/tools/a7out b/tools/a7out index 3ae3066..0def605 100755 --- a/tools/a7out +++ b/tools/a7out @@ -16,7 +16,7 @@ my @Mem; # 8K 18-bit words of main memory my @FD; # Array of open filehandles # Registers -my $PC = 0; # Program counter +my $PC = 020; # Program counter my $AC = 0; # Accumulator my $LINK = 0; # Link register, either 0 or LINKMASK my $MQ = 0; # MQ register @@ -172,6 +172,14 @@ sub simulate { oct("074") => \&opr, ); + # List of opcodes that DON'T auto-increment + # locations 10-17 when we have the indirect bit + my %NoIncr = ( + oct("000") => 1, # cal + oct("064") => 1, # eae + oct("074") => 1 # opr + ); + # Loop indefinitely while (1) { @@ -181,6 +189,14 @@ sub simulate { my $indirect = ( $instruction >> 13 ) & 1; my $addr = $instruction & MAXADDR; + # Auto-increment locations 010 to 017 if $indirect + # and this is an instruction that does increment + if ($indirect && ($addr >= 010) && ($addr <= 017) && + !defined($NoIncr{$opcode})) { + $Mem[$addr]++; + $Mem[$addr] &= MAXINT; + } + # Work out what any indirect address would be my $indaddr = ($indirect) ? $Mem[$addr] & MAXADDR : $addr; @@ -212,7 +228,12 @@ sub simulate { sub dump_memory { my ( $start, $end, $yeszero ) = @_; foreach my $i ( $start .. $end ) { - printf( STDERR "%06o: %06o\n", $i, $Mem[$i] ) + # Convert the word into possibly two ASCII characters + my $c1= ($Mem[$i] >> 9) & 0777; + $c1= ($c1 < 0200) ? chr($c1) : " "; + my $c2= $Mem[$i] & 0777; + $c2= ($c2 < 0200) ? chr($c2) : " "; + printf( STDERR "%06o: %06o %s%s\n", $i, $Mem[$i], $c1, $c2) if ( $yeszero || $Mem[$i] != 0 ); } } @@ -239,8 +260,8 @@ sub tad { dprintf( "tad AC (value %06o) with addr %06o (%06o)\n", $AC, $indaddr, $Mem[$indaddr] ); $AC = $AC + $Mem[$indaddr]; + $LINK = ($LINK ^ $AC) & LINKMASK; $AC = $AC & MAXINT; - $LINK = $AC & LINKMASK; $PC++; } @@ -445,6 +466,60 @@ sub opr { return; } +# Extended arithmetic element instructions +sub eae { + my ( $instruction, $addr, $indaddr ) = @_; + my $step = $instruction & EAESTEP; + my $maskedinstr= $instruction & EAEIMASK; + + if ( $maskedinstr == 0660500 ) { # lrss: long right shift, signed + # We ignore the MQ as it's not + # used by any user-mode programs + dprintf( "lrss %06o AC step %d\n", $AC, $step ); + + # Save the AC's sign into LINK + my $newlink = ( $AC << 1 ) & LINKMASK; + $AC = ( ( $LINK | $AC ) >> $step ) & MAXINT; + $LINK = $newlink; + $PC++; + return; + } + if ( $maskedinstr == 0660700 ) { # alss: long left shift, signed + # We don't fill the lsb with LINK yet + dprintf( "alss AC %06o step %d\n", $AC, $step ); + $AC = ( $AC << $step ) & MAXINT; + $PC++; + return; + } + if ( $maskedinstr == 0640700 ) { # als: long left shift + dprintf( "alss AC %06o step %d\n", $AC, $step ); + $AC = ( $AC << $step ) & MAXINT; + $PC++; + return; + } + if ( $instruction == 0652000 ) { # lmq: load MC from AC + dprintf( "lmq AC %06o into MQ\n", $AC); + $MQ= $AC; + $PC++; + return; + } + if ( $instruction == 0641002 ) { # lacq: load AC from MQ + dprintf( "lacq MQ %06o into AC\n", $MQ); + $AC= $MQ; + $PC++; + return; + } + if ( $instruction == 0640002 ) { # lacq: OR AC with MQ + dprintf( "omq MQ %06o and AC %06o\n", $MQ, $AC); + $AC |= $MQ; + $PC++; + return; + } + printf( STDERR "PC %06o: Unknown eae instruction %06o\n", + $PC, $instruction ); + exit(1); +} + # cal: used for system calls sub cal { my ( $instruction, $addr, $indaddr ) = @_; @@ -457,6 +532,8 @@ sub cal { 6 => \&sys_creat, 9 => \&sys_close, 14 => \&sys_exit, + 18 => \&sys_chmod, + 19 => \&sys_chown, ); # Simulate the syscall. Each syscall updates the $PC @@ -468,36 +545,6 @@ sub cal { } } -# Extended arithmetic element instructions -sub eae { - my ( $instruction, $addr, $indaddr ) = @_; - my $step = $instruction & EAESTEP; - $instruction &= EAEIMASK; - - if ( $instruction == 0660500 ) { # lrss: long right shift, signed - # We ignore the MQ as it's not - # used by any user-mode programs - dprintf( "lrss %06o AC step %d\n", $AC, $step ); - - # Save the AC's sign into LINK - my $newlink = ( $AC << 1 ) & LINKMASK; - $AC = ( ( $LINK | $AC ) >> $step ) & MAXINT; - $LINK = $newlink; - $PC++; - return; - } - if ( $instruction == 0660700 ) { # alss: long left shift, signed - # We don't fill the lsb with LINK yet - dprintf( "alss %06o AC step %d\n", $AC, $step ); - $AC = ( $AC << $step ) & MAXINT; - $PC++; - return; - } - printf( STDERR "PC %06o: Unknown eae instruction %06o\n", - $PC, $instruction ); - exit(1); -} - # Exit system call sub sys_exit { dprintf("exit system call\n"); @@ -558,9 +605,8 @@ sub sys_open { # AC is the opened fd on success, or -1 on error # Get the start address of the string - my $start = $Mem[ $PC + 1 ]; - # Convert this to a sensible ASCII filename + my $start = $Mem[ $PC + 1 ]; my $filename = mem2arg($start); # Choose to open read-only or write-only @@ -576,7 +622,7 @@ sub sys_open { # Creat system call sub sys_creat { - # Open seems to have 1 arguments: PC+1 is a pointer to the filename. + # Creat seems to have 1 argument: PC+1 is a pointer to the filename. # Some programs seem to have a second argument always set to 0. # AC is the opened fd on success, or -1 on error @@ -629,7 +675,8 @@ sub sys_read { my $c1 = getc($FH); last if ( !defined($c1) ); # No character, leave the loop - my $c2 = getc($FH) || ""; # No character, make it a NUL + my $c2 = getc($FH); # No character, make it a NUL + $c2= "" if (!defined($c2)); $Mem[$addr] = ( ord($c1) << 9 ) | ord($c2); # Pack both into one word $count++; @@ -642,7 +689,6 @@ sub sys_read { # Write system call sub sys_write { - # Write seems to have arguments: AC is the file descriptor, PC+1 is # the pointer to the buffer and PC+2 is the number of words to write @@ -677,6 +723,56 @@ sub sys_write { return; } +# Chmod system call +sub sys_chmod { + # Chmod gets the permission bits in AC and a pointer + # to the file's name in PC+1. s2.s has these instruction for chmod: + # lac u.ac; and o17 so only the lowest 4 + # bits are the permission bits that can be set. + # I'm going to guess these (from v1 chmod manual): + # 01 write for non-owner + # 02 read for non-owner + # 04 write for owner + # 10 read for owner + my $mode; + $mode|= 0002 if ($AC & 01); + $mode|= 0004 if ($AC & 02); + $mode|= 0220 if ($AC & 04); + $mode|= 0440 if ($AC & 010); + + my $start = $Mem[ $PC + 1 ]; + my $filename = mem2arg($start); + dprintf( "chmod %06o file %s\n", $mode, $filename); + + # Do the chmod on the file + my $result= chmod($mode, $filename); + + # Set AC to -1 if no files were changed, else 0 + $AC= ($result == 0) ? MAXINT : 0; + $PC += 2; + return; +} + +# Chown system call +sub sys_chown { + # Chown gets the numeric user-id in AC and a pointer + # to the file's name in PC+1. + # Get the start address of the string + # Convert this to a sensible ASCII filename + my $start = $Mem[ $PC + 1 ]; + my $filename = mem2arg($start); + dprintf( "chown file %s to uid %06o\n", $filename, $AC); + + + # Do the chown, leave group-id untouched. Get number of files changed + my $result= chown($AC, -1, $filename); + + # Set AC to -1 if no files were changed, else 0 + $AC= ($result == 0) ? MAXINT : 0; + $PC += 2; + return; +} + # Convert an 18-bit word into two ASCII characters and return them. # Don't return NUL characters sub word2ascii { @@ -709,38 +805,6 @@ sub mem2arg { 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 = ""; - - while (1) { - - # Stop when the address leave the 8K word address space - return ($result) if ( $addr > MAXADDR ); - - # Stop when the value there is zero - my $word = $Mem[$addr]; - return ($result) if ( $word == 0 ); - - # Get the top ASCII character, return if NUL - my $c1 = ( $word >> 9 ) & 0177; - return ($result) if ( $c1 == 0 ); - $result .= chr($c1); - - # Get the bottom ASCII character, return if NUL - my $c2 = $word & 0177; - return ($result) if ( $c2 == 0 ); - $result .= chr($c2); - - # Move up to the next address - $addr++; - } -} - # Print out debug messages sub dprintf { printf( STDERR @_ ) if ( ($debug) || ($singlestep) );