Browse Source

Initial commit

ApisNecros 4 months ago
commit
473e4f4d6e
2 changed files with 155 additions and 0 deletions
  1. 5 0
      tests/simple.icasm
  2. 150 0
      yaict.pl

+ 5 - 0
tests/simple.icasm

@@ -0,0 +1,5 @@
+ld 0d9, 10
+ld 0d10, 37
+add 0d9, 0d10, 0d2
+add 0d2, 3, 0d12
+hlt

+ 150 - 0
yaict.pl

@@ -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";