I got the z5 version of Czech to work last night (same pull sp bugs as the z3) version but z5 Calypso crashes before the first prompt. Somehow a return address of zero is ending up on the stack. There’s a call_1s storing to sp that seems to be implicated.
This is a very “is it plugged in?” question, but worth checking: is it properly throwing away the whole stack frame on return? Z-code routines aren’t required to keep the stack balanced.
It’s probably something like that. I looked for the obvious suspects like calling a function with more parameters, or even more locals, than it uses/defines. Didn’t see anything immediately wrong. Also, the stack pointer values matched perfectly with my C++ reference interpreter. The place where it’s failing, it’s calling a function that seems to immediately call another function (address passed in as a local). It’ll be something stupid. I keep forgetting big-endian order since 6502 assembly is little-endian, that was the cause of a dozen bugs right there.
Figured it out. One of my three “one operand” paths (the one that uses variables, big surprise) wasn’t properly setting the operand count, which caused the common call code to get really screwed up. Accidentally fixed it by refactoring the code so that call_1s and call_1n (the only two opcodes that actually need the operand count set) to set it specifically, which saved an instruction from the common dispatch. Now it looks like I get to implement copy_table…
Hey Andy, do any of your Apple 2’s support the SmartPort protocol? There’s already an interpreter for the 2gs that was written in the 90’s that might work for you. My interpreter currently supports either Disk II direct access (but that’s 140k per disk, not a great fit for V5 or particularly V8 files) or SmartPort. But I’ve only tested on an emulator. And the emulator I use doesn’t have any 2gs support.
-Dave
Ah the joys of assembly language development on old machines… no local variables, no stack frames, accidentally clobbering globals…
Managed to track down the last few Z5 issues and got the z5 version of Calypso running to completion, which was great! Turns out @scan_table with zero objects does happen, and that ends poorly if you test first before you check the counter.
Then I noticed that there was a lot of bloat in my number printing routine and refactored it to use my common divide routine instead. Wasn’t much slower, and was significantly less code.
Fixed a few more random bugs.
Then noticed that parsing on Z3 (and only Z3) was broken. Kept warping back in git time and found what I thought was the breaking change, and made a fix but… it didn’t help.
Hours later (well, at least one hour) I thought to disable the size optimizations I’d made for number printing and… it fixed the problem. Kept commenting out bits of the new code until it worked again and then it dawned on me… on Z3, @sread does an implicit @show_status which redraws the status line and… used the number printing routines I’d just size-optimized. And the divide routine uses the same parameter buffer that @sread does, so printing the score was causing the second parameter of @sread to be trashed before it could be handled. So my parse routine was actually working fine, but writing the result somewhere completely wrong, confusing calling code.
Sigh.
Now I’m extending my VM routines to handle more than 255 possible pages so larger Z5 and Z8 games have a chance of working.
-Dave
globals are fun ![]()
Hi David, they do indeed! I believe SmartPort support came along with the IWM (Integrated Woz Machine), which shipped in the //c, //c plus, and //gs.
My //c machines are in storage, but if you ever need any testing with SmartPort on //gs hardware (either in //gs mode, or 8-bit //e mode), I’d be glad to give it a shot.
Slow progress the last few weeks.
- Got dictionary parsing working when static memory is paged.
- Had to fix other issues since Trinity has word 5 separators, not 3.
- Parsing in Trinity works, although commands are still ignored for some reason.
- Anchorhead actually boots and will accept a command or two before crashing!
- It’s… not fast.
- The problem of course is I have 44k of memory to back a 512k story, and 20k of that is the dictionary
- Having said all that, it’s not unplayable either. I have no idea whether the .hdv access speed is accurately emulated by Virtual ][.
- It also throws a bunch of Inform runtime errors that probably indicate some gnarly low-level issue.
- I also remembered I left a bunch of debug tracing on that you can’t actually see on a “real” Apple 2.
- I should be able to accelerate the implementation somewhat, maybe 10% by improving VM fast paths.
- As mentioned in other threads, there are other options here as well
So I’m probably back to comparing a zmachine dump of my C++ 'terp with the 6502 version to track down divergences. That’ll be a slog.
-Dave
Since my last post, wasted a lot of time tracking down and fixing these two issues:
- Printing from static (and possibly paged) memory now works. Had one routine I had forgotten was “falling through” to common code and was still adjusting a pointer incorrectly.
- Parsing was utterly failing in Trinity because it was using a call_2s to call BUZZER-WORD and the result was being garbled. Finally noticed that my return code that would check for “non storing” calls was accidentally enabled on Z_VERSION>3 instead of >4, which was unfortunate because the code that actually set up the memory it was checking was >4. Derp.
This explained why Anchorhead (Z8) was working better than Trinity (Z4) at least.
-Dave
Still slow progress, less free time lately, but I did manage to squash a few more bugs
- A really fun (not) one where the game was crashing in the
$credisequence in Trinity, because it was setting the window split to 23 and my scrolling routine didn’t expect that and would try to start scrolling at line 23 and then read off the end of a table and read from a bogus “next line down” address, which I spotted pretty quickly, but I missed the additional issue that it would then use “the source line being 23” as a loop termination condition, so I would destroy random parts of memory doing another 255 iterations. - Found what I hope is was the last Z4-specific bug, where I was only checking the first six letters instead of nine letters, which led to weird parsing bugs.
Unfortunately there’s still something wrong near there… page misses that happen during static memory access (typically during dictionary lookups) don’t quite work correctly, so random words like “boy” and “buy” aren’t consistently recognized.
I could probably cobble around this by forcing static memory to never page fault, but that would seriously limit the amount of pageable memory I had left (I have 44k to store all static memory beyond the 44k mark of the story, and the entirely of all routines)
(I could increase this to 46k x 2 pretty easily, and I might be able to get an additional 8k (the two 4k banked parts at $D000-$DFFF) if I can get the interpreter footprint down to 8k total, which would be a very tight fit)
-Dave
The Apple 2 interpreter is pretty stable these days, at least for games smaller than 88k.
Unfortunately the bigger v5 and v8 games seem to fail in bizarre ways that I’m pretty sure are related to VM bugs but none of them have source code (Theatre, Anchorhead, etc) so it’s a bit of a black box trying to diagnose things. I generally have to enable the Z machine trace and then compare runs between my C++ interpreter and the asm version.
Theatre spews a bunch of garbage right after all the intro text. Anchorhead works, sort of, but spews a bunch of Inform runtime errors between commands.
I’ve been spending more time lately on the authoring language that started me down this rabbit hole.
-Dave
I tracked down the major issue in Anchorhead - @jin was completely broken on story files larger than 44k, which makes me wonder how Zork I worked at all. The issue is that the opcode didn’t run the macro that switches to “dynamic” memory before attempting to read the parent field. If the code was already executing from main (not aux) memory it happened to work before.