aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/vm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/vm.cpp')
-rw-r--r--engines/sci/engine/vm.cpp149
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)