diff --git a/lib/day9/day9.ex b/lib/day9/day9.ex index 750a688..5a64935 100644 --- a/lib/day9/day9.ex +++ b/lib/day9/day9.ex @@ -65,13 +65,13 @@ defmodule Day9 do def repeat(_, 0), do: [] def repeat(val, count), do: [val | repeat(val, count - 1)] - def run(state, parent, ip \\ 0) + def run(state, parent, ip \\ 0, debug \\ false) - def run(memory, parent, ip) when is_list(memory) do - run({memory, 0}, parent, ip) + def run(memory, parent, ip, debug) when is_list(memory) do + run({memory, 0}, parent, ip, debug) end - def run({memory, _relative_base} = state, parent, ip) when ip < length(memory) do + def run({memory, _relative_base} = state, parent, ip, debug) when ip < length(memory) do # IO.puts("IP: #{ip}") # IO.inspect(memory) @@ -80,14 +80,34 @@ defmodule Day9 do send(parent, {:halt, self(), memory}) {state, :cont, offset} -> - run(state, parent, ip + offset) + if debug do + receive do + :step -> + run(state, parent, ip + offset) + + :abort -> + send(parent, {:abort, self(), state}) + end + else + run(state, parent, ip + offset) + end {state, :jump, ip} -> - run(state, parent, ip) + if debug do + receive do + :step -> + run(state, parent, ip) + + :abort -> + send(parent, {:abort, self(), state}) + end + else + run(state, parent, ip) + end end end - def run(state, parent, _) do + def run(state, parent, _, _) do send(parent, {:ok, self(), state}) end @@ -101,9 +121,9 @@ defmodule Day9 do place = param + 2 case op |> div(pow(10, place)) |> rem(10) do - 0 -> Enum.at(memory, val) - 1 -> val - 2 -> Enum.at(memory, relative_base + val) + 0 -> {Enum.at(memory, val), {:position, val}} + 1 -> {val, {:immediate, val}} + 2 -> {Enum.at(memory, relative_base + val), {:relative, val}} end end @@ -111,8 +131,8 @@ defmodule Day9 do place = param + 2 case op |> div(pow(10, place)) |> rem(10) do - 2 -> relative_base + val - _ -> val + 2 -> {relative_base + val, {:relative, val}} + _ -> {val, {:immediate, val}} end end @@ -123,31 +143,38 @@ defmodule Day9 do end # halt - def eval([99 | _], memory, _) do + def eval([99 | _], memory, parent) do + send(parent, {:debug, "Halted", [], memory}) {memory, :halt} end # add - def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 1) do - a_val = get_param(op, 0, a, state) - b_val = get_param(op, 1, b, state) - dest = get_write_param(op, 2, dest, state) + def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 1) do + {a_val, a_param} = get_param(op, 0, a, state) + {b_val, b_param} = get_param(op, 1, b, state) + {dest, dest_param} = get_write_param(op, 2, dest, state) + new_state = {List.replace_at(memory, dest, a_val + b_val), relative_base} + + send(parent, {:debug, "Add", [a_param, b_param, dest_param], new_state}) { - {List.replace_at(memory, dest, a_val + b_val), relative_base}, + new_state, :cont, 4 } end # multiply - def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 2) do - a_val = get_param(op, 0, a, state) - b_val = get_param(op, 1, b, state) - dest = get_write_param(op, 2, dest, state) + def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 2) do + {a_val, a_param} = get_param(op, 0, a, state) + {b_val, b_param} = get_param(op, 1, b, state) + {dest, dest_param} = get_write_param(op, 2, dest, state) + new_state = {List.replace_at(memory, dest, a_val * b_val), relative_base} + + send(parent, {:debug, "Multiply", [a_param, b_param, dest_param], new_state}) { - {List.replace_at(memory, dest, a_val * b_val), relative_base}, + new_state, :cont, 4 } @@ -155,7 +182,7 @@ defmodule Day9 do # input def eval([op, addr | _], {memory, relative_base} = state, parent) when opcode(op, 3) do - addr = get_write_param(op, 0, addr, state) + {addr, addr_param} = get_write_param(op, 0, addr, state) send(parent, {:in, self()}) res = @@ -163,8 +190,12 @@ defmodule Day9 do {:in, val} -> val end + new_state = {List.replace_at(memory, addr, res), relative_base} + + send(parent, {:debug, "Input", [addr_param], new_state}) + { - {List.replace_at(memory, addr, res), relative_base}, + new_state, :cont, 2 } @@ -172,68 +203,92 @@ defmodule Day9 do # output def eval([op, param | _], state, parent) when opcode(op, 4) do - out = get_param(op, 0, param, state) + {out, out_param} = get_param(op, 0, param, state) send(parent, {:out, self(), out}) + send(parent, {:debug, "Output", [out_param], state}) {state, :cont, 2} end # jump if non-zero - def eval([op, condition, ip | _], state, _) when opcode(op, 5) do - case get_param(op, 0, condition, state) do - 0 -> - {state, :cont, 3} + def eval([op, condition, ip | _], state, parent) when opcode(op, 5) do + {cond_val, cond_param} = get_param(op, 0, condition, state) + {dest_val, dest_param} = get_param(op, 1, ip, state) - _ -> - {state, :jump, get_param(op, 1, ip, state)} - end + new_state = + case cond_val do + 0 -> + {state, :cont, 3} + + _ -> + {state, :jump, dest_val} + end + + send(parent, {:debug, "Jump if non-zero", [cond_param, dest_param], new_state}) + new_state end # jump if zero - def eval([op, condition, ip | _], state, _) when opcode(op, 6) do - case get_param(op, 0, condition, state) do - 0 -> - {state, :jump, get_param(op, 1, ip, state)} + def eval([op, condition, ip | _], state, parent) when opcode(op, 6) do + {cond_val, cond_param} = get_param(op, 0, condition, state) + {dest_val, dest_param} = get_param(op, 1, ip, state) - _ -> - {state, :cont, 3} - end + new_state = + case cond_val do + 0 -> + {state, :jump, dest_val} + + _ -> + {state, :cont, 3} + end + + send(parent, {:debug, "Jump if zero", [cond_param, dest_param], new_state}) + new_state end # less than - def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 7) do - a_val = get_param(op, 0, a, state) - b_val = get_param(op, 1, b, state) - dest = get_write_param(op, 2, dest, state) + def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 7) do + {a_val, a_param} = get_param(op, 0, a, state) + {b_val, b_param} = get_param(op, 1, b, state) + {dest, dest_param} = get_write_param(op, 2, dest, state) - memory = + new_state = { if a_val < b_val do List.replace_at(memory, dest, 1) else List.replace_at(memory, dest, 0) - end + end, + relative_base + } - {{memory, relative_base}, :cont, 4} + send(parent, {:debug, "Less than", [a_param, b_param, dest_param], new_state}) + {new_state, :cont, 4} end # equals - def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 8) do - a_val = get_param(op, 0, a, state) - b_val = get_param(op, 1, b, state) - dest = get_write_param(op, 2, dest, state) + def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 8) do + {a_val, a_param} = get_param(op, 0, a, state) + {b_val, b_param} = get_param(op, 1, b, state) + {dest, dest_param} = get_write_param(op, 2, dest, state) - memory = + new_state = { if a_val == b_val do List.replace_at(memory, dest, 1) else List.replace_at(memory, dest, 0) - end + end, + relative_base + } - {{memory, relative_base}, :cont, 4} + send(parent, {:debug, "Equal to", [a_param, b_param, dest_param], new_state}) + {new_state, :cont, 4} end # set relative base - def eval([op, new_base | _], {memory, relative_base} = state, _) when opcode(op, 9) do - {{memory, relative_base + get_param(op, 0, new_base, state)}, :cont, 2} + def eval([op, new_base | _], {memory, relative_base} = state, parent) when opcode(op, 9) do + {new_base_val, new_base_param} = get_param(op, 0, new_base, state) + new_state = {memory, relative_base + new_base_val} + send(parent, {:debug, "Set relative base", [new_base_param], new_state}) + {new_state, :cont, 2} end end diff --git a/lib/intcode/assembler.ex b/lib/intcode/assembler.ex index 506eed3..4305951 100644 --- a/lib/intcode/assembler.ex +++ b/lib/intcode/assembler.ex @@ -225,6 +225,22 @@ defmodule Assembler do add #{a}, $#{dest}, #{dest} """ + assemble_macro ["div", a, b, dest], + """ + mov 0, #{dest} + mov #{a}, tmp + do_sub: + clt $tmp, #{b}, _self + 2 + jnz -1, end + add 1, $#{dest}, #{dest} + sub $tmp, #{b}, tmp + jmp do_sub + + tmp: -1 + end: + """ + |> IO.puts() + assemble_macro ["not", src, dest], """ sub 1, #{src}, #{dest} diff --git a/lib/intcode/debugger.ex b/lib/intcode/debugger.ex new file mode 100644 index 0000000..a53cf23 --- /dev/null +++ b/lib/intcode/debugger.ex @@ -0,0 +1,67 @@ +defmodule Debugger do + def run() do + program = [1, 1, 2, 3, 1102, 4, 5, 0, 99] + memory_view_length = floor(columns() / 7) * floor(rows() / 2 - 10) + memory = Day9.expand_memory(program, memory_view_length) + + parent = self() + + pid = + spawn(fn -> + Day9.run(memory, parent, 0, true) + end) + + step(pid) + end + + def columns do + {:ok, cols} = :io.columns() + cols + end + + def rows do + {:ok, rows} = :io.rows() + rows + end + + def step(pid) do + IO.write([IO.ANSI.clear(), IO.ANSI.home()]) + + receive do + {:debug, msg, params, {memory, _}} -> + print_memory(memory) + IO.puts("#{msg}: #{inspect(params)}") + IO.gets("") + send(pid, :step) + step(pid) + + {msg, _, {memory, _}} when msg in [:halt, :abort, :ok] -> + print_memory(memory) + IO.gets("") + end + end + + def print_memory(memory, row_size \\ nil) + + def print_memory([], _) do + end + + def print_memory(memory, row_size) when is_nil(row_size) do + print_memory(memory, floor(columns() / 7)) + end + + def print_memory(memory, row_size) do + memory + |> Enum.take(row_size) + |> Enum.each(fn val -> + val + |> to_string() + |> String.pad_leading(7) + |> IO.write() + end) + + IO.write("\n\n") + + print_memory(Enum.drop(memory, row_size), row_size) + end +end diff --git a/lib/intcode/vm.asm b/lib/intcode/vm.asm new file mode 100644 index 0000000..d8ee611 --- /dev/null +++ b/lib/intcode/vm.asm @@ -0,0 +1,4 @@ + + +program: + data 1, 1, 2, 3