mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-11 19:54:03 +00:00
2fef2dcb84
svn-id: r10997
644 lines
18 KiB
C++
644 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: The script engine assumes it can freely cast from pointer to in32
|
|
// and back again with no ill effects. As far as I know, there is absolutely
|
|
// no guarantee that this will work.
|
|
//
|
|
// Maybe we can represent them as offsets into the memory manager's memory?
|
|
// Assuming, of course, that all the pointers we try to pass around this way
|
|
// point to somewhere in that block.
|
|
//
|
|
// I also have a feeling the handling of a script's local variables may be
|
|
// alignment-unsafe.
|
|
|
|
#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, (int32) (variables + parameter));
|
|
PUSHONSTACK((int32) (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((int32) (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)", (int32) (objectData + sizeof(int32) + sizeof(_standardHeader) + sizeof(_object_hub) + parameter));
|
|
PUSHONSTACK((int32) (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
|