diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/sci/engine/vm.cpp | 173 | ||||
-rw-r--r-- | engines/sci/engine/vm_types.h | 26 |
2 files changed, 107 insertions, 92 deletions
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 6d11a1ad8a..9c7f52d28e 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -131,21 +131,6 @@ static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) { return 0; } -static int validate_arithmetic(reg_t reg) { - if (reg.segment) { - // The results of this are likely unpredictable... It most likely means that a kernel function is returning something wrong. - // If such an error occurs, we usually need to find the last kernel function called and check its return value. - error("[VM] Attempt to read arithmetic value from non-zero segment [%04x]. Address: %04x:%04x", reg.segment, PRINT_REG(reg)); - return 0; - } - - return reg.offset; -} - -static int signed_validate_arithmetic(reg_t reg) { - return (int16)validate_arithmetic(reg); -} - static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index) { const char *names[4] = {"global", "local", "temp", "param"}; @@ -176,20 +161,6 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in return true; } -static bool validate_unsignedInteger(reg_t reg, uint16 &integer) { - if (reg.segment) - return false; - integer = reg.offset; - return true; -} - -static bool validate_signedInteger(reg_t reg, int16 &integer) { - if (reg.segment) - return false; - integer = (int16)reg.offset; - return true; -} - extern const char *opcodeNames[]; // from scriptdebug.cpp static reg_t arithmetic_lookForWorkaround(const byte opcode, const SciWorkaroundEntry *workaroundList, reg_t value1, reg_t value2) { @@ -427,12 +398,12 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt int activeBreakpointTypes = g_sci->_debugState._activeBreakpointTypes; while (framesize > 0) { - selector = validate_arithmetic(*argp++); - argc = validate_arithmetic(*argp); + selector = argp->requireUint16(); + argp++; + argc = argp->requireUint16(); - if (argc > 0x800) { // More arguments than the stack could possibly accomodate for + if (argc > 0x800) // More arguments than the stack could possibly accomodate for error("send_selector(): More than 0x800 arguments to function call"); - } #ifdef VM_DEBUG_SEND debugN("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj), @@ -1014,8 +985,8 @@ void run_vm(EngineState *s) { case op_bnot: { // 0x00 (00) // Binary not - int16 value; - if (validate_signedInteger(s->r_acc, value)) + int16 value = s->r_acc.toSint16(); + if (s->r_acc.isNumber()) s->r_acc = make_reg(0, 0xffff ^ value); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG); @@ -1087,8 +1058,9 @@ void run_vm(EngineState *s) { case op_mul: { // 0x03 (03) r_temp = POP32(); - int16 value1, value2; - if (validate_signedInteger(s->r_acc, value1) && validate_signedInteger(r_temp, value2)) + int16 value1 = s->r_acc.toSint16(); + int16 value2 = r_temp.toSint16(); + if (s->r_acc.isNumber() && r_temp.isNumber()) s->r_acc = make_reg(0, value1 * value2); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeMulWorkarounds, s->r_acc, r_temp); @@ -1097,8 +1069,9 @@ void run_vm(EngineState *s) { case op_div: { // 0x04 (04) r_temp = POP32(); - int16 divisor, dividend; - if (validate_signedInteger(s->r_acc, divisor) && validate_signedInteger(r_temp, dividend)) + int16 divisor = s->r_acc.toSint16(); + int16 dividend = r_temp.toSint16(); + if (s->r_acc.isNumber() && r_temp.isNumber()) s->r_acc = make_reg(0, (divisor != 0 ? dividend / divisor : 0)); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDivWorkarounds, s->r_acc, r_temp); @@ -1109,8 +1082,9 @@ void run_vm(EngineState *s) { r_temp = POP32(); if (getSciVersion() <= SCI_VERSION_0_LATE) { - uint16 modulo, value; - if (validate_unsignedInteger(s->r_acc, modulo) && validate_unsignedInteger(r_temp, value)) + uint16 modulo = s->r_acc.toUint16(); + uint16 value = r_temp.toUint16(); + if (s->r_acc.isNumber() && r_temp.isNumber()) s->r_acc = make_reg(0, (modulo != 0 ? value % modulo : 0)); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp); @@ -1121,8 +1095,10 @@ void run_vm(EngineState *s) { // for simplicity's sake and use the new code for SCI01 and newer // games. Fixes the battlecruiser mini game in SQ5 (room 850), // bug #3035755 - int16 modulo, value, result; - if (validate_signedInteger(s->r_acc, modulo) && validate_signedInteger(r_temp, value)) { + int16 modulo = s->r_acc.toSint16(); + int16 value = r_temp.toSint16(); + int16 result; + if (s->r_acc.isNumber() && r_temp.isNumber()) { modulo = ABS(modulo); result = (modulo != 0 ? value % modulo : 0); if (result < 0) @@ -1137,8 +1113,9 @@ void run_vm(EngineState *s) { case op_shr: { // 0x06 (06) // Shift right logical r_temp = POP32(); - uint16 value, shiftCount; - if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount)) + uint16 value = r_temp.toUint16(); + uint16 shiftCount = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, value >> shiftCount); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1148,8 +1125,9 @@ void run_vm(EngineState *s) { case op_shl: { // 0x07 (07) // Shift left logical r_temp = POP32(); - uint16 value, shiftCount; - if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount)) + uint16 value = r_temp.toUint16(); + uint16 shiftCount = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, value << shiftCount); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1158,8 +1136,9 @@ void run_vm(EngineState *s) { case op_xor: { // 0x08 (08) r_temp = POP32(); - uint16 value1, value2; - if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2)) + uint16 value1 = r_temp.toUint16(); + uint16 value2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, value1 ^ value2); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1168,8 +1147,9 @@ void run_vm(EngineState *s) { case op_and: { // 0x09 (09) r_temp = POP32(); - uint16 value1, value2; - if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2)) + uint16 value1 = r_temp.toUint16(); + uint16 value2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, value1 & value2); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeAndWorkarounds, r_temp, s->r_acc); @@ -1178,8 +1158,9 @@ void run_vm(EngineState *s) { case op_or: { // 0x0a (10) r_temp = POP32(); - uint16 value1, value2; - if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2)) + uint16 value1 = r_temp.toUint16(); + uint16 value2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, value1 | value2); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeOrWorkarounds, r_temp, s->r_acc); @@ -1187,8 +1168,8 @@ void run_vm(EngineState *s) { } case op_neg: { // 0x0b (11) - int16 value; - if (validate_signedInteger(s->r_acc, value)) + int16 value = s->r_acc.toSint16(); + if (s->r_acc.isNumber()) s->r_acc = make_reg(0, -value); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG); @@ -1232,8 +1213,9 @@ void run_vm(EngineState *s) { // Happens in SQ1, room 28, when throwing the water at Orat s->r_acc = make_reg(0, 1); } else { - int16 compare1, compare2; - if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + int16 compare1 = r_temp.toSint16(); + int16 compare2 = s->r_acc.toSint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 > compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1249,8 +1231,9 @@ void run_vm(EngineState *s) { warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc)); s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset); } else { - int16 compare1, compare2; - if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + int16 compare1 = r_temp.toSint16(); + int16 compare2 = s->r_acc.toSint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 >= compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeGeWorkarounds, r_temp, s->r_acc); @@ -1272,8 +1255,9 @@ void run_vm(EngineState *s) { // Happens in SQ1, room 58, when giving id-card to robot s->r_acc = make_reg(0, 1); } else { - int16 compare1, compare2; - if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + int16 compare1 = r_temp.toSint16(); + int16 compare2 = s->r_acc.toSint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 < compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1289,8 +1273,9 @@ void run_vm(EngineState *s) { warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc)); s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset); } else { - int16 compare1, compare2; - if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + int16 compare1 = r_temp.toSint16(); + int16 compare2 = s->r_acc.toSint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 <= compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeLeWorkarounds, r_temp, s->r_acc); @@ -1317,8 +1302,9 @@ void run_vm(EngineState *s) { else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset); else { - uint16 compare1, compare2; - if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + uint16 compare1 = r_temp.toUint16(); + uint16 compare2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 > compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1336,8 +1322,9 @@ void run_vm(EngineState *s) { else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset); else { - uint16 compare1, compare2; - if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + uint16 compare1 = r_temp.toUint16(); + uint16 compare2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 >= compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1356,8 +1343,9 @@ void run_vm(EngineState *s) { else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset); else { - uint16 compare1, compare2; - if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + uint16 compare1 = r_temp.toUint16(); + uint16 compare2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 < compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeUltWorkarounds, r_temp, s->r_acc); @@ -1375,8 +1363,9 @@ void run_vm(EngineState *s) { else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset); else { - uint16 compare1, compare2; - if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + uint16 compare1 = r_temp.toUint16(); + uint16 compare2 = s->r_acc.toUint16(); + if (r_temp.isNumber() && s->r_acc.isNumber()) s->r_acc = make_reg(0, compare1 <= compare2); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); @@ -1446,7 +1435,7 @@ void run_vm(EngineState *s) { xs_new = add_exec_stack_entry(s->_executionStack, make_reg(s->xs->addr.pc.segment, localCallOffset), s->xs->sp, s->xs->objp, - (validate_arithmetic(*call_base)) + s->restAdjust, + (call_base->requireUint16()) + s->restAdjust, call_base, NULL_SELECTOR, -1, localCallOffset, s->xs->objp, s->_executionStack.size()-1, s->xs->local_segment); s->restAdjust = 0; // Used up the &rest adjustment @@ -1466,7 +1455,7 @@ void run_vm(EngineState *s) { if (!oldScriptHeader) s->xs->sp -= s->restAdjust; - int argc = validate_arithmetic(s->xs->sp[0]); + int argc = s->xs->sp[0].requireUint16(); if (!oldScriptHeader) argc += s->restAdjust; @@ -1663,7 +1652,7 @@ void run_vm(EngineState *s) { r_temp.offset = s->variables[var_number] - s->variablesBase[var_number]; if (temp & 0x08) // Add accumulator offset if requested - r_temp.offset += signed_validate_arithmetic(s->r_acc); + r_temp.offset += s->r_acc.requireSint16(); r_temp.offset += opparams[1]; // Add index r_temp.offset *= 2; // variables are 16 bit @@ -1710,8 +1699,8 @@ void run_vm(EngineState *s) { case op_ipToa: { // 0x35 (53) // Increment Property and copy To Accumulator reg_t &opProperty = validate_property(s, obj, opparams[0]); - uint16 valueProperty; - if (validate_unsignedInteger(opProperty, valueProperty)) + uint16 valueProperty = opProperty.toUint16(); + if (opProperty.isNumber()) s->r_acc = make_reg(0, valueProperty + 1); else s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG); @@ -1722,8 +1711,8 @@ void run_vm(EngineState *s) { case op_dpToa: { // 0x36 (54) // Decrement Property and copy To Accumulator reg_t &opProperty = validate_property(s, obj, opparams[0]); - uint16 valueProperty; - if (validate_unsignedInteger(opProperty, valueProperty)) + uint16 valueProperty = opProperty.toUint16(); + if (opProperty.isNumber()) s->r_acc = make_reg(0, valueProperty - 1); else s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDptoaWorkarounds, opProperty, NULL_REG); @@ -1734,8 +1723,8 @@ void run_vm(EngineState *s) { case op_ipTos: { // 0x37 (55) // Increment Property and push to Stack reg_t &opProperty = validate_property(s, obj, opparams[0]); - uint16 valueProperty; - if (validate_unsignedInteger(opProperty, valueProperty)) + uint16 valueProperty = opProperty.toUint16(); + if (opProperty.isNumber()) valueProperty++; else valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset; @@ -1747,8 +1736,8 @@ void run_vm(EngineState *s) { case op_dpTos: { // 0x38 (56) // Decrement Property and push to Stack reg_t &opProperty = validate_property(s, obj, opparams[0]); - uint16 valueProperty; - if (validate_unsignedInteger(opProperty, valueProperty)) + uint16 valueProperty = opProperty.toUint16(); + if (opProperty.isNumber()) valueProperty--; else valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset; @@ -1871,8 +1860,8 @@ void run_vm(EngineState *s) { // Load global, local, temp or param variable into the accumulator, // using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - int16 value; - if (!validate_signedInteger(s->r_acc, value)) + int16 value = s->r_acc.toSint16(); + if (!s->r_acc.isNumber()) value = arithmetic_lookForWorkaround(opcode, opcodeLaiWorkarounds, s->r_acc, NULL_REG).offset; var_number = opparams[0] + value; s->r_acc = READ_VAR(var_type, var_number); @@ -1886,8 +1875,8 @@ void run_vm(EngineState *s) { // Load global, local, temp or param variable into the stack, // using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - int16 value; - if (!validate_signedInteger(s->r_acc, value)) + int16 value = s->r_acc.toSint16(); + if (!s->r_acc.isNumber()) value = arithmetic_lookForWorkaround(opcode, opcodeLsiWorkarounds, s->r_acc, NULL_REG).offset; var_number = opparams[0] + value; PUSH32(READ_VAR(var_type, var_number)); @@ -1925,7 +1914,7 @@ void run_vm(EngineState *s) { // of sense otherwise, with acc being used for two things // simultaneously... var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + var_number = opparams[0] + s->r_acc.requireSint16(); s->r_acc = POP32(); WRITE_VAR(var_type, var_number, s->r_acc); break; @@ -1937,7 +1926,7 @@ void run_vm(EngineState *s) { // Save the stack into the global, local, temp or param variable, // using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + var_number = opparams[0] + s->r_acc.requireSint16(); WRITE_VAR(var_type, var_number, POP32()); break; @@ -1983,7 +1972,7 @@ void run_vm(EngineState *s) { // Increment the global, local, temp or param variable and save it // to the accumulator, using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + var_number = opparams[0] + s->r_acc.requireSint16(); r_temp = READ_VAR(var_type, var_number); if (r_temp.segment) { // Pointer arithmetics! @@ -2000,7 +1989,7 @@ void run_vm(EngineState *s) { // Increment the global, local, temp or param variable and save it // to the stack, using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + var_number = opparams[0] + s->r_acc.requireSint16(); r_temp = READ_VAR(var_type, var_number); if (r_temp.segment) { // Pointer arithmetics! @@ -2053,7 +2042,7 @@ void run_vm(EngineState *s) { // Decrement the global, local, temp or param variable and save it // to the accumulator, using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + var_number = opparams[0] + s->r_acc.requireSint16(); r_temp = READ_VAR(var_type, var_number); if (r_temp.segment) { // Pointer arithmetics! @@ -2070,7 +2059,7 @@ void run_vm(EngineState *s) { // Decrement the global, local, temp or param variable and save it // to the stack, using the accumulator as an additional index var_type = opcode & 0x3; // Gets the variable type: g, l, t or p - var_number = opparams[0] + signed_validate_arithmetic(s->r_acc); + var_number = opparams[0] + s->r_acc.requireSint16(); r_temp = READ_VAR(var_type, var_number); if (r_temp.segment) { // Pointer arithmetics! diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h index edf35a122a..cc1f9b2685 100644 --- a/engines/sci/engine/vm_types.h +++ b/engines/sci/engine/vm_types.h @@ -56,6 +56,32 @@ struct reg_t { int16 toSint16() const { return (int16) offset; } + + uint16 requireUint16() const { + if (isNumber()) + return toUint16(); + else + // The results of this are likely unpredictable... It most likely + // means that a kernel function is returning something wrong. If + // such an error occurs, we usually need to find the last kernel + // function called and check its return value. + error("[VM] Attempt to read unsigned arithmetic value from non-zero segment %04x. Offset: %04x", segment, offset); + } + + int16 requireSint16() const { + if (isNumber()) + return toSint16(); + else + // The results of this are likely unpredictable... It most likely + // means that a kernel function is returning something wrong. If + // such an error occurs, we usually need to find the last kernel + // function called and check its return value. + error("[VM] Attempt to read signed arithmetic value from non-zero segment %04x. Offset: %04x", segment, offset); + } + + bool isNumber() const { + return !segment; + } }; static inline reg_t make_reg(SegmentId segment, uint16 offset) { |