Bug fixed in 6G to 6L: how to find what I6 code was changed?

I have an IFComp project in 6G that I want to keep as z-machine, and putting it to 6L or later would tip it into Glulx.

But I found a bug that appears in 6G but not 6L. It involves rewriting the player’s command when I don’t want Inform to. In the example below, 6G says (your command) then “y” for score, but it gives your command twice for think. 6L gives your command twice for score. So there was a fix between 6G and 6L.

"ITPC" by Andrew Schultz

r1 is a room.

check requesting the score:
	say "[the player's command].";
	if the player consents, do nothing;
	say "[the player's command].";

the block thinking rule is not listed in any rulebook.

check thinking:
	let X be indexed text;
	now X is the player's command;
	say "[the player's command].";
	if the player consents, do nothing;
	change the text of the player's command to X;
	say "[the player's command].";

This is an acceptable work-around, but I suspect there’s I6 code that could be fixed so things would be less tangled.

This is a bit above my pay-grade to fix with 3 weeks before IFComp, especially since I have a workaround so it’s not necessary. But I wanted to see if anyone knew offhand “Oh, yeah, that code fix would have to be in function X…”

Also, how would I research this in the future? AFAIK the bug-tracker for Inform 7 before 6L is gone. If I’m wrong, and it’s somewhere, that’d be great, as I could probably just look through the fixed bugs and see which applied to the player’s command and see what code changed, then include I6 code in my story file.

Beyond that, though, I’m still very bad at debugging I6 code. I’ve had cases where I was able to scratch the surface and patch a fix, but beyond that, I have trouble. This feels like a question I may’ve asked before (I hope not,) but any pointers of debugger code beyond RULES and RULES ALL would be a huge help.

Thanks for any and all help on this.

1 Like

There are extremely thorough per-release change logs: 6L02’s change log describes its differences from 6G60.

It doesn’t help you if the relevant change was in I6 that gets directly written by the compiler, but if the change stems from a change in the Standard Rules or the I6 Template layer (and my guess is that that’s likely the case here), you can always download the raw 6G60 distribution (tar.gz) and 6L02 distribution (tar.gz) and diff between them. Those links are to the Linux CLI packages, but it doesn’t matter for these purposes except inasmuch as those are the packages that don’t include an IDE-app that would be superfluous for these purposes.

(Also: fergawdsakes, man! Modern I7 has a lot of improvements. Rip the band-aid off and upgrade after the comp!)

Edited to add: There’s a caveat to the description of the change logs as very thorough: they’re very thorough regarding deliberate changes to documented behavior. There’s little or no detail regarding changes to things that had never been documented.

I suspect you’ll find the difference in the I6 implementation of “if the player consents” and which buffers it uses: if it uses the normal parse buffers (thus overwriting “the player’s command”) or its own separate set of buffers (leaving the command intact).

But I don’t have a 6G version on hand to test with. So I’d recommend your workaround:

To decide whether the player safely consents:
    let X be indexed text;
    let X be "[the player's command]";
    let Y be whether or not the player consents;
    change the text of the player's command to X;
    decide on Y.
2 Likes

uh… I hope to be clear on what is what… too many, uh… numberonym ?

Anyway, the inform 6 compiler used in Inform 7 6G/6L has the very useful $OMIT_UNUSED_ROUTINES ? in this case perhaps aschultz’s work can be kept in z-machine format with Inform 7 6L ?

Best regards from Italy,
dott. Piergiorgio.

1 Like

Unfortunately, the big issue with the Z-machine isn’t usually ROM but RAM. With versions of Inform past 6G, there’s no intrinsic distinction between “text” and “indexed text”, which means you always need the Flex memory allocation system even if you don’t do any fancy text processing, which eats up a whole lot of RAM.

Though, now that I think about it—since it sounds like Andrew’s using indexed text anyway, that may not be the issue. Huh. I wonder what else changed between 6G and 6L that would use RAM. Maybe it just needs more Flex allocation?

1 Like

As Draconis suggests, the difference may be in the handling of text buffer and parse array being used. In 6G60, the routine YesOrNo() (which handles the if the player consents... phrase), contains:

    ...
    #Ifdef TARGET_ZCODE;
    if (location == nothing || parent(player) == nothing) read buffer parse;
    else read buffer parse DrawStatusLine;
    j = parse->1;
    #Ifnot; ! TARGET_GLULX;
    ...

while in 6M62, the same lines are:

    ...
    #Ifdef TARGET_ZCODE;
    if (location == nothing || parent(player) == nothing) read buffer2 parse2;
    else read buffer2 parse2 DrawStatusLine;
    j = parse2->1;
    #Ifnot; ! TARGET_GLULX;
    ...

There is mention of this difference in the change log for 6M62:

Bug fixed (0001568) whereby the use of the "if the player consents" phrase, which asks for a yes/no answer, would invalidate the contents of the "player's command" variable.

I’m not sure what YesOrNo() looks like in 6L02 or 6L38. I don’t have a working installation of those versions to check. If you want to post one of those versions for comparison, I would look.

The arrays buffer2 and parse2 are already present in 6G60, just not used by YesOrNo(). You can replace the 6G60 default version of it with a modified version that does:

Include (-

[ YesOrNo i j;
    for (::) {
        #Ifdef TARGET_ZCODE;
        if (location == nothing || parent(player) == nothing) read buffer2 parse2;
        else read buffer2 parse2 DrawStatusLine;
        j = parse2->1;
        #Ifnot; ! TARGET_GLULX;
        if (location ~= nothing && parent(player) ~= nothing) DrawStatusLine(); ! in 6M62
        KeyboardPrimitive(buffer2, parse2);
        j = parse2-->0;
        #Endif; ! TARGET_
        if (j) { ! at least one word entered
            i = parse2-->1;
            if (i == YES1__WD or YES2__WD or YES3__WD) rtrue;
            if (i == NO1__WD or NO2__WD or NO3__WD) rfalse;
        }
        L__M(##Quit, 1); print "> ";
    }
];

-) instead of "Yes/No Questions" in "Parser.i6t".

… but really your workaround is just as good if this is your only use of if the player consents... phrase.

Note that the comment-labeled line is in the 6M62 version but not the 6G60 version. It won’t matter for your project, since it is Glulx-specific.

For the detail-oriented, there is an interesting item to note about the use of the read command here: I don’t think that DM4 ever mentions that a third parameter (the DrawStatusLine() routine) can be supplied. This may be an undocumented I6 feature that allows a type of usage specified in the Z-Machine Standards for the @read opcode.

3 Likes

It’s not. It just calls the third argument, DrawStatusLine(), before the @read/@aread opcode. You could do it yourself for the same result.

I think the point is just to be consistent with V3, where the interpreter redraws the status line automatically at read time. It’s not a very useful sort of consistency though.

1 Like

Agreed, this is indeed an awesome thing to know – it saved me 0x9000 bytes in I7!

But given that the bug was fixed in 6L, and I couldn’t see where or how, I figured I’d ask about if the solution was simple. The answers teach me so much about I6–I may never use it as a main language, but it demystifies the compiler for me a lot. Thanks, all.