1199 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			1199 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| #!/usr/bin/perl
 | |
| 
 | |
| my $indent = ' ' x 2;
 | |
| my $separator = '-' x 80;
 | |
| 
 | |
| ################################################################################
 | |
| # Input arguments
 | |
| #
 | |
| use Getopt::Std;
 | |
| my %opts;
 | |
| getopts('hva:d:r:kc', \%opts);
 | |
| 
 | |
| die("\n".
 | |
|     "Usage: $0 [options] fileSpec\n".
 | |
|     "\n".
 | |
|     "Options:\n".
 | |
|     "${indent}-h        display this help message\n".
 | |
|     "${indent}-v        verbose\n".
 | |
|     "${indent}-a bitNb  the number of program address bits\n".
 | |
|     "${indent}-d bitNb  the number of data bits\n".
 | |
|     "${indent}-r bitNb  the number of register address bits\n".
 | |
|     "${indent}-k        keep intermediate files\n".
 | |
|     "${indent}-c        clean temporary work files\n".
 | |
|     "\n".
 | |
|     "Compiles a Pascal program to assembler code for the nanoBlaze processor.\n".
 | |
|     "\n".
 | |
|     "More information with: perldoc $0\n".
 | |
|     "\n".
 | |
|     ""
 | |
|    ) if ($opts{h});
 | |
| 
 | |
| my $verbose              = $opts{v};
 | |
| my $keepIntermediateFiles= $opts{k};
 | |
| my $cleanTempFiles       = $opts{c};
 | |
| my $addressBitNb         = $opts{a} || 10;
 | |
| my $registerBitNb        = $opts{d} || 8;
 | |
| my $registerAddressBitNb = $opts{r} || 4;
 | |
| 
 | |
| my $pascalFileSpec = $ARGV[0] || 'nanoTest.pas';
 | |
| my $asmFileSpec = $ARGV[1] || 'nanoTest.asm';
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # System constants
 | |
| #
 | |
| my $mainProgram = 'mainProgram';
 | |
| my $wordHexCharNb = 4;
 | |
| my $firstRegister = 2;  # reserve 2 registers for internal calculations
 | |
| my $functionReturnRegister = 's0';
 | |
| my $conditionRegister = 's1';
 | |
| my $memoryAccessRegister = 's1';
 | |
| my $partialOperationRegister = 's1';
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Derived values
 | |
| #
 | |
|                                                                     # file specs
 | |
| my $baseFileSpec = $pascalFileSpec;
 | |
| $baseFileSpec =~ s/\..*//i;
 | |
| my $temp1FileSpec = "$baseFileSpec.tmp1";
 | |
| my $temp2FileSpec = "$baseFileSpec.tmp2"; 
 | |
| my $registersFileSpec = "${baseFileSpec}_registers.txt";  # register assignments
 | |
| my $asm1FileSpec = "$baseFileSpec.asm1";
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Assembler file formatting constants
 | |
| #
 | |
| my $asmFirstIndent = ' ' x 24;
 | |
| my $asmLineLength = 80;
 | |
| my $commentStart = $asmFirstIndent . ';';
 | |
| my $separator1 = fillString($commentStart, '=', $asmLineLength);
 | |
| my $separator2 = fillString($commentStart, '-', $asmLineLength);
 | |
| my $opcodeLength = 10;
 | |
| my $firstArgumentLength = 6;
 | |
| my $constantMaxLength = 8;
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # System variables
 | |
| #
 | |
| my $currentPass = 0;
 | |
| my %constants = ();
 | |
| my %variables = ();
 | |
| my %registers = ();
 | |
| my @routines = ();
 | |
| 
 | |
| ################################################################################
 | |
| # Functions
 | |
| #
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Swap temporary filespecs from one pass to the other
 | |
| #
 | |
