#!/home/rebelsky/perl/bin/perl

# A simple attempt to interpret printed versions of linearized
# intermediate representation trees.  See the documentation
# on program format at
# http://www.math.grin.edu/~rebelsky/Courses/CS362/98F/Assignments/project.html
#
# Version
#   0.3 of 16 December 1998
#
# History:
#   25 November 1998 
#     First working version.  Lacks many built-in functions and
#     binary operators.  Not fully tested.  Also needs ways to
#     turn on tracing and printing.
#   12 December 1998
#     Updated BUILTIN so that it can accept L(funcname), rather
#       than just funcname.
#     Fixed the JUMP instruction so that it does indexed jumps
#       (hmmm ... guess I'll need to play with that a little).
#     Changed some capitalization
#     Added the evil DEBUG operation for testing.
#     Did some work on the CALL implementation (but it still
#       doesn't work)
#   16 December 1998
#     Added more builtin functions.
#     Fixed a bug with the initial status of the heap pointer
#       revealed by the new builtin functions.


############################################################
# Globals #
###########

# Are we testing this interpreter?  1 for true, 0 for false.
local($TESTING) = 0;

# Are we tracing execution?  1 for true, 0 for false
local($TRACE) = 0;

# Where is the end of the code?
local($ENDCODE) = 0;

# The registers/temporaries
local($REGS) = ();
$#REGS = 100;	# Can be expanded, but a good starting point.

# The top of memory (in bytes?)
local($TOPMEM) = 32767,

# Memory (program plus data), in words.  Note that we'll assume that each
# character takes one word (ugh).
local(@MEMORY) = ();
$#MEMORY = $TOPMEM;

# The locations of the labels
local(%LABELS) = ();

# A map from register names to register numbers
%SPECREGS = (
  "ZERO", 0,
  "PC", 1,
  "SP", 2,
  "FP", 3,
  "HP", 4,
  "ACC",5,
  "RV", 6,
  "RA", 7,
  "A0", 10,
  "A1", 11,
  "A2", 12,
  "A3", 13,
  "A4", 14,
  "A5", 15,
  "A6", 16,
  "A7", 17,
  "A8", 18,
  "A9", 19,
);

# What value should the program exit with?  Zero typically means ``no
# errors''.
$EXITVAL = 0;

# Is the program active?
$ACTIVE = 0;


############################################################
# Main #
########

main();
exit $EXITVAL;

sub main {
  my($arg);		# One of the arguments from the command line
  my($prog) = "";	# The name of the program to execute
  my($print) = 0;	# Should we print the program when we're done?

  # Parse the command line
  foreach $arg (@ARGV) {
    # Should we trace?
    if ($arg eq "-t") {	
      print STDERR "Tracing on.\n";
      $TRACE = 1;
    }
    # Should we debug?
    elsif ($arg eq "-d") {
      print STDERR "Testing on.\n";
      $TESTING = 1;
    }
    # Should we print?
    elsif ($arg eq "-p") {
      print STDERR "Printing on.\n";
      $print = 1;
    }
    elsif (($arg eq "-help") || ($arg eq "?")) {
      usage();
    }
    else {
      if ($prog) {
        print STDERR "Attempt to specify multiple input files.\n";
        usage();
      }   
      else {
        $prog = $arg;
      }
    }
  } # foreach

  if (!$prog) {
    print STDERR "No input file specified.\n";
    usage();
  }

  print STDERR "Executing '$prog'\n";

  if (!-f $prog) {
    print STDERR "Cannot find '$prog'\n";
    exit 2;
  }
    
  readProgram($prog);
  runProgram();
  if ($print) { printProgram(); }
  return 0;
}

# Give usage instructions
sub usage {
  print STDERR "Usage: irt.pl [-p] [-t] file.irt\n";
  print STDERR "  -p prints the program at the end.\n";
  print STDERR "  -t traces the instructions.n";
  exit 1;
}


############################################################
# Routines #
############

