Ken Thompson sent e-mail in to explain that there can be labels mid-line, such as

sys write; 1:0; 4

so I modified the assembler logic to support this, and added some code to write_test.s
to verify that it works.
This commit is contained in:
Warren Toomey
2016-02-26 06:52:43 +10:00
parent f9b8c8eeb2
commit 018af6f43e
2 changed files with 87 additions and 64 deletions
+79 -63
View File
@@ -20,44 +20,51 @@ my @Mline; # Source lines associated with mem locations
my $origline; # The current input line of code my $origline; # The current input line of code
my $stage = 1; # Pass one or pass two my $stage = 1; # Pass one or pass two
my $debug = 0; # Run in debug mode
### Main program ### ### Main program ###
# Optional debug argument
if ( ( @ARGV > 0 ) && ( $ARGV[0] eq "-d" ) ) {
$debug = 1;
shift(@ARGV);
}
# Check the arguments # Check the arguments
die("Usage: $0 file1.s [file2.s ...]\n") if ( @ARGV < 1 ); die("Usage: $0 [-d] file1.s [file2.s ...]\n") if ( @ARGV < 1 );
# Define the syscalls as variables so that # Define the syscalls as variables so that
# I don't have to hand-code the logic below. # I don't have to hand-code the logic below.
# Also, start with the location counter at zero # Also, start with the location counter at zero
%Var= ( %Var = (
'.' => 0, '.' => 0,
save => 1, save => 1,
getuid => 2, getuid => 2,
open => 3, open => 3,
read => 4, read => 4,
write => 5, write => 5,
creat => 6, creat => 6,
seek => 7, seek => 7,
tell => 8, tell => 8,
close => 9, close => 9,
link => 10, link => 10,
unlink => 11, unlink => 11,
setuid => 12, setuid => 12,
rename => 13, rename => 13,
exit => 14, exit => 14,
time => 15, time => 15,
intrp => 16, intrp => 16,
chdir => 17, chdir => 17,
chmod => 18, chmod => 18,
chown => 19, chown => 19,
badcal => 20, badcal => 20,
syslog => 21, syslog => 21,
capt => 23, capt => 23,
rele => 24, rele => 24,
status => 25, status => 25,
smes => 27, smes => 27,
rmes => 28, rmes => 28,
fork => 29 fork => 29
); );
# Parse all the files # Parse all the files
@@ -68,16 +75,16 @@ foreach my $file (@ARGV) {
# Now do it all again, pass two # Now do it all again, pass two
$Var{'.'} = 0; $Var{'.'} = 0;
$stage = 2; $stage = 2;
print("Now in stage 2\n") if ($debug);
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
parse_file($file); parse_file($file);
} }
# Now print out the contents of memory # Now print out the contents of memory
for my $i ( 0 .. $#Mem ) { for my $i ( 0 .. $#Mem ) {
if (defined($Mem[$i])) { if ( defined( $Mem[$i] ) ) {
printf("%06o: %06o\t%s\n", printf( "%06o: %06o\t%s\n", $i, $Mem[$i], $Mline[$i] || "" );
$i, $Mem[$i], $Mline[$i] || ""); }
}
} }
exit(0); exit(0);
@@ -91,39 +98,39 @@ sub parse_file {
# Lose the end of line and any leading/trailing whitespace # Lose the end of line and any leading/trailing whitespace
# Discard any comments and preceding comment whitespace # Discard any comments and preceding comment whitespace
chomp($line); chomp($line);
$origline= $line; $origline = $line;
$line =~ s{^\s+}{}; $line =~ s{^\s+}{};
$line =~ s{\s+$}{}; $line =~ s{\s+$}{};
$line =~ s{\s*\".*}{}; $line =~ s{\s*\".*}{};
#print("=>$line<=\n"); #print("=>$line<=\n");
# Split into a section with possible labels and a # Split the line into commands that are ; separated
# section with definitely no labels. The ? makes the # and parse each one
# first pattern less greedy. Labels and statements. foreach my $cmd ( split( /;\s*/, $line ) ) {
$line =~ m{(.*?)([^:]*$)};
my $labelsect = $1;
my $stmntsect = $2;
#print(">$labelsect< >$stmntsect<\n"); # Split into a section with possible labels and a
# statement section with definitely no labels. The ?
# makes the first pattern less greedy.
#print("cmd is >$cmd<\n");
$cmd =~ m{(.*?)([^:]*$)};
my $labelsect = $1;
my $statement = $2;
# Split $labelsect into labels using the : character #print(">$labelsect< >$statement<\n");
my @labellist = split( /:\s*/, $labelsect );
# Split $stmntsect into statements using the ; character # Split $labelsect into labels using the : character
# Trim any whitespace first my @labellist = split( /:\s*/, $labelsect );
$stmntsect =~ s{^\s+}{};
my @stmntlist = split( /;\s*/, $stmntsect );
# First pass: parse the labels # First pass: parse the labels
if ( $stage == 1 ) { if ( $stage == 1 ) {
foreach my $l (@labellist) { parse_label($l); } foreach my $l (@labellist) {
parse_label($l);
}
}
# Parse the statements on both passes
parse_statement($statement);
} }
# Parse the statements on both passes
foreach my $s (@stmntlist) {
parse_statement($s);
}
#print("\n");
} }
close($IN); close($IN);
} }
@@ -153,6 +160,12 @@ sub parse_statement {
my $location = $Var{'.'}; my $location = $Var{'.'};
#printf( "Location: 0%o\n", $location ); #printf( "Location: 0%o\n", $location );
# Empty statement, nothing to do
return if ( $statement =~ m{^\s*$} );
# Lose any leading whitespace
$statement =~ s{^\s*}{};
# It's an assignment statement: lhs = rhs # It's an assignment statement: lhs = rhs
if ( $statement =~ m{(\S+)\s*=\s*(\S+)} ) { if ( $statement =~ m{(\S+)\s*=\s*(\S+)} ) {
my $lhs = $1; my $lhs = $1;
@@ -163,6 +176,7 @@ sub parse_statement {
my $result = parse_expression($rhs); my $result = parse_expression($rhs);
die("expression $rhs has no value in assignment\n") die("expression $rhs has no value in assignment\n")
if ( !defined($result) ); if ( !defined($result) );
#printf( "Setting variable %s to 0%o\n", $lhs, $result ); #printf( "Setting variable %s to 0%o\n", $lhs, $result );
$Var{$lhs} = $result; $Var{$lhs} = $result;
return; return;
@@ -173,7 +187,7 @@ sub parse_statement {
# Also save the input line that altered memory # Also save the input line that altered memory
if ( $stage == 2 ) { if ( $stage == 2 ) {
my $val = parse_expression($statement); my $val = parse_expression($statement);
$Mem[$location] = $val & 0777777; $Mem[$location] = $val & 0777777;
$Mline[$location] = $origline; $Mline[$location] = $origline;
#printf( "Saving 0%o into memory location 0%o\n", $val, $location ); #printf( "Saving 0%o into memory location 0%o\n", $val, $location );
} }
@@ -190,8 +204,8 @@ sub parse_expression {
# If it's a defined variable ( . , .. , etc.) # If it's a defined variable ( . , .. , etc.)
# return the value # return the value
return($Var{$expression}) return ( $Var{$expression} )
if (defined($Var{$expression})); if ( defined( $Var{$expression} ) );
# If it's a numeric literal, simply return it # If it's a numeric literal, simply return it
if ( $expression =~ m{^-?\d+$} ) { if ( $expression =~ m{^-?\d+$} ) {
@@ -337,8 +351,8 @@ sub parse_expression {
#printf( "Found the instruction %s: 0%o\n", $word1, $inst{$word1} ); #printf( "Found the instruction %s: 0%o\n", $word1, $inst{$word1} );
my $instruction = $inst{$word1}; my $instruction = $inst{$word1};
# Is this an indirect instruction? # Is this an indirect instruction?
my $indirect = defined($word3) && ($word3 eq "i") ? 020000 : 0; my $indirect = defined($word3) && ( $word3 eq "i" ) ? 020000 : 0;
# We have an expression for this instruction # We have an expression for this instruction
if ( ( $stage == 2 ) && defined($word2) ) { if ( ( $stage == 2 ) && defined($word2) ) {
@@ -380,10 +394,10 @@ sub parse_expression {
die("Unable to subtract $word1 on pass two\n") die("Unable to subtract $word1 on pass two\n")
if ( !defined($diff) ); if ( !defined($diff) );
} }
#print("Did a subtraction and got $diff\n"); #print("Did a subtraction and got $diff\n");
return ($diff); return ($diff);
} }
die("I have no idea what $expression is in pass two\n") if ( $stage == 2 ); die("I have no idea what $expression is in pass two\n") if ( $stage == 2 );
} }
@@ -395,6 +409,7 @@ sub add {
my $val2 = parse_expression($b); my $val2 = parse_expression($b);
#print("Adding $val1 + $val2\n"); #print("Adding $val1 + $val2\n");
return (undef) if ( !defined($val1) || !defined($val2) ); return (undef) if ( !defined($val1) || !defined($val2) );
#print( " with a good value of ", $val1 + $val2, "\n" ); #print( " with a good value of ", $val1 + $val2, "\n" );
return ( $val1 + $val2 ); return ( $val1 + $val2 );
} }
@@ -407,6 +422,7 @@ sub subtract {
my $val2 = parse_expression($b); my $val2 = parse_expression($b);
#print("Subtracting $val1 - $val2\n"); #print("Subtracting $val1 - $val2\n");
return (undef) if ( !defined($val1) || !defined($val2) ); return (undef) if ( !defined($val1) || !defined($val2) );
#print( " with a good value of ", $val1 - $val2, "\n" ); #print( " with a good value of ", $val1 - $val2, "\n" );
return ( $val1 - $val2 ); return ( $val1 - $val2 );
} }
+8 -1
View File
@@ -11,7 +11,13 @@ main:
lac d1 lac d1
sys write; hello; 7 sys write; hello; 7
" open file fred " Test if the assembler can dac into a mid-line label
lac helloptr
dac 1f
lac d1
sys write; 1:0; 7
" Try to open file fred
sys open; fred; 0; 0 sys open; fred; 0; 0
" read 5 words into the buffer from stdin: type in 10 or more characters! " read 5 words into the buffer from stdin: type in 10 or more characters!
@@ -42,6 +48,7 @@ out: 0
" Hello, world\n, two ASCII chars per word " Hello, world\n, two ASCII chars per word
hello: 0110145; 0154154; 0157054; 040; 0167157; 0162154; 0144012 hello: 0110145; 0154154; 0157054; 040; 0167157; 0162154; 0144012
helloptr: hello
" fred as a string, NUL terminated " fred as a string, NUL terminated
fred: 0146162; 0145144; 0 fred: 0146162; 0145144; 0