From b60c9a9a638f9bda12246373ced60216bf18185b Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 5 Dec 2019 22:48:17 -0500 Subject: [PATCH] Add simple Intcode assembler --- lib/day5/day5.ex | 10 ++- lib/intcode/assembler.ex | 155 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 lib/intcode/assembler.ex diff --git a/lib/day5/day5.ex b/lib/day5/day5.ex index d5e1e41..4bbaeee 100644 --- a/lib/day5/day5.ex +++ b/lib/day5/day5.ex @@ -66,11 +66,12 @@ defmodule Day5 do def run(memory, ip \\ 0) def run(memory, ip) when ip < length(memory) do + IO.puts("IP: #{ip}") IO.inspect(memory) case eval(Enum.drop(memory, ip), memory) do - {memory, :halt} -> - memory + {_memory, :halt} -> + :halt {memory, :cont, offset} -> run(memory, ip + offset) @@ -78,6 +79,8 @@ defmodule Day5 do {memory, :jump, ip} -> run(memory, ip) end + + :ok end def run(memory, _ip), do: memory @@ -146,7 +149,8 @@ defmodule Day5 do # output def eval([op, param | _], memory) when opcode(op, 4) do - get_param(op, 0, param, memory) |> IO.inspect() + out = get_param(op, 0, param, memory) + IO.puts("Output: #{out}") {memory, :cont, 2} end diff --git a/lib/intcode/assembler.ex b/lib/intcode/assembler.ex new file mode 100644 index 0000000..b01e3cc --- /dev/null +++ b/lib/intcode/assembler.ex @@ -0,0 +1,155 @@ +defmodule Assembler do + @asm """ + in #30 + clt 30 #8 #31 + jnz 31 #lessThan + ceq 30 #8 #31 + jnz 31 #equal + out #1001 + hlt + + lessThan: + out #999 + hlt + + equal: + out #1000 + hlt + """ + def test do + @asm + |> IO.inspect() + |> assemble(32) + end + + def assemble(asm, pad_length \\ 0) do + {memory, labels} = + asm + |> String.split("\n") + |> Enum.reject(&(String.length(&1) == 0)) + |> Enum.reduce({[], %{}}, fn line, {memory, labels} -> + cond do + Regex.match?(~r/^\s+$/, line) -> + {memory, labels} + + String.starts_with?(line, " ") -> + { + assemble_instruction(String.slice(line, 2..-1), memory), + labels + } + + String.ends_with?(line, ":") -> + { + memory, + Map.put(labels, String.slice(line, 0..-2), length(memory)) + } + + true -> + IO.inspect("Ignoring line: #{line}") + end + end) + + memory = + Enum.map(memory, fn + {:label, name} -> Map.fetch!(labels, name) + it -> it + end) + + memory = + if pad_length > length(memory) do + repeat(0, pad_length - length(memory)) ++ memory + else + memory + end + + IO.inspect(labels) + Enum.reverse(memory) + end + + def repeat(_, 0), do: [] + def repeat(n, count), do: [n | repeat(n, count - 1)] + + def pow(base, 1), do: base + def pow(base, exp), do: base * pow(base, exp - 1) + + def parse_params(params, param_types) do + params + |> String.split(" ") + |> Enum.zip(param_types) + |> Enum.with_index() + |> Enum.map_reduce(0, fn {{param, type}, index}, modes -> + val = + case Regex.run(~r/^#?(\d+)$/, param) do + [_, digits] -> + String.to_integer(digits) + + _ -> + [_, name] = Regex.run(~r/^#(\w+)$/, param) + {:label, name} + end + + modes = + if type == :read && String.starts_with?(param, "#") do + modes + pow(10, index + 2) + else + modes + end + + {val, modes} + end) + end + + def assemble_instruction(insn, memory \\ []) + + def assemble_instruction("add " <> params, memory) do + {[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) + + [dest, b, a, 1 + modes | memory] + end + + def assemble_instruction("mul " <> params, memory) do + {[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) + + [dest, b, a, 2 + modes | memory] + end + + def assemble_instruction("hlt", memory) do + [99 | memory] + end + + def assemble_instruction("in " <> params, memory) do + {[dest], modes} = parse_params(params, [:write]) + + [dest, 3 + modes | memory] + end + + def assemble_instruction("out " <> params, memory) do + {[val], modes} = parse_params(params, [:read]) + + [val, 4 + modes | memory] + end + + def assemble_instruction("jnz " <> params, memory) do + {[target, dest], modes} = parse_params(params, [:read, :read]) + + [dest, target, 5 + modes | memory] + end + + def assemble_instruction("jez " <> params, memory) do + {[target, dest], modes} = parse_params(params, [:read, :read]) + + [dest, target, 6 + modes | memory] + end + + def assemble_instruction("clt " <> params, memory) do + {[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) + + [dest, b, a, 7 + modes | memory] + end + + def assemble_instruction("ceq " <> params, memory) do + {[a, b, dest], modes} = parse_params(params, [:read, :read, :write]) + + [dest, b, a, 8 + modes | memory] + end +end