# CRASHANDBURN(message)
#   Guess.
sub CRASHANDBURN {
 print STDERR "BOOM!  $_[0]\n";
 $EXITVAL = 1;
 $ACTIVE = 0;
} # CRASHANDBURN 

# allocString(size)
#   Allocate a string of the appropriate size
sub allocString {
  my($size) = $_[0];
  # Get the original heap pointer
  my($base) = $REGS[$SPECREGS{"HP"}];
  # Update the heap pointer.
  $REGS[$SPECREGS{"HP"}] += 1 + $size;
  # Verify the heap pointer
  if ($REGS[$SPECREGS{"HP"}] > $REGS[$SPECREGS{"SP"}]) {
    CRASHANDBURN("Out of memory in string allocation.");
    return;
  }
  # Fill in the size
  $MEMORY[$base] = $size;
  # Return the address
  return $base;
} # allocString

# doTest(test, arg1, arg2)
#   Test whether arg1 and arg2 have the appropriate relationship.
sub doTest {
  OBSERVE("Testing whether $_[1] $_[0] $_[2]");
  my($result) = false;
  if ($_[0] eq "LT") {
    $result = 0+$_[1] < 0+$_[2];
  }
  elsif ($_[0] eq "GT") {
    $result = 0+$_[1] > 0+$_[2];
  }
  elsif ($_[0] eq "LE") {
    $result = 0+$_[1] <= 0+$_[2];
  }
  elsif ($_[0] eq "GE") {
    $result = 0+$_[1] >= 0+$_[2];
  }
  elsif ($_[0] eq "EQ") {
    $result = 0+$_[1] == 0+$_[2];
  }
  elsif ($_[0] eq "NE") {
    $result = 0+$_[1] != 0+$_[2];
  }
  else {
    CRASHANDBURN("Invalid conditional operator '$_[0]'");
  }
  OBSERVE("And the answer is: $result");
  return $result;
} # doTest

# execute(instruction)
#   Execute an instruction
sub execute {
  my($instruction) = $_[0];
  my(@info) = split(/ /, $instruction);
  shift(@info);
  if ($instruction =~ m/^BINOP/) {
    executeBINOP(@info);
  }
  elsif ($instruction =~ m/^BUILTIN/) {
    executeBUILTIN(@info);
  }
  elsif ($instruction =~ m/^CALL/) {
    executeCALL(@info);
  }
  elsif ($instruction =~ m/^CJUMP/) {
    executeCJUMP(@info);
  }
  elsif ($instruction =~ m/^DEBUG/) {
    print STDERR "$info[0]\n";
  }
  elsif ($instruction =~ m/^END/) {
    $ACTIVE = 0;
  }
  elsif ($instruction =~ m/^JUMP/) {
    executeJUMP(@info);
  }
  elsif ($instruction =~ m/^LABEL/) {
    # Do nothing
  }
  elsif ($instruction =~ m/^MOVE/) {
    executeMOVE($info[0], $info[1]);
  }
  elsif ($instruction =~ m/^NOOP/) {
    # Do nothing
  }
  elsif ($instruction =~ m/^RETURN/) {
    executeRETURN();
  }
  else {
    CRASHANDBURN("Unknown instruction: '$instruction'\n");
  }
} # execute

# getLength(address)
#   Get the length of the string starting at a particular location.
sub getLength {
  return $MEMORY[$_[0]];
} # getLength

# getString(address)
#   Get the string starting at the given address
sub getString {
  my($add) = $_[0];
  my($len) = $MEMORY[$add];
  my($str) = "";
  my($i);
  if (!isNumber($len)) {
    CRASHANDBURN("Invalid string access at memory location '$add'");
    return $str;
  }
  for($i = 1; $i <= $len; ++$i) {
    $str .= $MEMORY[$add + $i];
  }
  return $str;
} # getString

