Question about the call_vs opcode


I believe I have the correct area to post this question. I am trying to understand the Z specification and with the help of some of the posts here I am further along the way (thanks to all the contributors). I have the Folly interpreter running with debug set. When I run Zork 1, this is the first couple of lines from the debug log:

Finished release [optimized] target(s) in 0.06s
 Running `target/release/examples/term zork1-r119-s880429.z3 --debug`

50d5: call_vs 2afd 83a4 ffff → sp
5601: call_vs 2b07 local0 → local2
5619: add g31 #b4 → local2

And here is the relevant sections of the Zork 1 z3 file dumped based off of the addresses listed above:

00050d0 a0a1 b000 e000 2a03 83fd ffa4 00ff 97e1


00055f0 52d1 0090 6553 c8aa 00b2 0003 0000 0000
0005600 e000 2b2f 0107 e103 039b 0201 03ab 0005
0005610 0000 0000 0000 0000 5400 b42f 7403 522f

My question is, why the differences between the call addresses executed versus the call addresses listed in the operands? After scaling the call addresses in the operand by 2, there is still an offset of about 7 in one case and 10 in another case.

Any insights are much appreciated.


First up, your dumps are showing the words in a confusing mismatched endianness, so I have reversed them below (I think I got it right). So the first call should be (broken down into opcode, operand types, operands, and storer).

00050d0 a1a000b000    e0 03 2afd 83a4 ffff 00    e197

So the first call goes to 55fa, that’s the 03 byte:

00055f0 d15290005365aac8b200    03 0000 0000 00
0005600 00    e0 2f 2b07 01 03    e19b030102ab030500

Section 5.2 says routines start with a byte telling us how many locals there are. So this routine has 3 locals. Then, because this is a version 3 storyfile, section 5.2.1 tells us that there the initial values of those locals follows. In this case they’re all zero. Then the first instruction is at 5601, as the log says. And again, it’s another call, which I broke down into its parts.

1 Like

Hi Dannii,

Thanks a million for your quick feedback. Yeah, I need to see if there are other settings for the hexdump tool that I used to dump in bytes rather than 16 bit little endian words.

Thanks for the great explanation!


Indulge me a bit more. I’m new at this and some of this doesn’t make sense. Using my original example with Zork 1 dumped (in bytes now):

In the header, there is the program counter going to the first routine call:

00000000 03 00 00 77 4b 54 50 d5 38 99 03 e6 02 b0 2c 12

and then the first call at address 0x50d5 which is a call_vs:

000050d0 a1 a0 00 b0 00 e0 03 2a fd 83 a4 ff ff 00 e1 97

And then, based off of the debug that I have, it goes to routine at address 2*0x2afd. My question this time is, what does the value at 0x50d6 which is 0x03 do for me?


That’s the operand types. See chapter 4 of the standard.

Ok, I thought as much. Does this mean that there are:

3 large constants and then “omitted altogether” for 0b0000 0000 0000 0011?

And then what do these operands do for the subsequent call_vs call which is from 2*0x2afd = 0x55FA?

000055f0 d1 52 90 00 53 65 aa c8 b2 00 03 00 00 00 00 00
00005600 00 e0 2f 2b 07 01 03 e1 9b 03 01 02 ab 03 05 00

Thanks again,

It’s only one byte, not two: 0b0000 0011.

Then you look up chapter 15 to see what the operands do: The Z-Machine Standards Document
For calling they’re just passed in as the locals.

Thanks again for the reply. I did find the answer to my latest question in section 6.4.4.

1 Like