class Synacor::Debugger

Defined in:

synacor/debugger.cr

Constant Summary

MAX_VALUE = VM::MAX_VALUE
REGISTERS = VM::REGISTERS.to_a
TELEPORTER_R7 = 25734_u16

The value #solve_teleporter finds for $7. Hardcoded so we can skip the ~100s brute-force at runtime.

Constructors

Instance Method Summary

Constructor Detail

def self.new(vm : Synacor::VM, output : Athena::Console::Output::Interface) #

[View source]

Instance Method Detail

def ackermann(a : UInt16, b : UInt16, r7 : UInt16, memo) : UInt16 #

[View source]
def add_vm_input(input_str) #

[View source]
def breakpoints : Array(Int32) #

[View source]
def breakpoints=(breakpoints : Array(Int32)) #

[View source]
def clear_vm_output #

[View source]
def continue_until_breakpoint(breakpoint : Int32 | Nil = nil) #

[View source]
def continue_until_ret #

[View source]
def custom_main_loop(&block : -> Bool) #

[View source]
def debugger_loop #

[View source]
def execute_instruction(opcode, args) #

[View source]
def fix_teleporter #

Apply the teleporter fix without running the slow check:

  1. Set $7 to the calibrated value (a later routine derives the printed code from $7, so this must be the genuine value, not faked).
  2. Patch the confirmation function at 6027 so it returns 6 immediately, since the real recursion is far too deep to actually run. The caller at 5489 only inspects $0 after the call, so: 6027: set $0 6 (1, 32768, 6) 6030: ret (18)

[View source]
def interpret_instruction(md) #

[View source]
def nexti #

TODO allow nesting


[View source]
def output : ACON::Output::Interface #

[View source]
def output=(output : ACON::Output::Interface) #

[View source]
def print_breakpoints #

[View source]
def print_disassemble(pc : Int32 | Nil = self.vm.pc) #

[View source]
def print_registers #

[View source]
def print_solved_coin_problem #

[View source]
def print_solved_teleporter #

[View source]
def print_stack #

[View source]
def print_vm_input(position = false) #

[View source]
def print_vm_output #

[View source]
def remove_breakpoint(breakpoint) #

[View source]
def resume_program #

Hand control back to the VM: restore output to the real terminal and run the normal main loop from the current PC. Remaining buffered input (from any --load saves) is consumed first, then op_in falls back to STDIN, so the game continues interactively. HaltError and friends propagate up to RunCommand#execute, which handles them.


[View source]
def resume_requested : Bool #

[View source]
def resume_requested=(resume_requested : Bool) #

[View source]
def set_breakpoint(breakpoint) #

[View source]
def solve_coin_problem : Array(String) #

[View source]
def solve_teleporter : UInt16 #

The teleporter confirmation routine at address 6027 is a parameterized Ackermann function where register 7 ($7) is the free parameter:

f(0, b) = b + 1 f(a, 0) = f(a - 1, $7) f(a, b) = f(a - 1, f(a, b - 1)) (all mod MAX_VALUE)

The caller at 5483 sets $0=4, $1=1, calls it, and requires f(4, 1) == 6. We brute-force every candidate for $7, memoizing per-candidate so each evaluation is cheap.


[View source]
def stepi #

[View source]
def stepo #

[View source]
def vm : VM #

[View source]
def vm=(vm : VM) #

[View source]
def vm_output_io : IO::Memory #

[View source]
def vm_output_io=(vm_output_io : IO::Memory) #

[View source]