| sub swapTempFileSpecs {
 | |
|   my ($inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec) = @_;
 | |
|                                                           # swap to tmp1 -> tmp2
 | |
|   if ($outputFileSpec eq $temp1FileSpec) {
 | |
|     $inputFileSpec  = $temp1FileSpec;
 | |
|     $outputFileSpec = $temp2FileSpec;
 | |
|   }
 | |
|                                                           # swap to tmp2 -> tmp2
 | |
|   else {
 | |
|     $inputFileSpec  = $temp2FileSpec;
 | |
|     $outputFileSpec = $temp1FileSpec;
 | |
|   }
 | |
| 
 | |
|   return ($inputFileSpec, $outputFileSpec);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Fill string to a fixed length with a given character
 | |
| #
 | |
| sub fillString {
 | |
|   my ($string, $character, $length) = @_;
 | |
|                                                                    # fill string
 | |
|   $string .= $character x ($length - length($string));
 | |
| 
 | |
|   return ($string);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Assign registers to all variables
 | |
| #
 | |
| sub buildConstants {
 | |
|   my ($mainProgram, %constants) = @_;
 | |
|                                                               # loop on routines
 | |
|   foreach my $subroutine (keys(%constants)) {
 | |
| #print "$subroutine:\n";
 | |
|                                                                    # build array
 | |
|     $constants{$subroutine} =~ s/\s*\;\Z//;
 | |
|     $constants{$subroutine} =~ s/\s*=\s*/=/g;
 | |
|     my @procedureConstants = split(/\;/, $constants{$subroutine});
 | |
|                                                                     # build hash
 | |
|     my %procedureConstants;
 | |
|     for my $index (0 .. $#procedureConstants) {
 | |
|       my ($name, $value) = split(/\=/, $procedureConstants[$index]);
 | |
| #print "$name: $value\n";
 | |
|       $value =~ s/\$([0-9A-Fa-f]+)/0x$1/g;
 | |
|       foreach my $alreadyDeclared (keys(%procedureConstants)) {
 | |
|         $value =~ s/$alreadyDeclared/($procedureConstants{$alreadyDeclared})/g;
 | |
|       }
 | |
|       $value = eval($value);
 | |
|       $procedureConstants{$name} = $value;
 | |
| #print "  $name = $procedureConstants{$name}\n";
 | |
|     }
 | |
|     $constants{$subroutine} = \%procedureConstants;
 | |
|   }
 | |
|                                                         # convert to hexadecimal
 | |
|   foreach my $subroutine (keys(%constants)) {
 | |
|     my $replacement_ref = $constants{$subroutine};
 | |
|     foreach my $name (keys(%$replacement_ref)) {
 | |
|       my $value = $$replacement_ref{$name};
 | |
|       $value = '$' . sprintf('%X', $value);
 | |
|       $$replacement_ref{$name} = $value;
 | |
|     }
 | |
|   }
 | |
|   foreach my $subroutine (keys(%constants)) {
 | |
|       $line =~ s/$name/$$replacement_ref{$name}/g;
 | |
|   }
 | |
| 
 | |
|   return (%constants);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Assign registers to variables within a routine
 | |
| #
 | |
| sub assignRegistersToRoutine {
 | |
|   my ($startIndex, $variables) = @_;
 | |
|                                                                    # build array
 | |
|   $variables =~ s/\;\Z//;
 | |
|   my @variables = split(/\;/, $variables);
 | |
|                                                              # loop on variables
 | |
|   my $registerIndex = $startIndex;
 | |
|   for my $index (0 .. $#variables) {
 | |
|     $variables[$index] =~ s/word/s$registerIndex/;
 | |
|     $variables[$index] =~ s/uint8/s$registerIndex/;
 | |
|     $registerIndex = $registerIndex + 1;
 | |
| #print "  $variables[$index]\n";
 | |
|   }
 | |
|                                               # assign registers to main program
 | |
| 
 | |
|   return ($registerIndex-1, join(';', @variables));
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Assign registers to all variables
 | |
| #
 | |
| sub assignRegisters {
 | |
|   my ($mainProgram, $firstRegister, %variables) = @_;
 | |
|                                                               # loop on routines
 | |
|   my $registerMaxNb = 0;
 | |
|   foreach my $subroutine (keys(%variables)) {
 | |
|                                                                  # remove spaces
 | |
|       $variables{$subroutine} =~ s/\s*\:\s*/:/g;
 | |
|       $variables{$subroutine} =~ s/\s*\,\s*/,/g;
 | |
|                                                    # distribute type definitions
 | |
|       my $type;
 | |
|       do {
 | |
|         $variables{$subroutine} =~ s/\,(.*?)\:(.*?)\;/:$2;$1:$2;/;
 | |
|         $type = $2;
 | |
|       } while ($type ne '');
 | |
|                                 # assign registers to routine internal variables
 | |
|     if ($subroutine ne $mainProgram) {
 | |
| #print "$subroutine:\n";
 | |
|       my ($registerNb, $routineVariables) = assignRegistersToRoutine(
 | |
|         $firstRegister,
 | |
|         $variables{$subroutine}
 | |
|       );
 | |
|       if ($registerNb > $registerMaxNb) {
 | |
|         $registerMaxNb = $registerNb;
 | |
|       }
 | |
|       $variables{$subroutine} = $routineVariables;
 | |
| #print "    $variables{$subroutine}\n";
 | |
|     }
 | |
|   }
 | |
|                                               # assign registers to main program
 | |
| #print "$mainProgram:\n";
 | |
|   my ($registerNb, $routineVariables) = assignRegistersToRoutine(
 | |
|     $registerMaxNb + 1,
 | |
|     $variables{$mainProgram}
 | |
|   );
 | |
|   $variables{$mainProgram} = $routineVariables;
 | |
| #print "    $variables{$mainProgram}\n";
 | |
|                                                           # build hash of hashes
 | |
|   foreach my $subroutine (keys(%variables)) {
 | |
|     my @registers = split(/\;/, $variables{$subroutine});
 | |
|     my %assignedRegisters;
 | |
|     foreach my $variable (@registers) {
 | |
|       my ($var, $register) = split(/\:/, $variable);
 | |
|       $assignedRegisters{$var} = $register;
 | |
|     }
 | |
|     $variables{$subroutine} = \%assignedRegisters;
 | |
|   }
 | |
|   return (%variables);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Translate Pascal second operand to assembler source operand if possible
 | |
| #
 | |
| sub translateArgument {
 | |
|   my ($pascalOperand, $wordHexCharNb) = @_;
 | |
|   my $assemblerOperand = '';
 | |
|                                                                       # register
 | |
|   if ($pascalOperand =~ m/\As(\d+)\Z/) {
 | |
|     $assemblerOperand = "s$1";
 | |
|   }
 | |
|                                                       # decimal numeric constant
 | |
|   elsif ($pascalOperand =~ m/\A(\d+)\Z/) {
 | |
|     $assemblerOperand = sprintf("%0${wordHexCharNb}X", $pascalOperand);
 | |
|   }
 | |
|                                                   # hexadecimal numeric constant
 | |
|   elsif ($pascalOperand =~ m/\A\$([0-9A-Fa-f]+)\Z/) {
 | |
|     $assemblerOperand = sprintf("%0${wordHexCharNb}X", hex($1));
 | |
|   }
 | |
|                                                              # declared constant
 | |
|   else {
 | |
|     foreach my $routine (keys(%constants)) {
 | |
|       my $constants_ref = $constants{$routine};
 | |
|       foreach my $constant (sort(keys(%$constants_ref))) {
 | |
|         if ($pascalOperand eq $constant) {
 | |
|           $assemblerOperand = $pascalOperand;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return ($assemblerOperand);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Format assignment in ters of space characters
 | |
| #
 | |
| sub formatAssignment {
 | |
|   my ($assignment) = @_;
 | |
|                                                                # unary operators
 | |
|   $assignment =~ s/\A\s*\-\s*/0 - /g;
 | |
|                                                          # arithmetic operations
 | |
|   $assignment =~ s/\s*\+\s*/ + /g;
 | |
|   $assignment =~ s/\s*\-\s*/ - /g;
 | |
|   $assignment =~ s/\s*\*\s*/ * /g;
 | |
|   $assignment =~ s/\s*\/\s*/ \/ /g;
 | |
|                                                               # logic operations
 | |
|   $assignment =~ s/\s+and\s+/ and /ig;
 | |
|   $assignment =~ s/\s+or\s+/ or /ig;
 | |
|   $assignment =~ s/\s+xor\s+/ xor /ig;
 | |
|   $assignment =~ s/\s+shl\s+/ shl /ig;
 | |
|   $assignment =~ s/\s+shr\s+/ shr /ig;
 | |
|                                                                    # parenthesis
 | |
|   $assignment =~ s/\(\s+/(/g;
 | |
|   $assignment =~ s/\s+\)/)/g;
 | |
|   $assignment =~ s/\s*\[\s+/[/g;
 | |
|   $assignment =~ s/\s+\]/]/g;
 | |
| 
 | |
|   return ($assignment);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Extract first argument of an assignment
 | |
| #
 | |
| sub extractFirsrtArgument {
 | |
|   my ($assignment) = @_;
 | |
|   my $operator = '';
 | |
|   my $restOfAssignment = '';
 | |
|                                                        # starts with parenthesis
 | |
|   if ($assignment =~ m/\A\(/) {
 | |
|     my $index = 0;
 | |
|     my $level = 0;
 | |
|     my @characters = split(//, $assignment);
 | |
|     foreach my $character (@characters) {
 | |
|       if ($character eq '(') {$level = $level+1};
 | |
|       if ($character eq '[') {$level = $level+1};
 | |
|       if ($character eq ']') {$level = $level-1};
 | |
|       if ($character eq ')') {$level = $level-1};
 | |
|       if ($level == 0) {
 | |
|         last;
 | |
|       }
 | |
|       $index = $index+1;
 | |
|     }
 | |
|     $firstArgument = substr($assignment, 0, $index+1);
 | |
|     $restOfAssignment = substr($assignment, $index+1);
 | |
|   }
 | |
|                                                            # to first whitespace
 | |
|   else {
 | |
|     my $index = 0;
 | |
|     my $level = 0;
 | |
|     my @characters = split(//, $assignment);
 | |
|     foreach my $character (@characters) {
 | |
|       if ($character eq '(') {$level = $level+1};
 | |
|       if ($character eq '[') {$level = $level+1};
 | |
|       if ($character eq ']') {$level = $level-1};
 | |
|       if ($character eq ')') {$level = $level-1};
 | |
|       if ( ($character eq ' ') and ($level == 0) ) {
 | |
|         last;
 | |
|       }
 | |
|       $index = $index+1;
 | |
|     }
 | |
|     $firstArgument = substr($assignment, 0, $index);
 | |
|     $restOfAssignment = substr($assignment, $index);
 | |
|   }
 | |
|   $restOfAssignment =~ s/\A //;
 | |
| #print "|$firstArgument|$restOfAssignment|\n";
 | |
|                                                               # extract operator
 | |
|   if ($restOfAssignment ne '') {
 | |
|     ($operator, $restOfAssignment) = split(/ /, $restOfAssignment, 2);
 | |
|   }
 | |
| 
 | |
|   return ($firstArgument, $operator, $restOfAssignment);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Build expression out of argument and operator list
 | |
| #
 | |
| sub buildExpression {
 | |
|   my ($arguments_ref, $operators_ref) = @_;
 | |
|   my @arguments = @$arguments_ref;
 | |
|   my @operators = @$operators_ref;
 | |
|                                                          # loop on list elements
 | |
|   my $expression = $arguments[0];
 | |
| #print "0: $expression\n";
 | |
|   for my $index (1 .. $#arguments) {
 | |
| #print "$index: $operators[$index] $arguments[$index]\n";
 | |
|     $expression .= " $operators[$index] $arguments[$index]";
 | |
|   }
 | |
|   return ($expression);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Expand operation to 2 lines
 | |
| #
 | |
| sub expandTwo {
 | |
|   my ($destinationRegister, $arguments_ref, $operators_ref) = @_;
 | |
|   my @arguments = @$arguments_ref;
 | |
|   my @operators = @$operators_ref;
 | |
|   my $line = '';
 | |
| #print "    -> ";
 | |
| #for my $index (0..scalar(@arguments)-1) { print "$operators[$index]    $arguments[$index]    ";}
 | |
| #print "\n";
 | |
|                                                      # last argument is constant
 | |
|   my $lastArgument = $arguments[$#arguments];
 | |
|   my $lastOperator = $operators[$#operators];
 | |
|   my $isDeclaredConstant = 0;
 | |
|   foreach my $routine (keys(%constants)) {
 | |
|     my $constants_ref = $constants{$routine};
 | |
|     foreach my $constant (sort(keys(%$constants_ref))) {
 | |
|       if ($lastArgument eq $constant) {
 | |
|         $isDeclaredConstant = 1;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (
 | |
|     ($lastArgument =~ m/\A\d+\Z/) or
 | |
|     ($lastArgument =~ m/\A\$[0-9A-Fa-f]+\Z/) or
 | |
|     ($isDeclaredConstant)
 | |
|   ) {
 | |
|     pop(@arguments);
 | |
|     pop(@operators);
 | |
|     my $firstArgument = buildExpression(\@arguments, \@operators);
 | |
|     if ($destinationRegister ne $firstArgument) {
 | |
|       $line = "$destinationRegister := $firstArgument;\n";
 | |
|     }
 | |
|     $line .= "$destinationRegister := $destinationRegister $lastOperator $lastArgument;";
 | |
| #print "$line\n";
 | |
|   }
 | |
|                                                       # last argument is compund
 | |
|   elsif ($lastArgument =~ m/\A\((.+)\)\Z/)  {
 | |
|     my $firstArgument = $1;
 | |
|     pop(@arguments);
 | |
|     pop(@operators);
 | |
|     $lastArgument = buildExpression(\@arguments, \@operators);
 | |
|     if ($firstArgument =~ m/ $destinationRegister /) {
 | |
|       $line = "$partialOperationRegister := $firstArgument;\n";
 | |
|       $line .= "$destinationRegister := $destinationRegister $lastOperator $partialOperationRegister;";
 | |
|     }
 | |
|     else {
 | |
|       $line = "$destinationRegister := $firstArgument;\n";
 | |
|       $line .= "$destinationRegister := $destinationRegister $lastOperator $lastArgument;";
 | |
|     }
 | |
| #print "$line\n";
 | |
|   }
 | |
| 
 | |
|   return ($line);
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Expand assignment to multiple lines
 | |
| #
 | |
| sub expandAssignment {
 | |
|   my ($destinationRegister, $assignment) = @_;
 | |
|                                                # format assignment for treatment
 | |
|   $assignment = formatAssignment($assignment);
 | |
|                                                                 # default result
 | |
|   my $line = "$destinationRegister := $assignment;";
 | |
| #print "\n$line\n";
 | |
|                             # don't modify simple assignments and function calls
 | |
|   my $source = translateArgument($assignment, $wordHexCharNb);
 | |
|   my @arguments = ();
 | |
|   my @operators = ('');
 | |
|   if ( ($source eq '') and ($assignment !~ m/\Acall\s/) ){
 | |
|                                                             # analyse assignment
 | |
| #print "\n  $destinationRegister := $assignment\n";
 | |
|     my $done = 0;
 | |
|     do {
 | |
|       my ($firstArgument, $operator, $restOfAssignment) = extractFirsrtArgument($assignment);
 | |
| #print "    $firstArgument    $operator    $restOfAssignment\n";
 | |
|       if ($operator eq '') {
 | |
|         push(@arguments, $firstArgument);
 | |
|         $done = 1;
 | |
|       }
 | |
|       else {
 | |
|         push(@arguments, $firstArgument);
 | |
|         push(@operators, $operator);
 | |
|         $assignment = $restOfAssignment;
 | |
|       }
 | |
|     } until $done == 1;
 | |
| #print "  -> " . join(', ', @arguments) . "\n";
 | |
|                                                              # expand to 2 lines
 | |
|     my $newLine = expandTwo($destinationRegister, \@arguments, \@operators);
 | |
|                                                               # modify code line
 | |
|     if ($newLine ne '') {
 | |
|       $line = $newLine;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return ($line);
 | |
| }
 | |
| 
 | |
| ################################################################################
 | |
| # Program start
 | |
| #
 | |
| 
 | |
| #-------------------------------------------------------------------------------
 | |
| # Display information
 | |
| #
 | |
| if ($verbose > 0) {
 | |
|   print "$separator\n";
 | |
|   print "Compiling $pascalFileSpec to $asmFileSpec\n";
 | |
| }
 | |
| 
 | |
| # ==============================================================================
 | |
| # Rewrite file for easier parsing
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| my $inputFileSpec = $pascalFileSpec;
 | |
| my $outputFileSpec = $temp1FileSpec;
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: placing line ends\n";
 | |
| }
 | |
|                                                                # read input file
 | |
| my $singleLine = '';
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                             # remove leading and trailing spaces
 | |
|   $line =~ s/\A\s+//;
 | |
|   $line =~ s/\s+\Z//;
 | |
|                                                    # remove single line comments
 | |
|   $line =~ s/\{.*?\}//g;
 | |
|                                                    # write to single line string
 | |
|   if ($line ne '') {
 | |
|     $singleLine .= "$line ";
 | |
|   }
 | |
| }
 | |
| close(inputFile);
 | |
|                                                            # remove extra spaces
 | |
| $singleLine =~ s/\s+/ /g;
 | |
| $singleLine =~ s/\s\Z//;
 | |
|                                           # split constructs into multiple lines
 | |
| $singleLine =~ s/\s*;\s*/;\n/g;
 | |
| $singleLine =~ s/\sconst\s/\nconst\n/g;
 | |
| $singleLine =~ s/\svar\s/\nvar\n/g;
 | |
| $singleLine =~ s/\sprocedure\s+/\nprocedure /g;
 | |
| $singleLine =~ s/\sfunction\s+/\nfunction /g;
 | |
| $singleLine =~ s/\sbegin\s/\nbegin\n/g;
 | |
| $singleLine =~ s/\send\s*;\s/\nend;\n/g;
 | |
| $singleLine =~ s/\selse\s/\nelse\n/g;
 | |
| $singleLine =~ s/\srepeat\s/\nrepeat\n/g;
 | |
| $singleLine =~ s/\sif\s/\nif /g;
 | |
| $singleLine =~ s/\suntil\s/\nuntil /g;
 | |
| $singleLine =~ s/\sfor\s/\nfor /g;
 | |
| $singleLine =~ s/\swhile\s/\nwhile /g;
 | |
| $singleLine =~ s/\sthen\s/ then\n/g;
 | |
| $singleLine =~ s/\sdo\s/ do\n/g;
 | |
|                                         # take away new lines within parenthesis
 | |
| my $parameters;
 | |
| do {
 | |
|   $singleLine =~ s/\(([^\)]*?)\n([^\)]*?)\)/($1 $2)/m;
 | |
|   $parameters = $2;
 | |
| #if ($parameters ne '') { print "--> $1 $parameters\n"; }
 | |
| } while ($parameters ne '');
 | |
|                                            # add begin/end to single-line blocks
 | |
| my $singleLineBlock;
 | |
| do {
 | |
|   $singleLine =~ s/\nif (.*?) then\n(?!begin)(.*?)\;/\nif $1 then\nbegin\n$2;\nend;/;
 | |
|   $singleLineBlock = $2;
 | |
| #print "if $1 then begin $2; end;\n";
 | |
| } while ($singleLineBlock ne '');
 | |
| do {
 | |
|   $singleLine =~ s/\nfor (.*?) do\n(?!begin)(.*?)\;/\nfor $1 do\nbegin\n$2;\nend;/;
 | |
|   $singleLineBlock = $2;
 | |
| } while ($singleLineBlock ne '');
 | |
| 
 | |
|                                                        # remove comments, part 1
 | |
| $singleLine =~ s/\s*\{\s*/\n{/g;
 | |
| $singleLine =~ s/\s*\}\s*/}\n/g;
 | |
|                                                           # write to output file
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| print(outputFile "$singleLine\n");
 | |
| close(outputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Finish removing comments
 | |
| # 
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: removing comments\n";
 | |
| }
 | |
|                                                                # read input file
 | |
| my $commentOut = 0;
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
|                                                        # remove comments, part 2
 | |
|   if ($line =~ m/\{/) { $commentOut = 1; }
 | |
|   if ( ($commentOut == 0) and ($line ne '') ) {
 | |
|     print(outputFile "$line\n");
 | |
|   }
 | |
|   if ($line =~ m/\}/) { $commentOut = 0; }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Get constants and variables, indent code
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: finding constants and variables\n";
 | |
| }
 | |
| my $currentLevel = 0;
 | |
| my $currentRoutine;
 | |
| my $startOfProgramDeclatation = '';
 | |
| my $isStartOfProgramDeclatation = 1;
 | |
| my $isConstantsDeclatation = 0;
 | |
| my $isVariablesDeclatation = 0;
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                                              # find program name
 | |
|   if ($line =~ m/\A\s*program\s+(.*)\s*;/i) {
 | |
|     $mainProgram = $1;
 | |
| #print "Program name is |$mainProgram|\n";
 | |
|     $currentRoutine = $mainProgram;
 | |
|     @routines = ($currentRoutine);
 | |
|   }
 | |
|                                                     # find current function name
 | |
|   if ($line =~ m/\A(procedure|function)(\s|\Z)/i) {
 | |
|     $currentRoutine = $line;
 | |
|     $currentRoutine =~ s/\Aprocedure//i;
 | |
|     $currentRoutine =~ s/\Afunction//i;
 | |
|     $currentRoutine =~ s/\A\s+//;
 | |
|     $currentRoutine =~ s/;.*//;
 | |
|     $currentRoutine =~ s/\s*:.*//;
 | |
|     $currentRoutine =~ s/\(.*//;
 | |
|     push(@routines, $currentRoutine);
 | |
|     print(outputFile "\n");
 | |
| #print "$currentRoutine\n";
 | |
|     $isStartOfProgramDeclatation = 0;
 | |
|     $isVariablesDeclatation = 0;
 | |
|     $isConstantsDeclatation = 0;
 | |
|   }
 | |
|                                                           # find begin/end level
 | |
|   if ($line eq 'begin') {
 | |
|     $currentLevel = $currentLevel + 1;
 | |
| #print "-> $currentLevel\n";
 | |
|     if ( ($currentLevel == 1) and ($currentRoutine eq $mainProgram) ) {
 | |
| #print "$currentRoutine\n";
 | |
|       $isStartOfProgramDeclatation = 0;
 | |
|       print(outputFile "\n$startOfProgramDeclatation");
 | |
|     }
 | |
|     $isVariablesDeclatation = 0;
 | |
|     $isConstantsDeclatation = 0;
 | |
|   }
 | |
|   if ($line eq 'end;') {
 | |
|     $currentLevel = $currentLevel - 1;
 | |
| #print "-> $currentLevel\n";
 | |
|     if ($currentLevel == 0) {
 | |
|       $currentRoutine = $mainProgram;
 | |
|     }
 | |
|   }
 | |
|                                                                 # find constants
 | |
|   if ($isConstantsDeclatation) {
 | |
|     if ($line ne 'var') {
 | |
| #print "-> $line\n";
 | |
|       $constants{$currentRoutine} .= $line;
 | |
|     }
 | |
|   }
 | |
|   if ($line eq 'const') {
 | |
|     $isConstantsDeclatation = 1;
 | |
|     $isVariablesDeclatation = 0;
 | |
|   }
 | |
|                                                                 # find variables
 | |
|   if ($isVariablesDeclatation) {
 | |
| #print "-> $line\n";
 | |
|     $variables{$currentRoutine} .= $line;
 | |
|   }
 | |
|   if ($line eq 'var') {
 | |
|     $isVariablesDeclatation = 1;
 | |
|     $isConstantsDeclatation = 0;
 | |
|   }
 | |
|                                                         # determine indent level
 | |
|   my $indentLevel = $currentLevel;
 | |
|   if ($line eq 'begin') { $indentLevel = $indentLevel - 1; }
 | |
|   if ($isConstantsDeclatation) { $indentLevel = $indentLevel + 2; }
 | |
|   if ($isVariablesDeclatation) { $indentLevel = $indentLevel + 2; }
 | |
|   if ($line eq 'const') { $indentLevel = $indentLevel - 1; }
 | |
|   if ($line eq 'var') { $indentLevel = $indentLevel - 1; }
 | |
|   if ($line eq 'end.') { $indentLevel = $indentLevel - 1; }
 | |
|                                                           # write to output file
 | |
|   my $indentedLine = ($indent x $indentLevel) . $line;
 | |
|   $indentedLine = sprintf('%2d: ', $indentLevel) . $indentedLine;
 | |
|   if ($isStartOfProgramDeclatation == 0) {
 | |
|     print(outputFile "$indentedLine\n");
 | |
|   } else {
 | |
|     $startOfProgramDeclatation .= "$indentedLine\n";
 | |
|   }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| # ------------------------------------------------------------------------------
 | |
| #  Process constant declarations
 | |
| # 
 | |
| %constants = buildConstants($mainProgram, %constants);
 | |
| 
 | |
| # ------------------------------------------------------------------------------
 | |
| #  Assign registers to variables
 | |
| # 
 | |
| if ($verbose > 0) {
 | |
|   print $indent x 2 . "Writing registers assignments in \"$registersFileSpec\"\n";
 | |
| }
 | |
| %variables = assignRegisters($mainProgram, $firstRegister, %variables);
 | |
| open(registersFile, ">$registersFileSpec") or die "Unable to open file, $!";
 | |
| foreach my $subroutine (keys(%variables)) {
 | |
|   print(registersFile "$subroutine\n");
 | |
|   my $registers_ref = $variables{$subroutine};
 | |
|   my %registers = reverse(%$registers_ref);
 | |
|   foreach my $register (sort(keys(%registers))) {
 | |
|     print(registersFile "${indent}$register:$registers{$register}\n");
 | |
|   }
 | |
| }
 | |
| close(registersFile);
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Process constants and variables and functions
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: replacing constants and variables\n";
 | |
| }
 | |
|                                              # build main program variables hash
 | |
| my $variables_ref = $variables{$mainProgram};
 | |
| %mainProgramVariables = %$variables_ref;
 | |
|                                                           # loop on program code
 | |
| my $currentRoutine;
 | |
| my %localVariables;
 | |
| my $printLine = 1;
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
|                                           # write constants at beginning of file
 | |
| foreach my $routine (keys(%constants)) {
 | |
|   my $replacement_ref = $constants{$routine};
 | |
|   foreach my $constant (sort(keys(%$replacement_ref))) {
 | |
|     print(outputFile "const $constant = $$replacement_ref{$constant};\n");
 | |
|   }
 | |
| }
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                               # strip line nb and leading spaces
 | |
|   my $strippedLine = $line;
 | |
|   $strippedLine =~ s/\A\s*\d*\:*\s*//;
 | |
| #print "$strippedLine\n";
 | |
|                                                     # find current function name
 | |
|   if (
 | |
|     ($strippedLine =~ m/\Aprocedure /i) or
 | |
|     ($strippedLine =~ m/\Afunction /i) or
 | |
|     ($strippedLine =~ m/\Aprogram /i)
 | |
|   ) {
 | |
|     $currentRoutine = $strippedLine;
 | |
|     $currentRoutine =~ s/\A\S+\s+//;
 | |
|     $currentRoutine =~ s/\(.*//;
 | |
|     $currentRoutine =~ s/\;//;
 | |
| #print "$currentRoutine\n";
 | |
|                                                   # build current variables hash
 | |
|     my $variables_ref = $variables{$currentRoutine};
 | |
|     %localVariables = %$variables_ref;
 | |
|   }
 | |
|                      # cut out constant and variable declarations of the program
 | |
|   if ($strippedLine =~ m/\Aconst\Z/) {
 | |
|     $printLine = 0;
 | |
|   }
 | |
|   if ($strippedLine =~ m/\Avar\Z/) {
 | |
|     $printLine = 0;
 | |
|   }
 | |
|   if ($strippedLine eq 'begin') {
 | |
|     $printLine = 1;
 | |
|   }
 | |
|                                                              # replace variables
 | |
|   foreach my $variable (keys(%localVariables)) {
 | |
|     $line =~ s/$variable/$localVariables{$variable}/g;
 | |
|   }
 | |
|   foreach my $variable (keys(%mainProgramVariables)) {
 | |
|     $line =~ s/$variable/$mainProgramVariables{$variable}/g;
 | |
|   }
 | |
|                                                                   # write output
 | |
|   if ($printLine) {
 | |
|     print(outputFile "$line\n");
 | |
|   }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Label subroutines and loops
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: labelling subroutines and loops\n";
 | |
| }
 | |
|                                                           # loop on program code
 | |
| my $previousWasElse = 0;
 | |
| my $labelcount = 0;
 | |
| my $blockKind;
 | |
| my @labels;
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                                              # get current level
 | |
|   my $currentLevel = $line;
 | |
|   $currentLevel =~ s/\A\s*(\d*)\:.*/$1/;
 | |
|                                           # strip level depth and leading spaces
 | |
|   $line =~ s/\A\s*\d*\:\s*//;
 | |
| #print "$line\n";
 | |
|                                                      # remove "begin" statements
 | |
|   $line =~ s/\Abegin\Z//;
 | |
|                                              # assign labels to block statements
 | |
|   if ($line =~ m/\Aif /) {
 | |
|     if (not $previousWasElse) {
 | |
|       $labelcount = $labelcount + 1;
 | |
|       $labels[$currentLevel] = sprintf('if%02d', $labelcount);
 | |
|     }
 | |
|     $line = $labels[$currentLevel] . ': ' . $line;
 | |
|   }
 | |
|   if ($line =~ m/\Afor /) {
 | |
|     $labelcount = $labelcount + 1;
 | |
|     $labels[$currentLevel] = sprintf('for%02d', $labelcount);
 | |
|     $line = $labels[$currentLevel] . ': ' . $line;
 | |
|   }
 | |
|   if ($line =~ m/\Awhile /) {
 | |
|     $labelcount = $labelcount + 1;
 | |
|     $labels[$currentLevel] = sprintf('while%02d', $labelcount);
 | |
|     $line = $labels[$currentLevel] . ': ' . $line;
 | |
|   }
 | |
|                                       # assign labels to end of block statements
 | |
|   if ($line =~ m/\Aend\s*[;\.]/) {
 | |
|     if ($currentLevel == 0) {
 | |
|       $line = 'return;';
 | |
|     } else {
 | |
|       $line = 'end ' . $labels[$currentLevel] . ';';
 | |
|     }
 | |
|   }
 | |
|                                                        # specify procedure calls
 | |
|   for my $routine (@routines) {
 | |
|     $line =~ s/$routine\s*\:\=\s*/$functionReturnRegister := /g;
 | |
|     $line =~ s/$routine([ \(\;])/call $routine$1/g;
 | |
|   }
 | |
|   $line =~ s/\A(program|procedure|function) call /$1 /g;
 | |
|                                                 # store "previous line was else"
 | |
|   $previousWasElse = 0;
 | |
|   if ($line =~ m/\Aelse\Z/) {
 | |
|     $previousWasElse = 1;
 | |
|   }
 | |
|                                                                   # write output
 | |
|   if ($line ne '') {
 | |
|     if ($line =~ m/(program|procedure|function) /) {
 | |
|       print(outputFile "\n");
 | |
|     }
 | |
|     my $indentedLine = ($indent x $currentLevel) . $line;
 | |
|     $indentedLine = sprintf('%2d: ', $currentLevel) . $indentedLine;
 | |
|     print(outputFile "$indentedLine\n");
 | |
|   }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Break compound operations
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: breaking compound operations\n";
 | |
| }
 | |
|                                                           # loop on program code
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                                              # get current level
 | |
|   my $currentLevel = $line;
 | |
|   $currentLevel =~ s/\A\s*(\d*)\:.*/$1/;
 | |
|                                           # strip level depth and leading spaces
 | |
|   $line =~ s/\A\s*\d*\:\s*//;
 | |
| #print "$line\n";
 | |
|                                                              # check assignments
 | |
|   if ($line =~ m/s(\d+)\s*\:\=\s*(.+)\s*\;/) {
 | |
|     my $destinationRegister = "s$1";
 | |
|     my $assignment = $2;
 | |
|     $line = expandAssignment($destinationRegister, $assignment);
 | |
|   }
 | |
|                                                                   # write output
 | |
|   if ($line ne '') {
 | |
|     my $indentedLine = ($indent x $currentLevel) . $line;
 | |
|     $indentedLine = sprintf('%2d: ', $currentLevel) . $indentedLine;
 | |
|     print(outputFile "$indentedLine\n");
 | |
|   }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Assembler file: constants, subroutines, memory access
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: writing assembler for constants, subroutines, mem and nop\n";
 | |
| }
 | |
| foreach my $routine (keys(%constants)) {
 | |
|   my $constants_ref = $constants{$routine};
 | |
|   foreach my $constant (sort(keys(%$constants_ref))) {
 | |
|     my $length = length($constant);
 | |
|     if ($length > $constantMaxLength) { $constantMaxLength = $length; }
 | |
|   }
 | |
| }
 | |
|                                                          # assembler code header
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| print(outputFile "$separator1\n");
 | |
| print(outputFile "$commentStart $mainProgram\n");
 | |
| print(outputFile "$separator1\n");
 | |
| print(outputFile "\n");
 | |
|                                                           # loop on program code
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                           # strip level depth and leading spaces
 | |
|   $line =~ s/\A\s*\d*\:\s*//;
 | |
| #print "$line\n";
 | |
|                                                              # replace constants
 | |
|   if ($line =~ m/\Aconst (.*?)\s*\=\s*\$(.*?)\;/) {
 | |
|     my $constantName = fillString("$1,", ' ', $constantMaxLength+1);
 | |
|     my $constantValue = sprintf("%0${wordHexCharNb}X", hex($2));
 | |
|     $line = "$asmFirstIndent CONSTANT  $constantName $constantValue";
 | |
|   }
 | |
|                                                      # replace subroutines start
 | |
|   if ($line =~ m/\A(program|procedure|function) (.*?)\s*[\;\(]/) {
 | |
|     my $routineKind = $1;
 | |
|     my $routineName = $2;
 | |
|     print(outputFile "\n");
 | |
|     print(outputFile "$separator2\n");
 | |
|     print(outputFile "$commentStart $routineKind $routineName\n");
 | |
|     print(outputFile "$separator2\n");
 | |
|     print(outputFile ' ' x (length($asmFirstIndent) - length($routineName) - 2) . "$routineName: NOP\n");
 | |
|     $line = '';
 | |
|   }
 | |
|                                                     # replace subroutines return
 | |
|   $line =~ s/\Areturn\;/${asmFirstIndent}RETURN/;
 | |
|                                        # replace subroutine calls with arguments
 | |
|   if ($line =~ m/call (.*?)\s*\((.*?)\s*\)\s*\;/) {
 | |
|     my $routineName = $1;
 | |
|     my $routineArguments = $2;
 | |
|     $routineArguments =~ s/var //g;
 | |
|     $routineArguments =~ s/\;/,/g;
 | |
|     $argumentText = 'argument';
 | |
|     if($routineArguments =~ m/\,/) {
 | |
|       $argumentText .= 's';
 | |
|     }
 | |
|     print(
 | |
|       outputFile
 | |
|       "$asmFirstIndent"
 | |
|         . fillString('CALL', ' ', $opcodeLength)
 | |
|         . "$routineName  ; $argumentText: $routineArguments\n"
 | |
|     );
 | |
|     if ($line =~ m/\A(.*?)\s*\:\=\s* call/) {
 | |
|       $returnRegister = $1;
 | |
|       $line = "$returnRegister := s0;\n";
 | |
|     } else {
 | |
|       $line = '';
 | |
|     }
 | |
|   }
 | |
|                                     # replace subroutine calls without arguments
 | |
|   if ($line =~ m/\Acall (.*?)\;/) {
 | |
|     $line = $asmFirstIndent . fillString('CALL', ' ', $opcodeLength) . $1;
 | |
|   }
 | |
|                                                                   # memory write
 | |
|   if ($line =~ m/mem\[(.+?)\]\s*\:\=\s*(.+)\s*\;/) {
 | |
|     my $opcode = fillString('OUTPUT', ' ', $opcodeLength);
 | |
|     $line = "$memoryAccessRegister := $1;";
 | |
|     $line .= "\n${asmFirstIndent}${opcode}$2, ($memoryAccessRegister)";
 | |
|   }
 | |
|                                                                    # memory read
 | |
|   if ($line =~ m/\s*(.+)\s*\:\=\s*mem\[(.+?)\]\s*\;/) {
 | |
|     my $opcode = fillString('INPUT', ' ', $opcodeLength);
 | |
|     $line = "$memoryAccessRegister := $2;";
 | |
|     $line .= "\n${asmFirstIndent}${opcode}$memoryAccessRegister, ($memoryAccessRegister)" x 2;
 | |
|     $line .= "\n$1 := $memoryAccessRegister;";
 | |
|   }
 | |
|                                                                            # NOP
 | |
|   if ($line =~ m/\s*noOperation\s*\;/) {
 | |
|     $line = "${asmFirstIndent}NOP";
 | |
|   }
 | |
|                                                                   # write output
 | |
|   if ($line ne '') {
 | |
|     print(outputFile "$line\n");
 | |
|   }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| # ==============================================================================
 | |
| #  Assembler file: register transfers
 | |
| #
 | |
| $currentPass = $currentPass + 1;
 | |
| ($inputFileSpec, $outputFileSpec) = swapTempFileSpecs(
 | |
|   $inputFileSpec, $outputFileSpec, $temp1FileSpec, $temp2FileSpec
 | |
| );
 | |
| if ($verbose > 0) {
 | |
|   print "${indent}Pass $currentPass: writing assembler for load, add, and sub\n";
 | |
| }
 | |
|                                                           # loop on program code
 | |
| open(inputFile, "<$inputFileSpec") or die "Unable to open file, $!";
 | |
| open(outputFile, ">$outputFileSpec") or die "Unable to open file, $!";
 | |
| while(my $line = <inputFile>) {
 | |
|   chomp($line);
 | |
| #print "$line\n";
 | |
|                                                                           # LOAD
 | |
|   if ($line =~ m/s(\d+)\s*\:\=\s*(.+)\s*\;/) {
 | |
|     my $destinationRegister = fillString("s$1,", ' ', $firstArgumentLength);
 | |
|     my $source = translateArgument($2, $wordHexCharNb);
 | |
|     my $opcode = fillString('LOAD', ' ', $opcodeLength);
 | |
|     if ($source ne '') {
 | |
|       $line = "${asmFirstIndent}${opcode}${destinationRegister}$source";
 | |
|     }
 | |
|   }
 | |
|                                                                            # ADD
 | |
|   if ($line =~ m/s(\d+)\s*\:\=\s*s(\d+)\s*\+\s*(.+)\s*\;/) {
 | |
|     if ($1 eq $2) {
 | |
|       my $destinationRegister = fillString("s$1,", ' ', $firstArgumentLength);
 | |
|       my $source = translateArgument($3, $wordHexCharNb);
 | |
|       my $opcode = fillString('ADD', ' ', $opcodeLength);
 | |
|       if ($source ne '') {
 | |
|         $line = "${asmFirstIndent}${opcode}${destinationRegister}$source";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|                                                                            # SUB
 | |
|   if ($line =~ m/s(\d+)\s*\:\=\s*s(\d+)\s*\-\s*(.+)\s*\;/) {
 | |
|     if ($1 eq $2) {
 | |
|       my $destinationRegister = fillString("s$1,", ' ', $firstArgumentLength);
 | |
|       my $source = translateArgument($3, $wordHexCharNb);
 | |
|       my $opcode = fillString('SUB', ' ', $opcodeLength);
 | |
|       if ($source ne '') {
 | |
|         $line = "${asmFirstIndent}${opcode}${destinationRegister}$source";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|                                                                            # AND
 | |
|   if ($line =~ m/s(\d+)\s*\:\=\s*s(\d+)\s*and\s*(.+)\s*\;/i) {
 | |
|     if ($1 eq $2) {
 | |
|       my $destinationRegister = fillString("s$1,", ' ', $firstArgumentLength);
 | |
|       my $source = translateArgument($3, $wordHexCharNb);
 | |
|       my $opcode = fillString('AND', ' ', $opcodeLength);
 | |
|       if ($source ne '') {
 | |
|         $line = "${asmFirstIndent}${opcode}${destinationRegister}$source";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|                                                                             # OR
 | |
|   if ($line =~ m/s(\d+)\s*\:\=\s*s(\d+)\s*or\s*(.+)\s*\;/i) {
 | |
|     if ($1 eq $2) {
 | |
|       my $destinationRegister = fillString("s$1,", ' ', $firstArgumentLength);
 | |
|       my $source = translateArgument($3, $wordHexCharNb);
 | |
|       my $opcode = fillString('OR', ' ', $opcodeLength);
 | |
|       if ($source ne '') {
 | |
|         $line = "${asmFirstIndent}${opcode}${destinationRegister}$source";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|                                                                   # write output
 | |
|   if ($line ne '') {
 | |
|     print(outputFile "$line\n");
 | |
|   }
 | |
| }
 | |
| close(outputFile);
 | |
| close(inputFile);
 | |
|                                                         # keep intermediate file
 | |
| if ($keepIntermediateFiles) {
 | |
|   my $textfile = "$baseFileSpec$currentPass.txt";
 | |
|   use File::Copy;
 | |
|   unlink($textfile);
 | |
|   copy($outputFileSpec, $textfile) or die "File cannot be copied.";
 | |
| }
 | |
| 
 | |
| ################################################################################
 | |
| # Documentation (access it with: perldoc <scriptname>)
 | |
| #
 | |
| __END__
 | |
| 
 | |
| =head1 NAME
 | |
| 
 | |
| nanoPascal.pl - Transforms a Pascal program into assembler code
 | |
| 
 | |
| =head1 SYNOPSIS
 | |
| 
 | |
| nanoPascal.pl [options]
 | |
| 
 | |
| =head1 DESCRIPTION
 | |
| 
 | |
| This is a simple parser which translates Pascal expressions into their assembler
 | |
| code equivalents for the nanoBlaze processor.
 | |
| The process doesn't optimize the code.
 | |
| The expressions which couldn't be translated into assembler are left as Pascal
 | |
| for the user to translate manually.
 | |
| 
 | |
| =head1 OPTIONS
 | |
| 
 | |
| =over 8
 | |
| 
 | |
| =item B<-h>
 | |
| 
 | |
| Display a help message.
 | |
| 
 | |
| =item B<-v>
 | |
| 
 | |
| Be verbose.
 | |
| 
 | |
| =item B<-k>
 | |
| 
 | |
| Makes a copy of the intermediate files between the passes.
 | |
| 
 | |
| =item B<-c>
 | |
| 
 | |
| Cleans the temporary work files at the end of the process.
 | |
| 
 | |
| Specify a username in the bridge's whitelist.
 | |
| 
 | |
| =back
 | |
| 
 | |
| =head1 Limitations
 | |
| 
 | |
| There is currently no Pascal syntax error detection.
 | |
| 
 | |
| The script doesn't distinguish between constants having the same name within
 | |
| different procedures or functions.
 | |
| This can be corrected in future versions.
 | |
| 
 | |
| Procedure and function calls basically don't support passing parameters.
 | |
| This would require a stack mechanism.
 | |
| The only possible way to pass parameters is to declare global variables
 | |
| and use these as parameters for the procedure and function calls.
 | |
| 
 | |
| The Pascal C<if ... then> construct is either followed by a C<begin ... end>
 | |
| block or a single expression.
 | |
| The script only handles single-line expressions.
 | |
| Other more complex expressions (like a nested c<if .. then> need a
 | |
| C<begin ... end> structure.
 | |
| 
 | |
| =head1 AUTHOR
 | |
| 
 | |
| Francois Corthay, HEVs
 | |
| 
 | |
| =head1 VERSION
 | |
| 
 | |
| 1.1, 2014
 | |
| 
 | |
| =cut
 |