Wrap opcode execution with try-except to catch all ZCpuError subclasses except
ZCpuQuit and ZCpuRestart, which are normal control flow. This ensures we get
trace dumps for divide by zero, not implemented, and other errors.
Spec fixes: implement op_test (bitwise AND branch), add missing branch
to op_get_child, handle call-to-address-0 as no-op in op_call/_call.
I/O fixes: correct keyboard API (keyboard_input.read_line), non-TTY
fallbacks in trivialzui, stdout flush for immediate output. Graceful
handling of unmapped ZSCII characters. Add instruction trace buffer
for debugging.
The V3 A2 table needs newline (ZSCII 13) at code 7, matching viola's
implementation. The previous fix only removed the erroneous '<' but
didn't add the newline, leaving the table at 24 chars instead of 25.
DEFAULT_A2 had 26 chars instead of 25 due to an erroneous '<'. This
shifted all symbol mappings, causing periods to render as exclamation
marks and other garbled punctuation in Zork 1 output.
Python 2 `/` did integer division, Python 3 returns float. Changed to
`//` in zobjectparser (attribute byte offset) and zmemory (address
bounds checks). Zork 1 hit this on the first test_attr opcode.
ZStackBottom lacked attributes that normal stack operations need when
returning from a subroutine to the top level. Zork 1 hit this on the
first subroutine return during startup.
write_word() was routing all header writes through game_set_header()
which enforced overly strict per-byte permissions. Zork 1 legitimately
writes to header address 2 on startup. Now uses the same dynamic/static
boundary check that viola does, matching the reference implementation.
op_verify now performs actual checksum validation against the header
instead of raising NotImplemented. ZLexer is injected into ZCpu and
sread tokenizes input into the parse buffer per the V3 spec.
Implements op_print_addr, op_print_num, op_ret, and op_show_status following
TDD approach with tests first. Each opcode now properly decodes/prints text,
handles signed numbers, returns from routines, or acts as a no-op as appropriate.
Implemented 10 opcodes for object tree manipulation: get_sibling,
test_attr, set_attr, clear_attr, jin, remove_obj, print_obj,
get_prop_addr, get_next_prop, and get_prop_len.
Added 6 supporting methods to ZObjectParser: set_attribute,
clear_attribute, remove_object, get_property_data_address,
get_next_property, and get_property_length.
Fixed bug in insert_object where sibling chain walk never advanced
the prev pointer, potentially causing infinite loops.
Added 16 unit tests with MockObjectParser to verify CPU opcode
behavior. All tests passing.
Copies the cleaned-up zvm source (ruff-compliant, ty-clean) back into
the zmachine module. Adds __init__.py with proper exports and updates
.gitignore for debug.log/disasm.log.