|
@@ -0,0 +1,150 @@
|
|
|
+#!/bin/perl
|
|
|
+
|
|
|
+use warnings;
|
|
|
+use strict;
|
|
|
+use Data::Dumper;
|
|
|
+
|
|
|
+my $VERSION = 0.01;
|
|
|
+
|
|
|
+# The array of intcodes making up the program
|
|
|
+my @program;
|
|
|
+# Opcodes that may be encounted and their corresponding ints
|
|
|
+my %opcodes = (
|
|
|
+ "add" => "01",
|
|
|
+ "mult" => "02",
|
|
|
+ "in" => "03",
|
|
|
+ "out" => "04",
|
|
|
+ "jpt" => "05",
|
|
|
+ "jpf" => "06",
|
|
|
+ "lt" => "07",
|
|
|
+ "eq" => "08",
|
|
|
+ "mrb" => "09",
|
|
|
+ "hlt" => "99",
|
|
|
+);
|
|
|
+# Additional instructions that don't correspond to opcodes but need to be processed
|
|
|
+# my %instructions = (
|
|
|
+# # Load
|
|
|
+# "ld" => \&load,
|
|
|
+# );
|
|
|
+
|
|
|
+# Load a value into a memory address
|
|
|
+sub load
|
|
|
+{
|
|
|
+ my $loadAddress = shift;
|
|
|
+ my $valueToLoad = shift;
|
|
|
+
|
|
|
+ $program[$loadAddress] = $valueToLoad;
|
|
|
+}
|
|
|
+
|
|
|
+# Parse the assembly to IntCode
|
|
|
+sub parseAssembly
|
|
|
+{
|
|
|
+
|
|
|
+ my @assembly = @_;
|
|
|
+ # A hash of labels and their corresponding starting line number
|
|
|
+ my %labels;
|
|
|
+ # The current instruction number
|
|
|
+ my $instructionNumber = 0;
|
|
|
+
|
|
|
+ foreach my $line (@assembly)
|
|
|
+ {
|
|
|
+ $line =~ /^(\w+?:?)(?: (.+))?$/;
|
|
|
+ my $asmInstruction = $1;
|
|
|
+ my @parameters = split(/,\s?/, $2 || "");
|
|
|
+
|
|
|
+ # Store labels by the address of their next instruction
|
|
|
+ if($asmInstruction =~ /:$/)
|
|
|
+ {
|
|
|
+ $labels{substr($asmInstruction, 0, -1)} = ++$instructionNumber;
|
|
|
+ next;
|
|
|
+ }
|
|
|
+
|
|
|
+ if($asmInstruction eq "ld")
|
|
|
+ {
|
|
|
+ my $storeAt = getAddress(shift @parameters);
|
|
|
+ my $value = shift @parameters;
|
|
|
+ load($storeAt, $value);
|
|
|
+ }
|
|
|
+ elsif($opcodes{$asmInstruction})
|
|
|
+ {
|
|
|
+ parseOpcodeAndParams($opcodes{$asmInstruction}, \@parameters);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ die "Failed to find matching ASM instruction for: $asmInstruction";
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+# TODO
|
|
|
+sub parseOpcodeAndParams
|
|
|
+{
|
|
|
+ my $opcode = $_[0];
|
|
|
+ my @parameters = @{ $_[1] };
|
|
|
+ my $operand1 = $parameters[0];
|
|
|
+ if(isImmediate($parameters[0]))
|
|
|
+ {
|
|
|
+ $opcode = length($opcode) == 1 ? "10$opcode" : "1$opcode";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ $operand1 = getAddress($parameters[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ my $operand2 = $parameters[1];
|
|
|
+ if(isImmediate($parameters[1]))
|
|
|
+ {
|
|
|
+ $opcode = length($opcode) < 3 ? "100$opcode" : "1$opcode";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ $operand2 = getAddress($parameters[1])
|
|
|
+ }
|
|
|
+
|
|
|
+ my $storeAt = getAddress($parameters[2]);
|
|
|
+
|
|
|
+ $program[$instructionNumber++] = $opcode;
|
|
|
+ $program[$instructionNumber++] = $operand1;
|
|
|
+ $program[$instructionNumber++] = $operand2;
|
|
|
+ $program[$instructionNumber++] = $storeAt;
|
|
|
+}
|
|
|
+
|
|
|
+# Check if a parameter is Immediate Mode or Position
|
|
|
+#
|
|
|
+# Parameter is considered Position mode if it's prefixed with "0d"
|
|
|
+sub isImmediate
|
|
|
+{
|
|
|
+ return !(shift =~ /^0d/);
|
|
|
+}
|
|
|
+
|
|
|
+sub getAddress
|
|
|
+{
|
|
|
+ return substr(shift, 2);
|
|
|
+}
|
|
|
+
|
|
|
+sub readFile
|
|
|
+{
|
|
|
+ my @assembly;
|
|
|
+ my $filePath = shift;
|
|
|
+ open(my $fh, "<", $filePath) or die "Failed to open file";
|
|
|
+
|
|
|
+ while(my $line = <$fh>)
|
|
|
+ {
|
|
|
+ chomp $line;
|
|
|
+ # Remove comments from the line
|
|
|
+ $line =~ s/\/\/.+$//;
|
|
|
+ # Remove trailing whitespace
|
|
|
+ $line =~ s/\s+$//;
|
|
|
+ if ($line ne "") {
|
|
|
+ push(@assembly, $line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return @assembly;
|
|
|
+}
|
|
|
+
|
|
|
+parseAssembly(readFile($ARGV[0]));
|
|
|
+
|
|
|
+print "[".join(",", @program)."]";
|
|
|
+
|
|
|
+print "\n";
|