mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-18 07:39:08 +00:00
c2870adf60
svn-id: r13493
648 lines
16 KiB
C++
648 lines
16 KiB
C++
/* Copyright (C) 1994-2004 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 "common/stdafx.h"
|
|
#include "common/util.h"
|
|
#include "sword2/sword2.h"
|
|
#include "sword2/interpreter.h"
|
|
#include "sword2/logic.h"
|
|
|
|
namespace Sword2 {
|
|
|
|
#define STACK_SIZE 10
|
|
|
|
// The machine code table
|
|
|
|
#define OPCODE(x) { &Logic::x, #x }
|
|
|
|
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 push_ptr(ptr) push(_vm->_memory->ptrToInt(ptr))
|
|
|
|
#define pop() (assert(stackPtr < ARRAYSIZE(stack)), stack[--stackPtr])
|
|
|
|
uint32 *Logic::_scriptVars = NULL;
|
|
|
|
int Logic::runScript(char *scriptData, char *objectData, uint32 *offset) {
|
|
// 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.
|
|
|
|
int32 stack[STACK_SIZE];
|
|
int32 stackPtr = 0;
|
|
|
|
StandardHeader *header = (StandardHeader *) scriptData;
|
|
scriptData += sizeof(StandardHeader) + sizeof(ObjectHub);
|
|
|
|
// 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
|
|
|
|
uint32 ip = 0; // Code pointer
|
|
int scriptNumber;
|
|
|
|
// Get the start of variables and start of code
|
|
|
|
uint32 *localVars = (uint32 *) (scriptData + sizeof(int32));
|
|
char *code = scriptData + READ_LE_UINT32(scriptData) + sizeof(int32);
|
|
uint32 noScripts = READ_LE_UINT32(code);
|
|
|
|
code += sizeof(int32);
|
|
|
|
const uint32 *offsetTable = (const uint32 *) code;
|
|
|
|
if (*offset < noScripts) {
|
|
ip = FROM_LE_32(offsetTable[*offset]);
|
|
scriptNumber = *offset;
|
|
debug(4, "Start script %d with offset %d", *offset, ip);
|
|
} else {
|
|
uint i;
|
|
|
|
ip = *offset;
|
|
|
|
for (i = 1; i < noScripts; i++) {
|
|
if (FROM_LE_32(offsetTable[i]) >= ip)
|
|
break;
|
|
}
|
|
|
|
scriptNumber = i - 1;
|
|
debug(4, "Resume script %d with offset %d", scriptNumber, ip);
|
|
}
|
|
|
|
// WORKAROUND: The dreaded pyramid makes the torch untakeable when you
|
|
// speak to Titipoco. This is because one of the conditions for the
|
|
// torch to be takeable is that Titipoco isn't doing anything out of
|
|
// the ordinary. Global variable 913 has to be 0 to signify that he is
|
|
// in his "idle" state.
|
|
//
|
|
// Unfortunately, simply the act of speaking to him sets variable 913
|
|
// to 1 (probably to stop him from turning around every now and then).
|
|
// The script may then go on to set the variable to different values
|
|
// to trigger various behaviours in him, but if you have run out of
|
|
// these cases the script won't ever set it back to 0 again.
|
|
//
|
|
// So if his click hander (action script number 2) finishes, and
|
|
// variable 913 is 1, we set it back to 0 manually.
|
|
|
|
bool checkPyramidBug = scriptNumber == 2 && strcmp((char *) header->name, "titipoco_81") == 0;
|
|
|
|
code += noScripts * sizeof(int32);
|
|
|
|
// 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 (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;
|
|
}
|
|
|
|
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;
|
|
uint8 *ptr;
|
|
|
|
curCommand = code[ip++];
|
|
|
|
switch (curCommand) {
|
|
|
|
// Script-related opcodes
|
|
|
|
case CP_END_SCRIPT:
|
|
// End the script
|
|
runningScript = false;
|
|
|
|
// WORKAROUND: Pyramid Bug. See explanation above.
|
|
|
|
if (checkPyramidBug && _scriptVars[913] == 1) {
|
|
warning("Working around Titipoco script bug (the \"Pyramid Bug\")");
|
|
_scriptVars[913] = 0;
|
|
}
|
|
|
|
debug(9, "CP_END_SCRIPT");
|
|
break;
|
|
case CP_QUIT:
|
|
// Quit out for a cycle
|
|
*offset = ip;
|
|
debug(9, "CP_QUIT");
|
|
return 0;
|
|
case CP_TERMINATE:
|
|
// Quit out immediately without affecting the offset
|
|
// pointer
|
|
debug(9, "CP_TERMINATE");
|
|
return 3;
|
|
case CP_RESTART_SCRIPT:
|
|
// Start the script again
|
|
ip = FROM_LE_32(offsetTable[scriptNumber]);
|
|
debug(9, "CP_RESTART_SCRIPT");
|
|
break;
|
|
|
|
// Stack-related opcodes
|
|
|
|
case CP_PUSH_INT32:
|
|
// Push a long word value on to the stack
|
|
Read32ip(parameter);
|
|
push(parameter);
|
|
debug(9, "CP_PUSH_INT32: %d", parameter);
|
|
break;
|
|
case CP_PUSH_LOCAL_VAR32:
|
|
// Push the contents of a local variable
|
|
Read16ip(parameter);
|
|
parameter /= 4;
|
|
push(localVars[parameter]);
|
|
debug(9, "CP_PUSH_LOCAL_VAR32: localVars[%d] => %d", parameter, localVars[parameter]);
|
|
break;
|
|
case CP_PUSH_GLOBAL_VAR32:
|
|
// Push a global variable
|
|
assert(_scriptVars);
|
|
Read16ip(parameter);
|
|
push(_scriptVars[parameter]);
|
|
debug(9, "CP_PUSH_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, _scriptVars[parameter]);
|
|
break;
|
|
case CP_PUSH_LOCAL_ADDR:
|
|
// Push the address of a local variable
|
|
|
|
// From what I understand, some scripts store data
|
|
// (e.g. mouse pointers) in their local variable space
|
|
// from the very beginning, and use this mechanism to
|
|
// pass that data to the opcode function. I don't yet
|
|
// know the conceptual difference between this and the
|
|
// CP_PUSH_DEREFERENCED_STRUCTURE opcode.
|
|
|
|
Read16ip(parameter);
|
|
parameter /= 4;
|
|
ptr = (uint8 *) &localVars[parameter];
|
|
push_ptr(ptr);
|
|
debug(9, "CP_PUSH_LOCAL_ADDR: &localVars[%d] => %p", parameter, ptr);
|
|
break;
|
|
case CP_PUSH_STRING:
|
|
// Push the address of a string on to the stack
|
|
// Get the string size
|
|
Read8ip(parameter);
|
|
|
|
// ip now points to the string
|
|
ptr = (uint8 *) (code + ip);
|
|
push_ptr(ptr);
|
|
debug(9, "CP_PUSH_STRING: \"%s\"", ptr);
|
|
ip += (parameter + 1);
|
|
break;
|
|
case CP_PUSH_DEREFERENCED_STRUCTURE:
|
|
// Push the address of a dereferenced structure
|
|
Read32ip(parameter);
|
|
ptr = (uint8 *) (objectData + sizeof(int32) + sizeof(StandardHeader) + sizeof(ObjectHub) + parameter);
|
|
push_ptr(ptr);
|
|
debug(9, "CP_PUSH_DEREFERENCED_STRUCTURE: %d => %p", parameter, ptr);
|
|
break;
|
|
case CP_POP_LOCAL_VAR32:
|
|
// Pop a value into a local word variable
|
|
Read16ip(parameter);
|
|
parameter /= 4;
|
|
value = pop();
|
|
localVars[parameter] = value;
|
|
debug(9, "CP_POP_LOCAL_VAR32: localVars[%d] = %d", parameter, value);
|
|
break;
|
|
case CP_POP_GLOBAL_VAR32:
|
|
// Pop a global variable
|
|
Read16ip(parameter);
|
|
value = pop();
|
|
_scriptVars[parameter] = value;
|
|
debug(9, "CP_POP_GLOBAL_VAR32: scriptsVars[%d] = %d", parameter, value);
|
|
break;
|
|
case CP_ADDNPOP_LOCAL_VAR32:
|
|
Read16ip(parameter);
|
|
parameter /= 4;
|
|
value = pop();
|
|
localVars[parameter] += value;
|
|
debug(9, "CP_ADDNPOP_LOCAL_VAR32: localVars[%d] += %d => %d", parameter, value, localVars[parameter]);
|
|
break;
|
|
case CP_SUBNPOP_LOCAL_VAR32:
|
|
Read16ip(parameter);
|
|
parameter /= 4;
|
|
value = pop();
|
|
localVars[parameter] -= value;
|
|
debug(9, "CP_SUBNPOP_LOCAL_VAR32: localVars[%d] -= %d => %d", parameter, value, localVars[parameter]);
|
|
break;
|
|
case CP_ADDNPOP_GLOBAL_VAR32:
|
|
// Add and pop a global variable
|
|
Read16ip(parameter);
|
|
value = pop();
|
|
_scriptVars[parameter] += value;
|
|
debug(9, "CP_ADDNPOP_GLOBAL_VAR32: scriptVars[%d] += %d => %d", parameter, value, _scriptVars[parameter]);
|
|
break;
|
|
case CP_SUBNPOP_GLOBAL_VAR32:
|
|
// Sub and pop a global variable
|
|
Read16ip(parameter);
|
|
value = pop();
|
|
_scriptVars[parameter] -= value;
|
|
debug(9, "CP_SUBNPOP_GLOBAL_VAR32: scriptVars[%d] -= %d => %d", parameter, value, _scriptVars[parameter]);
|
|
break;
|
|
|
|
// Jump opcodes
|
|
|
|
case CP_SKIPONTRUE:
|
|
// Skip if the value on the stack is true
|
|
Read32ipLeaveip(parameter);
|
|
value = pop();
|
|
if (!value) {
|
|
ip += sizeof(int32);
|
|
debug(9, "CP_SKIPONTRUE: %d (IS FALSE (NOT SKIPPED))", parameter);
|
|
} else {
|
|
ip += parameter;
|
|
debug(9, "CP_SKIPONTRUE: %d (IS TRUE (SKIPPED))", parameter);
|
|
}
|
|
break;
|
|
case CP_SKIPONFALSE:
|
|
// Skip if the value on the stack is false
|
|
Read32ipLeaveip(parameter);
|
|
value = pop();
|
|
if (value) {
|
|
ip += sizeof(int32);
|
|
debug(9, "CP_SKIPONFALSE: %d (IS TRUE (NOT SKIPPED))", parameter);
|
|
} else {
|
|
ip += parameter;
|
|
debug(9, "CP_SKIPONFALSE: %d (IS FALSE (SKIPPED))", parameter);
|
|
}
|
|
break;
|
|
case CP_SKIPALWAYS:
|
|
// skip a block
|
|
Read32ipLeaveip(parameter);
|
|
ip += parameter;
|
|
debug(9, "CP_SKIPALWAYS: %d", parameter);
|
|
break;
|
|
case CP_SWITCH:
|
|
// switch
|
|
value = pop();
|
|
Read32ip(caseCount);
|
|
|
|
// Search the cases
|
|
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 = 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 (!foundCase)
|
|
ip += READ_LE_UINT32(code + ip);
|
|
|
|
debug(9, "CP_SWITCH: [SORRY, NO DEBUG INFO]");
|
|
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;
|
|
debug(9, "CP_SAVE_MCODE_START");
|
|
break;
|
|
case CP_CALL_MCODE:
|
|
// Call an mcode routine
|
|
Read16ip(parameter);
|
|
assert(parameter < ARRAYSIZE(opcodes));
|
|
// amount to adjust stack by (no of parameters)
|
|
Read8ip(value);
|
|
debug(9, "CP_CALL_MCODE: '%s', %d", 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'", retVal & 7, opcodes[parameter].desc);
|
|
}
|
|
parameterReturnedFromMcodeFunction = retVal >> 3;
|
|
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);
|
|
debug(9, "CP_JUMP_ON_RETURNED: %d => %d",
|
|
parameterReturnedFromMcodeFunction,
|
|
READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4));
|
|
ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4);
|
|
break;
|
|
|
|
// Operators
|
|
|
|
case OP_ISEQUAL:
|
|
b = pop();
|
|
a = pop();
|
|
push(a == b);
|
|
debug(9, "OP_ISEQUAL: RESULT = %d", a == b);
|
|
break;
|
|
case OP_NOTEQUAL:
|
|
b = pop();
|
|
a = pop();
|
|
push(a != b);
|
|
debug(9, "OP_NOTEQUAL: RESULT = %d", a != b);
|
|
break;
|
|
case OP_GTTHAN:
|
|
b = pop();
|
|
a = pop();
|
|
push(a > b);
|
|
debug(9, "OP_GTTHAN: RESULT = %d", a > b);
|
|
break;
|
|
case OP_LSTHAN:
|
|
b = pop();
|
|
a = pop();
|
|
push(a < b);
|
|
debug(9, "OP_LSTHAN: RESULT = %d", a < b);
|
|
break;
|
|
case OP_GTTHANE:
|
|
b = pop();
|
|
a = pop();
|
|
push(a >= b);
|
|
debug(9, "OP_GTTHANE: RESULT = %d", a >= b);
|
|
break;
|
|
case OP_LSTHANE:
|
|
b = pop();
|
|
a = pop();
|
|
push(a <= b);
|
|
debug(9, "OP_LSTHANE: RESULT = %d", a <= b);
|
|
break;
|
|
case OP_PLUS:
|
|
b = pop();
|
|
a = pop();
|
|
push(a + b);
|
|
debug(9, "OP_PLUS: RESULT = %d", a + b);
|
|
break;
|
|
case OP_MINUS:
|
|
b = pop();
|
|
a = pop();
|
|
push(a - b);
|
|
debug(9, "OP_MINUS: RESULT = %d", a - b);
|
|
break;
|
|
case OP_TIMES:
|
|
b = pop();
|
|
a = pop();
|
|
push(a * b);
|
|
debug(9, "OP_TIMES: RESULT = %d", a * b);
|
|
break;
|
|
case OP_DIVIDE:
|
|
b = pop();
|
|
a = pop();
|
|
push(a / b);
|
|
debug(9, "OP_DIVIDE: RESULT = %d", a / b);
|
|
break;
|
|
case OP_ANDAND:
|
|
b = pop();
|
|
a = pop();
|
|
push(a && b);
|
|
debug(9, "OP_ANDAND: RESULT = %d", a && b);
|
|
break;
|
|
case OP_OROR:
|
|
b = pop();
|
|
a = pop();
|
|
push(a || b);
|
|
debug(9, "OP_OROR: RESULT = %d", a || b);
|
|
break;
|
|
|
|
// Debugging opcodes, I think
|
|
|
|
case CP_DEBUGON:
|
|
debug(9, "CP_DEBUGON");
|
|
break;
|
|
case CP_DEBUGOFF:
|
|
debug(9, "CP_DEBUGOFF");
|
|
break;
|
|
case CP_TEMP_TEXT_PROCESS:
|
|
Read32ip(parameter);
|
|
debug(9, "CP_TEMP_TEXT_PROCESS: %d", parameter);
|
|
break;
|
|
default:
|
|
error("Invalid script command %d", curCommand);
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
} // End of namespace Sword2
|