Browse Source

Start adding debugger

debugger
Shadowfacts 2 years ago
parent
commit
ea5a0ff8ed
  1. 167
      lib/day9/day9.ex
  2. 16
      lib/intcode/assembler.ex
  3. 67
      lib/intcode/debugger.ex
  4. 4
      lib/intcode/vm.asm

167
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

16
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}

67
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

4
lib/intcode/vm.asm

@ -0,0 +1,4 @@
program:
data 1, 1, 2, 3
Loading…
Cancel
Save