tools/as7: added syscalls chmod and chown, added auto-increment
locations, fixed a bug in character input, added several EAE instructions. We now have cp.s, chmod.s and chown.s working.
This commit is contained in:
+134
-70
@@ -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) );
|
||||
|
||||
Reference in New Issue
Block a user