|
@@ -1,175 +0,0 @@
|
|
-#!/bin/perl
|
|
|
|
-
|
|
|
|
-use warnings;
|
|
|
|
-use strict;
|
|
|
|
-use Data::Dumper;
|
|
|
|
-
|
|
|
|
-my $VERSION = 0.01;
|
|
|
|
-
|
|
|
|
-# The array of intcodes making up the program
|
|
|
|
-my @program;
|
|
|
|
-# A hash of labels and their corresponding starting line number
|
|
|
|
-my %labels;
|
|
|
|
-# 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 = @_;
|
|
|
|
- # 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, \$instructionNumber);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- die "Failed to find matching ASM instruction for: $asmInstruction";
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-sub parseOpcodeAndParams
|
|
|
|
-{
|
|
|
|
- # A reference to the current instruction number
|
|
|
|
- # Need to assign first due to usage for opcode index
|
|
|
|
- my $instructionNumber = $_[2];
|
|
|
|
- # The opcode
|
|
|
|
- my $opcode = $_[0];
|
|
|
|
- # Store program index of opcode now because opcode may be modified in the parameter loop
|
|
|
|
- my $opcodeIdx = $$instructionNumber++;
|
|
|
|
- # Parameters for the opcode
|
|
|
|
- my @parameters = @{ $_[1] };
|
|
|
|
-
|
|
|
|
- # Store at is usually the last parameter, if there are any parameters
|
|
|
|
- my $storeAt = $#parameters > 0 ? getAddress(pop(@parameters)) : -1;
|
|
|
|
-
|
|
|
|
- # The amount of digits the opcode is expected to be when the parameter tries to add its parameter mode
|
|
|
|
- my $parameterModeIdx = 2;
|
|
|
|
-
|
|
|
|
- foreach my $param (@parameters)
|
|
|
|
- {
|
|
|
|
- if(isImmediate($param))
|
|
|
|
- {
|
|
|
|
- if(length($opcode) < $parameterModeIdx)
|
|
|
|
- {
|
|
|
|
- $opcode = "0$opcode";
|
|
|
|
- }
|
|
|
|
- $opcode = "1$opcode";
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- $param = getAddress($param);
|
|
|
|
- }
|
|
|
|
- $parameterModeIdx++;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- $program[$$instructionNumber++] = $param;
|
|
|
|
- if($$instructionNumber == 9)
|
|
|
|
- {
|
|
|
|
- print "$opcode, $param";
|
|
|
|
- die "Overwriting memory!";
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- # Insert opcode back where it belongs
|
|
|
|
- $program[$opcodeIdx] = $opcode;
|
|
|
|
- if($storeAt > -1)
|
|
|
|
- {
|
|
|
|
- # Add the storeAt address
|
|
|
|
- $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 =~ /^-?\d+$/;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-# Check if a parameter
|
|
|
|
-sub isLabel
|
|
|
|
-{
|
|
|
|
- $_[0] =~ /(\w+?)(?:\s*+\s*\d*)?/;
|
|
|
|
- print "Testing: $_[0]\n";
|
|
|
|
- return !!$labels{$1};
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-# Get the address a value represents
|
|
|
|
-sub getAddress
|
|
|
|
-{
|
|
|
|
- return (shift =~ s/^0d//r);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-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)."]\n";
|
|
|