Z-machine @catch and Quetzal

I’m seeing some discrepancy in how the return value of @catch is handled vis-à-vis Quetzal.

There are two relevant parts of Quetzal here:

§4.11: “in all versions other than V6 a dummy stack frame must be stored as the first in the file”

§6.2: “catch is hereby specified to return the number of frames currently on the system stack”

The question is: does the dummy frame count toward the number of frames on the system stack? This can be illustrated with the following simple program, compiled to version 5:

[Main i;
    @catch -> i;
    print i, "^";
];

For interpreters which implement Quetzal, this @catch should always return the same value. But that’s not the case. Interpreters which assume the dummy frame does not count return 1, while others return 2.

I tested several interpreters, and most returned 1. The notable exceptions are Fizmo and Lectrote, which both return 2 (ZMPP returns 0, which is obviously incorrect).

This should probably be clarified so that saves of games which make use of @catch can properly be shared between interpreters. The general consensus seems to be 1, but the most important thing is all interpreters agree; less important is what they agree on.

1 Like

If ZVM is the outlier then I can definitely fix it. And that’s probably a better reading of the spec: it does say that a dummy frame is stored in the file, not the running VM. (Very mysteriously, ZVM is actually adding 1 to the number of frames in its catch code. Why did I think that was necessary??)

Could also add a test to Praxix along the lines of what you have above, if we agree that it’s correct.

I’ve always had catch return zero from main on V6 in my interpreters.

A throw to zero is never valid as it would then imply returning from the main routine.

My interpreters do not treat the main routine in V6 any differently than the start of execution in any other version, so special casing it seemed ugly.

I don’t see why that is necessarily true.

And that’s probably a better reading of the spec: it does say that a dummy frame is stored in the file, not the running VM.

That’s my feeling as well. I’m sure I implemented it that way because Frotz did, and not because of Quetzal, but even so, I think it’s the proper method.

Edit: I think adding a test to Praxix is good, but it couldn’t be included in “all”, because it’s mandated by Quetzal, not the Z-machine standard itself: it’s not an inherent failure of an interpreter to not conform.

I don’t see why that is necessarily true.

It’s because once Main is executing there has to be a frame on the stack. In general Z-machine interpreters can manage the return value of @catch however they want, but this is in the context of Quetzal, which requires it to be the number of frames on the stack. So it can’t be less than 1.

It’s different outside of Main: I’m not sure you can get Inform to build a story that doesn’t include an initial call to a function, but I’ve built such a story that just does the following, outside of any functions calls:

@catch -> G00
@print_num G00
@new_line
@quit

It’s more reasonable that @catch here return 0, which is the case for many interpreters.

The file: catch.z8 (720 Bytes)

There doesn’t have to be a frame on the stack.

All versions of the z-machine except 6 begin execution without a call to a routine, but they can all have temporary stack values pushed without a frame.

V6 begins with a call to a routine which can contain locals, but is otherwise no different than the initiali execution environment of all other versions.

My interpreters have never used a separate stack frame for the initial environment, even in V6.

Edit: We may be saying the same thing here…I’m not referring to Inform at all, but the ‘Main’ routine specified in the z-spec.

But in the example I gave, there does have to be a frame on the stack. The initial call to Main in Inform is a routine call, so @catch in that program cannot return 0 (according to Quetzal).

Edit in reponse to your edit: Right, we were talking at cross purposes: I was explicitly referring to the program I posted above when I said @catch can’t return 0.

The z8 file?

Oh you mean the code in your original post.
I don’t know what the compiled code for that looks like.

Edit: Namely, is the ‘Main’ routine address placed in the START section of the header, or is there an explicit CALL instruction somewhere? If there’s no CALL, then my interpreter would return zero.

Namely, is the ‘Main’ routine address placed in the START section of the header, or is there an explicit CALL instruction somewhere? If there’s no CALL, then my interpreter would return zero.

In the case of Inform, there is a call instruction to Main, which is why ZMPP’s return value of 0 can’t be valid (in the context of Quetzal, which it supports).

The Z8 file I posted was built with a separate assembler, to allow arbitrary code to be run outside of any function. The first instruction (the @catch) is where the initial program counter starts.

Then it seems we are in agreement. :smile:

Praxix already tests a couple of conventions outside the spec, such as bit shifts of more than 15 bits. We could do the same for this - raise the issue, but without incrementing the failure count.

2 Likes