[Z-MACHINE] Packed Address Calculation Anomaly

I have code like this for computing a packed address:

if self.version < 4:
  return 2 * address
elif self.version < 6:
  return 4 * address
elif self.version < 8 and is_routine:
  return 4 * address + (8 * self.routine_offset)
elif self.version < 8:
  return 4 * address + (8 * self.string_offset)
  return 8 * address

That seems to match what the spec is telling me in section 1.2.3.

A decompilation of Zork 0 shows the following:

Routines offset: 0e930
Strings offset: 362e0

So those are the addresses I should get. I’ll focus on the routines offset here. I try to get those addresses via:

self.routine_offset = self.get_word(0x28)
self.string_offset = self.get_word(0x2A)

Here my get_word() implementation is this:

def get_word(self, address: int) -> int:
  return (self.memory[address] << 8) + self.memory[address + 1]

Here’s the problem. When I use the above calculation on Zork 0 v6, the following branch path is (correctly) executed:

return 4 * address + (8 * self.routine_offset)

However on that Zork 0 v6 file, it returns this:

Routines offset: 0x1d26
Static strings offset: 0x6c5c

Those are wrong compared to the decompilation. But if I just do this:

8 * self.routine_offset

Then the calculation is right and the correct address is returned.

So to bring it home:

4 * address + (8 * self.routine_offset)  # incorrectly returns 0x1d26
8 * self.routine_offset                  # correctly returns 0e930

What am I misreading here or doing incorrectly?

Actually, I think my question is stupid. I’m conflating getting a specific packed address with the offset location. For example, to get the starting location for the program counter I have logic like this:

if self.version == 6:
  self.pc = self.get_packed(self.get_word(0x06), True)
  self.pc = self.get_word(0x06)

Here you can see how I’m using my get_packed (which contains that calculation I started my previous post with). And that is returning the correct starting address for Zork 0 v6 as per the decompilation.

So my silly mistake here was conflating my output for the address locations with that.

What I should have recognized is that I can do the following:

self.routine_offset = self.get_word(0x28)
self.string_offset = self.get_word(0x2A)

print(f"Routines offset: {hex(8 * self.routine_offset)}")
print(f"Static strings offset: {hex(8 * self.string_offset)}")

Notice the 8 * bit there. With the problem as I originally posed it, I was just printing the offset information without the 8 * and expected it to match.

Which was silly.