Start adding debugger

This commit is contained in:
Shadowfacts 2019-12-09 16:27:43 -05:00
parent 1add491851
commit ea5a0ff8ed
4 changed files with 198 additions and 56 deletions

View File

@ -65,13 +65,13 @@ defmodule Day9 do
def repeat(_, 0), do: [] def repeat(_, 0), do: []
def repeat(val, count), do: [val | repeat(val, count - 1)] 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 def run(memory, parent, ip, debug) when is_list(memory) do
run({memory, 0}, parent, ip) run({memory, 0}, parent, ip, debug)
end 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.puts("IP: #{ip}")
# IO.inspect(memory) # IO.inspect(memory)
@ -80,14 +80,34 @@ defmodule Day9 do
send(parent, {:halt, self(), memory}) send(parent, {:halt, self(), memory})
{state, :cont, offset} -> {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} -> {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
end end
def run(state, parent, _) do def run(state, parent, _, _) do
send(parent, {:ok, self(), state}) send(parent, {:ok, self(), state})
end end
@ -101,9 +121,9 @@ defmodule Day9 do
place = param + 2 place = param + 2
case op |> div(pow(10, place)) |> rem(10) do case op |> div(pow(10, place)) |> rem(10) do
0 -> Enum.at(memory, val) 0 -> {Enum.at(memory, val), {:position, val}}
1 -> val 1 -> {val, {:immediate, val}}
2 -> Enum.at(memory, relative_base + val) 2 -> {Enum.at(memory, relative_base + val), {:relative, val}}
end end
end end
@ -111,8 +131,8 @@ defmodule Day9 do
place = param + 2 place = param + 2
case op |> div(pow(10, place)) |> rem(10) do case op |> div(pow(10, place)) |> rem(10) do
2 -> relative_base + val 2 -> {relative_base + val, {:relative, val}}
_ -> val _ -> {val, {:immediate, val}}
end end
end end
@ -123,31 +143,38 @@ defmodule Day9 do
end end
# halt # halt
def eval([99 | _], memory, _) do def eval([99 | _], memory, parent) do
send(parent, {:debug, "Halted", [], memory})
{memory, :halt} {memory, :halt}
end end
# add # add
def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 1) do def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 1) do
a_val = get_param(op, 0, a, state) {a_val, a_param} = get_param(op, 0, a, state)
b_val = get_param(op, 1, b, state) {b_val, b_param} = get_param(op, 1, b, state)
dest = get_write_param(op, 2, dest, 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, :cont,
4 4
} }
end end
# multiply # multiply
def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 2) do def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 2) do
a_val = get_param(op, 0, a, state) {a_val, a_param} = get_param(op, 0, a, state)
b_val = get_param(op, 1, b, state) {b_val, b_param} = get_param(op, 1, b, state)
dest = get_write_param(op, 2, dest, 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, :cont,
4 4
} }
@ -155,7 +182,7 @@ defmodule Day9 do
# input # input
def eval([op, addr | _], {memory, relative_base} = state, parent) when opcode(op, 3) do 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()}) send(parent, {:in, self()})
res = res =
@ -163,8 +190,12 @@ defmodule Day9 do
{:in, val} -> val {:in, val} -> val
end 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, :cont,
2 2
} }
@ -172,68 +203,92 @@ defmodule Day9 do
# output # output
def eval([op, param | _], state, parent) when opcode(op, 4) do 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, {:out, self(), out})
send(parent, {:debug, "Output", [out_param], state})
{state, :cont, 2} {state, :cont, 2}
end end
# jump if non-zero # jump if non-zero
def eval([op, condition, ip | _], state, _) when opcode(op, 5) do def eval([op, condition, ip | _], state, parent) when opcode(op, 5) do
case get_param(op, 0, condition, state) do {cond_val, cond_param} = get_param(op, 0, condition, state)
0 -> {dest_val, dest_param} = get_param(op, 1, ip, state)
{state, :cont, 3}
_ -> new_state =
{state, :jump, get_param(op, 1, ip, state)} case cond_val do
end 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 end
# jump if zero # jump if zero
def eval([op, condition, ip | _], state, _) when opcode(op, 6) do def eval([op, condition, ip | _], state, parent) when opcode(op, 6) do
case get_param(op, 0, condition, state) do {cond_val, cond_param} = get_param(op, 0, condition, state)
0 -> {dest_val, dest_param} = get_param(op, 1, ip, state)
{state, :jump, get_param(op, 1, ip, state)}
_ -> new_state =
{state, :cont, 3} case cond_val do
end 0 ->
{state, :jump, dest_val}
_ ->
{state, :cont, 3}
end
send(parent, {:debug, "Jump if zero", [cond_param, dest_param], new_state})
new_state
end end
# less than # less than
def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 7) do def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 7) do
a_val = get_param(op, 0, a, state) {a_val, a_param} = get_param(op, 0, a, state)
b_val = get_param(op, 1, b, state) {b_val, b_param} = get_param(op, 1, b, state)
dest = get_write_param(op, 2, dest, state) {dest, dest_param} = get_write_param(op, 2, dest, state)
memory = new_state = {
if a_val < b_val do if a_val < b_val do
List.replace_at(memory, dest, 1) List.replace_at(memory, dest, 1)
else else
List.replace_at(memory, dest, 0) 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 end
# equals # equals
def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 8) do def eval([op, a, b, dest | _], {memory, relative_base} = state, parent) when opcode(op, 8) do
a_val = get_param(op, 0, a, state) {a_val, a_param} = get_param(op, 0, a, state)
b_val = get_param(op, 1, b, state) {b_val, b_param} = get_param(op, 1, b, state)
dest = get_write_param(op, 2, dest, state) {dest, dest_param} = get_write_param(op, 2, dest, state)
memory = new_state = {
if a_val == b_val do if a_val == b_val do
List.replace_at(memory, dest, 1) List.replace_at(memory, dest, 1)
else else
List.replace_at(memory, dest, 0) 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 end
# set relative base # set relative base
def eval([op, new_base | _], {memory, relative_base} = state, _) when opcode(op, 9) do def eval([op, new_base | _], {memory, relative_base} = state, parent) when opcode(op, 9) do
{{memory, relative_base + get_param(op, 0, new_base, state)}, :cont, 2} {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
end end

View File

@ -225,6 +225,22 @@ defmodule Assembler do
add #{a}, $#{dest}, #{dest} 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], assemble_macro ["not", src, dest],
""" """
sub 1, #{src}, #{dest} sub 1, #{src}, #{dest}

67
lib/intcode/debugger.ex Normal file
View File

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

4
lib/intcode/vm.asm Normal file
View File

@ -0,0 +1,4 @@
program:
data 1, 1, 2, 3