Commit graph

8 commits

Author SHA1 Message Date
74538756d5
Handle object 0 (nothing) gracefully in object parser
Per the Z-machine spec, object 0 means "nothing" and operations on it
should be safe no-ops: get_child/get_sibling/get_parent return 0,
test_attr returns false, set/clear_attr are no-ops, etc. Previously
these threw ZObjectIllegalObjectNumber, crashing on games like Curses
that pass object 0 to get_child during room transitions.
2026-02-10 18:32:36 -05:00
38e60ae40c
Fix insert_object to remove from old parent before inserting
The old code inserted the object into the new parent first, then
tried to remove it from the old parent. This corrupted the sibling
chain because the object's sibling pointer was already modified.

The Z-spec says "if O already has a parent, it is first removed."
Now delegates to remove_object() before inserting.
2026-02-10 13:47:58 -05:00
11d939a70f
Relax version gates to accept V8 story files
V8 uses the same format as V5 (object model, opcodes, stack) with
two differences: packed address scaling (×8 instead of ×4) and max
file size (512KB instead of 256KB).

zmemory: add V8 size validation and packed_address case
zobjectparser: accept version 8 alongside 4-5 in all checks
zstackmanager: allow V8 stack initialization
V6-7 remain unsupported (different packed address format with offsets).
2026-02-10 13:37:22 -05:00
127ca5f56e
Fix BitField slice and off-by-one bugs in zobjectparser.py
Property parsing had 4 classes of bug, all producing wrong addresses
for object property data:

1. Off-by-one in shortname skip: addr += 2*len missed the length byte
   itself, causing property table scan to start 1 byte too early.
   Affected get_prop_addr_len and get_next_property.

2. V3 property number slices bf[4:0] extracted 4 bits not 5.
   Property numbers 16-31 were read as 0-15.

3. V3 property size slices bf[7:5] extracted 2 bits not 3.
   Properties larger than 4 bytes got wrong sizes.

4. V5 property number/size slices bf[5:0] extracted 5 bits not 6.

Also fixed get_property_length reading size from the wrong byte in
V5 two-byte headers (was reading first byte which has property
number, instead of second byte which has size).

Root cause for all slice bugs: BitField uses Python [start:stop)
semantics but code was written as [high:low] inclusive notation.
Same class of bug as the write_global fix in commit e5329d6.
2026-02-09 22:54:32 -05:00
e1c6a92368
Fix Python 3 integer division in zmachine modules
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.
2026-02-09 21:07:16 -05:00
1b9d84f41a
Port 10 object tree opcodes to hybrid z-machine interpreter
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.
2026-02-09 20:44:21 -05:00
5ea030a0ac
Re-copy fixed repos/zvm source into src/mudlib/zmachine
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.
2026-02-09 20:44:21 -05:00
dcc952d4c5
Port 12 trivial opcodes to hybrid z-machine interpreter 2026-02-09 20:44:21 -05:00