diff --git a/engines/agos/agos.h b/engines/agos/agos.h index 263811b..1d6f385 100644 --- a/engines/agos/agos.h +++ b/engines/agos/agos.h @@ -1285,6 +1285,8 @@ class AGOSEngine_PN : public AGOSEngine { void introSeq(); void setupBoxes(); int readfromline(); + + void printStackChain(); public: AGOSEngine_PN(OSystem *system); ~AGOSEngine_PN(); @@ -1359,6 +1361,10 @@ public: void opn_opcode63(); StackFrame *_stackbase; + struct { + int val; + void *tag; + } _longJmpEmulation; byte *_dataBase, *_textBase; uint32 _dataBaseSize, _textBaseSize; diff --git a/engines/agos/pn.cpp b/engines/agos/pn.cpp index d92efa9..e5bac77 100644 --- a/engines/agos/pn.cpp +++ b/engines/agos/pn.cpp @@ -34,6 +34,10 @@ namespace AGOS { AGOSEngine_PN::AGOSEngine_PN(OSystem *system) : AGOSEngine(system) { + + _stackbase = 0; + _longJmpEmulation.val = 0; + _longJmpEmulation.tag = 0; _dataBase = 0; _dataBaseSize = 0; @@ -70,7 +74,7 @@ AGOSEngine_PN::AGOSEngine_PN(OSystem *system) _objects = 0; _objectCountS = 0; - _bp = 0; + _bp = 0; _xofs = 0; _havinit = 0; _seed = 0; diff --git a/engines/agos/script_pn.cpp b/engines/agos/script_pn.cpp index 4f9aab2..c20e425 100644 --- a/engines/agos/script_pn.cpp +++ b/engines/agos/script_pn.cpp @@ -30,10 +30,23 @@ namespace AGOS { +#define LONGJMP_EMU + enum { kJmpClassNum = -1 }; +void AGOSEngine_PN::printStackChain() { + StackFrame *stackbase = _stackbase; + if (!stackbase) return; + printf(" "); + while (stackbase) { + printf(" > %p", (const void *)stackbase); + stackbase = stackbase->nextframe; + } + printf("\n"); +} + #define OPCODE(x) _OPCODE(AGOSEngine_PN, x) void AGOSEngine_PN::setupOpcodes() { @@ -324,8 +337,16 @@ void AGOSEngine_PN::opn_opcode24() { // That value then is returned to actCallD, which once again // returns it. In the end, this amounts to a setScriptReturn(true) // (but possibly in a different level than the current one). +printf("opn_opcode24: classnum %d, savearea %p, _cjmpbuff %p (%d)\n", + _stackbase ? _stackbase->classnum : -1000, _stackbase ? (const void *)_stackbase->savearea : 0, (const void *)_cjmpbuff, _stackbase && _cjmpbuff == _stackbase->savearea); +printStackChain(); +#ifdef LONGJMP_EMU + _longJmpEmulation.val = 2; + _longJmpEmulation.tag = _stackbase->savearea; +#else longjmp(*(_stackbase->savearea), 2); setScriptReturn(false); +#endif } void AGOSEngine_PN::opn_opcode25() { @@ -334,8 +355,16 @@ void AGOSEngine_PN::opn_opcode25() { // That value then is returned to actCallD, which once again // returns it. In the end, this amounts to a setScriptReturn(false) // (but possibly in a different level than the current one). +printf("opn_opcode25: classnum %d, savearea %p, _cjmpbuff %p (%d)\n", + _stackbase ? _stackbase->classnum : -1000, _stackbase ? (const void *)_stackbase->savearea : 0, (const void *)_cjmpbuff, _stackbase && _cjmpbuff == _stackbase->savearea); +printStackChain(); +#ifdef LONGJMP_EMU + _longJmpEmulation.val = 1; + _longJmpEmulation.tag = _stackbase->savearea; +#else longjmp(*(_stackbase->savearea), 1); setScriptReturn(false); +#endif } void AGOSEngine_PN::opn_opcode26() { @@ -354,15 +383,26 @@ void AGOSEngine_PN::opn_opcode27() { void AGOSEngine_PN::opn_opcode28() { addstack(varval()); _stackbase->savearea = _cjmpbuff; +printf("opn_opcode28: classnum %d, _cjmpbuff %p\n", + _stackbase ? _stackbase->classnum : -1000, (const void *)_cjmpbuff); +printStackChain(); setScriptReturn(false); } void AGOSEngine_PN::opn_opcode29() { popstack(varval()); - // Jump back to the last doline indicated by the top stackfram. + // Jump back to the last doline indicated by the top stackframe. // The -1 tells it to simply go on with its business. +printf("opn_opcode29: classnum %d, savearea %p, _cjmpbuff %p (%d)\n", + _stackbase ? _stackbase->classnum : -1000, _stackbase ? (const void *)_stackbase->savearea : 0, (const void *)_cjmpbuff, _stackbase && _cjmpbuff == _stackbase->savearea); +printStackChain(); +#ifdef LONGJMP_EMU + _longJmpEmulation.val = -1; + _longJmpEmulation.tag = _stackbase->savearea; +#else longjmp(*(_stackbase->savearea), -1); setScriptReturn(false); +#endif } void AGOSEngine_PN::opn_opcode30() { @@ -520,19 +560,38 @@ void AGOSEngine_PN::opn_opcode39() { } void AGOSEngine_PN::opn_opcode40() { - setScriptReturn(doaction() | doaction()); + int a = doaction(); +#ifdef LONGJMP_EMU + if (_longJmpEmulation.val != 0) + return; +#endif + int b = doaction(); + setScriptReturn(a | b); } void AGOSEngine_PN::opn_opcode41() { - setScriptReturn(doaction() & doaction()); + int a = doaction(); +#ifdef LONGJMP_EMU + if (_longJmpEmulation.val != 0) + return; +#endif + int b = doaction(); + setScriptReturn(a & b); } void AGOSEngine_PN::opn_opcode42() { - setScriptReturn(doaction() ^ doaction()); + int a = doaction(); +#ifdef LONGJMP_EMU + if (_longJmpEmulation.val != 0) + return; +#endif + int b = doaction(); + setScriptReturn(a ^ b); } void AGOSEngine_PN::opn_opcode43() { - setScriptReturn(!(doaction())); + int a = doaction(); + setScriptReturn(!a); } void AGOSEngine_PN::opn_opcode44() { @@ -877,18 +936,50 @@ int AGOSEngine_PN::doline(int needsave) { int x; jmp_buf *old_jmpbuf = NULL; jmp_buf *mybuf; + + assert(!_stackbase == !needsave); - mybuf = (jmp_buf *)malloc(sizeof(jmp_buf)); + mybuf = (jmp_buf *)calloc(1, sizeof(jmp_buf)); if (mybuf == NULL) error("doline: Out of memory - stack overflow"); +#ifdef LONGJMP_EMU + _longJmpEmulation.val = 0; + _longJmpEmulation.tag = mybuf; + +setjmp_emu_label: + if (_longJmpEmulation.tag != mybuf) + return 0; + + x = _longJmpEmulation.val; + _longJmpEmulation.val = 0; +#else x = setjmp(*mybuf); +#endif // Looking at the longjmp calls below, x can be -1, 1 or 2 // (and of course 0 when it returns directly, as always). + + if (x == 0) { + // Remember the previous active jmpbuf (gets restored + // when a longjmp with a positive param occurs). + old_jmpbuf = _cjmpbuff; + _cjmpbuff = mybuf; + if (needsave) + _stackbase->savearea = mybuf; + } + +printf("doline(%d): setjmp returned %d -- classnum %d, savearea %p, _cjmpbuff %p (%d)\n", + needsave, x, + _stackbase ? _stackbase->classnum : -1000, _stackbase ? (const void *)_stackbase->savearea : 0, (const void *)_cjmpbuff, + (x==0) || _stackbase && _cjmpbuff == _stackbase->savearea); +printStackChain(); + if (x > 0) { dumpstack(); // Restore the active jmpbuf to its previous value, // then return the longjmp value (will be 2-1=1 or 1-1=0). +printf(" restoring _cjmpbuff to old_jmpbuf %p, savearea %p (%d)\n", + (const void *)old_jmpbuf, _stackbase ? (const void *)_stackbase->savearea : 0, _stackbase && _stackbase->savearea == old_jmpbuf); _cjmpbuff = old_jmpbuf; free((char *)mybuf); return (x - 1); @@ -899,16 +990,11 @@ int AGOSEngine_PN::doline(int needsave) { // This is used to "return" over possibly multiple // layers of nested script invocations. // Kind of like throwing an exception. +printf(" restoring _cjmpbuff to mybuf %p, savearea %p (%d)\n", + (const void *)mybuf, _stackbase ? (const void *)_stackbase->savearea : 0, _stackbase && _stackbase->savearea == mybuf); _cjmpbuff = mybuf; goto carryon; } - - // Remember the previous active jmpbuf (gets restored - // above when a longjmp with a positive param occurs). - old_jmpbuf = _cjmpbuff; - _cjmpbuff = mybuf; - if (needsave) - _stackbase->savearea = mybuf; do { _linct = ((*_linebase) & 127) - 1; @@ -922,6 +1008,10 @@ int AGOSEngine_PN::doline(int needsave) { carryon: do { x = doaction(); +#ifdef LONGJMP_EMU + if (_longJmpEmulation.val != 0) + goto setjmp_emu_label; +#endif } while (x && !shouldQuit()); skipln: @@ -1063,7 +1153,7 @@ void AGOSEngine_PN::addstack(int type) { StackFrame *a; int i; - a = (StackFrame *)malloc(sizeof(StackFrame)); + a = (StackFrame *)calloc(1, sizeof(StackFrame)); if (a == NULL) error("addstack: Out of memory - stack overflow"); @@ -1100,6 +1190,8 @@ void AGOSEngine_PN::junkstack() { error("junkstack: Stack underflow or unknown longjmp"); a = _stackbase->nextframe; +printf("junkstack: classnum %d, savearea %p, _cjmpbuff %p (%d)\n", + _stackbase ? _stackbase->classnum : -1000, _stackbase ? (const void *)_stackbase->savearea : 0, (const void *)_cjmpbuff, _stackbase && _cjmpbuff == _stackbase->savearea); if (_stackbase->classnum == kJmpClassNum) free((char *)_stackbase->savearea); free((char *)_stackbase); @@ -1107,10 +1199,16 @@ void AGOSEngine_PN::junkstack() { } void AGOSEngine_PN::popstack(int type) { - int i; + int i = 0; - while ((_stackbase != NULL) && (_stackbase->classnum != type)) + while ((_stackbase != NULL) && (_stackbase->classnum != type)) { junkstack(); + ++i; + } + +printf("popstack(%d): dropped %d, now: classnum %d, savearea %p, _cjmpbuff %p (%d)\n", + type, i, + _stackbase ? _stackbase->classnum : -1000, _stackbase ? (const void *)_stackbase->savearea : 0, (const void *)_cjmpbuff, _stackbase && _cjmpbuff == _stackbase->savearea); if (_stackbase == NULL) error("popstack: Stack underflow or unknown longjmp");