diff options
Diffstat (limited to 'engines/sci/engine/vm.cpp')
-rw-r--r-- | engines/sci/engine/vm.cpp | 149 |
1 files changed, 95 insertions, 54 deletions
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 7342f8ca7b..8398b7d8c2 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -34,6 +34,7 @@ #include "sci/engine/features.h" #include "sci/engine/state.h" #include "sci/engine/kernel.h" +#include "sci/engine/object.h" #include "sci/engine/script.h" #include "sci/engine/seg_manager.h" #include "sci/engine/selector.h" // for SELECTOR @@ -94,7 +95,7 @@ static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, // validation functionality -static reg_t &validate_property(Object *obj, int index) { +static reg_t &validate_property(EngineState *s, Object *obj, int index) { // A static dummy reg_t, which we return if obj or index turn out to be // invalid. Note that we cannot just return NULL_REG, because client code // may modify the value of the returned reg_t. @@ -105,6 +106,11 @@ static reg_t &validate_property(Object *obj, int index) { if (!obj) error("validate_property: Sending to disposed object"); + if (getSciVersion() == SCI_VERSION_3) + index = obj->locateVarSelector(s->_segMan, index); + else + index >>= 1; + if (index < 0 || (uint)index >= obj->getVarCount()) { // This is same way sierra does it and there are some games, that contain such scripts like // iceman script 998 (fred::canBeHere, executed right at the start) @@ -144,13 +150,13 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in const char *names[4] = {"global", "local", "temp", "param"}; if (index < 0 || index >= max) { - Common::String txt = Common::String::printf( + Common::String txt = Common::String::format( "[VM] Attempt to use invalid %s variable %04x ", names[type], index); if (max == 0) txt += "(variable type invalid)"; else - txt += Common::String::printf("(out of range [%d..%d])", 0, max - 1); + txt += Common::String::format("(out of range [%d..%d])", 0, max - 1); if (type == VAR_PARAM || type == VAR_TEMP) { int total_offset = r - stack_base; @@ -323,7 +329,11 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP scr = s->_segMan->getScript(seg); } - const int temp = scr->validateExportFunc(pubfunct); + int temp = scr->validateExportFunc(pubfunct, false); + + if (getSciVersion() == SCI_VERSION_3) + temp += scr->getCodeBlockOffset(); + if (!temp) { #ifdef ENABLE_SCI32 // HACK: Temporarily switch to a warning in SCI32 games until we can figure out why Torin has @@ -423,7 +433,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } #ifdef VM_DEBUG_SEND - printf("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj), + debugN("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj), s->_segMan->getObjectName(send_obj), selector, g_sci->getKernel()->getSelectorName(selector).c_str()); #endif // VM_DEBUG_SEND @@ -438,9 +448,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #ifdef VM_DEBUG_SEND if (argc) - printf("Varselector: Write %04x:%04x\n", PRINT_REG(argp[1])); + debugN("Varselector: Write %04x:%04x\n", PRINT_REG(argp[1])); else - printf("Varselector: Read\n"); + debugN("Varselector: Read\n"); #endif // VM_DEBUG_SEND // argc == 0: read selector @@ -496,39 +506,39 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #ifndef VM_DEBUG_SEND if (activeBreakpointTypes & BREAK_SELECTOREXEC) { if (g_sci->checkSelectorBreakpoint(BREAK_SELECTOREXEC, send_obj, selector)) { - printf("[execute selector]"); + debugN("[execute selector]"); int displaySize = 0; for (int argNr = 1; argNr <= argc; argNr++) { if (argNr == 1) - printf(" - "); + debugN(" - "); reg_t curParam = argp[argNr]; if (curParam.segment) { - printf("[%04x:%04x] ", PRINT_REG(curParam)); + debugN("[%04x:%04x] ", PRINT_REG(curParam)); displaySize += 12; } else { - printf("[%04x] ", curParam.offset); + debugN("[%04x] ", curParam.offset); displaySize += 7; } if (displaySize > 50) { if (argNr < argc) - printf("..."); + debugN("..."); break; } } - printf("\n"); + debugN("\n"); } } #else // VM_DEBUG_SEND if (activeBreakpointTypes & BREAK_SELECTOREXEC) g_sci->checkSelectorBreakpoint(BREAK_SELECTOREXEC, send_obj, selector); - printf("Funcselector("); + debugN("Funcselector("); for (int i = 0; i < argc; i++) { - printf("%04x:%04x", PRINT_REG(argp[i+1])); + debugN("%04x:%04x", PRINT_REG(argp[i+1])); if (i + 1 < argc) - printf(", "); + debugN(", "); } - printf(") at %04x:%04x\n", PRINT_REG(funcp)); + debugN(") at %04x:%04x\n", PRINT_REG(funcp)); #endif // VM_DEBUG_SEND { @@ -583,7 +593,7 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t // Returns new TOS element for the execution stack // _localsSegment may be -1 if derived from the called object - //printf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos, s->_executionStack.size(), origin, s->execution_stack); + //debug("Exec stack: [%d/%d], origin %d, at %p", s->execution_stack_pos, s->_executionStack.size(), origin, s->execution_stack); ExecStack xstack; @@ -630,7 +640,6 @@ static reg_t pointer_add(EngineState *s, reg_t base, int offset) { case SEG_TYPE_DYNMEM: base.offset += offset; return base; - default: // FIXME: Changed this to warning, because iceman does this during dancing with girl. // Investigate why that is so and either fix the underlying issue or implement a more @@ -655,46 +664,46 @@ static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc, static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *kernelSubCall, EngineState *s, int argc, reg_t *argv, reg_t result) { Kernel *kernel = g_sci->getKernel(); if (!kernelSubCall) { - printf("k%s: ", kernelCall->name); + debugN("k%s: ", kernelCall->name); } else { int callNameLen = strlen(kernelCall->name); if (strncmp(kernelCall->name, kernelSubCall->name, callNameLen) == 0) { const char *subCallName = kernelSubCall->name + callNameLen; - printf("k%s(%s): ", kernelCall->name, subCallName); + debugN("k%s(%s): ", kernelCall->name, subCallName); } else { - printf("k%s(%s): ", kernelCall->name, kernelSubCall->name); + debugN("k%s(%s): ", kernelCall->name, kernelSubCall->name); } } for (int parmNr = 0; parmNr < argc; parmNr++) { if (parmNr) - printf(", "); + debugN(", "); uint16 regType = kernel->findRegType(argv[parmNr]); if (regType & SIG_TYPE_NULL) - printf("0"); + debugN("0"); else if (regType & SIG_TYPE_UNINITIALIZED) - printf("UNINIT"); + debugN("UNINIT"); else if (regType & SIG_IS_INVALID) - printf("INVALID"); + debugN("INVALID"); else if (regType & SIG_TYPE_INTEGER) - printf("%d", argv[parmNr].offset); + debugN("%d", argv[parmNr].offset); else { - printf("%04x:%04x", PRINT_REG(argv[parmNr])); + debugN("%04x:%04x", PRINT_REG(argv[parmNr])); switch (regType) { case SIG_TYPE_OBJECT: - printf(" (%s)", s->_segMan->getObjectName(argv[parmNr])); + debugN(" (%s)", s->_segMan->getObjectName(argv[parmNr])); break; case SIG_TYPE_REFERENCE: if (kernelCall->function == kSaid) { SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]); if (saidSpec.isRaw) { - printf(" ('"); + debugN(" ('"); g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw); - printf("')"); + debugN("')"); } else { - printf(" (non-raw said-spec)"); + debugN(" (non-raw said-spec)"); } } else { - printf(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str()); + debugN(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str()); } default: break; @@ -702,9 +711,9 @@ static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunct } } if (result.segment) - printf(" = %04x:%04x\n", PRINT_REG(result)); + debugN(" = %04x:%04x\n", PRINT_REG(result)); else - printf(" = %d\n", result.offset); + debugN(" = %d\n", result.offset); } static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { @@ -749,7 +758,7 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { if (kernelCall.debugLogging) logKernelCall(&kernelCall, NULL, s, argc, argv, s->r_acc); if (kernelCall.debugBreakpoint) { - printf("Break on k%s\n", kernelCall.name); + debugN("Break on k%s\n", kernelCall.name); g_sci->_debugState.debugging = true; g_sci->_debugState.breakpointWasHit = true; } @@ -804,7 +813,7 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { if (kernelSubCall.debugLogging) logKernelCall(&kernelCall, &kernelSubCall, s, argc, argv, s->r_acc); if (kernelSubCall.debugBreakpoint) { - printf("Break on k%s\n", kernelSubCall.name); + debugN("Break on k%s\n", kernelSubCall.name); g_sci->_debugState.debugging = true; g_sci->_debugState.breakpointWasHit = true; } @@ -830,7 +839,7 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4]) memset(opparams, 0, sizeof(opparams)); for (int i = 0; g_opcode_formats[opcode][i]; ++i) { - //printf("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp); + //debugN("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp); assert(i < 3); switch (g_opcode_formats[opcode][i]) { @@ -1552,7 +1561,19 @@ void run_vm(EngineState *s) { case 0x26: // (38) case 0x27: // (39) - error("Dummy opcode 0x%x called", opcode); // should never happen + if (getSciVersion() == SCI_VERSION_3) { + if (extOpcode == 0x4c) + s->r_acc = obj->getInfoSelector(); + else if (extOpcode == 0x4d) + PUSH32(obj->getInfoSelector()); + else if (extOpcode == 0x4e) + s->r_acc = obj->getSuperClassSelector(); // TODO: is this correct? + // TODO: There are also opcodes in + // here to get the superclass, and possibly the species too. + else + error("Dummy opcode 0x%x called", opcode); // should never happen + } else + error("Dummy opcode 0x%x called", opcode); // should never happen break; case op_class: // 0x28 (40) @@ -1650,27 +1671,27 @@ void run_vm(EngineState *s) { case op_pToa: // 0x31 (49) // Property To Accumulator - s->r_acc = validate_property(obj, (opparams[0] >> 1)); + s->r_acc = validate_property(s, obj, opparams[0]); break; case op_aTop: // 0x32 (50) // Accumulator To Property - validate_property(obj, (opparams[0] >> 1)) = s->r_acc; + validate_property(s, obj, opparams[0]) = s->r_acc; break; case op_pTos: // 0x33 (51) // Property To Stack - PUSH32(validate_property(obj, opparams[0] >> 1)); + PUSH32(validate_property(s, obj, opparams[0])); break; case op_sTop: // 0x34 (52) // Stack To Property - validate_property(obj, (opparams[0] >> 1)) = POP32(); + validate_property(s, obj, opparams[0]) = POP32(); break; case op_ipToa: { // 0x35 (53) // Increment Property and copy To Accumulator - reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + reg_t &opProperty = validate_property(s, obj, opparams[0]); uint16 valueProperty; if (validate_unsignedInteger(opProperty, valueProperty)) s->r_acc = make_reg(0, valueProperty + 1); @@ -1682,7 +1703,7 @@ void run_vm(EngineState *s) { case op_dpToa: { // 0x36 (54) // Decrement Property and copy To Accumulator - reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + reg_t &opProperty = validate_property(s, obj, opparams[0]); uint16 valueProperty; if (validate_unsignedInteger(opProperty, valueProperty)) s->r_acc = make_reg(0, valueProperty - 1); @@ -1694,7 +1715,7 @@ void run_vm(EngineState *s) { case op_ipTos: { // 0x37 (55) // Increment Property and push to Stack - reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + reg_t &opProperty = validate_property(s, obj, opparams[0]); uint16 valueProperty; if (validate_unsignedInteger(opProperty, valueProperty)) valueProperty++; @@ -1707,7 +1728,7 @@ void run_vm(EngineState *s) { case op_dpTos: { // 0x38 (56) // Decrement Property and push to Stack - reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + reg_t &opProperty = validate_property(s, obj, opparams[0]); uint16 valueProperty; if (validate_unsignedInteger(opProperty, valueProperty)) valueProperty--; @@ -1723,19 +1744,27 @@ void run_vm(EngineState *s) { s->r_acc.segment = s->xs->addr.pc.segment; switch (g_sci->_features->detectLofsType()) { - case SCI_VERSION_1_1: - s->r_acc.offset = opparams[0] + local_script->getScriptSize(); + case SCI_VERSION_0_EARLY: + s->r_acc.offset = s->xs->addr.pc.offset + opparams[0]; break; case SCI_VERSION_1_MIDDLE: s->r_acc.offset = opparams[0]; break; + case SCI_VERSION_1_1: + s->r_acc.offset = opparams[0] + local_script->getScriptSize(); + break; + case SCI_VERSION_3: + // In theory this can break if the variant with a one-byte argument is + // used. For now, assume it doesn't happen. + s->r_acc.offset = local_script->relocateOffsetSci3(s->xs->addr.pc.offset-2); + break; default: - s->r_acc.offset = s->xs->addr.pc.offset + opparams[0]; + error("Unknown lofs type"); } if (s->r_acc.offset >= scr->getBufSize()) { error("VM: lofsa operation overflowed: %04x:%04x beyond end" - " of script (at %04x)\n", PRINT_REG(s->r_acc), scr->getBufSize()); + " of script (at %04x)", PRINT_REG(s->r_acc), scr->getBufSize()); } break; @@ -1744,14 +1773,20 @@ void run_vm(EngineState *s) { r_temp.segment = s->xs->addr.pc.segment; switch (g_sci->_features->detectLofsType()) { + case SCI_VERSION_0_EARLY: + r_temp.offset = s->xs->addr.pc.offset + opparams[0]; + break; + case SCI_VERSION_1_MIDDLE: + r_temp.offset = opparams[0]; + break; case SCI_VERSION_1_1: r_temp.offset = opparams[0] + local_script->getScriptSize(); break; - case SCI_VERSION_1_MIDDLE: + case SCI_VERSION_3: r_temp.offset = opparams[0]; break; default: - r_temp.offset = s->xs->addr.pc.offset + opparams[0]; + error("Unknown lofs type"); } if (r_temp.offset >= scr->getBufSize()) { @@ -1774,7 +1809,13 @@ void run_vm(EngineState *s) { break; case op_pushSelf: // 0x3e (62) - if (!(extOpcode & 1)) { + // Compensate for a bug in non-Sierra compilers, which seem to generate + // pushSelf instructions with the low bit set. This makes the following + // heuristic fail and leads to endless loops and crashes. Our + // interpretation of this seems correct, as other SCI tools, like for + // example SCI Viewer, have issues with these scripts (e.g. script 999 + // in Circus Quest). Fixes bug #3038686. + if (!(extOpcode & 1) || g_sci->getGameId() == GID_FANMADE) { PUSH32(s->xs->objp); } else { // Debug opcode op_file, skip null-terminated string (file name) |