aboutsummaryrefslogtreecommitdiff
path: root/engines/saga/sthread.cpp
diff options
context:
space:
mode:
authorFilippos Karapetis2009-01-02 13:59:34 +0000
committerFilippos Karapetis2009-01-02 13:59:34 +0000
commit32f73fd34cfbae2c80ab2d502617a45511f486f2 (patch)
tree10e53d4370ec3bdbef0cd944264395f3be038d31 /engines/saga/sthread.cpp
parentb8fe877fa8e376c0b4b8971f83c649c58bc288d0 (diff)
downloadscummvm-rg350-32f73fd34cfbae2c80ab2d502617a45511f486f2.tar.gz
scummvm-rg350-32f73fd34cfbae2c80ab2d502617a45511f486f2.tar.bz2
scummvm-rg350-32f73fd34cfbae2c80ab2d502617a45511f486f2.zip
Rewrote the SAGA script system to use an opcode table, like in other engines
svn-id: r35670
Diffstat (limited to 'engines/saga/sthread.cpp')
-rw-r--r--engines/saga/sthread.cpp531
1 files changed, 13 insertions, 518 deletions
diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp
index ab1c771cbc..9e015179d9 100644
--- a/engines/saga/sthread.cpp
+++ b/engines/saga/sthread.cpp
@@ -37,9 +37,6 @@
namespace Saga {
-#define RID_SCENE1_VOICE_START 57
-#define RID_SCENE1_VOICE_END 186
-
ScriptThread *Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) {
ScriptThread *newThread;
@@ -165,7 +162,7 @@ void Script::executeThreads(uint msec) {
}
if (!(thread->_flags & kTFlagWaiting)) {
- if (runThread(thread, STHREAD_TIMESLICE)) {
+ if (runThread(thread)) {
break;
}
}
@@ -196,535 +193,30 @@ void Script::completeThread(void) {
executeThreads(0);
}
-bool Script::runThread(ScriptThread *thread, uint instructionLimit) {
- const char*operandName;
- uint instructionCount;
+bool Script::runThread(ScriptThread *thread) {
uint16 savedInstructionOffset;
-
- byte *addr;
- byte mode;
- uint16 jmpOffset1;
- int16 iparam1;
- int16 iparam2;
- int16 iparam3;
-
- bool disContinue;
- byte argumentsCount;
- uint16 functionNumber;
- uint16 checkStackTopIndex;
- ScriptFunctionType scriptFunction;
-
+ bool stopParsing = false;
+ bool breakOut = false;
int operandChar;
- int i;
MemoryReadStream scriptS(thread->_moduleBase, thread->_moduleBaseSize);
scriptS.seek(thread->_instructionOffset);
- for (instructionCount = 0; instructionCount < instructionLimit; instructionCount++) {
+ for (uint instructionCount = 0; instructionCount < STHREAD_TIMESLICE; instructionCount++) {
if (thread->_flags & (kTFlagAsleep))
break;
savedInstructionOffset = thread->_instructionOffset;
operandChar = scriptS.readByte();
-
-#define CASEOP(opName) case opName: \
- if (operandChar == opName) { \
- operandName = #opName; \
- debug(2, "%s", operandName); \
- }
-
debug(8, "Executing thread offset: %u (%x) stack: %d", thread->_instructionOffset, operandChar, thread->pushedSize());
- operandName="";
- switch (operandChar) {
- CASEOP(opNextBlock)
- // Some sort of "jump to the start of the next memory
- // page" instruction, I think.
- thread->_instructionOffset = (((thread->_instructionOffset) >> 10) + 1) << 10;
- break;
-
-// STACK INSTRUCTIONS
- CASEOP(opDup)
- thread->push(thread->stackTop());
- break;
- CASEOP(opDrop)
- thread->pop();
- break;
- CASEOP(opZero)
- thread->push(0);
- break;
- CASEOP(opOne)
- thread->push(1);
- break;
- CASEOP(opConstint)
- CASEOP(opStrlit)
- iparam1 = scriptS.readSint16LE();
- thread->push(iparam1);
- debug(8, "0x%X", iparam1);
- break;
-
-// DATA INSTRUCTIONS
- CASEOP(opGetFlag)
- addr = thread->baseAddress(scriptS.readByte());
- iparam1 = scriptS.readSint16LE();
- addr += (iparam1 >> 3);
- iparam1 = (1 << (iparam1 & 7));
- thread->push((*addr) & iparam1 ? 1 : 0);
- break;
- CASEOP(opGetInt)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- thread->push(readUint16(addr, mode));
- debug(8, "0x%X", readUint16(addr, mode));
- break;
- CASEOP(opPutFlag)
- addr = thread->baseAddress(scriptS.readByte());
- iparam1 = scriptS.readSint16LE();
- addr += (iparam1 >> 3);
- iparam1 = (1 << (iparam1 & 7));
- if (thread->stackTop()) {
- *addr |= iparam1;
- } else {
- *addr &= ~iparam1;
- }
- break;
- CASEOP(opPutInt)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- writeUint16(addr, thread->stackTop(), mode);
- break;
- CASEOP(opPutFlagV)
- addr = thread->baseAddress(scriptS.readByte());
- iparam1 = scriptS.readSint16LE();
- addr += (iparam1 >> 3);
- iparam1 = (1 << (iparam1 & 7));
- if (thread->pop()) {
- *addr |= iparam1;
- } else {
- *addr &= ~iparam1;
- }
- break;
- CASEOP(opPutIntV)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- writeUint16(addr, thread->pop(), mode);
- break;
-
-// FUNCTION CALL INSTRUCTIONS
- CASEOP(opCall)
- argumentsCount = scriptS.readByte();
- iparam1 = scriptS.readByte();
- if (iparam1 != kAddressModule) {
- error("Script::runThread iparam1 != kAddressModule");
- }
- addr = thread->baseAddress(iparam1);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- thread->push(argumentsCount);
-
- jmpOffset1 = scriptS.pos();
- // NOTE: The original pushes the program
- // counter as a pointer here. But I don't think
- // we will have to do that.
- thread->push(jmpOffset1);
- // NOTE2: program counter is 32bit - so we should "emulate" it size - because kAddressStack relies on it
- thread->push(0);
- thread->_instructionOffset = iparam1;
-
- break;
- CASEOP(opCcall)
- CASEOP(opCcallV)
- argumentsCount = scriptS.readByte();
- functionNumber = scriptS.readUint16LE();
- if (functionNumber >= ((_vm->getGameId() == GID_IHNM) ?
- IHNM_SCRIPT_FUNCTION_MAX : ITE_SCRIPT_FUNCTION_MAX)) {
- error("Script::runThread() Invalid script function number (%d)", functionNumber);
- }
-
- debug(2, "Calling #%d %s argCount=%i", functionNumber, _scriptFunctionsList[functionNumber].scriptFunctionName, argumentsCount);
- scriptFunction = _scriptFunctionsList[functionNumber].scriptFunction;
- checkStackTopIndex = thread->_stackTopIndex + argumentsCount;
- disContinue = false;
- (this->*scriptFunction)(thread, argumentsCount, disContinue);
- if (disContinue) {
- return true;
- }
- if (scriptFunction == &Saga::Script::sfScriptGotoScene ||
- scriptFunction == &Saga::Script::sfVsetTrack) {
- return true; // cause abortAllThreads called and _this_ thread destroyed
- }
- thread->_stackTopIndex = checkStackTopIndex;
-
- if (operandChar == opCcall) {// CALL function
- thread->push(thread->_returnValue);
- }
-
- if (thread->_flags & kTFlagAsleep)
- instructionCount = instructionLimit; // break out of loop!
- break;
- CASEOP(opEnter)
- thread->push(thread->_frameIndex);
- thread->_frameIndex = thread->_stackTopIndex;
- thread->_stackTopIndex -= (scriptS.readSint16LE() / 2);
- break;
- CASEOP(opReturn)
- thread->_returnValue = thread->pop();
- CASEOP(opReturnV)
- thread->_stackTopIndex = thread->_frameIndex;
- thread->_frameIndex = thread->pop();
- if (thread->pushedSize() == 0) {
- thread->_flags |= kTFlagFinished;
- return true;
- } else {
- thread->pop(); //cause it 0
- thread->_instructionOffset = thread->pop();
-
- // Pop all the call parameters off the stack
- iparam1 = thread->pop();
- while (iparam1--) {
- thread->pop();
- }
-
- if (operandChar == opReturn) {
- thread->push(thread->_returnValue);
- }
- }
- break;
-
-// BRANCH INSTRUCTIONS
- CASEOP(opJmp)
- jmpOffset1 = scriptS.readUint16LE();
- thread->_instructionOffset = jmpOffset1;
- break;
- CASEOP(opJmpTrueV)
- jmpOffset1 = scriptS.readUint16LE();
- if (thread->pop()) {
- thread->_instructionOffset = jmpOffset1;
- }
- break;
- CASEOP(opJmpFalseV)
- jmpOffset1 = scriptS.readUint16LE();
- if (!thread->pop()) {
- thread->_instructionOffset = jmpOffset1;
- }
- break;
- CASEOP(opJmpTrue)
- jmpOffset1 = scriptS.readUint16LE();
- if (thread->stackTop()) {
- thread->_instructionOffset = jmpOffset1;
- }
- break;
- CASEOP(opJmpFalse)
- jmpOffset1 = scriptS.readUint16LE();
- if (!thread->stackTop()) {
- thread->_instructionOffset = jmpOffset1;
- }
- break;
- CASEOP(opJmpSwitch)
- iparam1 = scriptS.readSint16LE();
- iparam2 = thread->pop();
- while (iparam1--) {
- iparam3 = scriptS.readUint16LE();
- thread->_instructionOffset = scriptS.readUint16LE();
- if (iparam3 == iparam2) {
- break;
- }
- }
- if (iparam1 < 0) {
- thread->_instructionOffset = scriptS.readUint16LE();
- }
- break;
- CASEOP(opJmpRandom)
- // Supposedly the number of possible branches.
- // The original interpreter ignores it.
- scriptS.readUint16LE();
- iparam1 = scriptS.readSint16LE();
- iparam1 = _vm->_rnd.getRandomNumber(iparam1 - 1);
- while (1) {
- iparam2 = scriptS.readSint16LE();
- thread->_instructionOffset = scriptS.readUint16LE();
-
- iparam1 -= iparam2;
- if (iparam1 < 0) {
- break;
- }
- }
- break;
-
-// UNARY INSTRUCTIONS
- CASEOP(opNegate)
- thread->push(-thread->pop());
- break;
- CASEOP(opNot)
- thread->push(!thread->pop());
- break;
- CASEOP(opCompl)
- thread->push(~thread->pop());
- break;
-
- CASEOP(opIncV)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- iparam1 = readUint16(addr, mode);
- writeUint16(addr, iparam1 + 1, mode);
- break;
- CASEOP(opDecV)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- iparam1 = readUint16(addr, mode);
- writeUint16(addr, iparam1 - 1, mode);
- break;
- CASEOP(opPostInc)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- iparam1 = readUint16(addr, mode);
- thread->push(iparam1);
- writeUint16(addr, iparam1 + 1, mode);
- break;
- CASEOP(opPostDec)
- mode = scriptS.readByte();
- addr = thread->baseAddress(mode);
- iparam1 = scriptS.readSint16LE();
- addr += iparam1;
- iparam1 = readUint16(addr, mode);
- thread->push(iparam1);
- writeUint16(addr, iparam1 - 1, mode);
- break;
-
-// ARITHMETIC INSTRUCTIONS
- CASEOP(opAdd)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 += iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opSub)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 -= iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opMul)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 *= iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opDiv)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 /= iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opMod)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 %= iparam2;
- thread->push(iparam1);
- break;
-
-// COMPARISION INSTRUCTIONS
- CASEOP(opEq)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 == iparam2) ? 1 : 0);
- debug(8, "0x%X 0x%X", iparam1, iparam2);
- break;
- CASEOP(opNe)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 != iparam2) ? 1 : 0);
- break;
- CASEOP(opGt)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 > iparam2) ? 1 : 0);
- break;
- CASEOP(opLt)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 < iparam2) ? 1 : 0);
- break;
- CASEOP(opGe)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 >= iparam2) ? 1 : 0);
- break;
- CASEOP(opLe)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 <= iparam2) ? 1 : 0);
- break;
-
-// SHIFT INSTRUCTIONS
- CASEOP(opRsh)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 >>= iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opLsh)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 <<= iparam2;
- thread->push(iparam1);
- break;
-
-// BITWISE INSTRUCTIONS
- CASEOP(opAnd)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 &= iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opOr)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 |= iparam2;
- thread->push(iparam1);
- break;
- CASEOP(opXor)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- iparam1 ^= iparam2;
- thread->push(iparam1);
- break;
-
-// LOGICAL INSTRUCTIONS
- CASEOP(opLAnd)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 && iparam2) ? 1 : 0);
- break;
- CASEOP(opLOr)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push((iparam1 || iparam2) ? 1 : 0);
- break;
- CASEOP(opLXor)
- iparam2 = thread->pop();
- iparam1 = thread->pop();
- thread->push(((iparam1 && !iparam2) || (!iparam1 && iparam2)) ? 1 : 0);
- break;
-
-// GAME INSTRUCTIONS
- CASEOP(opSpeak) {
- int stringsCount;
- uint16 actorId;
- uint16 speechFlags;
- int sampleResourceId = -1;
- int16 first;
- const char *strings[ACTOR_SPEECH_STRING_MAX];
-
- if (_vm->_actor->isSpeaking()) {
- thread->wait(kWaitTypeSpeech);
- return false;
- }
-
- stringsCount = scriptS.readByte();
- actorId = scriptS.readUint16LE();
- speechFlags = scriptS.readByte();
- scriptS.readUint16LE(); // x,y skip
-
- if (stringsCount == 0)
- error("opSpeak stringsCount == 0");
-
- if (stringsCount > ACTOR_SPEECH_STRING_MAX)
- error("opSpeak stringsCount=0x%X exceed ACTOR_SPEECH_STRING_MAX", stringsCount);
-
- iparam1 = first = thread->stackTop();
- for (i = 0; i < stringsCount; i++) {
- iparam1 = thread->pop();
- strings[i] = thread->_strings->getString(iparam1);
- }
- // now data contains last string index
-
- if (_vm->getFeatures() & GF_OLD_ITE_DOS) { // special ITE dos
- if ((_vm->_scene->currentSceneNumber() == ITE_DEFAULT_SCENE) &&
- (iparam1 >= 288) && (iparam1 <= (RID_SCENE1_VOICE_END - RID_SCENE1_VOICE_START + 288))) {
- sampleResourceId = RID_SCENE1_VOICE_START + iparam1 - 288;
- }
- } else {
- if (thread->_voiceLUT->voicesCount > first) {
- sampleResourceId = thread->_voiceLUT->voices[first];
- }
- }
-
- if (sampleResourceId < 0 || sampleResourceId > 4000)
- sampleResourceId = -1;
-
- if (_vm->getGameId() == GID_ITE && !sampleResourceId)
- sampleResourceId = -1;
-
- _vm->_actor->actorSpeech(actorId, strings, stringsCount, sampleResourceId, speechFlags);
-
- if (!(speechFlags & kSpeakAsync)) {
- thread->wait(kWaitTypeSpeech);
- }
- }
- break;
- CASEOP(opDialogBegin)
- if (_conversingThread) {
- thread->wait(kWaitTypeDialogBegin);
- return false;
- }
- _conversingThread = thread;
- _vm->_interface->converseClear();
- break;
- CASEOP(opDialogEnd)
- if (thread == _conversingThread) {
- _vm->_interface->activate();
- _vm->_interface->setMode(kPanelConverse);
- thread->wait(kWaitTypeDialogEnd);
- return false;
- }
- break;
- CASEOP(opReply) {
- const char *str;
- byte replyNum;
- byte flags;
- replyNum = scriptS.readByte();
- flags = scriptS.readByte();
- iparam1 = 0;
- int strID = thread->pop();
-
- if (flags & kReplyOnce) {
- iparam1 = scriptS.readSint16LE();
- addr = thread->_staticBase + (iparam1 >> 3);
- if (*addr & (1 << (iparam1 & 7))) {
- break;
- }
- }
-
- str = thread->_strings->getString(strID);
- if (_vm->_interface->converseAddText(str, strID, replyNum, flags, iparam1))
- warning("Error adding ConverseText (%s, %d, %d, %d)", str, replyNum, flags, iparam1);
- }
- break;
- CASEOP(opAnimate)
- scriptS.readUint16LE();
- scriptS.readUint16LE();
- jmpOffset1 = scriptS.readByte();
- thread->_instructionOffset += jmpOffset1;
- break;
-
- default:
- error("Script::runThread() Invalid opcode encountered 0x%X", operandChar);
- }
+ stopParsing = false;
+ (this->*_scriptOpsList[operandChar].scriptOp)(thread, &scriptS, stopParsing, breakOut);
+ debug(4, "Calling op %s", this->_scriptOpsList[operandChar].scriptOpName);
+ if (stopParsing)
+ return true;
if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
error("Wrong flags %d in thread", thread->_flags);
@@ -740,6 +232,9 @@ bool Script::runThread(ScriptThread *thread, uint instructionLimit) {
scriptS.seek(thread->_instructionOffset);
}
+
+ if (breakOut)
+ break;
}
return false;
}