From 32f73fd34cfbae2c80ab2d502617a45511f486f2 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Fri, 2 Jan 2009 13:59:34 +0000 Subject: Rewrote the SAGA script system to use an opcode table, like in other engines svn-id: r35670 --- engines/saga/sthread.cpp | 531 ++--------------------------------------------- 1 file changed, 13 insertions(+), 518 deletions(-) (limited to 'engines/saga/sthread.cpp') 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; } -- cgit v1.2.3