# getValue(mem)
#   Get a value given in one of the many formats
sub getValue {
  my($info) = $_[0];

  # Is it a register?
  if ($info =~ m/^R\(/) {
    $info =~ s/^R\(//;
    $info =~ s/\)$//;
    # Make sure that it's a number
    if (!isNumber($info)) {
      CRASHANDBURN("Invalid attempt to access register '$info'");
      return 0;
    }
    
    # Make sure there are sufficiently many registers in use
    else {
      if ($info > $#REGS) { $#REGS = $info; }
      return $REGS[$info];
    }
  } # it's a register

  # Is it one of the designated registers?
  elsif ($info =~ m/^PC/) { return $REGS[$SPECREGS{"PC"}]; }
  elsif ($info =~ m/^SP/) { return $REGS[$SPECREGS{"SP"}]; }
  elsif ($info =~ m/^FP/) { return $REGS[$SPECREGS{"FP"}]; }
  elsif ($info =~ m/^HP/) { return $REGS[$SPECREGS{"HP"}]; }
  elsif ($info =~ m/^ACC/) { return $REGS[$SPECREGS{"ACC"}]; }
  elsif ($info =~ m/^RV/) { return $REGS[$SPECREGS{"RV"}]; }
  elsif ($info =~ m/^RA/) { return $REGS[$SPECREGS{"RA"}]; }
  elsif ($info =~ m/^A0/) { return $REGS[$SPECREGS{"A0"}]; }
  elsif ($info =~ m/^A1/) { return $REGS[$SPECREGS{"A1"}]; }
  elsif ($info =~ m/^A2/) { return $REGS[$SPECREGS{"A2"}]; }
  elsif ($info =~ m/^A3/) { return $REGS[$SPECREGS{"A3"}]; }
  elsif ($info =~ m/^A4/) { return $REGS[$SPECREGS{"A4"}]; }
  elsif ($info =~ m/^A5/) { return $REGS[$SPECREGS{"A5"}]; }
  elsif ($info =~ m/^A6/) { return $REGS[$SPECREGS{"A6"}]; }
  elsif ($info =~ m/^A7/) { return $REGS[$SPECREGS{"A7"}]; }
  elsif ($info =~ m/^A8/) { return $REGS[$SPECREGS{"A8"}]; }
  elsif ($info =~ m/^A9/) { return $REGS[$SPECREGS{"A9"}]; }

  # Is it a label?
  elsif ($info =~ m/^L/) {
    $info =~ s/^L\(//;
    $info =~ s/\)$//;
    return $LABELS{$info};
  } # it's a label

  # Is it a memory location?
  elsif ($info =~ m/^M/) {
    $info =~ s/^M\(//;
    $info =~ s/\)//;
    my($address) = getValue($info);
    if (($address < 0) || ($address > $TOPMEM)) {
      CRASHANDBURN("Attempt to access invalid location '$address'\n");
      return 0;
    }
    else {
      return $MEMORY[$address];
    }
  }

  # Is it a number?
  elsif (isNumber($info)) {
    return $info;
  }

  # Is it anything else?
  else {
    CRASHANDBURN("Invalid parameter: '$info'");
    return 0;
  }
} # getValue

# isNumber(str)
#   Does the string represent a number
sub isNumber {
  return $_[0] =~ m/^[0-9]*$/;
}

# printProgram()
#   Prints the program in a readable format (not in official
#   format).
sub printProgram {
  my($lines) = $ENDCODE;
  my($line);
  my($label);

  print "\nLABELS: \n";
  foreach $label (sort(keys(%LABELS))) {
    printf "  %s: %08d\n", $label, $LABELS{$label};
  } # for each label
  print "\n";

  print "CODE: \n";
  for($line = 0; $line < $lines; ++$line) {
    printf "  %08d: %s\n", $line, $MEMORY[$line]
  } # for each line
} # printProgram

# readProgram(program-name) 
#   Read in the program, filling in the appropriate globals.
sub readProgram {
  my($prog) = $_[0];	# The name of the program
  my($str);
  my(@str);

  open(PROGRAM, "< $prog");
  while (<PROGRAM>) {
    chop($_);
    OBSERVE("Read '$_'");
    s/^[ \t]*//;
    # Is it a comment?  If so, move on.
    if (m/^#/) {
      next;
    }
    # Is it a label?  If so, add it to the list of labels and move on
    elsif (m/^LABEL/) {
      my($lab) = $_;
      $lab =~ s/^LABEL[ \t]*//;
      $LABELS{$lab} = $ENDCODE;
      # $MEMORY[$ENDCODE++] = $_;
    }
    # Is it a string?  If so, store it appropriately?
    elsif (m/^STRING/) {
      $str = $_;
      $str =~ s/^STRING *//;
      $str =~ s/\\n/\n/g;
      @str = split(//,$str);
      $MEMORY[$ENDCODE++] = (1 + $#str);
      # OBSERVE("The length of the string is " . (1+$#str));
      for ($i = 0; $i <= $#str; ++$i) {
        # OBSERVE("Adding character $i: " . $str[$i]);
        $MEMORY[$ENDCODE++] = $str[$i];
      }
      # OBSERVE("Done with string");
    } # if it's a string
    # Anything else just gets added to the program
    else {
      $MEMORY[$ENDCODE++] = $_;
    }
  } # while there are lines left in the program
  close(PROGRAM);
} # readProgram

# runProgram()
#   Run the current program.
sub runProgram {
  # Variables
  my($instruction);	# The current instruction

  # Set the program counter, heap pointer, and stack pointer
  $REGS[$SPECREGS{"PC"}] = 0;
  $REGS[$SPECREGS{"HP"}] = $ENDCODE;
  $REGS[$SPECREGS{"SP"}] = $TOPMEM;

  # Note that the program is still active
  $ACTIVE = 1;

  # Keep running until the program is no longer active.
  while ($ACTIVE) {
    $instruction = $MEMORY[$REGS[$SPECREGS{"PC"}]];
    if ($TRACE) {
      printf "\t%08d: %s\n", $REGS[$SPECREGS{"PC"}], $instruction;
    }
    $REGS[$SPECREGS{"PC"}] += 1;
    execute($instruction);
  } # while
} # runProgram

# storeString(base, string)
#   Store a string in program memory.
sub storeString {
  my($base) = $_[0];
  my($str) = $_[1];
  my($i);
  for ($i = 0; $i < length($str); ++$i) {
    $MEMORY[$base+1+$i] = substr $str, $i, 1;
  } # for
} # storeString

# OBSERVE(str)
#   Print an observation if debugging is on
sub OBSERVE {
  if ($TESTING) {
    print STDERR "  ### $_[0] ###\n";
  }
} # OBSERVE


############################################################
# Instructions #
################

# executeBINOP(op,arg1,arg2)
#   Execute a binary operation on two arguments, storing the result
#   in a register.  Note that the bitwise operators are not yet
#   implemented (and are not likely to be implemented in the near
#   future).
sub executeBINOP {
  my($op,$arg1,$arg2) = @_;
  my($val);
  if ($op eq "PLUS") { $val = getValue($arg1) + getValue($arg2); }
  elsif ($op eq "MINUS") { $val = getValue($arg1) - getValue($arg2); }
  elsif ($op eq "MUL") { $val = getValue($arg1) * getValue($arg2); }
  elsif ($op eq "DIV") { $val = getValue($arg1) / getValue($arg2); }
  elsif ($op eq "AND") { $val = getValue($arg1) && getValue($arg2); }
  else {
    CRASHANDBURN("Unknown operator: '$op'");
    $val = 0;
  }
  $REGS[$SPECREGS{"ACC"}] = $val;
} # executeBINOP

# executeBUILTIN(name,args)
#   Call a built-in function, given a set of arguments.
sub executeBUILTIN {
  my($fun) = $_[0];
  $fun =~ s/L\((.*)\)/$1/;
  # Non-function.  Included primarily for ease of writing the
  # remaining stuff
  if ($fun eq "") {
    print STDERR "Called unnamed builtin function.  Skipping.\n";
  }

# allocRecord(size)
#   Allocate a record.  Initialize all fields to 0.
  elsif ($fun eq "allocRecord") {
    # Get the argument
    my($size) = getValue($_[1]);
    # Get the original heap pointer
    my($base) = $REGS[$SPECREGS{"HP"}];
    # Update the heap pointer.
    $REGS[$SPECREGS{"HP"}] += 1 + $size;
    # Verify the heap pointer
    if ($REGS[$SPECREGS{"HP"}] > $REGS[$SPECREGS{"SP"}]) {
      CRASHANDBURN("Out of memory in record allocation.");
      return;
    }
    # Do the initialization
    my($i);
    for($i = 0; $i < $size; ++$i) {
      $MEMORY[$base+$i] = 0;
    } # for
    # And set up the return value
    $REGS[$SPECREGS{"RV"}] = $base;
  } # allocRecord

# chr(int charnum)
#   Compute the letter corresponding to a particular ASCII
#   value
  elsif ($fun eq "chr") {
    my($ascii) = getValue($_[1]);
    # Verify the ascii value
    if (($ascii < 0) || ($ascii > 255)) {
      CRASHANDBURN("Invalid character number: $ascii");
      return;
    }
    my($newstr) = allocString(1);
    storeString($newstr, chr($ascii));
    $REGS[$SPECREGS{"RV"}] = $newstr;
  }

# concat(str1,str2)
#   Concatenate two strings, creating a new string in the process.
  elsif ($fun eq "concat") {
    my($str1) = getValue($_[1]);
    my($str2) = getValue($_[2]);
    my($newstr) = allocString(getLength($str1) + getLength($str2));
    storeString($newstr, getString($str1) . getString($str2));
    $REGS[$SPECREGS{"RV"}] = $newstr;
  } # concat

# exit(exitval)
#   Exit the program with a specified exit code.  Note that 0
#   is supposed to be "successfully completed."
  elsif ($fun eq "exit") {
    exit(getValue($_[1]));
  } # exit

# flush()
#   Not supported
  elsif ($fun eq "flush") {
    print STDERR "flush is not supported.\n";
  } # flush

# getchar()
#   Read one character.  Ugh.
  elsif ($fun eq "getchar") {
    my($newstr) = allocString(1);
    storeString($newstr, getc);
    $REGS[$SPECREGS{"RV"}] = $newstr;
  } # getchar

# initArray(size,init)
#   Allocate and initialize an array of integers.
#   Note that arrays are stored with the size in the initial
#     word.
  elsif ($fun eq "initArray") {
    # Get the arguments
    my($size) = getValue($_[1]);
    my($init) = getValue($_[2]);
    # Get the original heap pointer
    my($base) = $REGS[$SPECREGS{"HP"}];
    # Update the heap pointer.
    $REGS[$SPECREGS{"HP"}] += 1 + $size;
    # Verify the heap pointer
    if ($REGS[$SPECREGS{"HP"}] > $REGS[$SPECREGS{"SP"}]) {
      CRASHANDBURN("Out of memory at array allocation.");
      return;
    }
    # Fill in the size
    $MEMORY[$base] = $size;
    # Do the initialization
    my($i);
    for($i = 1; $i <= $size; ++$i) {
      $MEMORY[$base+$i] = $init;
    } # for
    # And set up the return value
    $REGS[$SPECREGS{"RV"}] = $base;
  } # initArray

# not(val)
#   Guess
  elsif ($fun eq "not") {
    if (getValue($_[0]) == 0) {
      $REGS[$SPECREGS{"RV"}] = 1;
    }
    else {
      $REGS[$SPECREGS{"RV"}] = 0;
    }
  } # not

# ord(string)
#   Get the ordinal value of the first character of the string.
  elsif ($fun eq "ord") {
    $REGS[$SPECREGS{"RV"}] = ord(getString(getValue($_[1])));
  } # ord

# print(string)
#   Print a string.  Takes the address of a string as a parameter.
  elsif ($fun eq "print") {
    print getString(getValue($_[1]));
  } # print

# println(string)
#   Print a string followed by a carriage return.  Takes the address of a 
#   string as a parameter.
  elsif ($fun eq "println") {
    print getString(getValue($_[1])) . "\n";
  } # println

# printFrame(size)
#   Special function to print the current frame.  Takes the size
#   of the frame as a parameter.
  elsif ($fun eq "printFrame") {
    my($fp) = getValue("FP");
    my($size) = getValue($_[1]);
    my($i);
    my($address);
    print "FP: $fp\n";
    print "SP: " . getValue("SP") . "\n";
    for ($i = 0; $i < $size; ++$i) {
      $address = $fp - $i;
      print "M($address): " . getValue("M($address)") . "\n";
    }
  } # printFrame

# printInt(val)
#   Print an integer
  elsif ($fun eq "printInt") {
    print "" . getValue($_[1]);
  }

# printlnInt(val)
#    Print an integer followed by a carriage return
  elsif ($fun eq "printlnInt") {
    print getValue($_[1]) . "\n";
  }

# size(string)
#    Get the size of a string.
  elsif ($fun eq "size") {
    my($add) = getValue($_[1]);
    my($len) = $MEMORY[$add];
    if (!isNumber($len)) {
      CRASHANDBURN("Attempt to get the length of a non-string");
    }
    else {
      $REGS[$SPECREGS{"RV"}] = $len;
    }
  } # size

# stringEqual(str1,str2)
#   Compare two strings for equality.
  elsif ($fun eq "stringEqual") {
    my($str1) = getString(getValue($_[1]));
    my($str2) = getString(getValue($_[2]));
    if ($str1 eq $str2) {
      $REGS[$SPECREGS{"RV"}] = 1;
    }
    else {
      $REGS[$SPECREGS{"RV"}] = 0;
    }
  } # stringEqual

# stringLess(str1,str2)
#   Determine if the first string is less than the second.
  elsif ($fun eq "stringLess") {
    my($str1) = getString(getValue($_[1]));
    my($str2) = getString(getValue($_[2]));
    if ($str1 lt $str2) {
      $REGS[$SPECREGS{"RV"}] = 1;
    }
    else {
      $REGS[$SPECREGS{"RV"}] = 0;
    }
  } # stringLess

# stringLessEqual(str1,str2)
#   Determine if the first string is less than or equal to the
#   second.
  elsif ($fun eq "stringLessEqual") {
    my($str1) = getString(getValue($_[1]));
    my($str2) = getString(getValue($_[2]));
    if ($str1 le $str2) {
      $REGS[$SPECREGS{"RV"}] = 1;
    }
    else {
      $REGS[$SPECREGS{"RV"}] = 0;
    }
  } # stringLessEqual

# stringGreat(str1,str2)
#   Determine if the first string is greater than the second.
  elsif ($fun eq "stringGreat") {
    my($str1) = getString(getValue($_[1]));
    my($str2) = getString(getValue($_[2]));
    if ($str1 gt $str2) {
      $REGS[$SPECREGS{"RV"}] = 1;
    }
    else {
      $REGS[$SPECREGS{"RV"}] = 0;
    }
  } # stringGreat

# stringGreatEqual(str1,str2)
#   Determine if the first string is greater than or equal to 
#   the second.
  elsif ($fun eq "stringGreatEqual") {
    my($str1) = getString(getValue($_[1]));
    my($str2) = getString(getValue($_[2]));
    if ($str1 ge $str2) {
      $REGS[$SPECREGS{"RV"}] = 1;
    }
    else {
      $REGS[$SPECREGS{"RV"}] = 0;
    }
  } # stringGreatEqual

# substring(str,first,n)
#   Generate a substring of a string.
  elsif ($fun eq "substring") {
    # Get parameters
    my($str) = getValue($_[1]);
    my($first) = getValue($_[2]);
    my($n) = getValue($_[3]);
    # Get the original string
    my($orig) = getString($str);
    # Sanity check.  Is it a reasonable request?
    if (($first < 0) || ($first + $n > getLength($str))) {
      CRASHANDBURN("Substring of '$orig' out of range: starts at $first, length $n");
      return;
    }
    # Build the new string
    my($newstr) = allocString($n);
    storeString($newstr, substr $orig,$first,$n);
    $REGS[$SPECREGS{"RV"}] = $newstr;
  } # substring
    
# Everything else
  else {
    print STDERR "Don't understand call to built-in function '$fun'.  Skipping.\n";
  }
} # executeBUILTIN

# executeCALL(fun,arg0,arg1,...,argn)
#   Call a function.  Not yet tested.
sub executeCALL {
  my($i);
  my($arg);

  # Verify the number of arguments
  if ($#_ > 9) {
    CRASHANDBURN("Attempt to call a function with more than ten arguments");
    return;
  }

  # Store the return address.  Note that we've already incremented the
  # program counter, so we want to use the new program counter.
  $REGS[$SPECREGS{"RA"}] = $REGS[$SPECREGS{"PC"}];
  # Copy the parameters
  for ($i = 1; $i <= $#_; ++$i) {
    $arg = "A" . ($i-1);
    $REGS[$SPECREGS{$arg}] = getValue($_[$i]);
  } # for
  # Branch to the code for the function (set it as the next instruction)
  OBSERVE("Using return address of " . $REGS[$SPECREGS{"RA"}]);
  $REGS[$SPECREGS{"PC"}] = getValue($_[0]);
} # executeCALL

# executeCJUMP(test, arg1, arg2, label)
#   Conditionally jump to a location in the program.
sub executeCJUMP {
  if (doTest($_[0], getValue($_[1]), getValue($_[2]))) {
    $REGS[$SPECREGS{"PC"}] = getValue($_[3]);
  }
} # executeCJUMP

# executeJUMP(index,label1,label2,...label1)
#   Jump to a location in the program.  Checks the value of the
#     jump index and then jumps appropriately.
#   According to Appel's specifications, if there is only one index,
#     it should be the same as the label and we should jump to the
#     label.
sub executeJUMP {
  # Special case
  if ($#_ == 1) {
    $REGS[$SPECREGS{"PC"}] = getValue($_[1]);
  }
  # Normal case
  else {
    my($jumper) = getValue($_[0]);
    if (($jumper < 1) || ($jumper > $#_)) {
      CRASHANDBURN("Attempt to jump to invalid label (index $jumper of $#_).");
      return;
    }
    $REGS[$SPECREGS{"PC"}] = getValue($_[$jumper]);
  }
} # executeJUMP

# executeMOVE(dest,source)
#   Copy a word from the source to the destination
sub executeMOVE {
  my($val) = getValue($_[1]);
  my($dest) = $_[0];
  my($regnum);
  my($address);
  # Do we want to move into a register?
  if ($dest =~ m/^R\(/) {
    $regnum = $dest;
    $regnum =~ s/^R\(//;
    $regnum =~ s/\)//;
    if (!isNumber($regnum)) {
      CRASHANDBURN("Attempt to move a value into invalid register: '$dest'");
      return;
    }
    # Make sure there are sufficiently many registers
    if ($info > $#REGS) { $#REGS = $info; }
    $REGS[$regnum] = $val;
    return;
  } # Is it a numbered register?

  # Is it a special register? 
  $regnum = $SPECREGS{$dest};
  if ($regnum != 0) {
    $REGS[$regnum] = $val;
    return;
  }

  # Is it a location in memory?
  if ($dest =~ m/^M/) {
    $dest =~ s/^M\(//;
    $dest =~ s/\)$//;
    $address = getValue($dest);
    if (($address < 0) || ($address > $TOPMEM)) {
      CRASHANDBURN("Attempt to access invalid location '$address'\n");
      return;
    }
    else {
      $MEMORY[$address] = $val;
      return;
    }
  } # is it a location in memory?
  
  # Default: none of the above
  CRASHANDBURN("Attempt to MOVE data to invalid destination: '$dest'");
} # executeMOVE

# executeRETURN()
#   Return from a function.  Not yet tested.
sub executeRETURN {
  $REGS[$SPECREGS{"PC"}] = $REGS[$SPECREGS{"RA"}];
} # executeRETURN


