aboutsummaryrefslogtreecommitdiff
path: root/sword2/interpreter.cpp
diff options
context:
space:
mode:
authorTorbjörn Andersson2003-12-14 16:33:27 +0000
committerTorbjörn Andersson2003-12-14 16:33:27 +0000
commit4420b15d9f59227cc57bb60d5b38c73639437a62 (patch)
tree0516b8935d48adc82ea6d758e154af1b75fcbc44 /sword2/interpreter.cpp
parentce04dd9f7e77fe1c31eb14aebde2f69c16d33684 (diff)
downloadscummvm-rg350-4420b15d9f59227cc57bb60d5b38c73639437a62.tar.gz
scummvm-rg350-4420b15d9f59227cc57bb60d5b38c73639437a62.tar.bz2
scummvm-rg350-4420b15d9f59227cc57bb60d5b38c73639437a62.zip
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
Diffstat (limited to 'sword2/interpreter.cpp')
-rw-r--r--sword2/interpreter.cpp891
1 files changed, 429 insertions, 462 deletions
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.
@@ -324,79 +321,130 @@ int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) {
}
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;
}
}