2009-02-17 15:02:16 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 20:11:12 +00:00
|
|
|
#include "common/debug.h"
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-27 02:23:00 +00:00
|
|
|
#include "sci/scicore/resource.h"
|
2009-02-27 02:23:40 +00:00
|
|
|
#include "sci/engine/state.h"
|
2009-02-27 02:23:00 +00:00
|
|
|
#include "sci/scicore/versions.h"
|
2009-02-24 23:16:42 +00:00
|
|
|
#include "sci/engine/intmap.h"
|
2009-02-24 05:51:55 +00:00
|
|
|
#include "sci/engine/kdebug.h"
|
|
|
|
#include "sci/engine/kernel.h"
|
2009-02-15 08:34:13 +00:00
|
|
|
#include "sci/engine/kernel_types.h"
|
2009-02-16 09:23:58 +00:00
|
|
|
#include "sci/engine/seg_manager.h"
|
2009-02-15 08:34:13 +00:00
|
|
|
#include "sci/engine/gc.h"
|
2009-02-21 19:33:01 +00:00
|
|
|
#include "sci/sfx/sfx_player.h"
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 10:23:36 +00:00
|
|
|
namespace Sci {
|
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
reg_t NULL_REG = NULL_REG_INITIALIZER;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
//#define VM_DEBUG_SEND
|
|
|
|
#undef STRICT_SEND // Disallows variable sends with more than one parameter
|
|
|
|
#undef STRICT_READ // Disallows reading from out-of-bounds parameters and locals
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
int script_abort_flag = 0; // Set to 1 to abort execution
|
|
|
|
int script_error_flag = 0; // Set to 1 if an error occured, reset each round by the VM
|
|
|
|
int script_checkloads_flag = 0; // Print info when scripts get (un)loaded
|
|
|
|
int script_step_counter = 0; // Counts the number of steps executed
|
|
|
|
int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
extern int _debug_step_running;
|
|
|
|
extern int _debug_seeking;
|
|
|
|
extern int _weak_validations;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
CallsStruct *send_calls = NULL;
|
2009-02-15 06:10:59 +00:00
|
|
|
int send_calls_allocated = 0;
|
|
|
|
int bp_flag = 0;
|
2009-02-21 22:06:42 +00:00
|
|
|
static reg_t _dummy_register;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// validation functionality
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static inline reg_t &validate_property(Object *obj, int index) {
|
2009-02-15 22:28:12 +00:00
|
|
|
if (!obj) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (sci_debug_flags & 4)
|
2009-02-21 14:11:41 +00:00
|
|
|
sciprintf("[VM] Sending to disposed object!\n");
|
2009-02-15 06:10:59 +00:00
|
|
|
_dummy_register = NULL_REG;
|
2009-02-21 22:06:42 +00:00
|
|
|
return _dummy_register;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (index < 0 || index >= obj->variables_nr) {
|
|
|
|
if (sci_debug_flags & 4)
|
2009-02-21 14:11:41 +00:00
|
|
|
sciprintf("[VM] Invalid property #%d (out of [0..%d]) requested!\n", index,
|
|
|
|
obj->variables_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
_dummy_register = NULL_REG;
|
2009-02-21 22:06:42 +00:00
|
|
|
return _dummy_register;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-21 22:06:42 +00:00
|
|
|
return obj->variables[index];
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static inline StackPtr validate_stack_addr(EngineState *s, StackPtr sp) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (sp >= s->stack_base && sp < s->stack_top)
|
|
|
|
return sp;
|
|
|
|
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
if (sci_debug_flags & 4)
|
2009-02-24 15:14:15 +00:00
|
|
|
sciprintf("[VM] Stack index %d out of valid range [%d..%d]\n", (int)(sp - s->stack_base), 0, (int)(s->stack_top - s->stack_base - 1));
|
2009-02-15 06:10:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
static inline int validate_arithmetic(reg_t reg) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (reg.segment) {
|
|
|
|
if (!_weak_validations)
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
if (sci_debug_flags & 4)
|
|
|
|
sciprintf("[VM] Attempt to read arithmetic value from non-zero segment [%04x]\n", reg.segment);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return reg.offset;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
static inline int signed_validate_arithmetic(reg_t reg) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (reg.segment) {
|
|
|
|
if (!_weak_validations)
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
if (sci_debug_flags & 4)
|
|
|
|
sciprintf("[VM] Attempt to read arithmetic value from non-zero segment [%04x]\n", reg.segment);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
if (reg.offset&0x8000)
|
2009-02-15 22:28:12 +00:00
|
|
|
return (signed)(reg.offset) - 65536;
|
2009-02-15 06:10:59 +00:00
|
|
|
else
|
|
|
|
return reg.offset;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
static inline int validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index, int line) {
|
2009-02-15 06:10:59 +00:00
|
|
|
const char *names[4] = {"global", "local", "temp", "param"};
|
|
|
|
|
|
|
|
if (index < 0 || index >= max) {
|
|
|
|
sciprintf("[VM] Attempt to use invalid %s variable %04x ", names[type], index);
|
|
|
|
if (max == 0)
|
|
|
|
sciprintf("(variable type invalid)");
|
|
|
|
else
|
2009-02-15 22:28:12 +00:00
|
|
|
sciprintf("(out of range [%d..%d])", 0, max - 1);
|
2009-02-15 06:10:59 +00:00
|
|
|
sciprintf(" in %s, line %d\n", __FILE__, line);
|
|
|
|
if (!_weak_validations)
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
|
|
|
|
#ifdef STRICT_READ
|
|
|
|
return 1;
|
2009-02-20 23:09:29 +00:00
|
|
|
#else // !STRICT_READ
|
2009-02-15 06:10:59 +00:00
|
|
|
if (type == VAR_PARAM || type == VAR_TEMP) {
|
|
|
|
int total_offset = r - stack_base;
|
|
|
|
if (total_offset < 0 || total_offset >= VM_STACK_SIZE) {
|
2009-02-20 23:09:29 +00:00
|
|
|
sciprintf("[VM] Access would be outside even of the stack (%d); access denied\n", total_offset);
|
2009-02-15 06:10:59 +00:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
sciprintf("[VM] Access within stack boundaries; access granted.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
static inline reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t default_value) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (!validate_variable(r, stack_base, type, max, index, line))
|
|
|
|
return r[index];
|
|
|
|
else
|
|
|
|
return default_value;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
static inline void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t value) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (!validate_variable(r, stack_base, type, max, index, line))
|
|
|
|
r[index] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
# define ASSERT_ARITHMETIC(v) validate_arithmetic(v)
|
|
|
|
|
|
|
|
#else
|
2009-02-20 23:09:29 +00:00
|
|
|
// Non-validating alternatives
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
# define validate_stack_addr(s, sp) sp
|
|
|
|
# define validate_arithmetic(r) ((r).offset)
|
2009-02-20 23:09:29 +00:00
|
|
|
# define signed_validate_arithmetic(r) ((int) ((r).offset) & 0x8000 ? (signed) ((r).offset) - 65536 : ((r).offset))
|
2009-02-15 06:10:59 +00:00
|
|
|
# define validate_variable(r, sb, t, m, i, l)
|
|
|
|
# define validate_read_var(r, sb, t, m, i, l) ((r)[i])
|
|
|
|
# define validate_write_var(r, sb, t, m, i, l, v) ((r)[i] = (v))
|
2009-02-21 22:06:42 +00:00
|
|
|
# define validate_property(o, p) ((o)->variables[p])
|
2009-02-15 06:10:59 +00:00
|
|
|
# define ASSERT_ARITHMETIC(v) (v).offset
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define READ_VAR(type, index, def) validate_read_var(variables[type], s->stack_base, type, variables_max[type], index, __LINE__, def)
|
|
|
|
#define WRITE_VAR(type, index, value) validate_write_var(variables[type], s->stack_base, type, variables_max[type], index, __LINE__, value)
|
|
|
|
#define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value));
|
|
|
|
|
|
|
|
#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc)))
|
|
|
|
#define ACC_AUX_LOAD() aux_acc = signed_validate_arithmetic(s->r_acc)
|
|
|
|
#define ACC_AUX_STORE() s->r_acc = make_reg(0, aux_acc)
|
|
|
|
|
2009-02-21 22:06:42 +00:00
|
|
|
#define OBJ_PROPERTY(o, p) (validate_property(o, p))
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int script_error(EngineState *s, const char *file, int line, const char *reason) {
|
2009-02-15 06:10:59 +00:00
|
|
|
sciprintf("Script error in file %s, line %d: %s\n", file, line, reason);
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#define CORE_ERROR(area, msg) script_error(s, "[" area "] " __FILE__, __LINE__, msg)
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
reg_t get_class_address(EngineState *s, int classnr, int lock, reg_t caller) {
|
2009-02-28 11:12:59 +00:00
|
|
|
Class *the_class = s->classtable + classnr;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (NULL == s) {
|
|
|
|
sciprintf("vm.c: get_class_address(): NULL passed for \"s\"\n");
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
if (classnr < 0 || s->classtable_size <= classnr || the_class->script < 0) {
|
|
|
|
sciprintf("[VM] Attempt to dereference class %x, which doesn't exist (max %x)\n", classnr, s->classtable_size);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_error_flag = script_debug_flag = 1;
|
|
|
|
return NULL_REG;
|
|
|
|
} else {
|
2009-02-15 22:01:09 +00:00
|
|
|
if (!the_class->reg.segment) {
|
|
|
|
script_get_segment(s, the_class->script, lock);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:01:09 +00:00
|
|
|
if (!the_class->reg.segment) {
|
2009-02-15 06:10:59 +00:00
|
|
|
sciprintf("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;"
|
2009-02-21 04:25:56 +00:00
|
|
|
" Entering debugger.\n", classnr, the_class->script, the_class->script);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_error_flag = script_debug_flag = 1;
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
} else
|
2009-02-15 22:01:09 +00:00
|
|
|
if (caller.segment != the_class->reg.segment)
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->incrementLockers(the_class->reg.segment, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:01:09 +00:00
|
|
|
return the_class->reg;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Operating on the stack
|
|
|
|
// 16 bit:
|
2009-02-15 06:10:59 +00:00
|
|
|
#define PUSH(v) PUSH32(make_reg(0, v))
|
|
|
|
#define POP() (validate_arithmetic(POP32()))
|
2009-02-20 23:09:29 +00:00
|
|
|
// 32 bit:
|
2009-02-15 06:10:59 +00:00
|
|
|
#define PUSH32(a) (*(validate_stack_addr(s, (xs->sp)++)) = (a))
|
|
|
|
#define POP32() (*(validate_stack_addr(s, --(xs->sp))))
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Getting instruction parameters
|
2009-02-21 21:16:41 +00:00
|
|
|
#define GET_OP_BYTE() ((uint8)code_buf[(xs->addr.pc.offset)++])
|
2009-02-15 06:10:59 +00:00
|
|
|
#define GET_OP_WORD() (getUInt16(code_buf + ((xs->addr.pc.offset) += 2) - 2))
|
|
|
|
#define GET_OP_FLEX() ((opcode & 1)? GET_OP_BYTE() : GET_OP_WORD())
|
2009-02-21 21:16:41 +00:00
|
|
|
#define GET_OP_SIGNED_BYTE() ((int8)(code_buf[(xs->addr.pc.offset)++]))
|
2009-02-15 06:10:59 +00:00
|
|
|
#define GET_OP_SIGNED_WORD() ((getInt16(code_buf + ((xs->addr.pc.offset) += 2) - 2)))
|
|
|
|
#define GET_OP_SIGNED_FLEX() ((opcode & 1)? GET_OP_SIGNED_BYTE() : GET_OP_SIGNED_WORD())
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
#define SEG_GET_HEAP(s, reg) s->seg_manager->getHeap(reg)
|
2009-02-15 06:10:59 +00:00
|
|
|
#define OBJ_SPECIES(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SPECIES_OFFSET))
|
2009-02-20 23:09:29 +00:00
|
|
|
// Returns an object's species
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
#define OBJ_SUPERCLASS(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SUPERCLASS_OFFSET))
|
2009-02-20 23:09:29 +00:00
|
|
|
// Returns an object's superclass
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
inline ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackPtr sp, reg_t calling_obj, uint16 argc, StackPtr argp) {
|
2009-02-15 06:10:59 +00:00
|
|
|
int seg;
|
2009-02-21 21:16:41 +00:00
|
|
|
uint16 temp;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
if (!s->seg_manager->scriptIsLoaded(script, SCRIPT_ID)) // Script not present yet?
|
2009-02-15 06:10:59 +00:00
|
|
|
script_instantiate(s, script);
|
|
|
|
else
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->unmarkScriptDeleted(script);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
seg = s->seg_manager->segGet(script);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
temp = s->seg_manager->validateExportFunc(pubfunct, seg);
|
2009-02-15 22:28:12 +00:00
|
|
|
VERIFY(temp, "Invalid pubfunct in export table");
|
|
|
|
if (!temp) {
|
2009-02-15 06:10:59 +00:00
|
|
|
sciprintf("Request for invalid exported function 0x%x of script 0x%x\n", pubfunct, script);
|
|
|
|
script_error_flag = script_debug_flag = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Check if a breakpoint is set on this method
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->have_bp & BREAK_EXPORT) {
|
2009-02-28 11:12:59 +00:00
|
|
|
Breakpoint *bp;
|
2009-02-21 21:16:41 +00:00
|
|
|
uint32 bpaddress;
|
2009-02-15 22:28:12 +00:00
|
|
|
|
|
|
|
bpaddress = (script << 16 | pubfunct);
|
|
|
|
|
|
|
|
bp = s->bp_list;
|
|
|
|
while (bp) {
|
|
|
|
if (bp->type == BREAK_EXPORT && bp->data.address == bpaddress) {
|
|
|
|
sciprintf("Break on script %d, export %d\n", script, pubfunct);
|
|
|
|
script_debug_flag = 1;
|
|
|
|
bp_flag = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bp = bp->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
return add_exec_stack_entry(s, make_reg(seg, temp), sp, calling_obj, argc, argp, -1, calling_obj, s->execution_stack_pos, seg);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
static void _exec_varselectors(EngineState *s) {
|
2009-02-20 23:09:29 +00:00
|
|
|
// Executes all varselector read/write ops on the TOS
|
|
|
|
// Now check the TOS to execute all varselector entries
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->execution_stack_pos >= 0)
|
|
|
|
while (s->execution_stack[s->execution_stack_pos].type == EXEC_STACK_TYPE_VARSELECTOR) {
|
2009-02-20 23:09:29 +00:00
|
|
|
// varselector access?
|
|
|
|
if (s->execution_stack[s->execution_stack_pos].argc) { // write?
|
2009-02-15 06:10:59 +00:00
|
|
|
reg_t temp = s->execution_stack[s->execution_stack_pos].variables_argp[1];
|
|
|
|
*(s->execution_stack[s->execution_stack_pos].addr.varp) = temp;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
} else // No, read
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = *(s->execution_stack[s->execution_stack_pos].addr.varp);
|
|
|
|
|
|
|
|
--(s->execution_stack_pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPtr sp, int framesize, StackPtr argp) {
|
2009-02-20 23:09:29 +00:00
|
|
|
// send_obj and work_obj are equal for anything but 'super'
|
|
|
|
// Returns a pointer to the TOS exec_stack element
|
2009-02-15 06:10:59 +00:00
|
|
|
#ifdef VM_DEBUG_SEND
|
|
|
|
int i;
|
|
|
|
#endif
|
|
|
|
reg_t *varp;
|
|
|
|
reg_t funcp;
|
|
|
|
int selector;
|
|
|
|
int argc;
|
2009-02-20 23:09:29 +00:00
|
|
|
int origin = s->execution_stack_pos; // Origin: Used for debugging
|
2009-02-28 11:12:59 +00:00
|
|
|
ExecStack *retval = s->execution_stack + s->execution_stack_pos;
|
2009-02-15 06:10:59 +00:00
|
|
|
int print_send_action = 0;
|
2009-02-28 11:12:59 +00:00
|
|
|
// We return a pointer to the new active ExecStack
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// The selector calls we catch are stored below:
|
2009-02-15 06:10:59 +00:00
|
|
|
int send_calls_nr = -1;
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (NULL == s) {
|
2009-02-28 11:12:59 +00:00
|
|
|
sciprintf("vm.c: ExecStack(): NULL passed for \"s\"\n");
|
2009-02-15 22:28:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
while (framesize > 0) {
|
|
|
|
selector = validate_arithmetic(*argp++);
|
|
|
|
argc = validate_arithmetic(*argp);
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
if (argc > 0x800) { // More arguments than the stack could possibly accomodate for
|
2009-02-15 06:10:59 +00:00
|
|
|
CORE_ERROR("SEND", "More than 0x800 arguments to function call\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Check if a breakpoint is set on this method
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->have_bp & BREAK_SELECTOR) {
|
2009-02-28 11:12:59 +00:00
|
|
|
Breakpoint *bp;
|
2009-02-15 06:10:59 +00:00
|
|
|
char method_name [256];
|
|
|
|
|
2009-02-23 03:04:52 +00:00
|
|
|
sprintf(method_name, "%s::%s", obj_get_name(s, send_obj), s->_selectorNames[selector].c_str());
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
bp = s->bp_list;
|
|
|
|
while (bp) {
|
|
|
|
int cmplen = strlen(bp->data.name);
|
|
|
|
if (bp->data.name[cmplen - 1] != ':')
|
|
|
|
cmplen = 256;
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (bp->type == BREAK_SELECTOR && !strncmp(bp->data.name, method_name, cmplen)) {
|
2009-02-20 23:09:29 +00:00
|
|
|
sciprintf("Break on %s (in ["PREG"])\n", method_name, PRINT_REG(send_obj));
|
2009-02-15 06:10:59 +00:00
|
|
|
script_debug_flag = print_send_action = 1;
|
|
|
|
bp_flag = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bp = bp->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef VM_DEBUG_SEND
|
2009-02-23 03:04:52 +00:00
|
|
|
sciprintf("Send to "PREG", selector %04x (%s):", PRINT_REG(send_obj), selector, s->_selectorNames[selector].c_str());
|
2009-02-20 23:09:29 +00:00
|
|
|
#endif // VM_DEBUG_SEND
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 22:06:42 +00:00
|
|
|
if (++send_calls_nr == (send_calls_allocated - 1)) {
|
|
|
|
send_calls_allocated *= 2;
|
2009-02-28 11:12:59 +00:00
|
|
|
send_calls = (CallsStruct *)sci_realloc(send_calls, sizeof(CallsStruct) * send_calls_allocated);
|
2009-02-21 22:06:42 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
switch (lookup_selector(s, send_obj, selector, &varp, &funcp)) {
|
2009-02-28 11:12:59 +00:00
|
|
|
case kSelectorNone:
|
2009-02-20 23:09:29 +00:00
|
|
|
sciprintf("Send to invalid selector 0x%x of object at "PREG"\n", 0xffff & selector, PRINT_REG(send_obj));
|
2009-02-15 06:10:59 +00:00
|
|
|
script_error_flag = script_debug_flag = 1;
|
|
|
|
--send_calls_nr;
|
|
|
|
break;
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
case kSelectorVariable:
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
#ifdef VM_DEBUG_SEND
|
|
|
|
sciprintf("Varselector: ");
|
|
|
|
if (argc)
|
|
|
|
sciprintf("Write "PREG"\n", PRINT_REG(argp[1]));
|
|
|
|
else
|
|
|
|
sciprintf("Read\n");
|
2009-02-20 23:09:29 +00:00
|
|
|
#endif // VM_DEBUG_SEND
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
switch (argc) {
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0: // Read selector
|
2009-02-15 06:10:59 +00:00
|
|
|
if (print_send_action) {
|
|
|
|
sciprintf("[read selector]\n");
|
|
|
|
print_send_action = 0;
|
|
|
|
}
|
2009-02-20 23:09:29 +00:00
|
|
|
// fallthrough
|
2009-02-15 06:10:59 +00:00
|
|
|
case 1:
|
|
|
|
#ifndef STRICT_SEND
|
|
|
|
default:
|
|
|
|
#endif
|
2009-02-20 23:09:29 +00:00
|
|
|
{ // Argument is supplied -> Selector should be set
|
2009-02-15 22:28:12 +00:00
|
|
|
if (print_send_action) {
|
|
|
|
reg_t oldReg = *varp;
|
|
|
|
reg_t newReg = argp[1];
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
sciprintf("[write to selector: change "PREG" to "PREG"]\n", PRINT_REG(oldReg), PRINT_REG(newReg));
|
2009-02-15 22:28:12 +00:00
|
|
|
print_send_action = 0;
|
|
|
|
}
|
2009-02-20 23:09:29 +00:00
|
|
|
send_calls[send_calls_nr].address.var = varp; // register the call
|
2009-02-15 22:28:12 +00:00
|
|
|
send_calls[send_calls_nr].argp = argp;
|
|
|
|
send_calls[send_calls_nr].argc = argc;
|
|
|
|
send_calls[send_calls_nr].selector = selector;
|
2009-02-20 23:09:29 +00:00
|
|
|
send_calls[send_calls_nr].type = EXEC_STACK_TYPE_VARSELECTOR; // Register as a varselector
|
|
|
|
}
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
#ifdef STRICT_SEND
|
|
|
|
default:
|
|
|
|
--send_calls_nr;
|
2009-02-20 23:09:29 +00:00
|
|
|
sciprintf("Send error: Variable selector %04x in "PREG" called with %04x params\n", selector, PRINT_REG(send_obj), argc);
|
|
|
|
script_debug_flag = 1; // Enter debug mode
|
2009-02-15 06:10:59 +00:00
|
|
|
_debug_seeking = _debug_step_running = 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
case kSelectorMethod:
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
#ifdef VM_DEBUG_SEND
|
|
|
|
sciprintf("Funcselector(");
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
sciprintf(PREG, PRINT_REG(argp[i+1]));
|
|
|
|
if (i + 1 < argc)
|
|
|
|
sciprintf(", ");
|
|
|
|
}
|
|
|
|
sciprintf(") at "PREG"\n", PRINT_REG(funcp));
|
2009-02-20 23:09:29 +00:00
|
|
|
#endif // VM_DEBUG_SEND
|
2009-02-15 06:10:59 +00:00
|
|
|
if (print_send_action) {
|
|
|
|
sciprintf("[invoke selector]\n");
|
|
|
|
print_send_action = 0;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
send_calls[send_calls_nr].address.func = funcp; // register call
|
2009-02-15 06:10:59 +00:00
|
|
|
send_calls[send_calls_nr].argp = argp;
|
|
|
|
send_calls[send_calls_nr].argc = argc;
|
|
|
|
send_calls[send_calls_nr].selector = selector;
|
|
|
|
send_calls[send_calls_nr].type = EXEC_STACK_TYPE_CALL;
|
|
|
|
send_calls[send_calls_nr].sp = sp;
|
2009-02-20 23:09:29 +00:00
|
|
|
sp = CALL_SP_CARRY; // Destroy sp, as it will be carried over
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
break;
|
2009-02-20 23:09:29 +00:00
|
|
|
} // switch(lookup_selector())
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
framesize -= (2 + argc);
|
|
|
|
argp += argc + 1;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Iterate over all registered calls in the reverse order. This way, the first call is
|
|
|
|
// placed on the TOS; as soon as it returns, it will cause the second call to be executed.
|
2009-02-15 06:10:59 +00:00
|
|
|
for (; send_calls_nr >= 0; send_calls_nr--)
|
2009-02-20 23:09:29 +00:00
|
|
|
if (send_calls[send_calls_nr].type == EXEC_STACK_TYPE_VARSELECTOR) // Write/read variable?
|
2009-02-22 13:11:43 +00:00
|
|
|
retval = add_exec_stack_varselector(s, work_obj, send_calls[send_calls_nr].argc, send_calls[send_calls_nr].argp,
|
2009-02-20 23:09:29 +00:00
|
|
|
send_calls[send_calls_nr].selector, send_calls[send_calls_nr].address.var, origin);
|
2009-02-15 06:10:59 +00:00
|
|
|
else
|
2009-02-20 23:09:29 +00:00
|
|
|
retval = add_exec_stack_entry(s, send_calls[send_calls_nr].address.func, send_calls[send_calls_nr].sp, work_obj,
|
|
|
|
send_calls[send_calls_nr].argc, send_calls[send_calls_nr].argp,
|
|
|
|
send_calls[send_calls_nr].selector, send_obj, origin, SCI_XS_CALLEE_LOCALS);
|
2009-02-15 06:10:59 +00:00
|
|
|
_exec_varselectors(s);
|
|
|
|
|
|
|
|
retval = s->execution_stack + s->execution_stack_pos;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
ExecStack *add_exec_stack_varselector(EngineState *s, reg_t objp, int argc, StackPtr argp, Selector selector, reg_t *address, int origin) {
|
|
|
|
ExecStack *xstack = add_exec_stack_entry(s, NULL_REG, address, objp, argc, argp, selector, objp, origin, SCI_XS_CALLEE_LOCALS);
|
2009-02-20 23:09:29 +00:00
|
|
|
// Store selector address in sp
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xstack->addr.varp = address;
|
|
|
|
xstack->type = EXEC_STACK_TYPE_VARSELECTOR;
|
|
|
|
|
|
|
|
return xstack;
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
ExecStack *add_exec_stack_entry(EngineState *s, reg_t pc, StackPtr sp, reg_t objp, int argc,
|
|
|
|
StackPtr argp, Selector selector, reg_t sendp, int origin, SegmentId locals_segment) {
|
2009-02-20 23:09:29 +00:00
|
|
|
// Returns new TOS element for the execution stack
|
|
|
|
// locals_segment may be -1 if derived from the called object
|
2009-02-28 11:12:59 +00:00
|
|
|
ExecStack *xstack = NULL;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (!s->execution_stack)
|
2009-02-28 11:12:59 +00:00
|
|
|
s->execution_stack = (ExecStack *)sci_malloc(sizeof(ExecStack) * (s->execution_stack_size = 16));
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
if (++(s->execution_stack_pos) == s->execution_stack_size) // Out of stack space?
|
2009-02-28 11:12:59 +00:00
|
|
|
s->execution_stack = (ExecStack*)sci_realloc(s->execution_stack, sizeof(ExecStack) * (s->execution_stack_size += 8));
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
//sciprintf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos, s->execution_stack_size, origin, s->execution_stack);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xstack = s->execution_stack + s->execution_stack_pos;
|
|
|
|
|
|
|
|
xstack->objp = objp;
|
|
|
|
if (locals_segment != SCI_XS_CALLEE_LOCALS)
|
|
|
|
xstack->local_segment = locals_segment;
|
|
|
|
else
|
|
|
|
xstack->local_segment = pc.segment;
|
|
|
|
|
|
|
|
xstack->sendp = sendp;
|
|
|
|
xstack->addr.pc = pc;
|
|
|
|
xstack->fp = xstack->sp = sp;
|
|
|
|
xstack->argc = argc;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
xstack->variables_argp = argp; // Parameters
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
*argp = make_reg(0, argc); // SCI code relies on the zeroeth argument to equal argc
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Additional debug information
|
2009-02-15 06:10:59 +00:00
|
|
|
xstack->selector = selector;
|
|
|
|
xstack->origin = origin;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
xstack->type = EXEC_STACK_TYPE_CALL; // Normal call
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
return xstack;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DISABLE_VALIDATONS
|
|
|
|
# define kernel_matches_signature(a, b, c, d) 1
|
|
|
|
#endif
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
void vm_handle_fatal_error(EngineState *s, int line, const char *file) {
|
2009-02-21 14:11:41 +00:00
|
|
|
fprintf(stderr, "Fatal VM error in %s, L%d; aborting...\n", file, line);
|
2009-02-21 18:17:08 +00:00
|
|
|
error("Could not recover, exitting...\n");
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static inline Script *script_locate_by_segment(EngineState *s, SegmentId seg) {
|
|
|
|
MemObject *memobj = GET_SEGMENT(*s->seg_manager, seg, MEM_OBJ_SCRIPT);
|
2009-02-15 06:10:59 +00:00
|
|
|
if (memobj)
|
|
|
|
return &(memobj->data.script);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
|
2009-02-28 11:12:59 +00:00
|
|
|
MemObject *mobj = GET_SEGMENT_ANY(*s->seg_manager, base.segment);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (!mobj) {
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
sciprintf("[VM] Error: Attempt to add %d to invalid pointer "PREG"!", offset, PRINT_REG(base));
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mobj->type) {
|
|
|
|
|
|
|
|
case MEM_OBJ_LOCALS:
|
2009-02-15 22:28:12 +00:00
|
|
|
base.offset += 2 * offset;
|
2009-02-15 06:10:59 +00:00
|
|
|
return base;
|
|
|
|
|
|
|
|
case MEM_OBJ_SCRIPT:
|
|
|
|
case MEM_OBJ_STACK:
|
|
|
|
case MEM_OBJ_DYNMEM:
|
|
|
|
base.offset += offset;
|
|
|
|
return base;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
sciprintf("[VM] Error: Attempt to add %d to pointer "PREG": Pointer arithmetics of this type unsupported!", offset, PRINT_REG(base));
|
|
|
|
return NULL_REG;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
static inline void gc_countdown(EngineState *s) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->gc_countdown-- <= 0) {
|
|
|
|
s->gc_countdown = script_gc_interval;
|
|
|
|
run_gc(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static byte _fake_return_buffer[2] = {op_ret << 1, op_ret << 1};
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
void run_vm(EngineState *s, int restoring) {
|
2009-02-20 23:09:29 +00:00
|
|
|
reg_t *variables[4]; // global, local, temp, param, as immediate pointers
|
|
|
|
reg_t *variables_base[4]; // Used for referencing VM ops
|
2009-02-28 11:12:59 +00:00
|
|
|
SegmentId variables_seg[4]; // Same as above, contains segment IDs
|
2009-02-15 06:10:59 +00:00
|
|
|
#ifndef DISABLE_VALIDATIONS
|
2009-02-20 23:09:29 +00:00
|
|
|
int variables_max[4]; // Max. values for all variables
|
|
|
|
unsigned int code_buf_size = 0 ; // (Avoid spurious warning)
|
2009-02-15 06:10:59 +00:00
|
|
|
#endif
|
|
|
|
int temp;
|
2009-02-21 21:16:41 +00:00
|
|
|
int16 aux_acc; // Auxiliary 16 bit accumulator
|
2009-02-20 23:09:29 +00:00
|
|
|
reg_t r_temp; // Temporary register
|
2009-02-28 11:12:59 +00:00
|
|
|
StackPtr s_temp; // Temporary stack pointer
|
2009-02-21 21:16:41 +00:00
|
|
|
int16 opparams[4]; // opcode parameters
|
2009-02-20 23:09:29 +00:00
|
|
|
|
|
|
|
int restadjust = s->r_amp_rest;
|
|
|
|
// &rest adjusts the parameter count by this value
|
|
|
|
// Current execution data:
|
2009-02-28 11:12:59 +00:00
|
|
|
ExecStack *xs = s->execution_stack + s->execution_stack_pos;
|
|
|
|
ExecStack *xs_new = NULL;
|
|
|
|
Object *obj = obj_get(s, xs->objp);
|
|
|
|
Script *local_script = script_locate_by_segment(s, xs->local_segment);
|
2009-02-20 23:09:29 +00:00
|
|
|
int old_execution_stack_base = s->execution_stack_base;
|
2009-02-22 13:11:43 +00:00
|
|
|
// Used to detect the stack bottom, for "physical" returns
|
2009-02-20 23:09:29 +00:00
|
|
|
byte *code_buf = NULL; // (Avoid spurious warning)
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (!local_script) {
|
|
|
|
script_error(s, __FILE__, __LINE__, "Program Counter gone astray");
|
|
|
|
return;
|
|
|
|
}
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
if (NULL == s) {
|
|
|
|
sciprintf("vm.c: run_vm(): NULL passed for \"s\"\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!restoring)
|
|
|
|
s->execution_stack_base = s->execution_stack_pos;
|
|
|
|
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
2009-02-20 23:09:29 +00:00
|
|
|
// Initialize maximum variable count
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->script_000->locals_block)
|
|
|
|
variables_max[VAR_GLOBAL] = s->script_000->locals_block->nr;
|
|
|
|
else
|
|
|
|
variables_max[VAR_GLOBAL] = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
variables_seg[VAR_GLOBAL] = s->script_000->locals_segment;
|
|
|
|
variables_seg[VAR_TEMP] = variables_seg[VAR_PARAM] = s->stack_segment;
|
|
|
|
variables_base[VAR_TEMP] = variables_base[VAR_PARAM] = s->stack_base;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// SCI code reads the zeroeth argument to determine argc
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->script_000->locals_block)
|
2009-02-20 23:09:29 +00:00
|
|
|
variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL] = s->script_000->locals_block->locals;
|
2009-02-15 06:10:59 +00:00
|
|
|
else
|
|
|
|
variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL] = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
s->execution_stack_pos_changed = 1; // Force initialization
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
byte opcode;
|
|
|
|
int old_pc_offset;
|
2009-02-28 11:12:59 +00:00
|
|
|
StackPtr old_sp = xs->sp;
|
2009-02-15 06:10:59 +00:00
|
|
|
byte opnumber;
|
2009-02-20 23:09:29 +00:00
|
|
|
int var_type; // See description below
|
2009-02-15 06:10:59 +00:00
|
|
|
int var_number;
|
|
|
|
|
|
|
|
old_pc_offset = xs->addr.pc.offset;
|
|
|
|
|
|
|
|
if (s->execution_stack_pos_changed) {
|
2009-02-28 11:12:59 +00:00
|
|
|
Script *scr;
|
2009-02-15 06:10:59 +00:00
|
|
|
xs = s->execution_stack + s->execution_stack_pos;
|
|
|
|
s->execution_stack_pos_changed = 0;
|
|
|
|
|
|
|
|
scr = script_locate_by_segment(s, xs->addr.pc.segment);
|
|
|
|
if (!scr) {
|
2009-02-20 23:09:29 +00:00
|
|
|
// No script? Implicit return via fake instruction buffer
|
2009-02-28 13:41:43 +00:00
|
|
|
warning("Running on non-existant script in segment %x!", xs->addr.pc.segment);
|
2009-02-15 06:10:59 +00:00
|
|
|
code_buf = _fake_return_buffer;
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
code_buf_size = 2;
|
|
|
|
#endif
|
|
|
|
xs->addr.pc.offset = 1;
|
|
|
|
|
|
|
|
scr = NULL;
|
|
|
|
obj = NULL;
|
|
|
|
} else {
|
|
|
|
obj = obj_get(s, xs->objp);
|
|
|
|
code_buf = scr->buf;
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
code_buf_size = scr->buf_size;
|
|
|
|
#endif
|
2009-02-20 23:09:29 +00:00
|
|
|
/*if (!obj) {
|
2009-02-15 06:10:59 +00:00
|
|
|
SCIkdebug(SCIkWARNING, "Running with non-existant self= "PREG"\n", PRINT_REG(xs->objp));
|
2009-02-20 23:09:29 +00:00
|
|
|
}*/
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
local_script = script_locate_by_segment(s, xs->local_segment);
|
|
|
|
if (!local_script) {
|
2009-02-20 20:11:12 +00:00
|
|
|
warning("Could not find local script from segment %x", xs->local_segment);
|
2009-02-15 06:10:59 +00:00
|
|
|
local_script = NULL;
|
|
|
|
variables_base[VAR_LOCAL] = variables[VAR_LOCAL] = NULL;
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
variables_max[VAR_LOCAL] = 0;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
|
|
|
|
variables_seg[VAR_LOCAL] = local_script->locals_segment;
|
|
|
|
if (local_script->locals_block)
|
2009-02-20 23:09:29 +00:00
|
|
|
variables_base[VAR_LOCAL] = variables[VAR_LOCAL] = local_script->locals_block->locals;
|
2009-02-15 06:10:59 +00:00
|
|
|
else
|
2009-02-20 23:09:29 +00:00
|
|
|
variables_base[VAR_LOCAL] = variables[VAR_LOCAL] = NULL;
|
2009-02-15 06:10:59 +00:00
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (local_script->locals_block)
|
|
|
|
variables_max[VAR_LOCAL] = local_script->locals_block->nr;
|
|
|
|
else
|
|
|
|
variables_max[VAR_LOCAL] = 0;
|
|
|
|
variables_max[VAR_TEMP] = xs->sp - xs->fp;
|
|
|
|
variables_max[VAR_PARAM] = xs->argc + 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
variables[VAR_TEMP] = xs->fp;
|
|
|
|
variables[VAR_PARAM] = xs->variables_argp;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
script_error_flag = 0; // Set error condition to false
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (script_abort_flag)
|
2009-02-20 23:09:29 +00:00
|
|
|
return; // Emergency
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Debug if this has been requested:
|
2009-02-15 06:10:59 +00:00
|
|
|
if (script_debug_flag || sci_debug_flags) {
|
2009-02-20 23:09:29 +00:00
|
|
|
script_debug(s, &(xs->addr.pc), &(xs->sp), &(xs->fp), &(xs->objp), &restadjust, variables_seg, variables, variables_base,
|
2009-02-15 06:10:59 +00:00
|
|
|
#ifdef DISABLE_VALIDATIONS
|
2009-02-15 22:28:12 +00:00
|
|
|
NULL,
|
2009-02-15 06:10:59 +00:00
|
|
|
#else
|
2009-02-15 22:28:12 +00:00
|
|
|
variables_max,
|
2009-02-15 06:10:59 +00:00
|
|
|
#endif
|
2009-02-15 22:28:12 +00:00
|
|
|
bp_flag);
|
2009-02-15 06:10:59 +00:00
|
|
|
bp_flag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (xs->sp < xs->fp)
|
|
|
|
script_error(s, "[VM] "__FILE__, __LINE__, "Stack underflow");
|
|
|
|
|
|
|
|
variables_max[VAR_TEMP] = xs->sp - xs->fp;
|
|
|
|
|
|
|
|
if (xs->addr.pc.offset >= code_buf_size)
|
|
|
|
script_error(s, "[VM] "__FILE__, __LINE__, "Program Counter gone astray");
|
|
|
|
#endif
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
opcode = GET_OP_BYTE(); // Get opcode
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
opnumber = opcode >> 1;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
for (temp = 0; formats[opnumber][temp]; temp++)
|
2009-02-15 22:28:12 +00:00
|
|
|
switch (formats[opnumber][temp]) {
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
case Script_Byte:
|
|
|
|
opparams[temp] = GET_OP_BYTE();
|
|
|
|
break;
|
|
|
|
case Script_SByte:
|
|
|
|
opparams[temp] = GET_OP_SIGNED_BYTE();
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
case Script_Word:
|
|
|
|
opparams[temp] = GET_OP_WORD();
|
|
|
|
break;
|
|
|
|
case Script_SWord:
|
|
|
|
opparams[temp] = GET_OP_SIGNED_WORD();
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
case Script_Variable:
|
|
|
|
case Script_Property:
|
|
|
|
|
|
|
|
case Script_Local:
|
|
|
|
case Script_Temp:
|
|
|
|
case Script_Global:
|
|
|
|
case Script_Param:
|
2009-02-15 22:28:12 +00:00
|
|
|
opparams[temp] = GET_OP_FLEX();
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
case Script_SVariable:
|
|
|
|
case Script_SRelative:
|
2009-02-15 22:28:12 +00:00
|
|
|
opparams[temp] = GET_OP_SIGNED_FLEX();
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
case Script_Offset:
|
2009-02-15 22:28:12 +00:00
|
|
|
opparams[temp] = GET_OP_FLEX();
|
|
|
|
break;
|
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
case Script_None:
|
|
|
|
case Script_End:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Script_Invalid:
|
|
|
|
default:
|
|
|
|
sciprintf("opcode %02x: Invalid!", opcode);
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
}
|
|
|
|
|
2009-02-20 15:44:22 +00:00
|
|
|
// TODO: Replace the following by an opcode table, and several methods for
|
|
|
|
// each opcode.
|
2009-02-15 06:10:59 +00:00
|
|
|
switch (opnumber) {
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x00: // bnot
|
2009-02-15 22:28:12 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(0xffff ^ /*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x01: // add
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp = POP32();
|
|
|
|
if (r_temp.segment || s->r_acc.segment) {
|
|
|
|
reg_t r_ptr;
|
|
|
|
int offset;
|
2009-02-20 23:09:29 +00:00
|
|
|
// Pointer arithmetics!
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->r_acc.segment) {
|
|
|
|
if (r_temp.segment) {
|
2009-02-21 14:11:41 +00:00
|
|
|
sciprintf("Error: Attempt to add two pointers, stack="PREG" and acc="PREG"!\n",
|
|
|
|
PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
2009-02-15 06:10:59 +00:00
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
offset = 0;
|
|
|
|
} else {
|
2009-02-15 22:28:12 +00:00
|
|
|
r_ptr = s->r_acc;
|
2009-02-15 06:10:59 +00:00
|
|
|
offset = r_temp.offset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r_ptr = r_temp;
|
|
|
|
offset = s->r_acc.offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->r_acc = pointer_add(s, r_ptr, offset);
|
|
|
|
|
|
|
|
} else
|
|
|
|
s->r_acc = make_reg(0, r_temp.offset + s->r_acc.offset);
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x02: // sub
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp = POP32();
|
|
|
|
if (r_temp.segment || s->r_acc.segment) {
|
|
|
|
reg_t r_ptr;
|
|
|
|
int offset;
|
2009-02-20 23:09:29 +00:00
|
|
|
// Pointer arithmetics!
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->r_acc.segment) {
|
|
|
|
if (r_temp.segment) {
|
2009-02-21 14:11:41 +00:00
|
|
|
sciprintf("Error: Attempt to subtract two pointers, stack="PREG" and acc="PREG"!\n",
|
|
|
|
PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
2009-02-15 06:10:59 +00:00
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
offset = 0;
|
|
|
|
} else {
|
2009-02-15 22:28:12 +00:00
|
|
|
r_ptr = s->r_acc;
|
2009-02-15 06:10:59 +00:00
|
|
|
offset = r_temp.offset;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r_ptr = r_temp;
|
|
|
|
offset = s->r_acc.offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->r_acc = pointer_add(s, r_ptr, -offset);
|
|
|
|
|
|
|
|
} else
|
|
|
|
s->r_acc = make_reg(0, r_temp.offset - s->r_acc.offset);
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x03: // mul
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(((int16)POP()) * (int16)/*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x04: // div
|
2009-02-15 06:10:59 +00:00
|
|
|
ACC_AUX_LOAD();
|
2009-02-21 21:16:41 +00:00
|
|
|
aux_acc = aux_acc != 0 ? ((int16)POP()) / aux_acc : 0;
|
2009-02-15 06:10:59 +00:00
|
|
|
ACC_AUX_STORE();
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x05: // mod
|
2009-02-15 06:10:59 +00:00
|
|
|
ACC_AUX_LOAD();
|
2009-02-21 21:16:41 +00:00
|
|
|
aux_acc = aux_acc != 0 ? ((int16)POP()) % aux_acc : 0;
|
2009-02-15 06:10:59 +00:00
|
|
|
ACC_AUX_STORE();
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x06: // shr
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(((uint16) POP()) >> /*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x07: // shl
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) << /*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x08: // xor
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/);
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x09: // and
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/);
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x0a: // or
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/);
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x0b: // neg
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L(-/*acc*/);
|
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x0c: // not
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
|
2009-02-22 13:11:43 +00:00
|
|
|
// Must allow pointers to be negated, as this is used for checking whether objects exist
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x0d: // eq?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
|
|
|
r_temp = POP32();
|
|
|
|
s->r_acc = make_reg(0, REG_EQ(r_temp, s->r_acc));
|
2009-02-20 23:09:29 +00:00
|
|
|
// Explicitly allow pointers to be compared
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x0e: // ne?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
|
|
|
r_temp = POP32();
|
|
|
|
s->r_acc = make_reg(0, !REG_EQ(r_temp, s->r_acc));
|
2009-02-20 23:09:29 +00:00
|
|
|
// Explicitly allow pointers to be compared
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x0f: // gt?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L((int16)POP() > (int16)/*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
case 0x10: // ge?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L((int16)POP() >= (int16)/*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x11: // lt?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L((int16)POP() < (int16)/*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x12: // le?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
2009-02-21 21:16:41 +00:00
|
|
|
s->r_acc = ACC_ARITHMETIC_L((int16)POP() <= (int16)/*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x13: // ugt?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
|
|
|
r_temp = POP32();
|
|
|
|
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x14: // uge?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
|
|
|
r_temp = POP32();
|
|
|
|
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x15: // ult?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
|
|
|
r_temp = POP32();
|
|
|
|
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x16: // ule?
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_prev = s->r_acc;
|
|
|
|
r_temp = POP32();
|
|
|
|
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x17: // bt
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->r_acc.offset || s->r_acc.segment)
|
|
|
|
xs->addr.pc.offset += opparams[0];
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x18: // bnt
|
2009-02-15 06:10:59 +00:00
|
|
|
if (!(s->r_acc.offset || s->r_acc.segment))
|
|
|
|
xs->addr.pc.offset += opparams[0];
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x19: // jmp
|
2009-02-15 06:10:59 +00:00
|
|
|
xs->addr.pc.offset += opparams[0];
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x1a: // ldi
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = make_reg(0, opparams[0]);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x1b: // push
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x1c: // pushi
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH(opparams[0]);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x1d: // toss
|
2009-02-15 06:10:59 +00:00
|
|
|
xs->sp--;
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x1e: // dup
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp = xs->sp[-1];
|
|
|
|
PUSH32(r_temp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x1f: { // link
|
2009-02-15 06:10:59 +00:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < opparams[0]; i++)
|
|
|
|
xs->sp[i] = NULL_REG;
|
|
|
|
xs->sp += opparams[0];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x20: { // call
|
|
|
|
int argc = (opparams[1] >> 1) // Given as offset, but we need count
|
2009-02-15 22:28:12 +00:00
|
|
|
+ 1 + restadjust;
|
2009-02-28 11:12:59 +00:00
|
|
|
StackPtr call_base = xs->sp - argc;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xs->sp[1].offset += restadjust;
|
2009-02-20 23:09:29 +00:00
|
|
|
xs_new = add_exec_stack_entry(s, make_reg(xs->addr.pc.segment, xs->addr.pc.offset + opparams[0]),
|
|
|
|
xs->sp, xs->objp, (validate_arithmetic(*call_base)) + restadjust,
|
|
|
|
call_base, NULL_SELECTOR, xs->objp, s->execution_stack_pos, xs->local_segment);
|
2009-02-22 13:11:43 +00:00
|
|
|
restadjust = 0; // Used up the &rest adjustment
|
2009-02-15 06:10:59 +00:00
|
|
|
xs->sp = call_base;
|
|
|
|
|
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x21: // callk
|
2009-02-15 06:10:59 +00:00
|
|
|
gc_countdown(s);
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
xs->sp -= (opparams[1] >> 1) + 1;
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
|
|
|
|
xs->sp -= restadjust;
|
2009-02-22 13:11:43 +00:00
|
|
|
s->r_amp_rest = 0; // We just used up the restadjust, remember?
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (opparams[0] >= s->kfunct_nr) {
|
|
|
|
sciprintf("Invalid kernel function 0x%x requested\n", opparams[0]);
|
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
} else {
|
|
|
|
int argc = ASSERT_ARITHMETIC(xs->sp[0]);
|
|
|
|
|
|
|
|
if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
|
|
|
|
argc += restadjust;
|
|
|
|
|
|
|
|
if (s->kfunct_table[opparams[0]].signature
|
2009-02-20 23:09:29 +00:00
|
|
|
&& !kernel_matches_signature(s, s->kfunct_table[opparams[0]].signature, argc, xs->sp + 1)) {
|
|
|
|
sciprintf("[VM] Invalid arguments to kernel call %x\n", opparams[0]);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
} else {
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = s->kfunct_table[opparams[0]].fun(s, opparams[0], argc, xs->sp + 1);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-02-22 13:11:43 +00:00
|
|
|
// Call kernel function
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Calculate xs again: The kernel function might
|
2009-02-22 13:11:43 +00:00
|
|
|
// have spawned a new VM
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xs_new = s->execution_stack + s->execution_stack_pos;
|
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
|
2009-02-15 06:10:59 +00:00
|
|
|
restadjust = s->r_amp_rest;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x22: // callb
|
2009-02-15 06:10:59 +00:00
|
|
|
temp = ((opparams[1] >> 1) + restadjust + 1);
|
|
|
|
s_temp = xs->sp;
|
|
|
|
xs->sp -= temp;
|
|
|
|
|
|
|
|
xs->sp[0].offset += restadjust;
|
2009-02-20 23:09:29 +00:00
|
|
|
xs_new = execute_method(s, 0, opparams[0], s_temp, xs->objp, xs->sp[0].offset, xs->sp);
|
2009-02-22 13:11:43 +00:00
|
|
|
restadjust = 0; // Used up the &rest adjustment
|
|
|
|
if (xs_new) // in case of error, keep old stack
|
2009-02-15 06:10:59 +00:00
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
break;
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x23: // calle
|
2009-02-15 06:10:59 +00:00
|
|
|
temp = ((opparams[2] >> 1) + restadjust + 1);
|
|
|
|
s_temp = xs->sp;
|
|
|
|
xs->sp -= temp;
|
|
|
|
|
|
|
|
xs->sp[0].offset += restadjust;
|
2009-02-20 23:09:29 +00:00
|
|
|
xs_new = execute_method(s, opparams[0], opparams[1], s_temp, xs->objp, xs->sp[0].offset, xs->sp);
|
2009-02-22 13:11:43 +00:00
|
|
|
restadjust = 0; // Used up the &rest adjustment
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
if (xs_new) // in case of error, keep old stack
|
2009-02-15 06:10:59 +00:00
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x24: // ret
|
2009-02-15 06:10:59 +00:00
|
|
|
do {
|
2009-02-28 11:12:59 +00:00
|
|
|
StackPtr old_sp2 = xs->sp;
|
|
|
|
StackPtr old_fp = xs->fp;
|
|
|
|
ExecStack *old_xs = s->execution_stack + s->execution_stack_pos;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
if (s->execution_stack_pos == s->execution_stack_base) { // Have we reached the base?
|
|
|
|
s->execution_stack_base = old_execution_stack_base; // Restore stack base
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
--(s->execution_stack_pos);
|
|
|
|
|
|
|
|
s->execution_stack_pos_changed = 1;
|
2009-02-22 13:11:43 +00:00
|
|
|
s->r_amp_rest = restadjust; // Update &rest
|
|
|
|
return; // "Hard" return
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (old_xs->type == EXEC_STACK_TYPE_VARSELECTOR) {
|
2009-02-22 13:11:43 +00:00
|
|
|
// varselector access?
|
|
|
|
if (old_xs->argc) // write?
|
2009-02-15 06:10:59 +00:00
|
|
|
*(old_xs->addr.varp) = old_xs->variables_argp[1];
|
2009-02-22 13:11:43 +00:00
|
|
|
else // No, read
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = *(old_xs->addr.varp);
|
|
|
|
}
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Not reached the base, so let's do a soft return
|
2009-02-15 06:10:59 +00:00
|
|
|
--(s->execution_stack_pos);
|
|
|
|
xs = old_xs - 1;
|
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
xs = s->execution_stack + s->execution_stack_pos;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
if (xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer
|
2009-02-15 22:28:12 +00:00
|
|
|
|| xs->type != EXEC_STACK_TYPE_CALL) {
|
2009-02-21 04:10:21 +00:00
|
|
|
xs->sp = old_sp2;
|
2009-02-15 06:10:59 +00:00
|
|
|
xs->fp = old_fp;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (xs->type == EXEC_STACK_TYPE_VARSELECTOR);
|
2009-02-22 13:11:43 +00:00
|
|
|
// Iterate over all varselector accesses
|
2009-02-15 06:10:59 +00:00
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
xs_new = xs;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x25: // send
|
2009-02-15 06:10:59 +00:00
|
|
|
s_temp = xs->sp;
|
2009-02-22 13:11:43 +00:00
|
|
|
xs->sp -= ((opparams[0] >> 1) + restadjust); // Adjust stack
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xs->sp[1].offset += restadjust;
|
2009-02-21 21:16:41 +00:00
|
|
|
xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp, (int)(opparams[0] >> 1) + (uint16)restadjust, xs->sp);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (xs_new && xs_new != xs)
|
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
|
|
|
|
restadjust = 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x28: // class
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = get_class_address(s, (unsigned)opparams[0], SCRIPT_GET_LOCK, xs->addr.pc);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x2a: // self
|
2009-02-15 06:10:59 +00:00
|
|
|
s_temp = xs->sp;
|
2009-02-22 13:11:43 +00:00
|
|
|
xs->sp -= ((opparams[0] >> 1) + restadjust); // Adjust stack
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xs->sp[1].offset += restadjust;
|
2009-02-21 21:16:41 +00:00
|
|
|
xs_new = send_selector(s, xs->objp, xs->objp, s_temp, (int)(opparams[0] >> 1) + (uint16)restadjust, xs->sp);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (xs_new && xs_new != xs)
|
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
|
|
|
|
restadjust = 0;
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x2b: // super
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp = get_class_address(s, opparams[0], SCRIPT_GET_LOAD, xs->addr.pc);
|
|
|
|
|
|
|
|
if (!r_temp.segment)
|
|
|
|
CORE_ERROR("VM", "Invalid superclass in object");
|
|
|
|
else {
|
|
|
|
s_temp = xs->sp;
|
2009-02-22 13:11:43 +00:00
|
|
|
xs->sp -= ((opparams[1] >> 1) + restadjust); // Adjust stack
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
xs->sp[1].offset += restadjust;
|
2009-02-21 21:16:41 +00:00
|
|
|
xs_new = send_selector(s, r_temp, xs->objp, s_temp, (int)(opparams[1] >> 1) + (uint16)restadjust, xs->sp);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (xs_new && xs_new != xs)
|
|
|
|
s->execution_stack_pos_changed = 1;
|
|
|
|
|
|
|
|
restadjust = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x2c: // &rest
|
|
|
|
temp = (uint16) opparams[0]; // First argument
|
|
|
|
restadjust = xs->argc - temp + 1; // +1 because temp counts the paramcount while argc doesn't
|
2009-02-15 06:10:59 +00:00
|
|
|
if (restadjust < 0)
|
|
|
|
restadjust = 0;
|
|
|
|
|
|
|
|
for (; temp <= xs->argc; temp++)
|
|
|
|
PUSH32(xs->variables_argp[temp]);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x2d: // lea
|
2009-02-21 21:16:41 +00:00
|
|
|
temp = (uint16) opparams[0] >> 1;
|
2009-02-22 13:11:43 +00:00
|
|
|
var_number = temp & 0x03; // Get variable type
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Get variable block offset
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp.segment = variables_seg[var_number];
|
|
|
|
r_temp.offset = variables[var_number] - variables_base[var_number];
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
if (temp & 0x08) // Add accumulator offset if requested
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp.offset += signed_validate_arithmetic(s->r_acc);
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
r_temp.offset += opparams[1]; // Add index
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp.offset *= sizeof(reg_t);
|
2009-02-22 13:11:43 +00:00
|
|
|
// That's the immediate address now
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = r_temp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x2e: // selfID
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = xs->objp;
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x30: // pprev
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(s->r_prev);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x31: // pToa
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x32: // aTop
|
2009-02-15 06:10:59 +00:00
|
|
|
OBJ_PROPERTY(obj, (opparams[0] >> 1)) = s->r_acc;
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x33: // pTos
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(OBJ_PROPERTY(obj, opparams[0] >> 1));
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x34: // sTop
|
2009-02-15 06:10:59 +00:00
|
|
|
OBJ_PROPERTY(obj, (opparams[0] >> 1)) = POP32();
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x35: // ipToa
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(1 + /*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x36: // dpToa
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(-1 + /*acc*/);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x37: // ipTos
|
2009-02-15 06:10:59 +00:00
|
|
|
ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
|
|
|
|
temp = ++OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
|
|
|
|
PUSH(temp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x38: // dpTos
|
2009-02-15 06:10:59 +00:00
|
|
|
ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
|
|
|
|
temp = --OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
|
|
|
|
PUSH(temp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x39: // lofsa
|
2009-02-15 06:10:59 +00:00
|
|
|
s->r_acc.segment = xs->addr.pc.segment;
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version >= SCI_VERSION(1, 001, 000))
|
|
|
|
s->r_acc.offset = opparams[0] + local_script->script_size;
|
2009-02-15 06:10:59 +00:00
|
|
|
else
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE)
|
|
|
|
s->r_acc.offset = opparams[0];
|
|
|
|
else
|
|
|
|
s->r_acc.offset = xs->addr.pc.offset + opparams[0];
|
2009-02-15 06:10:59 +00:00
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (s->r_acc.offset >= code_buf_size) {
|
|
|
|
sciprintf("VM: lofsa operation overflowed: "PREG" beyond end"
|
2009-02-20 23:09:29 +00:00
|
|
|
" of script (at %04x)\n", PRINT_REG(s->r_acc), code_buf_size);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_error_flag = script_debug_flag = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x3a: // lofss
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp.segment = xs->addr.pc.segment;
|
|
|
|
|
|
|
|
if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE)
|
2009-02-15 22:28:12 +00:00
|
|
|
r_temp.offset = opparams[0];
|
|
|
|
else
|
2009-02-15 06:10:59 +00:00
|
|
|
r_temp.offset = xs->addr.pc.offset + opparams[0];
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (r_temp.offset >= code_buf_size) {
|
|
|
|
sciprintf("VM: lofss operation overflowed: "PREG" beyond end"
|
2009-02-20 23:09:29 +00:00
|
|
|
" of script (at %04x)\n", PRINT_REG(r_temp), code_buf_size);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_error_flag = script_debug_flag = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
PUSH32(r_temp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x3b: // push0
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH(0);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x3c: // push1
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH(1);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x3d: // push2
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH(2);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x3e: // pushSelf
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(xs->objp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x40: // lag
|
|
|
|
case 0x41: // lal
|
|
|
|
case 0x42: // lat
|
|
|
|
case 0x43: // lap
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
|
|
|
s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x44: // lsg
|
|
|
|
case 0x45: // lsl
|
|
|
|
case 0x46: // lst
|
|
|
|
case 0x47: // lsp
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
|
|
|
PUSH32(READ_VAR(var_type, var_number, s->r_acc));
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x48: // lagi
|
|
|
|
case 0x49: // lali
|
|
|
|
case 0x4a: // lati
|
|
|
|
case 0x4b: // lapi
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
|
|
|
s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x4c: // lsgi
|
|
|
|
case 0x4d: // lsli
|
|
|
|
case 0x4e: // lsti
|
|
|
|
case 0x4f: // lspi
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
|
|
|
PUSH32(READ_VAR(var_type, var_number, s->r_acc));
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x50: // sag
|
|
|
|
case 0x51: // sal
|
|
|
|
case 0x52: // sat
|
|
|
|
case 0x53: // sap
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
|
|
|
WRITE_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x54: // ssg
|
|
|
|
case 0x55: // ssl
|
|
|
|
case 0x56: // sst
|
|
|
|
case 0x57: // ssp
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
|
|
|
WRITE_VAR(var_type, var_number, POP32());
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x58: // sagi
|
|
|
|
case 0x59: // sali
|
|
|
|
case 0x5a: // sati
|
|
|
|
case 0x5b: // sapi
|
2009-02-20 23:09:29 +00:00
|
|
|
// Special semantics because it wouldn't really make a whole lot
|
|
|
|
// of sense otherwise, with acc being used for two things
|
2009-02-22 13:11:43 +00:00
|
|
|
// simultaneously...
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
|
|
|
WRITE_VAR(var_type, var_number, s->r_acc = POP32());
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x5c: // ssgi
|
|
|
|
case 0x5d: // ssli
|
|
|
|
case 0x5e: // ssti
|
|
|
|
case 0x5f: // sspi
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
|
|
|
WRITE_VAR(var_type, var_number, POP32());
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x60: // +ag
|
|
|
|
case 0x61: // +al
|
|
|
|
case 0x62: // +at
|
|
|
|
case 0x63: // +ap
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = make_reg(0, 1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
WRITE_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x64: // +sg
|
|
|
|
case 0x65: // +sl
|
|
|
|
case 0x66: // +st
|
|
|
|
case 0x67: // +sp
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
2009-02-20 23:09:29 +00:00
|
|
|
r_temp = make_reg(0, 1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(r_temp);
|
|
|
|
WRITE_VAR(var_type, var_number, r_temp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x68: // +agi
|
|
|
|
case 0x69: // +ali
|
|
|
|
case 0x6a: // +ati
|
|
|
|
case 0x6b: // +api
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = make_reg(0, 1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
WRITE_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x6c: // +sgi
|
|
|
|
case 0x6d: // +sli
|
|
|
|
case 0x6e: // +sti
|
|
|
|
case 0x6f: // +spi
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
2009-02-20 23:09:29 +00:00
|
|
|
r_temp = make_reg(0, 1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(r_temp);
|
|
|
|
WRITE_VAR(var_type, var_number, r_temp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x70: // -ag
|
|
|
|
case 0x71: // -al
|
|
|
|
case 0x72: // -at
|
|
|
|
case 0x73: // -ap
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = make_reg(0, -1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
WRITE_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x74: // -sg
|
|
|
|
case 0x75: // -sl
|
|
|
|
case 0x76: // -st
|
|
|
|
case 0x77: // -sp
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0];
|
2009-02-20 23:09:29 +00:00
|
|
|
r_temp = make_reg(0, -1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(r_temp);
|
|
|
|
WRITE_VAR(var_type, var_number, r_temp);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x78: // -agi
|
|
|
|
case 0x79: // -ali
|
|
|
|
case 0x7a: // -ati
|
|
|
|
case 0x7b: // -api
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
2009-02-20 23:09:29 +00:00
|
|
|
s->r_acc = make_reg(0, -1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
WRITE_VAR(var_type, var_number, s->r_acc);
|
|
|
|
break;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
case 0x7c: // -sgi
|
|
|
|
case 0x7d: // -sli
|
|
|
|
case 0x7e: // -sti
|
|
|
|
case 0x7f: // -spi
|
|
|
|
var_type = (opcode >> 1) & 0x3; // Gets the variable type: g, l, t or p
|
2009-02-15 06:10:59 +00:00
|
|
|
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
|
2009-02-20 23:09:29 +00:00
|
|
|
r_temp = make_reg(0, -1 + validate_arithmetic(READ_VAR(var_type, var_number, s->r_acc)));
|
2009-02-15 06:10:59 +00:00
|
|
|
PUSH32(r_temp);
|
|
|
|
WRITE_VAR(var_type, var_number, r_temp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
script_error(s, __FILE__, __LINE__, "Illegal opcode");
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
} // switch(opcode >> 1)
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
if (s->execution_stack_pos_changed) // Force initialization
|
2009-02-15 06:10:59 +00:00
|
|
|
xs = xs_new;
|
|
|
|
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (xs != s->execution_stack + s->execution_stack_pos) {
|
2009-02-24 15:14:15 +00:00
|
|
|
sciprintf("Error: xs is stale (%d vs %d); last command was %02x\n", (int)(xs - s->execution_stack), s->execution_stack_pos, opnumber);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (script_error_flag) {
|
2009-02-22 13:11:43 +00:00
|
|
|
_debug_step_running = 0; // Stop multiple execution
|
|
|
|
_debug_seeking = 0; // Stop special seeks
|
2009-02-15 06:10:59 +00:00
|
|
|
xs->addr.pc.offset = old_pc_offset;
|
|
|
|
xs->sp = old_sp;
|
|
|
|
} else
|
|
|
|
++script_step_counter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static inline int _obj_locate_varselector(EngineState *s, Object *obj, Selector slc) {
|
2009-02-22 13:11:43 +00:00
|
|
|
// Determines if obj explicitly defines slc as a varselector
|
|
|
|
// Returns -1 if not found
|
2009-02-15 22:28:12 +00:00
|
|
|
|
|
|
|
if (s->version < SCI_VERSION(1, 001, 000)) {
|
2009-02-15 06:10:59 +00:00
|
|
|
int varnum = obj->variable_names_nr;
|
|
|
|
int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET;
|
|
|
|
int i;
|
|
|
|
byte *buf = obj->base_obj + selector_name_offset;
|
|
|
|
|
2009-02-21 21:16:41 +00:00
|
|
|
obj->base_vars = (uint16 *) buf;
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
for (i = 0; i < varnum; i++)
|
2009-02-22 13:11:43 +00:00
|
|
|
if (getUInt16(buf + (i << 1)) == slc) // Found it?
|
|
|
|
return i; // report success
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
return -1; // Failed
|
2009-02-15 22:28:12 +00:00
|
|
|
} else {
|
2009-02-15 06:10:59 +00:00
|
|
|
byte *buf = (byte *) obj->base_vars;
|
|
|
|
int i;
|
|
|
|
int varnum = obj->variables[1].offset;
|
|
|
|
|
|
|
|
if (!(obj->variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS))
|
|
|
|
buf = ((byte *) obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR])->base_vars);
|
|
|
|
|
|
|
|
for (i = 0; i < varnum; i++)
|
2009-02-22 13:11:43 +00:00
|
|
|
if (getUInt16(buf + (i << 1)) == slc) // Found it?
|
|
|
|
return i; // report success
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
return -1; // Failed
|
2009-02-15 22:28:12 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static inline int _class_locate_funcselector(EngineState *s, Object *obj, Selector slc) {
|
2009-02-22 13:11:43 +00:00
|
|
|
// Determines if obj is a class and explicitly defines slc as a funcselector
|
2009-02-20 23:09:29 +00:00
|
|
|
// Does NOT say anything about obj's superclasses, i.e. failure may be
|
2009-02-22 13:11:43 +00:00
|
|
|
// returned even if one of the superclasses defines the funcselector.
|
2009-02-15 06:10:59 +00:00
|
|
|
int funcnum = obj->methods_nr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < funcnum; i++)
|
2009-02-22 13:11:43 +00:00
|
|
|
if (VM_OBJECT_GET_FUNCSELECTOR(obj, i) == slc) // Found it?
|
|
|
|
return i; // report success
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
return -1; // Failed
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static inline SelectorType _lookup_selector_function(EngineState *s, int seg_id, Object *obj, Selector selector_id, reg_t *fptr) {
|
2009-02-15 06:10:59 +00:00
|
|
|
int index;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// "recursive" lookup
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
while (obj) {
|
|
|
|
index = _class_locate_funcselector(s, obj, selector_id);
|
|
|
|
|
|
|
|
if (index >= 0) {
|
2009-02-15 22:28:12 +00:00
|
|
|
if (fptr) {
|
|
|
|
if (s->version < SCI_VERSION(1, 001, 000))
|
2009-02-20 23:09:29 +00:00
|
|
|
*fptr = make_reg(obj->pos.segment, getUInt16((byte *)(obj->base_method + index + obj->methods_nr + 1)));
|
2009-02-15 06:10:59 +00:00
|
|
|
else
|
2009-02-20 23:09:29 +00:00
|
|
|
*fptr = make_reg(obj->pos.segment, getUInt16((byte *)(obj->base_method + index * 2 + 2)));
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
return kSelectorMethod;
|
2009-02-15 06:10:59 +00:00
|
|
|
} else {
|
|
|
|
seg_id = obj->variables[SCRIPT_SUPERCLASS_SELECTOR].segment;
|
|
|
|
obj = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
return kSelectorNone;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
SelectorType lookup_selector(EngineState *s, reg_t obj_location, Selector selector_id, reg_t **vptr, reg_t *fptr) {
|
|
|
|
Object *obj = obj_get(s, obj_location);
|
|
|
|
Object *species;
|
2009-02-15 06:10:59 +00:00
|
|
|
int index;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Early SCI versions used the LSB in the selector ID as a read/write
|
2009-02-22 13:11:43 +00:00
|
|
|
// toggle, meaning that we must remove it for selector lookup.
|
2009-02-15 06:10:59 +00:00
|
|
|
if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
|
|
|
|
selector_id &= ~1;
|
|
|
|
|
|
|
|
if (!obj) {
|
|
|
|
CORE_ERROR("SLC-LU", "Attempt to send to non-object or invalid script");
|
|
|
|
sciprintf("Address was "PREG"\n", PRINT_REG(obj_location));
|
2009-02-28 11:12:59 +00:00
|
|
|
return kSelectorNone;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_CLASS(obj))
|
|
|
|
species = obj;
|
|
|
|
else
|
|
|
|
species = obj_get(s, obj->variables[SCRIPT_SPECIES_SELECTOR]);
|
|
|
|
|
|
|
|
|
|
|
|
if (!obj) {
|
|
|
|
CORE_ERROR("SLC-LU", "Error while looking up Species class");
|
|
|
|
sciprintf("Original address was "PREG"\n", PRINT_REG(obj_location));
|
|
|
|
sciprintf("Species address was "PREG"\n", PRINT_REG(obj->variables[SCRIPT_SPECIES_SELECTOR]));
|
2009-02-28 11:12:59 +00:00
|
|
|
return kSelectorNone;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
index = _obj_locate_varselector(s, obj, selector_id);
|
|
|
|
|
|
|
|
if (index >= 0) {
|
2009-02-22 13:11:43 +00:00
|
|
|
// Found it as a variable
|
2009-02-15 06:10:59 +00:00
|
|
|
if (vptr)
|
|
|
|
*vptr = obj->variables + index;
|
2009-02-28 11:12:59 +00:00
|
|
|
return kSelectorVariable;
|
2009-02-15 22:28:12 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
return _lookup_selector_function(s, obj_location.segment, obj, selector_id, fptr);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Detects SCI versions by their different script header
|
2009-02-21 10:47:56 +00:00
|
|
|
void script_detect_versions(EngineState *s) {
|
2009-02-15 06:10:59 +00:00
|
|
|
int c;
|
|
|
|
resource_t *script = {0};
|
|
|
|
|
2009-02-28 20:45:36 +00:00
|
|
|
if (s->resmgr->findResource(sci_heap, 0, 0)) {
|
2009-02-15 22:28:12 +00:00
|
|
|
version_require_later_than(s, SCI_VERSION(1, 001, 000));
|
2009-02-15 06:10:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (c = 0; c < 1000; c++) {
|
2009-02-28 20:45:36 +00:00
|
|
|
if ((script = s->resmgr->findResource(sci_script, c, 0))) {
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
int id = getInt16(script->data);
|
|
|
|
|
|
|
|
if (id > 15) {
|
|
|
|
version_require_earlier_than(s, SCI_VERSION_FTU_NEW_SCRIPT_HEADER);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
SegmentId script_get_segment(EngineState *s, int script_nr, int load) {
|
|
|
|
SegmentId segment;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD)
|
|
|
|
script_instantiate(s, script_nr);
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
segment = s->seg_manager->segGet(script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (segment > 0) {
|
|
|
|
if ((load & SCRIPT_GET_LOCK) == SCRIPT_GET_LOCK)
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->incrementLockers(segment, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
return segment;
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
reg_t script_lookup_export(EngineState *s, int script_nr, int export_index) {
|
2009-02-28 11:12:59 +00:00
|
|
|
SegmentId seg = script_get_segment(s, script_nr, SCRIPT_GET_DONT_LOAD);
|
|
|
|
MemObject *memobj;
|
|
|
|
Script *script = NULL;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (!seg) {
|
|
|
|
CORE_ERROR("EXPORTS", "Script invalid or not loaded");
|
2009-02-21 04:25:56 +00:00
|
|
|
sciprintf("Script was script.%03d (0x%x)\n",
|
2009-02-15 22:28:12 +00:00
|
|
|
script_nr, script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
memobj = GET_SEGMENT(*s->seg_manager, seg, MEM_OBJ_SCRIPT);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (memobj)
|
|
|
|
script = &(memobj->data.script);
|
|
|
|
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
if (script
|
2009-02-15 22:28:12 +00:00
|
|
|
&& export_index < script->exports_nr
|
|
|
|
&& export_index >= 0)
|
2009-02-15 06:10:59 +00:00
|
|
|
#endif
|
|
|
|
return make_reg(seg, getUInt16((byte *)(script->export_table + export_index)));
|
|
|
|
#ifndef DISABLE_VALIDATIONS
|
|
|
|
else {
|
|
|
|
CORE_ERROR("EXPORTS", "Export invalid or script missing ");
|
|
|
|
if (!script)
|
|
|
|
sciprintf("(script.%03d missing)\n", script_nr);
|
|
|
|
else
|
|
|
|
sciprintf("(script.%03d: Sought export %d/%d)\n",
|
2009-02-15 22:28:12 +00:00
|
|
|
script_nr, export_index, script->exports_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, reg))
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int script_instantiate_common(EngineState *s, int script_nr, resource_t **script, resource_t **heap, int *was_new) {
|
2009-02-15 06:10:59 +00:00
|
|
|
int seg;
|
|
|
|
int seg_id;
|
2009-02-15 22:28:12 +00:00
|
|
|
int marked_for_deletion;
|
2009-02-28 11:12:59 +00:00
|
|
|
MemObject *mem;
|
2009-02-15 06:10:59 +00:00
|
|
|
reg_t reg;
|
|
|
|
|
|
|
|
*was_new = 1;
|
|
|
|
|
2009-02-28 20:45:36 +00:00
|
|
|
*script = s->resmgr->findResource(sci_script, script_nr, 0);
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version >= SCI_VERSION(1, 001, 000))
|
2009-02-28 20:45:36 +00:00
|
|
|
*heap = s->resmgr->findResource(sci_heap, script_nr, 0);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (!*script || (s->version >= SCI_VERSION(1, 001, 000) && !heap)) {
|
2009-02-15 06:10:59 +00:00
|
|
|
sciprintf("Script 0x%x requested but not found\n", script_nr);
|
2009-02-22 13:11:43 +00:00
|
|
|
//script_debug_flag = script_error_flag = 1;
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version >= SCI_VERSION(1, 001, 000)) {
|
2009-02-15 06:10:59 +00:00
|
|
|
if (*heap)
|
|
|
|
sciprintf("Inconsistency: heap resource WAS found\n");
|
|
|
|
else if (*script)
|
|
|
|
sciprintf("Inconsistency: script resource WAS found\n");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (NULL == s) {
|
|
|
|
sciprintf("vm.c: script_instantiate(): NULL passed for \"s\"\n");
|
2009-02-15 06:10:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
seg = s->seg_manager->segGet(script_nr);
|
|
|
|
if (s->seg_manager->scriptIsLoaded(script_nr, SCRIPT_ID)) {
|
|
|
|
marked_for_deletion = s->seg_manager->scriptMarkedDeleted(script_nr);
|
2009-02-15 22:28:12 +00:00
|
|
|
if (!marked_for_deletion) {
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->incrementLockers(seg, SEG_ID);
|
2009-02-15 22:28:12 +00:00
|
|
|
return seg;
|
|
|
|
} else {
|
2009-02-15 06:10:59 +00:00
|
|
|
seg_id = seg;
|
2009-02-21 23:27:24 +00:00
|
|
|
mem = s->seg_manager->heap[seg];
|
|
|
|
s->seg_manager->freeScript(mem);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-02-22 13:11:43 +00:00
|
|
|
} else if (!(mem = s->seg_manager->allocateScript(s, script_nr, &seg_id))) { // ALL YOUR SCRIPT BASE ARE BELONG TO US
|
2009-02-20 23:09:29 +00:00
|
|
|
sciprintf("Not enough heap space for script size 0x%x of script 0x%x, should this happen?`\n", (*script)->size, script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->initialiseScript(mem, s, script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
reg.segment = seg_id;
|
|
|
|
reg.offset = 0;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Set heap position (beyond the size word)
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->setLockers(1, reg.segment, SEG_ID);
|
|
|
|
s->seg_manager->setExportTableOffset(0, reg.segment, SEG_ID);
|
|
|
|
s->seg_manager->setSynonymsOffset(0, reg.segment, SEG_ID);
|
|
|
|
s->seg_manager->setSynonymsNr(0, reg.segment, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
*was_new = 0;
|
|
|
|
|
|
|
|
return seg_id;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int script_instantiate_sci0(EngineState *s, int script_nr) {
|
2009-02-15 06:10:59 +00:00
|
|
|
int objtype;
|
|
|
|
unsigned int objlength;
|
|
|
|
reg_t reg, reg_tmp;
|
|
|
|
int seg_id;
|
|
|
|
int relocation = -1;
|
2009-02-22 13:11:43 +00:00
|
|
|
int magic_pos_adder; // Usually 0; 2 for older SCI versions
|
2009-02-15 06:10:59 +00:00
|
|
|
resource_t *script;
|
|
|
|
int was_new;
|
|
|
|
|
|
|
|
seg_id = script_instantiate_common(s, script_nr, &script, NULL, &was_new);
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
if (was_new)
|
|
|
|
return seg_id;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
reg.segment = seg_id;
|
|
|
|
reg.offset = 0;
|
|
|
|
|
|
|
|
if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
|
2009-02-20 23:09:29 +00:00
|
|
|
//
|
2009-02-15 06:10:59 +00:00
|
|
|
int locals_nr = getUInt16(script->data);
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Old script block
|
|
|
|
// There won't be a localvar block in this case
|
2009-02-20 23:09:29 +00:00
|
|
|
// Instead, the script starts with a 16 bit int specifying the
|
2009-02-22 13:11:43 +00:00
|
|
|
// number of locals we need; these are then allocated and zeroed.
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->mcpyInOut(0, script->data, script->size, reg.segment, SEG_ID);
|
2009-02-22 13:11:43 +00:00
|
|
|
magic_pos_adder = 2; // Step over the funny prefix
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (locals_nr)
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptInitialiseLocalsZero(reg.segment, locals_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
} else {
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->mcpyInOut(0, script->data, script->size, reg.segment, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
magic_pos_adder = 0;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Now do a first pass through the script objects to find the
|
|
|
|
// export table and local variable block
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
objlength = 0;
|
|
|
|
reg_tmp = reg;
|
|
|
|
reg.offset = magic_pos_adder;
|
|
|
|
|
|
|
|
do {
|
|
|
|
reg_t data_base;
|
|
|
|
reg_t addr;
|
2009-02-22 13:11:43 +00:00
|
|
|
reg.offset += objlength; // Step over the last checked object
|
2009-02-15 06:10:59 +00:00
|
|
|
objtype = SEG_GET_HEAP(s, reg);
|
2009-02-15 22:28:12 +00:00
|
|
|
if (!objtype) break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2));
|
|
|
|
|
|
|
|
data_base = reg;
|
|
|
|
data_base.offset += 4;
|
|
|
|
|
|
|
|
addr = data_base;
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
switch (objtype) {
|
2009-02-15 06:10:59 +00:00
|
|
|
case sci_obj_exports: {
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->setExportTableOffset(data_base.offset, reg.segment, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-02-15 22:28:12 +00:00
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
case sci_obj_synonyms:
|
2009-02-22 13:11:43 +00:00
|
|
|
s->seg_manager->setSynonymsOffset(addr.offset, reg.segment, SEG_ID); // +4 is to step over the header
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->setSynonymsNr((objlength) / 4, reg.segment, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case sci_obj_localvars:
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptInitialiseLocals(data_base);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case sci_obj_class: {
|
|
|
|
int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET;
|
|
|
|
int species;
|
|
|
|
reg_tmp.offset = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET;
|
|
|
|
species = OBJ_SPECIES(s, reg_tmp);
|
|
|
|
if (species < 0 || species >= s->classtable_size) {
|
|
|
|
sciprintf("Invalid species %d(0x%x) not in interval "
|
2009-02-15 22:28:12 +00:00
|
|
|
"[0,%d) while instantiating script %d\n",
|
|
|
|
species, species, s->classtable_size,
|
|
|
|
script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
script_debug_flag = script_error_flag = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->classtable[species].script = script_nr;
|
|
|
|
s->classtable[species].reg = addr;
|
|
|
|
s->classtable[species].reg.offset = classpos;
|
2009-02-22 13:11:43 +00:00
|
|
|
// Set technical class position-- into the block allocated for it
|
2009-02-15 22:28:12 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (objtype != 0);
|
2009-02-22 13:11:43 +00:00
|
|
|
// And now a second pass to adjust objects and class pointers, and the general pointers
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
objlength = 0;
|
2009-02-22 13:11:43 +00:00
|
|
|
reg.offset = magic_pos_adder; // Reset counter
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
reg_t addr;
|
2009-02-22 13:11:43 +00:00
|
|
|
reg.offset += objlength; // Step over the last checked object
|
2009-02-15 06:10:59 +00:00
|
|
|
objtype = SEG_GET_HEAP(s, reg);
|
2009-02-15 22:28:12 +00:00
|
|
|
if (!objtype) break;
|
2009-02-15 06:10:59 +00:00
|
|
|
objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2));
|
2009-02-22 13:11:43 +00:00
|
|
|
reg.offset += 4; // Step over header
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
addr = reg;
|
|
|
|
|
|
|
|
switch (objtype) {
|
|
|
|
case sci_obj_code:
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptAddCodeBlock(addr);
|
2009-02-15 06:10:59 +00:00
|
|
|
break;
|
|
|
|
case sci_obj_object:
|
2009-02-22 13:11:43 +00:00
|
|
|
case sci_obj_class: { // object or class?
|
2009-02-28 11:12:59 +00:00
|
|
|
Object *obj = s->seg_manager->scriptObjInit(s, addr);
|
|
|
|
Object *base_obj;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Instantiate the superclass, if neccessary
|
2009-02-20 23:09:29 +00:00
|
|
|
obj->variables[SCRIPT_SPECIES_SELECTOR] = INST_LOOKUP_CLASS(obj->variables[SCRIPT_SPECIES_SELECTOR].offset);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
base_obj = obj_get(s, obj->variables[SCRIPT_SPECIES_SELECTOR]);
|
|
|
|
obj->variable_names_nr = base_obj->variables_nr;
|
|
|
|
obj->base_obj = base_obj->base_obj;
|
2009-02-22 13:11:43 +00:00
|
|
|
// Copy base from species class, as we need its selector IDs
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
obj->variables[SCRIPT_SUPERCLASS_SELECTOR] = INST_LOOKUP_CLASS(obj->variables[SCRIPT_SUPERCLASS_SELECTOR].offset);
|
2009-02-22 13:11:43 +00:00
|
|
|
} // if object or class
|
2009-02-15 22:28:12 +00:00
|
|
|
break;
|
2009-02-22 13:11:43 +00:00
|
|
|
case sci_obj_pointers: // A relocation table
|
2009-02-15 06:10:59 +00:00
|
|
|
relocation = addr.offset;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
reg.offset -= 4; // Step back on header
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
} while ((objtype != 0) && (((unsigned)reg.offset) < script->size - 2));
|
|
|
|
|
|
|
|
if (relocation >= 0)
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptRelocate(make_reg(reg.segment, relocation));
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptFreeUnusedObjects(reg.segment);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
return reg.segment; // instantiation successful
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int script_instantiate_sci11(EngineState *s, int script_nr) {
|
2009-02-15 06:10:59 +00:00
|
|
|
resource_t *script, *heap;
|
|
|
|
int seg_id;
|
|
|
|
int heap_start;
|
|
|
|
reg_t reg;
|
|
|
|
int was_new;
|
|
|
|
|
|
|
|
seg_id = script_instantiate_common(s, script_nr, &script, &heap, &was_new);
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
if (was_new)
|
|
|
|
return seg_id;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
heap_start = script->size;
|
|
|
|
if (script->size & 2)
|
|
|
|
heap_start ++;
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->mcpyInOut(0, script->data, script->size, seg_id, SEG_ID);
|
|
|
|
s->seg_manager->mcpyInOut(heap_start, heap->data, heap->size, seg_id, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (getUInt16(script->data + 6) > 0)
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->setExportTableOffset(6, seg_id, SEG_ID);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
reg.segment = seg_id;
|
2009-02-15 22:28:12 +00:00
|
|
|
reg.offset = heap_start + 4;
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptInitialiseLocals(reg);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->scriptRelocateExportsSci11(seg_id);
|
|
|
|
s->seg_manager->scriptInitialiseObjectsSci11(s, seg_id);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
reg.offset = getUInt16(heap->data);
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->heapRelocate(s, reg);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
return seg_id;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int script_instantiate(EngineState *s, int script_nr) {
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version >= SCI_VERSION(1, 001, 000))
|
2009-02-15 06:10:59 +00:00
|
|
|
return script_instantiate_sci11(s, script_nr);
|
|
|
|
else
|
|
|
|
return script_instantiate_sci0(s, script_nr);
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
void script_uninstantiate_sci0(EngineState *s, int script_nr, SegmentId seg) {
|
2009-02-15 22:28:12 +00:00
|
|
|
reg_t reg = make_reg(seg, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) ? 2 : 0);
|
2009-02-15 06:10:59 +00:00
|
|
|
int objtype, objlength;
|
|
|
|
|
2009-02-22 13:11:43 +00:00
|
|
|
// Make a pass over the object in order uninstantiate all superclasses
|
2009-02-15 06:10:59 +00:00
|
|
|
objlength = 0;
|
|
|
|
|
|
|
|
do {
|
2009-02-22 13:11:43 +00:00
|
|
|
reg.offset += objlength; // Step over the last checked object
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
objtype = SEG_GET_HEAP(s, reg);
|
2009-02-15 22:28:12 +00:00
|
|
|
if (!objtype) break;
|
2009-02-22 13:11:43 +00:00
|
|
|
objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2)); // use SEG_UGET_HEAP ??
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
reg.offset += 4; // Step over header
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
if ((objtype == sci_obj_object) || (objtype == sci_obj_class)) { // object or class?
|
2009-02-15 06:10:59 +00:00
|
|
|
int superclass;
|
|
|
|
|
|
|
|
reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
superclass = OBJ_SUPERCLASS(s, reg); // Get superclass...
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (superclass >= 0) {
|
|
|
|
int superclass_script = s->classtable[superclass].script;
|
|
|
|
|
|
|
|
if (superclass_script == script_nr) {
|
2009-02-21 23:27:24 +00:00
|
|
|
if (s->seg_manager->getLockers(reg.segment, SEG_ID))
|
|
|
|
s->seg_manager->decrementLockers(reg.segment, SEG_ID); // Decrease lockers if this is us ourselves
|
2009-02-15 06:10:59 +00:00
|
|
|
} else
|
|
|
|
script_uninstantiate(s, superclass_script);
|
2009-02-20 23:09:29 +00:00
|
|
|
// Recurse to assure that the superclass lockers number gets decreased
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
|
2009-02-20 23:09:29 +00:00
|
|
|
} // if object or class
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
reg.offset -= 4; // Step back on header
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
} while (objtype != 0);
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
void script_uninstantiate(EngineState *s, int script_nr) {
|
2009-02-15 22:28:12 +00:00
|
|
|
reg_t reg = make_reg(0, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) ? 2 : 0);
|
2009-02-15 06:10:59 +00:00
|
|
|
int i;
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
reg.segment = s->seg_manager->segGet(script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
if (!s->seg_manager->scriptIsLoaded(script_nr, SCRIPT_ID) || reg.segment <= 0) { // Is it already loaded?
|
2009-02-28 13:41:43 +00:00
|
|
|
//warning("unloading script 0x%x requested although not loaded", script_nr);
|
2009-02-20 23:09:29 +00:00
|
|
|
// This is perfectly valid SCI behaviour
|
2009-02-15 06:10:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->decrementLockers(reg.segment, SEG_ID); // One less locker
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
if (s->seg_manager->getLockers(reg.segment, SEG_ID) > 0)
|
2009-02-15 06:10:59 +00:00
|
|
|
return;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Free all classtable references to this script
|
2009-02-15 06:10:59 +00:00
|
|
|
for (i = 0; i < s->classtable_size; i++)
|
|
|
|
if (s->classtable[i].reg.segment == reg.segment)
|
|
|
|
s->classtable[i].reg = NULL_REG;
|
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
if (s->version < SCI_VERSION(1, 001, 000))
|
2009-02-15 06:10:59 +00:00
|
|
|
script_uninstantiate_sci0(s, script_nr, reg.segment);
|
|
|
|
else
|
|
|
|
sciprintf("FIXME: Add proper script uninstantiation for SCI 1.1\n");
|
|
|
|
|
2009-02-21 23:27:24 +00:00
|
|
|
if (s->seg_manager->getLockers(reg.segment, SEG_ID))
|
2009-02-20 23:09:29 +00:00
|
|
|
return; // if xxx.lockers > 0
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Otherwise unload it completely
|
|
|
|
// Explanation: I'm starting to believe that this work is done by SCI itself.
|
2009-02-21 23:27:24 +00:00
|
|
|
s->seg_manager->markScriptDeleted(script_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (script_checkloads_flag)
|
|
|
|
sciprintf("Unloaded script 0x%x.\n", script_nr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
static void _init_stack_base_with_selector(EngineState *s, Selector selector) {
|
2009-02-21 21:16:41 +00:00
|
|
|
s->stack_base[0] = make_reg(0, (uint16)selector);
|
2009-02-15 06:10:59 +00:00
|
|
|
s->stack_base[1] = NULL_REG;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
static EngineState *_game_run(EngineState *s, int restoring) {
|
|
|
|
EngineState *successor = NULL;
|
2009-02-15 06:10:59 +00:00
|
|
|
int game_is_finished = 0;
|
|
|
|
do {
|
|
|
|
s->execution_stack_pos_changed = 0;
|
2009-02-15 22:28:12 +00:00
|
|
|
run_vm(s, (successor || restoring) ? 1 : 0);
|
2009-02-20 23:09:29 +00:00
|
|
|
if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested?
|
2009-02-26 23:13:00 +00:00
|
|
|
successor = NULL;
|
2009-02-17 13:51:52 +00:00
|
|
|
free(s->execution_stack);
|
2009-02-15 06:10:59 +00:00
|
|
|
s->execution_stack = NULL;
|
|
|
|
s->execution_stack_pos = -1;
|
|
|
|
s->execution_stack_pos_changed = 0;
|
|
|
|
|
|
|
|
game_exit(s);
|
|
|
|
script_free_engine(s);
|
|
|
|
script_init_engine(s, s->version);
|
|
|
|
game_init(s);
|
|
|
|
sfx_reset_player();
|
|
|
|
_init_stack_base_with_selector(s, s->selector_map.play);
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
send_selector(s, s->game_obj, s->game_obj, s->stack_base, 2, s->stack_base);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
script_abort_flag = 0;
|
2009-02-20 23:09:29 +00:00
|
|
|
s->restarting_flags = SCI_GAME_WAS_RESTARTED | SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
} else {
|
2009-02-15 06:10:59 +00:00
|
|
|
successor = s->successor;
|
|
|
|
if (successor) {
|
|
|
|
game_exit(s);
|
|
|
|
script_free_vm_memory(s);
|
2009-02-24 22:25:39 +00:00
|
|
|
delete s;
|
2009-02-15 06:10:59 +00:00
|
|
|
s = successor;
|
|
|
|
|
2009-02-21 22:06:42 +00:00
|
|
|
if (!send_calls_allocated) {
|
|
|
|
send_calls_allocated = 16;
|
2009-02-28 11:12:59 +00:00
|
|
|
send_calls = (CallsStruct *)sci_calloc(sizeof(CallsStruct), 16);
|
2009-02-21 22:06:42 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (script_abort_flag == SCRIPT_ABORT_WITH_REPLAY) {
|
|
|
|
sciprintf("Restarting with replay()\n");
|
2009-02-20 23:09:29 +00:00
|
|
|
s->execution_stack_pos = -1; // Restart with replay
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
_init_stack_base_with_selector(s, s->selector_map.replay);
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
send_selector(s, s->game_obj, s->game_obj, s->stack_base, 2, s->stack_base);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
script_abort_flag = 0;
|
|
|
|
|
|
|
|
} else
|
|
|
|
game_is_finished = 1;
|
|
|
|
}
|
|
|
|
} while (!game_is_finished);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int objinfo(EngineState *s, reg_t pos);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
int game_run(EngineState **_s) {
|
|
|
|
EngineState *s = *_s;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
sciprintf(" Calling %s::play()\n", s->game_name);
|
2009-02-20 23:09:29 +00:00
|
|
|
_init_stack_base_with_selector(s, s->selector_map.play); // Call the play selector
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
// Now: Register the first element on the execution stack-
|
|
|
|
if (!send_selector(s, s->game_obj, s->game_obj, s->stack_base, 2, s->stack_base) || script_error_flag) {
|
2009-02-15 06:10:59 +00:00
|
|
|
objinfo(s, s->game_obj);
|
|
|
|
sciprintf("Failed to run the game! Aborting...\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2009-02-20 23:09:29 +00:00
|
|
|
// and ENGAGE!
|
2009-02-15 06:10:59 +00:00
|
|
|
*_s = s = _game_run(s, 0);
|
|
|
|
|
|
|
|
sciprintf(" Game::play() finished.\n");
|
2009-02-20 23:09:29 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-02-20 23:41:15 +00:00
|
|
|
#if 0
|
2009-02-21 10:47:56 +00:00
|
|
|
int game_restore(EngineState **_s, char *game_name) {
|
|
|
|
EngineState *s;
|
2009-02-15 06:10:59 +00:00
|
|
|
int debug_state = _debugstate_valid;
|
|
|
|
|
|
|
|
sciprintf("Restoring savegame '%s'...\n", game_name);
|
|
|
|
s = gamestate_restore(*_s, game_name);
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
sciprintf("Restoring gamestate '%s' failed.\n", game_name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
_debugstate_valid = debug_state;
|
|
|
|
script_abort_flag = 0;
|
|
|
|
s->restarting_flags = 0;
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
s->execution_stack_pos = -1; // Resatart with replay
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
_init_stack_base_with_selector(s, s->selector_map.replay);
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
send_selector(s, s->game_obj, s->game_obj, s->stack_base, 2, s->stack_base);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
*_s = s = _game_run(s, 1);
|
|
|
|
|
|
|
|
sciprintf(" Game::play() finished.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2009-02-20 23:41:15 +00:00
|
|
|
#endif
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-28 11:12:59 +00:00
|
|
|
Object *obj_get(EngineState *s, reg_t offset) {
|
|
|
|
MemObject *memobj = GET_OBJECT_SEGMENT(*s->seg_manager, offset.segment);
|
|
|
|
Object *obj = NULL;
|
2009-02-15 06:10:59 +00:00
|
|
|
int idx;
|
|
|
|
|
|
|
|
if (memobj != NULL) {
|
2009-02-20 23:09:29 +00:00
|
|
|
if (memobj->type == MEM_OBJ_CLONES && ENTRY_IS_VALID(&memobj->data.clones, offset.offset))
|
2009-02-15 06:10:59 +00:00
|
|
|
obj = &(memobj->data.clones.table[offset.offset].entry);
|
|
|
|
else if (memobj->type == MEM_OBJ_SCRIPT) {
|
2009-02-20 23:09:29 +00:00
|
|
|
if (offset.offset <= memobj->data.script.buf_size && offset.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
|
2009-02-15 22:28:12 +00:00
|
|
|
&& RAW_IS_OBJECT(memobj->data.script.buf + offset.offset)) {
|
2009-02-15 06:10:59 +00:00
|
|
|
idx = RAW_GET_CLASS_INDEX(&(memobj->data.script), offset);
|
|
|
|
if (idx >= 0 && idx < memobj->data.script.objects_nr)
|
|
|
|
obj = memobj->data.script.objects + idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:47:56 +00:00
|
|
|
const char *obj_get_name(EngineState *s, reg_t pos) {
|
2009-02-28 11:12:59 +00:00
|
|
|
Object *obj = obj_get(s, pos);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return "<no such object>";
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
return (const char *)(obj->base + obj->variables[SCRIPT_NAME_SELECTOR].offset);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-02-20 23:09:29 +00:00
|
|
|
void quit_vm() {
|
|
|
|
script_abort_flag = 1; // Terminate VM
|
2009-02-15 06:10:59 +00:00
|
|
|
_debugstate_valid = 0;
|
|
|
|
_debug_seeking = 0;
|
|
|
|
_debug_step_running = 0;
|
|
|
|
}
|
2009-02-21 10:23:36 +00:00
|
|
|
|
|
|
|
} // End of namespace Sci
|