mirror of
https://github.com/libretro/scummvm.git
synced 2025-03-07 10:48:43 +00:00

console from the SCUMM engine. I decided that would be easier than to clean up the original console code. Unfortunately there's a bunch of code that I just copied - a pretty lousy form of code-reusal. It'd be nice if the console could be made part of the Engine class, or something like that. Most of the debug commands seem to be working. Some aren't relevant for ScummVM, and some are a bit obscure so I'm not quite sure what they're supposed to be doing. svn-id: r10978
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 "bs2/driver/driver96.h"
|
|
#include "bs2/console.h"
|
|
#include "bs2/interpreter.h"
|
|
#include "bs2/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
|