mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
etc. to the different opcodes. Until now it has done so by casting the pointer to an int32 (opcode parameters are represented as arrays of int32) and then the opcode function casts it back to whatever pointer it needs. At least in C there is no guarantee that a pointer can be represented as an integer type (though apparently C99 may define such a type), so this has struck me as unsafe ever since I first noticed it. However, since all such pointers appear to point to the memory block owned by the memory manager, we can easily convert them to integers by treating them as offsets into the memory block. So that's what I have done. I hope I caught all the occurences in the opcode functions, or we're going to have some pretty interesting regressions on our hands... svn-id: r11241
635 lines
18 KiB
C++
635 lines
18 KiB
C++
/* Copyright (C) 1994-2003 Revolution Software Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "sword2/driver/driver96.h"
|
|
#include "sword2/console.h"
|
|
#include "sword2/interpreter.h"
|
|
#include "sword2/logic.h"
|
|
|
|
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, y) { x, &Logic::y, #y }
|
|
|
|
void Logic::setupOpcodes(void) {
|
|
static const OpcodeEntry opcodes[MAX_FN_NUMBER + 1] = {
|
|
/* 00 */
|
|
OPCODE(1, fnTestFunction),
|
|
OPCODE(1, fnTestFlags),
|
|
OPCODE(2, fnRegisterStartPoint),
|
|
OPCODE(2, fnInitBackground),
|
|
/* 04 */
|
|
OPCODE(1, fnSetSession),
|
|
OPCODE(1, fnBackSprite),
|
|
OPCODE(1, fnSortSprite),
|
|
OPCODE(1, fnForeSprite),
|
|
/* 08 */
|
|
OPCODE(1, fnRegisterMouse),
|
|
OPCODE(3, fnAnim),
|
|
OPCODE(2, fnRandom),
|
|
OPCODE(1, fnPreLoad),
|
|
/* 0C */
|
|
OPCODE(2, fnAddSubject),
|
|
OPCODE(1, fnInteract),
|
|
OPCODE(0, fnChoose),
|
|
OPCODE(7, fnWalk),
|
|
/* 10 */
|
|
OPCODE(5, fnWalkToAnim),
|
|
OPCODE(6, fnTurn),
|
|
OPCODE(5, fnStandAt),
|
|
OPCODE(3, fnStand),
|
|
/* 14 */
|
|
OPCODE(3, fnStandAfterAnim),
|
|
OPCODE(2, fnPause),
|
|
OPCODE(4, fnMegaTableAnim),
|
|
OPCODE(1, fnAddMenuObject),
|
|
/* 18 */
|
|
OPCODE(0, fnStartConversation),
|
|
OPCODE(0, fnEndConversation),
|
|
OPCODE(3, fnSetFrame),
|
|
OPCODE(3, fnRandomPause),
|
|
/* 1C */
|
|
OPCODE(3, fnRegisterFrame),
|
|
OPCODE(1, fnNoSprite),
|
|
OPCODE(2, fnSendSync),
|
|
OPCODE(1, fnUpdatePlayerStats),
|
|
/* 20 */
|
|
OPCODE(1, fnPassGraph),
|
|
OPCODE(1, fnInitFloorMouse),
|
|
OPCODE(1, fnPassMega),
|
|
OPCODE(6, fnFaceXY),
|
|
/* 24 */
|
|
OPCODE(1, fnEndSession),
|
|
OPCODE(0, fnNoHuman),
|
|
OPCODE(0, fnAddHuman),
|
|
OPCODE(1, fnWeWait),
|
|
/* 28 */
|
|
OPCODE(8, fnTheyDoWeWait),
|
|
OPCODE(7, fnTheyDo),
|
|
OPCODE(6, fnWalkToTalkToMega),
|
|
OPCODE(0, fnFadeDown),
|
|
/* 2C */
|
|
OPCODE(0, fnISpeak),
|
|
OPCODE(0, fnTotalRestart),
|
|
OPCODE(0, fnSetWalkGrid),
|
|
OPCODE(5, fnSpeechProcess),
|
|
/* 30 */
|
|
OPCODE(3, fnSetScaling),
|
|
OPCODE(0, fnStartEvent),
|
|
OPCODE(0, fnCheckEventWaiting),
|
|
OPCODE(1, fnRequestSpeech),
|
|
/* 34 */
|
|
OPCODE(1, fnGosub),
|
|
OPCODE(3, fnTimedWait),
|
|
OPCODE(5, fnPlayFx),
|
|
OPCODE(1, fnStopFx),
|
|
/* 38 */
|
|
OPCODE(2, fnPlayMusic),
|
|
OPCODE(0, fnStopMusic),
|
|
OPCODE(2, fnSetValue),
|
|
OPCODE(1, fnNewScript),
|
|
/* 3C */
|
|
OPCODE(0, fnGetSync),
|
|
OPCODE(0, fnWaitSync),
|
|
OPCODE(0, fnRegisterWalkGrid),
|
|
OPCODE(4, fnReverseMegaTableAnim),
|
|
/* 40 */
|
|
OPCODE(3, fnReverseAnim),
|
|
OPCODE(0, fnAddToKillList),
|
|
OPCODE(3, fnSetStandbyCoords),
|
|
OPCODE(1, fnBackPar0Sprite),
|
|
/* 44 */
|
|
OPCODE(1, fnBackPar1Sprite),
|
|
OPCODE(1, fnForePar0Sprite),
|
|
OPCODE(1, fnForePar1Sprite),
|
|
OPCODE(1, fnSetPlayerActionEvent),
|
|
/* 48 */
|
|
OPCODE(2, fnSetScrollCoordinate),
|
|
OPCODE(3, fnStandAtAnim),
|
|
OPCODE(1, fnSetScrollLeftMouse),
|
|
OPCODE(1, fnSetScrollRightMouse),
|
|
/* 4C */
|
|
OPCODE(1, fnColour),
|
|
OPCODE(1, fnFlash),
|
|
OPCODE(1, fnPreFetch),
|
|
OPCODE(3, fnGetPlayerSaveData),
|
|
/* 50 */
|
|
OPCODE(3, fnPassPlayerSaveData),
|
|
OPCODE(2, fnSendEvent),
|
|
OPCODE(1, fnAddWalkGrid),
|
|
OPCODE(1, fnRemoveWalkGrid),
|
|
/* 54 */
|
|
OPCODE(0, fnCheckForEvent),
|
|
OPCODE(2, fnPauseForEvent),
|
|
OPCODE(0, fnClearEvent),
|
|
OPCODE(5, fnFaceMega),
|
|
/* 58 */
|
|
OPCODE(2, fnPlaySequence),
|
|
OPCODE(1, fnShadedSprite),
|
|
OPCODE(1, fnUnshadedSprite),
|
|
OPCODE(0, fnFadeUp),
|
|
/* 60 */
|
|
OPCODE(1, fnDisplayMsg),
|
|
OPCODE(0, fnSetObjectHeld),
|
|
OPCODE(3, fnAddSequenceText),
|
|
OPCODE(0, fnResetGlobals),
|
|
/* 64 */
|
|
OPCODE(1, fnSetPalette),
|
|
OPCODE(1, fnRegisterPointerText),
|
|
OPCODE(1, fnFetchWait),
|
|
OPCODE(1, fnRelease),
|
|
/* 68 */
|
|
OPCODE(1, fnPrepareMusic),
|
|
OPCODE(1, fnSoundFetch),
|
|
OPCODE(1, fnPrepareMusic), // Again, apparently
|
|
OPCODE(1, fnSmackerLeadIn),
|
|
/* 6C */
|
|
OPCODE(1, fnSmackerLeadOut),
|
|
OPCODE(0, fnStopAllFx),
|
|
OPCODE(1, fnCheckPlayerActivity),
|
|
OPCODE(0, fnResetPlayerActivityDelay),
|
|
/* 70 */
|
|
OPCODE(0, fnCheckMusicPlaying),
|
|
OPCODE(0, fnPlayCredits),
|
|
OPCODE(0, fnSetScrollSpeedNormal),
|
|
OPCODE(0, fnSetScrollSpeedSlow),
|
|
/* 74 */
|
|
OPCODE(0, fnRemoveChooser),
|
|
OPCODE(3, fnSetFxVolAndPan),
|
|
OPCODE(3, fnSetFxVol),
|
|
OPCODE(0, fnRestoreGame),
|
|
/* 78 */
|
|
OPCODE(0, fnRefreshInventory),
|
|
OPCODE(0, fnChangeShadows)
|
|
};
|
|
|
|
_opcodes = opcodes;
|
|
};
|
|
|
|
int32 Logic::executeOpcode(int i, int32 *params) {
|
|
OpcodeProc op = _opcodes[i].proc;
|
|
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 }
|
|
|
|
void Logic::setGlobalInterpreterVariables(int32 *vars) {
|
|
_globals = vars;
|
|
}
|
|
|
|
int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) {
|
|
#define STACK_SIZE 10
|
|
|
|
_standardHeader *header = (_standardHeader *) scriptData;
|
|
scriptData += sizeof(_standardHeader) + sizeof(_object_hub);
|
|
|
|
// The script data format:
|
|
// int32_TYPE 1 Size of variable space in bytes
|
|
// ... The variable space
|
|
// int32_TYPE 1 numberOfScripts
|
|
// int32_TYPE numberOfScripts The offsets for each script
|
|
|
|
// 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;
|
|
|
|
// 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);
|
|
const char *code = scriptData + (int32) READ_LE_UINT32(scriptData) + sizeof(int32);
|
|
uint32 noScripts = (int32) READ_LE_UINT32(code);
|
|
|
|
if (*offset < noScripts) {
|
|
ip = READ_LE_UINT32((const int *) code + *offset + 1);
|
|
debug(5, "Start script %d with offset %d", *offset, ip);
|
|
} else {
|
|
ip = *offset;
|
|
debug(5, "Start script with offset %d", ip);
|
|
}
|
|
|
|
code += noScripts * sizeof(int32) + 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) {
|
|
error("Invalid script in object %s", header->name);
|
|
return 0;
|
|
}
|
|
|
|
int codeLen = READ_LE_UINT32(checksumBlock + 1);
|
|
int checksum = 0;
|
|
|
|
for (count = 0; count < codeLen; count++)
|
|
checksum += (unsigned char) code[count];
|
|
|
|
if (checksum != (int32) READ_LE_UINT32(checksumBlock + 2)) {
|
|
error("Checksum error in object %s", header->name);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int runningScript = 1;
|
|
|
|
while (runningScript) {
|
|
curCommand = code[ip++];
|
|
|
|
switch (curCommand) {
|
|
case CP_END_SCRIPT:
|
|
// End the script
|
|
debug(5, "End script",0);
|
|
runningScript = 0;
|
|
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));
|
|
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]);
|
|
break;
|
|
case CP_POP_LOCAL_VAR32:
|
|
// Pop a value into a local word variable
|
|
Read16ip(parameter);
|
|
POPOFFSTACK(value);
|
|
debug(5, "Pop %d into local var %d", value, parameter);
|
|
*((int32 *) (variables + parameter)) = value;
|
|
break;
|
|
case CP_CALL_MCODE:
|
|
// Call an mcode routine
|
|
Read16ip(parameter);
|
|
assert(parameter <= MAX_FN_NUMBER);
|
|
// amount to adjust stack by (no of parameters)
|
|
Read8ip(value);
|
|
debug(5, "Call mcode %d with stack = %x", parameter, stack2 + stackPointer2 - 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;
|
|
break;
|
|
case CP_PUSH_LOCAL_ADDR:
|
|
// push the address of a local variable
|
|
Read16ip(parameter);
|
|
debug(5, "Push address of local variable %d (%x)", parameter, memory->ptrToInt((const uint8 *) (variables + parameter)));
|
|
PUSHONSTACK(memory->ptrToInt((uint8 *) (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);
|
|
break;
|
|
case CP_SKIPONFALSE:
|
|
// Skip if the value on the stack is false
|
|
Read32ipLeaveip(parameter);
|
|
POPOFFSTACK(value);
|
|
debug(5, "Skip %d if %d is false", parameter, value);
|
|
if (value)
|
|
ip += sizeof(int32);
|
|
else
|
|
ip += parameter;
|
|
break;
|
|
case CP_SKIPALWAYS:
|
|
// skip a block
|
|
Read32ipLeaveip(parameter);
|
|
debug(5, "Skip %d", parameter);
|
|
ip += parameter;
|
|
break;
|
|
case CP_SWITCH:
|
|
// 9 switch
|
|
POPOFFSTACK(value);
|
|
Read32ip(caseCount);
|
|
|
|
// Search the cases
|
|
foundCase = 0;
|
|
for (count = 0; count < caseCount && !foundCase; count++) {
|
|
if (value == (int32) READ_LE_UINT32(code + ip)) {
|
|
// We have found the case, so lets
|
|
// jump to it
|
|
foundCase = 1;
|
|
ip += READ_LE_UINT32(code + ip + sizeof(int32));
|
|
} else
|
|
ip += sizeof(int32) * 2;
|
|
}
|
|
|
|
// 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));
|
|
break;
|
|
case CP_SUBNPOP_LOCAL_VAR32:
|
|
Read16ip(parameter);
|
|
POPOFFSTACK(value);
|
|
*((int32 *) (variables + parameter)) -= value;
|
|
debug(5, "-= %d into local var %d->%d", value, parameter, *(int32 *) (variables + parameter));
|
|
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;
|
|
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
|
|
|
|
_globals[parameter] = value;
|
|
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]);
|
|
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]);
|
|
break;
|
|
case CP_DEBUGON:
|
|
// Turn debugging on
|
|
_debugFlag = true;
|
|
break;
|
|
case CP_DEBUGOFF:
|
|
// Turn debugging on
|
|
_debugFlag = false;
|
|
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]);
|
|
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]);
|
|
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]);
|
|
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]);
|
|
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]);
|
|
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]);
|
|
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]);
|
|
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);
|
|
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(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)", memory->ptrToInt((const uint8 *) (objectData + sizeof(int32) + sizeof(_standardHeader) + sizeof(_object_hub) + parameter)));
|
|
PUSHONSTACK(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);
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
} // End of namespace Sword2
|