I’ve been working on an interpreter and a compiler for a few months now.
The interpreter works reasonably well and passes every test suite (Czech, etude, praxix, etc) I can find.
It also crashes on some games I’ve been trying to play with it.
Easiest repro is Anchorhead. If I press R at startup, my interpreter crashes because something is calling @get_parentget_parent on object zero. Couldn’t see anything in the standard on what should happen here.
If I start a new game, as soon as I get the keys from the office and take inventory, it crashes because @get_prop on object zero is also out of range.
Should I just be ignoring these errors in my ‘terp?
Are there interpreters out there that can dump a live disassembly so I can compare and figure out where mine is diverging?
Thanks,
-Dave
EDIT: I had downloaded Yazmin, but it didn’t seem to support disassembly. Then I thought to look at its source code, and yup… for get_prop, it will “assert” the object is greater than zero, then just return 0 for 0. And for get_parent, calling with 0 returns 0 without even an assert, so I guess it’s just one of those things not written down anywhere that people just do.
It’s illegal. Older games do it anyway, assuming the interpreter will return zero.
Around 1998 or 1999, I think, we started a crusade against the “vile zero error from hell”. This meant setting up interpreters to show a (non-fatal) warning, and also setting up the Inform compiler to support run-time error checking.
I guess it’s just one of those things not written down anywhere that people just do.
There was a lot of discussion 25 years ago! It’s timed out, I guess.
A number of post-Infocom games have been released which contain errors, most often trying to perform illegal operations on object 0. Many interpreters silently ignored these errors, which can make it very to notice and track down bugs.
This is in the context of urging interpreter authors to manage this behavior:
It is desirable for modern interpreters to be able to notify players about these bugs, but this can also ruin gameplay. It is highly recommended, then, that interpreters have four levels of error checking, selectable by the user (through a command-line or menu option, or similar):
Never report the bug.
Report the first instance of each type of error.
Report every error.
Fatal error and close the interpreter on any error.
Okay. I guess I kind of expected a modern classic like Anchorhead to not have two VZEFH’s in the first five minutes of gameplay
I guess my question does still stand though, it would be interesting to see if I’ve done something else terribly wrong that’s going to cause other issues later. I’d hate to accidentally release a buggy game that only works properly on my own buggy interpreter.
(I do have Yazmin now at least as one reference point)
That’s one thing that kind of blows my mind about IF and my life, I guess.
I was always a fan of interactive fiction. I remember playing Theatre soon after it came out in the 90’s on a unix machine at work. This was back when r.a.i-f was still a thing.
Then… well, I got married, had kids, had a career (still ongoing) in video games, kids grew up, partner and I moved to the UK, and I got interested in IF again just in time to have half the content of ifdb blocked by the stupid, pointless UK online safety act.
And the many of the same people I recognize from 30 years ago… are still around.
And I’m writing modern tools that target machines from 45 years ago because, why not.
Ugh, I just tried my demo game on Yazmin and it behaves differently (not all exits show up as you move around the map) and Yazmin crashes on undefined opcode zero if I take inventory.
So either I’m generating bad Z-code that my bad interpreter knows how to handle, or there are bugs in Yazmin.
ZVM has no special handing for get_parent or get_prop, so if you pass in object 0 it just acts as if there was an object 0. I don’t know if that’s better or worse than testing and returning 0 would have been. Never had any bug reports about Anchorhead though.
Nice. Unfortunately I couldn’t find pre-built versions for Mac on (link to foss heptapod removed by forum software) and I’m not set up for C# development.
Okay, here’s a technical question - when generating code that produces temporaries on the stack, my compiler evaluates the operands in reverse order so that if they do go to TOS, the first parameter will end up first on the stack.
Then my interpreter evaluates parameters from left to right.
Those obviously work well together, but if the rest of the planet does that differently, no wonder I’m not playing well with others.
EDIT: Just checked Frotz source (which refuses to compile properly, unfortunately, due to weird unix audio dependencies and blorb support that refuses to disable) and it works the way I expect.
Though from that discussion the consensus was that it’s okay for the globals to overlap other things. It should really be testing that the end of the globals and the end of the prop table are in dynamic memory, not that dynamic memory is longer than both added together.
Yeah, that’s expected. In a “real” game I imagine there will be enough other things after the globals in dynamic memory.
But I wonder if some interpreters can’t cope with fewer than 240 globals. I’ll add an option to pad them out and see if that fixes it running properly on Yazmin.
Interesting. I wrote code to add padding (even including the default properties, although I store them after globals anyway), but Yazmin still crashes.
Also, I write the header, the globals, the default properties, and then the rest of the object table, and the top of static memory is more than 480 bytes away from the globals.
I’ll try forcing all 240 globals next.
But here’s a version with an extra 44 bytes of padding.