From 4420b15d9f59227cc57bb60d5b38c73639437a62 Mon Sep 17 00:00:00 2001 From: Torbjörn Andersson Date: Sun, 14 Dec 2003 16:33:27 +0000 Subject: Mostly cleanups, but I believe this also fixes an endian problem in the "restart script" opcode. To actually verify this, though, I'd need to find a case where any other script than script 0 at offset 0 is restarted... svn-id: r11638 --- sword2/interpreter.cpp | 891 ++++++++++++++++++++++++------------------------- sword2/interpreter.h | 6 +- sword2/logic.h | 14 +- sword2/speech.cpp | 6 +- 4 files changed, 437 insertions(+), 480 deletions(-) (limited to 'sword2') diff --git a/sword2/interpreter.cpp b/sword2/interpreter.cpp index d30ecee760..15a990e054 100644 --- a/sword2/interpreter.cpp +++ b/sword2/interpreter.cpp @@ -24,197 +24,188 @@ namespace Sword2 { -// This file serves two purposes. It is compiled as part of the test functions -// of Linc, and also as part of the game - -// I assume Linc was the name of some sort of development tool. Anyway, I've -// removed the pieces of code that were labelled as INSIDE_LINC, because we -// don't have it, and probably wouldn't have much use for it if we did. - // The machine code table -#define MAX_FN_NUMBER 117 - #define OPCODE(x) { &Logic::x, #x } -void Logic::setupOpcodes(void) { - static const OpcodeEntry opcodes[MAX_FN_NUMBER + 1] = { - /* 00 */ - OPCODE(fnTestFunction), - OPCODE(fnTestFlags), - OPCODE(fnRegisterStartPoint), - OPCODE(fnInitBackground), - /* 04 */ - OPCODE(fnSetSession), - OPCODE(fnBackSprite), - OPCODE(fnSortSprite), - OPCODE(fnForeSprite), - /* 08 */ - OPCODE(fnRegisterMouse), - OPCODE(fnAnim), - OPCODE(fnRandom), - OPCODE(fnPreLoad), - /* 0C */ - OPCODE(fnAddSubject), - OPCODE(fnInteract), - OPCODE(fnChoose), - OPCODE(fnWalk), - /* 10 */ - OPCODE(fnWalkToAnim), - OPCODE(fnTurn), - OPCODE(fnStandAt), - OPCODE(fnStand), - /* 14 */ - OPCODE(fnStandAfterAnim), - OPCODE(fnPause), - OPCODE(fnMegaTableAnim), - OPCODE(fnAddMenuObject), - /* 18 */ - OPCODE(fnStartConversation), - OPCODE(fnEndConversation), - OPCODE(fnSetFrame), - OPCODE(fnRandomPause), - /* 1C */ - OPCODE(fnRegisterFrame), - OPCODE(fnNoSprite), - OPCODE(fnSendSync), - OPCODE(fnUpdatePlayerStats), - /* 20 */ - OPCODE(fnPassGraph), - OPCODE(fnInitFloorMouse), - OPCODE(fnPassMega), - OPCODE(fnFaceXY), - /* 24 */ - OPCODE(fnEndSession), - OPCODE(fnNoHuman), - OPCODE(fnAddHuman), - OPCODE(fnWeWait), - /* 28 */ - OPCODE(fnTheyDoWeWait), - OPCODE(fnTheyDo), - OPCODE(fnWalkToTalkToMega), - OPCODE(fnFadeDown), - /* 2C */ - OPCODE(fnISpeak), - OPCODE(fnTotalRestart), - OPCODE(fnSetWalkGrid), - OPCODE(fnSpeechProcess), - /* 30 */ - OPCODE(fnSetScaling), - OPCODE(fnStartEvent), - OPCODE(fnCheckEventWaiting), - OPCODE(fnRequestSpeech), - /* 34 */ - OPCODE(fnGosub), - OPCODE(fnTimedWait), - OPCODE(fnPlayFx), - OPCODE(fnStopFx), - /* 38 */ - OPCODE(fnPlayMusic), - OPCODE(fnStopMusic), - OPCODE(fnSetValue), - OPCODE(fnNewScript), - /* 3C */ - OPCODE(fnGetSync), - OPCODE(fnWaitSync), - OPCODE(fnRegisterWalkGrid), - OPCODE(fnReverseMegaTableAnim), - /* 40 */ - OPCODE(fnReverseAnim), - OPCODE(fnAddToKillList), - OPCODE(fnSetStandbyCoords), - OPCODE(fnBackPar0Sprite), - /* 44 */ - OPCODE(fnBackPar1Sprite), - OPCODE(fnForePar0Sprite), - OPCODE(fnForePar1Sprite), - OPCODE(fnSetPlayerActionEvent), - /* 48 */ - OPCODE(fnSetScrollCoordinate), - OPCODE(fnStandAtAnim), - OPCODE(fnSetScrollLeftMouse), - OPCODE(fnSetScrollRightMouse), - /* 4C */ - OPCODE(fnColour), - OPCODE(fnFlash), - OPCODE(fnPreFetch), - OPCODE(fnGetPlayerSaveData), - /* 50 */ - OPCODE(fnPassPlayerSaveData), - OPCODE(fnSendEvent), - OPCODE(fnAddWalkGrid), - OPCODE(fnRemoveWalkGrid), - /* 54 */ - OPCODE(fnCheckForEvent), - OPCODE(fnPauseForEvent), - OPCODE(fnClearEvent), - OPCODE(fnFaceMega), - /* 58 */ - OPCODE(fnPlaySequence), - OPCODE(fnShadedSprite), - OPCODE(fnUnshadedSprite), - OPCODE(fnFadeUp), - /* 60 */ - OPCODE(fnDisplayMsg), - OPCODE(fnSetObjectHeld), - OPCODE(fnAddSequenceText), - OPCODE(fnResetGlobals), - /* 64 */ - OPCODE(fnSetPalette), - OPCODE(fnRegisterPointerText), - OPCODE(fnFetchWait), - OPCODE(fnRelease), - /* 68 */ - OPCODE(fnPrepareMusic), - OPCODE(fnSoundFetch), - OPCODE(fnPrepareMusic), // Again, apparently - OPCODE(fnSmackerLeadIn), - /* 6C */ - OPCODE(fnSmackerLeadOut), - OPCODE(fnStopAllFx), - OPCODE(fnCheckPlayerActivity), - OPCODE(fnResetPlayerActivityDelay), - /* 70 */ - OPCODE(fnCheckMusicPlaying), - OPCODE(fnPlayCredits), - OPCODE(fnSetScrollSpeedNormal), - OPCODE(fnSetScrollSpeedSlow), - /* 74 */ - OPCODE(fnRemoveChooser), - OPCODE(fnSetFxVolAndPan), - OPCODE(fnSetFxVol), - OPCODE(fnRestoreGame), - /* 78 */ - OPCODE(fnRefreshInventory), - OPCODE(fnChangeShadows) - }; - - _opcodes = opcodes; -} - -int32 Logic::executeOpcode(int i, int32 *params) { - OpcodeProc op = _opcodes[i].proc; - - debug(5, "%s", _opcodes[i].desc); - - return (this->*op) (params); -} - -// FIXME: Is the handling of script local variables really alignment-safe? - -#define CHECKSTACKPOINTER2 assert(stackPointer2 >= 0 && stackPointer2 < STACK_SIZE); -#define PUSHONSTACK(x) { stack2[stackPointer2] = (x); stackPointer2++; CHECKSTACKPOINTER2 } -#define POPOFFSTACK(x) { x = stack2[stackPointer2 - 1]; stackPointer2--; CHECKSTACKPOINTER2 } -#define DOOPERATION(x) { stack2[stackPointer2 - 2] = (x); stackPointer2--; CHECKSTACKPOINTER2 } +typedef int32 (Logic::*OpcodeProc)(int32 *); +struct OpcodeEntry { + OpcodeProc proc; + const char *desc; +}; + +static const OpcodeEntry opcodes[] = { + /* 00 */ + OPCODE(fnTestFunction), + OPCODE(fnTestFlags), + OPCODE(fnRegisterStartPoint), + OPCODE(fnInitBackground), + /* 04 */ + OPCODE(fnSetSession), + OPCODE(fnBackSprite), + OPCODE(fnSortSprite), + OPCODE(fnForeSprite), + /* 08 */ + OPCODE(fnRegisterMouse), + OPCODE(fnAnim), + OPCODE(fnRandom), + OPCODE(fnPreLoad), + /* 0C */ + OPCODE(fnAddSubject), + OPCODE(fnInteract), + OPCODE(fnChoose), + OPCODE(fnWalk), + /* 10 */ + OPCODE(fnWalkToAnim), + OPCODE(fnTurn), + OPCODE(fnStandAt), + OPCODE(fnStand), + /* 14 */ + OPCODE(fnStandAfterAnim), + OPCODE(fnPause), + OPCODE(fnMegaTableAnim), + OPCODE(fnAddMenuObject), + /* 18 */ + OPCODE(fnStartConversation), + OPCODE(fnEndConversation), + OPCODE(fnSetFrame), + OPCODE(fnRandomPause), + /* 1C */ + OPCODE(fnRegisterFrame), + OPCODE(fnNoSprite), + OPCODE(fnSendSync), + OPCODE(fnUpdatePlayerStats), + /* 20 */ + OPCODE(fnPassGraph), + OPCODE(fnInitFloorMouse), + OPCODE(fnPassMega), + OPCODE(fnFaceXY), + /* 24 */ + OPCODE(fnEndSession), + OPCODE(fnNoHuman), + OPCODE(fnAddHuman), + OPCODE(fnWeWait), + /* 28 */ + OPCODE(fnTheyDoWeWait), + OPCODE(fnTheyDo), + OPCODE(fnWalkToTalkToMega), + OPCODE(fnFadeDown), + /* 2C */ + OPCODE(fnISpeak), + OPCODE(fnTotalRestart), + OPCODE(fnSetWalkGrid), + OPCODE(fnSpeechProcess), + /* 30 */ + OPCODE(fnSetScaling), + OPCODE(fnStartEvent), + OPCODE(fnCheckEventWaiting), + OPCODE(fnRequestSpeech), + /* 34 */ + OPCODE(fnGosub), + OPCODE(fnTimedWait), + OPCODE(fnPlayFx), + OPCODE(fnStopFx), + /* 38 */ + OPCODE(fnPlayMusic), + OPCODE(fnStopMusic), + OPCODE(fnSetValue), + OPCODE(fnNewScript), + /* 3C */ + OPCODE(fnGetSync), + OPCODE(fnWaitSync), + OPCODE(fnRegisterWalkGrid), + OPCODE(fnReverseMegaTableAnim), + /* 40 */ + OPCODE(fnReverseAnim), + OPCODE(fnAddToKillList), + OPCODE(fnSetStandbyCoords), + OPCODE(fnBackPar0Sprite), + /* 44 */ + OPCODE(fnBackPar1Sprite), + OPCODE(fnForePar0Sprite), + OPCODE(fnForePar1Sprite), + OPCODE(fnSetPlayerActionEvent), + /* 48 */ + OPCODE(fnSetScrollCoordinate), + OPCODE(fnStandAtAnim), + OPCODE(fnSetScrollLeftMouse), + OPCODE(fnSetScrollRightMouse), + /* 4C */ + OPCODE(fnColour), + OPCODE(fnFlash), + OPCODE(fnPreFetch), + OPCODE(fnGetPlayerSaveData), + /* 50 */ + OPCODE(fnPassPlayerSaveData), + OPCODE(fnSendEvent), + OPCODE(fnAddWalkGrid), + OPCODE(fnRemoveWalkGrid), + /* 54 */ + OPCODE(fnCheckForEvent), + OPCODE(fnPauseForEvent), + OPCODE(fnClearEvent), + OPCODE(fnFaceMega), + /* 58 */ + OPCODE(fnPlaySequence), + OPCODE(fnShadedSprite), + OPCODE(fnUnshadedSprite), + OPCODE(fnFadeUp), + /* 5C */ + OPCODE(fnDisplayMsg), + OPCODE(fnSetObjectHeld), + OPCODE(fnAddSequenceText), + OPCODE(fnResetGlobals), + /* 60 */ + OPCODE(fnSetPalette), + OPCODE(fnRegisterPointerText), + OPCODE(fnFetchWait), + OPCODE(fnRelease), + /* 64 */ + OPCODE(fnPrepareMusic), + OPCODE(fnSoundFetch), + OPCODE(fnPrepareMusic), // Again, apparently + OPCODE(fnSmackerLeadIn), + /* 68 */ + OPCODE(fnSmackerLeadOut), + OPCODE(fnStopAllFx), + OPCODE(fnCheckPlayerActivity), + OPCODE(fnResetPlayerActivityDelay), + /* 6C */ + OPCODE(fnCheckMusicPlaying), + OPCODE(fnPlayCredits), + OPCODE(fnSetScrollSpeedNormal), + OPCODE(fnSetScrollSpeedSlow), + /* 70 */ + OPCODE(fnRemoveChooser), + OPCODE(fnSetFxVolAndPan), + OPCODE(fnSetFxVol), + OPCODE(fnRestoreGame), + /* 74 */ + OPCODE(fnRefreshInventory), + OPCODE(fnChangeShadows) +}; + +#define push(value) \ +do { \ + assert(stackPtr < ARRAYSIZE(stack)); \ + stack[stackPtr++] = (value); \ +} while (false); + +#define pop() (assert(stackPtr < ARRAYSIZE(stack)), stack[--stackPtr]) void Logic::setGlobalInterpreterVariables(int32 *vars) { _globals = vars; } int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) { - bool checkPyramidBug = false; + // Interestingly, unlike our BASS engine the stack is a local variable. + // This has some interesting implications which may or may not be + // necessary to the BS2 engine. - #define STACK_SIZE 10 + int32 stack[STACK_SIZE]; + int32 stackPtr = 0; + + bool checkPyramidBug = false; _standardHeader *header = (_standardHeader *) scriptData; scriptData += sizeof(_standardHeader) + sizeof(_object_hub); @@ -227,35 +218,35 @@ int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) { // Initialise some stuff - int ip = 0; // Code pointer - int curCommand,parameter, value; // Command and parameter variables - int32 stack2[STACK_SIZE]; // The current stack - int32 stackPointer2 = 0; // Position within stack - int parameterReturnedFromMcodeFunction = 0; // Allow scripts to return things - int savedStartOfMcode = 0; // For saving start of mcode commands - - int count; - int retVal; - int caseCount, foundCase; - int scriptNumber, foundScript; - const char *tempScrPtr; + uint32 ip = 0; // Code pointer + int scriptNumber; // Get the start of variables and start of code - debug(5, "Enter interpreter data %x, object %x, offset %d", scriptData, objectData, *offset); - - // FIXME: 'scriptData' and 'variables' used to be const. However, - // this code writes into 'variables' so it can not be const. - char *variables = scriptData + sizeof(int32); + uint32 *variables = (uint32 *) (scriptData + sizeof(int32)); const char *code = scriptData + (int32) READ_LE_UINT32(scriptData) + sizeof(int32); uint32 noScripts = (int32) READ_LE_UINT32(code); + code += sizeof(int32); + + const uint32 *offsetTable = (const uint32 *) code; + if (*offset < noScripts) { - ip = READ_LE_UINT32((const int *) code + *offset + 1); - debug(5, "Start script %d with offset %d", *offset, ip); + ip = READ_LE_UINT32(&offsetTable[*offset]); + scriptNumber = *offset; + debug(4, "Start script %d with offset %d", *offset, ip); } else { + uint i; + ip = *offset; - debug(5, "Start script with offset %d", ip); + + for (i = 1; i < noScripts; i++) { + if (ip < READ_LE_UINT32(code + 4 * i)) + break; + } + + scriptNumber = i - 1; + debug(4, "Resume script %d with offset %d", scriptNumber, ip); } // WORKAROUND: The dreaded pyramid makes the torch untakeable when you @@ -273,19 +264,13 @@ int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) { // So if his click hander (action script number 2) finishes, and // variable 913 is 1, we set it back to 0 manually. - if (strcmp((char *) header->name, "titipoco_81") == 0 && - ip >= (int32) READ_LE_UINT32((const int *) code + 3) && - ip < (int32) READ_LE_UINT32((const int *) code + 4)) { - checkPyramidBug = true; - } + checkPyramidBug = strcmp((char *) header->name, "titipoco_81") == 0 && scriptNumber == 2; - code += noScripts * sizeof(int32) + sizeof(int32); + code += noScripts * sizeof(int32); -#ifdef DONTPROCESSSCRIPTCHECKSUM - code += sizeof(int32) * 3; -#else // Code should nop be pointing at an identifier and a checksum const int *checksumBlock = (const int *) code; + code += sizeof(int32) * 3; if (READ_LE_UINT32(checksumBlock) != 12345678) { @@ -296,25 +281,37 @@ int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) { int codeLen = READ_LE_UINT32(checksumBlock + 1); int checksum = 0; - for (count = 0; count < codeLen; count++) - checksum += (unsigned char) code[count]; + for (int i = 0; i < codeLen; i++) + checksum += (unsigned char) code[i]; if (checksum != (int32) READ_LE_UINT32(checksumBlock + 2)) { error("Checksum error in object %s", header->name); return 0; } -#endif - int runningScript = 1; + bool runningScript = true; + + int parameterReturnedFromMcodeFunction = 0; // Allow scripts to return things + int savedStartOfMcode = 0; // For saving start of mcode commands while (runningScript) { + int32 a, b; + int curCommand, parameter, value; // Command and parameter variables + int retVal; + int caseCount; + bool foundCase; + int32 ptrval; + curCommand = code[ip++]; switch (curCommand) { + + // Script-related opcodes + case CP_END_SCRIPT: // End the script debug(5, "End script"); - runningScript = 0; + runningScript = false; // WORKAROUND: Pyramid Bug. See explanation above. @@ -323,80 +320,131 @@ int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) { _globals[913] = 0; } + break; + case CP_QUIT: + // Quit out for a cycle + debug(5, "Quit script for a cycle"); + *offset = ip; + return 0; + case CP_TERMINATE: + // Quit out immediately without affecting the offset + // pointer + debug(5, "Terminate script"); + return 3; + case CP_RESTART_SCRIPT: + // Start the script again + debug(5, "Restart script"); + ip = READ_LE_UINT32(&offsetTable[scriptNumber]); + break; + + // Stack-related opcodes + + case CP_PUSH_INT32: + // Push a long word value on to the stack + Read32ip(parameter); + debug(5, "Push int32 %d", parameter); + push(parameter); break; case CP_PUSH_LOCAL_VAR32: // Push the contents of a local variable Read16ip(parameter); - debug(5, "Push local var %d (%d)", parameter, *(int32 *) (variables + parameter)); - PUSHONSTACK(*(int32 *) (variables + parameter)); + parameter /= 4; + debug(5, "Push local var %d (%d)", parameter, variables[parameter]); + push(variables[parameter]); break; case CP_PUSH_GLOBAL_VAR32: // Push a global variable Read16ip(parameter); assert(_globals); debug(5, "Push global var %d (%d)", parameter, _globals[parameter]); - PUSHONSTACK(_globals[parameter]); + push(_globals[parameter]); + break; + case CP_PUSH_LOCAL_ADDR: + // push the address of a local variable + Read16ip(parameter); + parameter /= 4; + ptrval = _vm->_memory->ptrToInt((const uint8 *) &variables[parameter]); + debug(5, "Push address of local variable %d (%x)", parameter, ptrval); + push(ptrval); + break; + case CP_PUSH_STRING: + // Push the address of a string on to the stack + // Get the string size + Read8ip(parameter); + + // ip points to the string + ptrval = _vm->_memory->ptrToInt((const uint8 *) (code + ip)); + debug(5, "Push address of string (%x)\n", ptrval); + push(ptrval); + ip += (parameter + 1); + break; + case CP_PUSH_DEREFERENCED_STRUCTURE: + // Push the address of a dereferenced structure + Read32ip(parameter); + ptrval = _vm->_memory->ptrToInt((const uint8 *) (objectData + sizeof(int32) + sizeof(_standardHeader) + sizeof(_object_hub) + parameter)); + debug(5, "Push address of far variable (%x)", ptrval); + push(ptrval); break; case CP_POP_LOCAL_VAR32: // Pop a value into a local word variable Read16ip(parameter); - POPOFFSTACK(value); + parameter /= 4; + value = pop(); debug(5, "Pop %d into local var %d", value, parameter); - *((int32 *) (variables + parameter)) = value; + variables[parameter] = value; break; - case CP_CALL_MCODE: - // Call an mcode routine + case CP_POP_GLOBAL_VAR32: + // Pop a global variable Read16ip(parameter); - assert(parameter <= MAX_FN_NUMBER); - // amount to adjust stack by (no of parameters) - Read8ip(value); - retVal = executeOpcode(parameter, stack2 + stackPointer2 - value); - - stackPointer2 -= value; - CHECKSTACKPOINTER2 - - switch (retVal & 7) { - case IR_STOP: - // Quit out for a cycle - *offset = ip; - return 0; - case IR_CONT: - // Continue as normal - break; - case IR_TERMINATE: - // Return without updating the - // offset - return 2; - case IR_REPEAT: - // Return setting offset to - // start of this function call - *offset = savedStartOfMcode; - return 0; - case IR_GOSUB: - // that's really neat - *offset = ip; - return 2; - default: - assert(false); - } - parameterReturnedFromMcodeFunction = retVal >> 3; + value = pop(); + debug(5, "Pop %d into global var %d", value, parameter); + _globals[parameter] = value; break; - case CP_PUSH_LOCAL_ADDR: - // push the address of a local variable + case CP_ADDNPOP_LOCAL_VAR32: Read16ip(parameter); - debug(5, "Push address of local variable %d (%x)", parameter, _vm->_memory->ptrToInt((const uint8 *) (variables + parameter))); - PUSHONSTACK(_vm->_memory->ptrToInt((uint8 *) (variables + parameter))); + parameter /= 4; + value = pop(); + variables[parameter] += value; + debug(5, "+= %d into local var %d -> %d", value, parameter, variables[parameter]); break; - case CP_PUSH_INT32: - // Push a long word value on to the stack - Read32ip(parameter); - debug(5, "Push int32 %d (%x)", parameter, parameter); - PUSHONSTACK(parameter); + case CP_SUBNPOP_LOCAL_VAR32: + Read16ip(parameter); + parameter /= 4; + value = pop(); + variables[parameter] -= value; + debug(5, "-= %d into local var %d -> %d", value, parameter, variables[parameter]); + break; + case CP_ADDNPOP_GLOBAL_VAR32: + // Add and pop a global variable + Read16ip(parameter); + value = pop(); + _globals[parameter] += value; + debug(5, "+= %d into global var %d -> %d", value, parameter, _globals[parameter]); + break; + case CP_SUBNPOP_GLOBAL_VAR32: + // Sub and pop a global variable + Read16ip(parameter); + value = pop(); + _globals[parameter] -= value; + debug(5, "-= %d into global var %d -> %d", value, parameter, _globals[parameter]); + break; + + // Jump opcodes + + case CP_SKIPONTRUE: + // Skip if the value on the stack is true + Read32ipLeaveip(parameter); + value = pop(); + debug(5, "Skip %d if %d is false", parameter, value); + if (!value) + ip += sizeof(int32); + else + ip += parameter; break; case CP_SKIPONFALSE: // Skip if the value on the stack is false Read32ipLeaveip(parameter); - POPOFFSTACK(value); + value = pop(); debug(5, "Skip %d if %d is false", parameter, value); if (value) ip += sizeof(int32); @@ -410,251 +458,170 @@ int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) { ip += parameter; break; case CP_SWITCH: - // 9 switch - POPOFFSTACK(value); + // switch + value = pop(); Read32ip(caseCount); // Search the cases - foundCase = 0; - for (count = 0; count < caseCount && !foundCase; count++) { + foundCase = false; + for (int i = 0; i < caseCount && !foundCase; i++) { if (value == (int32) READ_LE_UINT32(code + ip)) { // We have found the case, so lets // jump to it - foundCase = 1; + foundCase = true; ip += READ_LE_UINT32(code + ip + sizeof(int32)); } else ip += sizeof(int32) * 2; } - // If we found no matching case then use the - // default + // If we found no matching case then use the default if (!foundCase) ip += READ_LE_UINT32(code + ip); break; - case CP_ADDNPOP_LOCAL_VAR32: - Read16ip(parameter); - POPOFFSTACK(value); - *((int32 *) (variables + parameter)) += value; - debug(5, "+= %d into local var %d->%d", value, parameter, *(int32 *) (variables + parameter)); + case CP_SAVE_MCODE_START: + // Save the start position on an mcode instruction in + // case we need to restart it again + savedStartOfMcode = ip - 1; break; - case CP_SUBNPOP_LOCAL_VAR32: + case CP_CALL_MCODE: + // Call an mcode routine Read16ip(parameter); - POPOFFSTACK(value); - *((int32 *) (variables + parameter)) -= value; - debug(5, "-= %d into local var %d->%d", value, parameter, *(int32 *) (variables + parameter)); + assert(parameter < ARRAYSIZE(opcodes)); + // amount to adjust stack by (no of parameters) + Read8ip(value); + debug(5, "Calling '%s' with %d parameters", opcodes[parameter].desc, value); + stackPtr -= value; + assert(stackPtr >= 0); + retVal = (this->*opcodes[parameter].proc)(&stack[stackPtr]); + + switch (retVal & 7) { + case IR_STOP: + // Quit out for a cycle + *offset = ip; + return 0; + case IR_CONT: + // Continue as normal + break; + case IR_TERMINATE: + // Return without updating the offset + return 2; + case IR_REPEAT: + // Return setting offset to start of this + // function call + *offset = savedStartOfMcode; + return 0; + case IR_GOSUB: + // that's really neat + *offset = ip; + return 2; + default: + error("Bad return code (%d) from '%s'", opcodes[parameter].desc, retVal & 7); + } + parameterReturnedFromMcodeFunction = retVal >> 3; break; - case CP_SKIPONTRUE: - // Skip if the value on the stack is TRUE - Read32ipLeaveip(parameter); - POPOFFSTACK(value); - debug(5, "Skip %d if %d is false", parameter, value); - if (!value) - ip += sizeof(int32); - else - ip += parameter; + case CP_JUMP_ON_RETURNED: + // Jump to a part of the script depending on + // the return value from an mcode routine + + // Get the maximum value + Read8ip(parameter); + ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4); break; - case CP_POP_GLOBAL_VAR32: - // Pop a global variable - Read16ip(parameter); - POPOFFSTACK(value); - debug(5, "Pop %d into global var %d", value, parameter); -#ifdef TRACEGLOBALVARIABLESET - TRACEGLOBALVARIABLESET(parameter, value); -#endif + // Operators - _globals[parameter] = value; + case OP_ISEQUAL: + b = pop(); + a = pop(); + push(a == b); + debug(5, "operation %d == %d", a, b); break; - case CP_ADDNPOP_GLOBAL_VAR32: - // Add and pop a global variable - Read16ip(parameter); - POPOFFSTACK(value); - _globals[parameter] += value; - debug(5, "+= %d into global var %d->%d", value, parameter, _globals[parameter]); + case OP_NOTEQUAL: + b = pop(); + a = pop(); + push(a != b); + debug(5, "operation %d != %d", a, b); break; - case CP_SUBNPOP_GLOBAL_VAR32: - // Sub and pop a global variable - Read16ip(parameter); - POPOFFSTACK(value); - _globals[parameter] -= value; - debug(5, "-= %d into global var %d->%d", value, parameter, _globals[parameter]); + case OP_GTTHAN: + b = pop(); + a = pop(); + push(a > b); + debug(5, "operation %d > %d", a, b); break; - case CP_DEBUGON: - // Turn debugging on - _debugFlag = true; + case OP_LSTHAN: + b = pop(); + a = pop(); + push(a < b); + debug(5, "operation %d < %d", a, b); break; - case CP_DEBUGOFF: - // Turn debugging on - _debugFlag = false; + case OP_GTTHANE: + b = pop(); + a = pop(); + push(a >= b); + debug(5, "operation %d >= %d", a, b); break; - case CP_QUIT: - // Quit out for a cycle - *offset = ip; - return 0; - case CP_TERMINATE: - // Quit out immediately without affecting the offset - // pointer - return 3; - - // Operators - - case OP_ISEQUAL: - // '==' - debug(5, "%d == %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] == stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] == stack2[stackPointer2 - 1]); + case OP_LSTHANE: + b = pop(); + a = pop(); + push(a <= b); + debug(5, "operation %d <= %d", a, b); break; case OP_PLUS: - // '+' - debug(5, "%d + %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] + stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] + stack2[stackPointer2 - 1]); + b = pop(); + a = pop(); + push(a + b); + debug(5, "operation %d + %d", a, b); break; case OP_MINUS: - // '-' - debug(5, "%d - %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] - stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] - stack2[stackPointer2 - 1]); + b = pop(); + a = pop(); + push(a - b); + debug(5, "operation %d - %d", a, b); break; case OP_TIMES: - // '*' - debug(5, "%d * %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] * stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] * stack2[stackPointer2 - 1]); + b = pop(); + a = pop(); + push(a * b); + debug(5, "operation %d * %d", a, b); break; case OP_DIVIDE: - // '/' - debug(5, "%d / %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] / stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] / stack2[stackPointer2 - 1]); - break; - case OP_NOTEQUAL: - // '!=' - debug(5, "%d != %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] != stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] != stack2[stackPointer2 - 1]); + b = pop(); + a = pop(); + push(a / b); + debug(5, "operation %d / %d", a, b); break; case OP_ANDAND: - // '&&' - debug(5, "%d && %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] && stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] && stack2[stackPointer2 - 1]); - break; - case OP_GTTHAN: - // '>' - debug(5, "%d > %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] > stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] > stack2[stackPointer2 - 1]); + b = pop(); + a = pop(); + push(a && b); + debug(5, "operation %d && %d", a, b); break; - case OP_LSTHAN: - // '<' - debug(5, "%d < %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] < stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] < stack2[stackPointer2 - 1]); + case OP_OROR: + b = pop(); + a = pop(); + push(a || b); + debug(5, "operation %d || %d", a, b); break; - case CP_JUMP_ON_RETURNED: - // Jump to a part of the script depending on - // the return value from an mcode routine - // Get the maximum value - Read8ip(parameter); - ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4); + // Debugging opcodes, I think + + case CP_DEBUGON: + // Turn debugging on + _debugFlag = true; + break; + case CP_DEBUGOFF: + // Turn debugging on + _debugFlag = false; break; case CP_TEMP_TEXT_PROCESS: // Process a text line - // This was apparently used in Linc Read32ip(parameter); debug(5, "Process text id %d", parameter); break; - case CP_SAVE_MCODE_START: - // Save the start position on an mcode instruction in - // case we need to restart it again - savedStartOfMcode = ip - 1; - break; - case CP_RESTART_SCRIPT: - // Start the script again - // Do a ip search to find the script we are running - - tempScrPtr = scriptData + READ_LE_UINT32(scriptData) + sizeof(int32); - scriptNumber = 0; - foundScript = 0; - - for (count = 1; count < (int) noScripts && !foundScript; count++) { - if (ip < ((const int *) tempScrPtr)[count + 1]) { - scriptNumber = count - 1; - foundScript = 1; - } - } - - if (!foundScript) - scriptNumber = count - 1; - - // So we know what script we are running, lets restart - // it - - ip = ((const int *) tempScrPtr)[scriptNumber + 1]; - break; - case CP_PUSH_STRING: - // Push the address of a string on to the stack - // Get the string size - Read8ip(parameter); - - // ip points to the string - PUSHONSTACK(_vm->_memory->ptrToInt((const uint8 *) (code + ip))); - ip += (parameter + 1); - break; - case CP_PUSH_DEREFERENCED_STRUCTURE: - // Push the address of a dereferenced structure - Read32ip(parameter); - debug(5, "Push address of far variable (%x)", _vm->_memory->ptrToInt((const uint8 *) (objectData + sizeof(int32) + sizeof(_standardHeader) + sizeof(_object_hub) + parameter))); - PUSHONSTACK(_vm->_memory->ptrToInt((const uint8 *) (objectData + sizeof(int32) + sizeof(_standardHeader) + sizeof(_object_hub) + parameter))); - break; - case OP_GTTHANE: - // '>=' - debug(5, "%d >= %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] >= stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] >= stack2[stackPointer2 - 1]); - break; - case OP_LSTHANE: - // '<=' - debug(5, "%d <= %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] <= stack2[stackPointer2 - 1]); - DOOPERATION(stack2[stackPointer2 - 2] <= stack2[stackPointer2 - 1]); - break; - case OP_OROR: - // '||' - debug(5, "%d || %d -> %d", - stack2[stackPointer2 - 2], - stack2[stackPointer2 - 1], - stack2[stackPointer2 - 2] || stack2[stackPointer2 - 1]); - DOOPERATION (stack2[stackPointer2 - 2] || stack2[stackPointer2 - 1]); - break; default: - error("Interpreter error: Invalid token %d", curCommand); + error("Invalid script command %d", curCommand); return 3; } } diff --git a/sword2/interpreter.h b/sword2/interpreter.h index 4b0d4a1329..dc8881d56d 100644 --- a/sword2/interpreter.h +++ b/sword2/interpreter.h @@ -54,11 +54,11 @@ enum { CP_SKIPALWAYS = 8, // Skip a block of code CP_SWITCH = 9, // Switch on last stack value CP_ADDNPOP_LOCAL_VAR32 = 10, // Add to a local varible - CP_SUBNPOP_LOCAL_VAR32 = 11, // Subtract to a local variable + CP_SUBNPOP_LOCAL_VAR32 = 11, // Subtract from a local variable CP_SKIPONTRUE = 12, // Skip if the bottom value on the stack is true CP_POP_GLOBAL_VAR32 = 13, // Pop a global variable - CP_ADDNPOP_GLOBAL_VAR32 = 14, - CP_SUBNPOP_GLOBAL_VAR32 = 15, + CP_ADDNPOP_GLOBAL_VAR32 = 14, // Add to a global variable + CP_SUBNPOP_GLOBAL_VAR32 = 15, // Subtract from a global variable CP_DEBUGON = 16, // Turn debugging on CP_DEBUGOFF = 17, // Turn debugging off CP_QUIT = 18, // Quit for a cycle diff --git a/sword2/logic.h b/sword2/logic.h index 8a2dfbab86..86d67df318 100644 --- a/sword2/logic.h +++ b/sword2/logic.h @@ -30,6 +30,8 @@ namespace Sword2 { +#define STACK_SIZE 10 + #define MAX_events 10 // There won't be many, will there? Probably 2 at most i reckon @@ -59,14 +61,6 @@ private: // Set this to turn debugging on bool _debugFlag; - typedef int32 (Logic::*OpcodeProc)(int32 *); - struct OpcodeEntry { - OpcodeProc proc; - const char *desc; - }; - - const OpcodeEntry *_opcodes; - // denotes the res id of the game-object-list in current use uint32 _currentRunList; @@ -76,7 +70,6 @@ private: // each object has one of these tacked onto the beginning _object_hub *_curObjectHub; - void setupOpcodes(void); void processKillList(void); // Stores resource id of the wav to use as lead-out from smacker @@ -180,7 +173,6 @@ public: memset(_eventList, 0, sizeof(_eventList)); memset(_syncList, 0, sizeof(_syncList)); _router = new Router(_vm); - setupOpcodes(); initStartMenu(); } @@ -210,8 +202,6 @@ public: void setGlobalInterpreterVariables(int32 *vars); int runScript(char *scriptData, char *objectData, uint32 *offset); - int32 executeOpcode(int op, int32 *params); - struct _event_unit { uint32 id; uint32 interact_id; diff --git a/sword2/speech.cpp b/sword2/speech.cpp index 3ae4b9bbe9..5422dd7c21 100644 --- a/sword2/speech.cpp +++ b/sword2/speech.cpp @@ -126,7 +126,7 @@ int32 Logic::fnChoose(int32 *params) { OBJECT_HELD = 0; // clear it so it doesn't keep happening! IN_SUBJECT = 0; // clear the subject list - return IR_CONT + (response << 3); + return IR_CONT | (response << 3); } // new thing for skipping chooser with "nothing else to say" text @@ -140,7 +140,7 @@ int32 Logic::fnChoose(int32 *params) { // return special subject chosen code (same as in normal // chooser routine below) - return IR_CONT + (_subjectList[0].ref << 3); + return IR_CONT | (_subjectList[0].ref << 3); } if (!_choosing) { @@ -232,7 +232,7 @@ int32 Logic::fnChoose(int32 *params) { RESULT = _subjectList[hit].res; // return special subject chosen code - return IR_CONT + (_subjectList[hit].ref << 3); + return IR_CONT | (_subjectList[hit].ref << 3); } int32 Logic::fnStartConversation(int32 *params) { -- cgit v1.2.3