Glulx/FyreVM Problem

Hey folks,

If you don’t know Glulx opcodes, this posting is not for you. Nothing to see, move along.

Note the trace file at: dl.dropboxusercontent.com/u/336 … aceops.txt

Within is a looping trace of opcodes that never asks for line or key input and keeps running the interpreter loop. I’m pretty sure this code used to work, but am not sure what’s happening.

This happens after loading FyreVM with a game file and a save file. So it just reset all the internals from a Quetzal file and starts up the loop.

If you can read Glulx opcodes, can you give me a clue what’s happening?

Thanks,

David C.
textfyre.com

I’m a little confused about where we’re supposed to be looking. Are you saying that the whole trace is looping?

Edit: Oh, hold on. Maybe I get it.

Okay, I think I see—there’s a repeated bundle in there which was just a little to long for me to notice by just scrolling through. You didn’t happen to compile the I6 with the -k flag, did you? (Or, if you can recompile with only that flag changed, that would work too.) If so, the gameinfo.dbg file would tell you the names of these functions, which might be more helpful that a summary of the opcodes. But I can start summarizing in the meantime, in case not.

This is an I7 game. I theoretically could compile the game from CL, but I’m not sure exactly how I would do that (been a long time).

If you have a recent enough version of the Windows IDE, it was pass the -k flag for you. If not, your best bet would be to copy the commands from the progress pane after a successful compile (though you’ll have to figure out what directory they’re running from).

Okay, I knew one of these looked familiar. I’m pretty sure the cycle aloadb, jge, add, jump, mul comes from BlkSize in Flex.i6t. That should narrow down the possibilities for the other functions…the caller looks like BlkValueExtent…

Got the debug info…

dl.dropboxusercontent.com/u/336 … /debug.txt

Oh, I know this game :slight_smile:. Unfortunately, this is I7 debug info. I’m afraid that won’t help (and it’s mildly spoilerly).

Not terribly concerned about spoilers in this forum. (:

vaporware pointed out I should verify my save data…which I’m doing now. Should have results shortly.

Since I’m at a slow point again, I’ll post that what I’ve worked out so far:

int_1502994 -> LIST_OF_TY_CopyRawArray
int_1478162 -> BlkType
int_1480497 -> BlkValueExtent
int_1478174 -> BlkSize
int_1480886 -> BlkValueWrite
int_1480669 -> BlkValueRead
int_59120 -> KOVComparisonFunction
int_1481483 -> BlkValueCompare
int_59375 -> KOVSupportFunction
int_1502789 -> LIST_OF_TY_Support

Edit: Got as far back as determining that the culprit calls BlkValueCreate(85,0,64), with LIST_OF_TY = 85, but discussion on the MUD suggests this is no longer useful information.

So it looks like I’ve handcuffed myself a bit.

FyreVM and my EngineWrapper for Zifmia have gone through changes over the years. When I was building Zifmia (web based IF), I decided to disregard Save/Restore and serialize the entire EngineWrapper to a byte array and store it in an OO database.

Somewhere along the line, I seemed to have broken the save/restore code.

So my choices are:

  1. build a test program to resolve the save/restore routines.
  2. use a 3rd party binary serialization routine (protobuf) and use that instead of Quetzal save files.

I was —> <— to being done with this thing.

David C.
www.textfyre.com

So proto-buf is a no go. It requires you tag everything with [ProtoMember(n)] and I’m pretty sure it doesn’t work the way the built-in .NET binaryformatter works. I know for a fact that the built-in binary serialization handles private members automatically. None of the other serialization routines work that way.

There are only two places where the code is significantly different in the WinRT version of FyreVM: The InitOpcodeDict() routine required changes for WinRT:

Old Version:

private void InitOpcodeDict() { MethodInfo[] methods = typeof(Engine).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance); foreach (MethodInfo mi in methods) { object[] attrs = mi.GetCustomAttributes(typeof(OpcodeAttribute), false); if (attrs.Length > 0) { OpcodeAttribute attr = (OpcodeAttribute)(attrs[0]); Delegate handler = Delegate.CreateDelegate(typeof(OpcodeHandler), this, mi); opcodeDict.Add(attr.Number, new Opcode(attr, (OpcodeHandler)handler)); } }

WinRT Version:

private void InitOpcodeDict() { List<MethodInfo> methods = typeof(Engine).GetRuntimeMethods().Where<MethodInfo>(info => !info.IsStatic && !info.IsPublic).ToList<MethodInfo>(); foreach (MethodInfo mi in methods) { List<Attribute> attrs = mi.GetCustomAttributes(typeof(OpcodeAttribute), false).ToList<Attribute>(); if (attrs.Count > 0) { OpcodeAttribute attr = (OpcodeAttribute)(attrs[0]); Delegate handler = mi.CreateDelegate(typeof(OpcodeHandler), this); opcodeDict.Add(attr.Number, new Opcode(attr, (OpcodeHandler)handler)); } } }

…and the WriteToStream() routine in the Quetzal class:

[code] public void WriteToStream(Stream stream)
{
BigEndian.WriteInt32(stream, FORM); // IFF tag
BigEndian.WriteInt32(stream, 0); // file length (filled in later)
BigEndian.WriteInt32(stream, IFZS); // FORM sub-ID for Quetzal

        uint totalSize = 4; // includes sub-ID
        foreach (KeyValuePair<uint, byte[]> pair in chunks)
        {
            BigEndian.WriteInt32(stream, pair.Key);                 // chunk type
            BigEndian.WriteInt32(stream, (uint)pair.Value.Length);  // chunk length
            stream.Write(pair.Value, 0, pair.Value.Length);         // chunk data
            totalSize += 8 + (uint)(pair.Value.Length);
        }

        if (totalSize % 2 == 1)
            stream.WriteByte(0);    // padding (not counted in file length)

        stream.Seek(4, SeekOrigin.Begin);
        BigEndian.WriteInt32(stream, totalSize);
        //stream.SetLength(totalSize);
    }

[/code]

Note the commented line at the end there. That is not commented out in the non-WinRT version of FyreVM.

I can’t explain the infinite loop.

David C.
www.textfyre.com