In my interpreter, I computed the number of objects by assuming the first object’s property blob address was immediately after the object table, and subtract and divide by object size. This seems to work in practice, but it also assumes the first object’s property is immediately after the object table.
(Which is annoying, because I want to play with having some object property blobs living in static memory to conserve dynamic memory)
I’m wondering if there is a better, more robust way to do this?
If it turns out lots of other interpreters assume the first object property blob is right after the object table as well, I probably want to maintain that. Nothing is preventing me from having all remaining objects have their property blobs at arbitrary points in dynamic or static memory.
The only reason I need the object count is for debugging - so I know where to stop when dumping all objects, and also so the various object primitives can sanity check their inputs.
Many, perhaps most don’t compute the object count at all! That’s not something you need to do to run a program. It’s more important for debuggers etc.
The remarks for section 12 of the spec say
The largest valid object number is not directly stored anywhere in the Z-machine. Utility programs like Infodump deduce this number by assuming that, initially, the object entries end where the first property table begins.
If you’re writing an interpreter and want to determine the largest valid object number, loop from the address of the first object entry until the “lowest property address seen”, updating this “lowest property address” from each object entry you see. In pseudocode:
objectAddrStart <== address from header (at $0a) + size of property defaults table
objectAddr <== objectAddrStart
lowestPropertyAddr <== infinity (or game file size, or static addr)
while objectAddr < lowestPropertyAddr:
objectPropertiesAddr <== properties field from objectAddr entry
lowestPropertyAddr <== minimum(lowestPropertyAddr, objectPropertiesAddr)
objectAddr <== objectAddr + size of table entry
lastObjectID <== ((objectAddr - objectAddrStart) / size of table entry) - 1
Are there any Z-machine compilers that don’t put object property tables right after the object tree? Dialog does, though the tables are always empty (the current version doesn’t use the property system).
I’ve placed the property tables elsewhere when making test cases, but those are manually assembled.
Other than the property table thing, there is no other reliable way to count z-machine objects. For dumping objects I use something similar to the pseudocode above: iterate until the next object would be above the lowest property address seen so far. Just hope that the compiler placed the properties after the objects which..so far…seems more or less universal.
And developers have little incentive to change that, because being able to disassemble your Z-code files with standard tools is very useful when debugging your compiler!
As for placing property tables outside dynmem, as discussed in another topic, this probably works in Ozmoo, but that’s really kind of a coincidence, and it may change. I wouldn’t count on it working in other 8-bit interpreters.
When Ozmoo accesses something that it knows to be in dynamic memory, it will sometimes use faster ways of accessing the data - dynmem is always stored in a continuous block of RAM, so it can just add an offset to find the physical address in RAM, whereas when reading from static memory, it needs to find the right virtual memory block, possibly reading it from disk first (on some platforms, including C64).
My homemade interpreter determines the size of the object entry table, divides it by the length of each entry in the object entry table. That is the highest object number in the game. The entry size depends on the ZIP version.
It only works with the object layout in Infocom games which starts at the addressed pointed by the header’s OBJECT field:
Default property values (2 bytes each) for the maximum number of properties in an object
Object entry table (one entry for each object):
ZIP 1-3: 9 bytes per entry (4 bytes for attribute, 3 family bytes for parent, sibling, and child objects, and 2 bytes for the packed address to the property table)
ZIP 4-5: 14 bytes per entry (6 bytes for attribute, 6 family bytes for parent, sibling, and child objects, and 2 bytes for the packed address to the property table)
Property Tables where each entry contains the name and object-specific property info
The size of the object entry table is the difference between the address of the first object’s property table and the address of the first object’s entry in the object entry table:
Address to the first object’s entry in the object entry table is after all the default property values
Address to the first object’s property table is store in the last two bytes of the first object’s entry in the object entry table