How do interpreters typically compute the object count?

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.

-Dave

If I understand correctly the problem, the objects can form (for example) a list, array or tree.

In this case when we have array there are various ways to store the object count.

From what you say, I see that the first object has a pointer to the address located just after the last object.

There can be slightly different method to store this count. For example:

  1. Store the number of objects as the first element of this array (does not need to scan the objects),
  2. Place NULL pointer as the last entry in the array (objects need to be scanned first).

I’m not sure however, which element of the interpreter where you have objects you are referring to.

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.

While @JaredReisinger’s annotated spec also includes

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
2 Likes

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. :slight_smile:

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!

2 Likes

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).

1 Like

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

In code, it could be like this:

start_addr = get_header_info('object') + get_game_constant("highest_property_num") \* 2)
end_addr = get_word(start_addr + entry_size - 2) # word is 2 byte integer
high_obj_num = int((end_addr - start_addr) / entry_size)