aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/vm.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2009-02-15 11:39:07 +0000
committerEugene Sandulenko2009-02-15 11:39:07 +0000
commite241843bec22600ab4ef98e7a085e82aac73fc93 (patch)
tree61a793884d3462e1feb80e80f202d8816d0c8ec4 /engines/sci/engine/vm.cpp
parente9f742806362a84ffdb176a7414318dd2ab4df89 (diff)
downloadscummvm-rg350-e241843bec22600ab4ef98e7a085e82aac73fc93.tar.gz
scummvm-rg350-e241843bec22600ab4ef98e7a085e82aac73fc93.tar.bz2
scummvm-rg350-e241843bec22600ab4ef98e7a085e82aac73fc93.zip
- Remove some unneeded files
- Mass rename .c to .cpp svn-id: r38227
Diffstat (limited to 'engines/sci/engine/vm.cpp')
-rw-r--r--engines/sci/engine/vm.cpp2410
1 files changed, 2410 insertions, 0 deletions
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
new file mode 100644
index 0000000000..d2831fbd69
--- /dev/null
+++ b/engines/sci/engine/vm.cpp
@@ -0,0 +1,2410 @@
+/***************************************************************************
+ vm.c Copyright (C) 1999 -- 2002 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CJR) [jameson@linuxgames.com]
+
+***************************************************************************/
+
+
+#include "sci/include/sciresource.h"
+#include "sci/include/engine.h"
+#include "sci/include/versions.h"
+#include "sci/include/kdebug.h"
+#include "sci/engine/kernel_types.h"
+#include "sci/include/seg_manager.h"
+#include "sci/engine/gc.h"
+#include "sci/include/sfx_player.h"
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+reg_t NULL_REG = NULL_REG_INITIALIZER;
+
+/*#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 */
+
+
+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 */
+
+extern int _debug_step_running; /* scriptdebug.c */
+extern int _debug_seeking; /* scriptdebug.c */
+extern int _weak_validations; /* scriptdebug.c */
+
+
+calls_struct_t *send_calls = NULL;
+int send_calls_allocated = 0;
+int bp_flag = 0;
+static reg_t _dummy_register = NULL_REG_INITIALIZER;
+
+
+static int jump_initialized = 0;
+#ifdef HAVE_SETJMP_H
+static jmp_buf vm_error_address;
+#endif
+
+/*-- validation functionality --*/
+
+#ifndef DISABLE_VALIDATIONS
+
+static inline reg_t *
+validate_property(object_t *obj, int index)
+{
+ if (!obj)
+ {
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Sending to disposed object!\n");
+ _dummy_register = NULL_REG;
+ return &_dummy_register;
+ }
+
+ if (index < 0 || index >= obj->variables_nr) {
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Invalid property #%d (out of [0..%d]) requested!\n", index,
+ obj->variables_nr);
+
+ _dummy_register = NULL_REG;
+ return &_dummy_register;
+ }
+
+ return obj->variables + index;
+}
+
+static inline stack_ptr_t
+validate_stack_addr(state_t *s, stack_ptr_t sp)
+{
+ if (sp >= s->stack_base && sp < s->stack_top)
+ return sp;
+
+ script_debug_flag = script_error_flag = 1;
+ if (sci_debug_flags & 4)
+ sciprintf("[VM] Stack index %d out of valid range [%d..%d]\n",
+ sp - s->stack_base, 0, s->stack_top - s->stack_base -1);
+ return 0;
+}
+
+
+static inline int
+validate_arithmetic(reg_t reg)
+{
+ 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;
+}
+
+static inline int
+signed_validate_arithmetic(reg_t reg)
+{
+ 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;
+ }
+
+ if (reg.offset&0x8000)
+ return (signed) (reg.offset)-65536;
+ else
+ return reg.offset;
+}
+
+static inline int
+validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index, int line)
+{
+ 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
+ sciprintf("(out of range [%d..%d])", 0, max-1);
+ sciprintf(" in %s, line %d\n", __FILE__, line);
+ if (!_weak_validations)
+ script_debug_flag = script_error_flag = 1;
+
+#ifdef STRICT_READ
+ return 1;
+#else /* !STRICT_READ */
+ if (type == VAR_PARAM || type == VAR_TEMP) {
+ int total_offset = r - stack_base;
+ if (total_offset < 0 || total_offset >= VM_STACK_SIZE) {
+ sciprintf("[VM] Access would be outside even of the stack (%d); access denied\n",
+ total_offset);
+ return 1;
+ } else {
+ sciprintf("[VM] Access within stack boundaries; access granted.\n");
+ return 0;
+ }
+ };
+#endif
+ }
+
+ return 0;
+}
+
+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)
+{
+ if (!validate_variable(r, stack_base, type, max, index, line))
+ return r[index];
+ else
+ return default_value;
+}
+
+static inline void
+validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t value)
+{
+ if (!validate_variable(r, stack_base, type, max, index, line))
+ r[index] = value;
+}
+
+# define ASSERT_ARITHMETIC(v) validate_arithmetic(v)
+
+#else
+/*-- Non-validating alternatives -- */
+
+# define validate_stack_addr(s, sp) sp
+# define validate_arithmetic(r) ((r).offset)
+# define signed_validate_arithmetic(r) ((int) ((r).offset)&0x8000 ? (signed) ((r).offset)-65536 : ((r).offset))
+# 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))
+# define validate_property(o, p) (&((o)->variables[p]))
+# 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)
+
+#define OBJ_PROPERTY(o, p) (*validate_property(o, p))
+
+/*==--------------------------==*/
+
+int
+script_error(state_t *s, const char *file, int line, const char *reason)
+{
+ 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)
+
+reg_t
+get_class_address(state_t *s, int classnr, int lock, reg_t caller)
+{
+ class_t *class = s->classtable + classnr;
+
+ if (NULL == s) {
+ sciprintf("vm.c: get_class_address(): NULL passed for \"s\"\n");
+ return NULL_REG;
+ }
+
+ if (classnr < 0
+ || s->classtable_size <= classnr
+ || class->script < 0) {
+ sciprintf("[VM] Attempt to dereference class %x, which doesn't exist (max %x)\n",
+ classnr, s->classtable_size);
+ script_error_flag = script_debug_flag = 1;
+ return NULL_REG;
+ } else {
+ if (!class->reg.segment) {
+ script_get_segment(s, class->script, lock);
+
+ if (!class->reg.segment) {
+ sciprintf("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;"
+ " Entering debugger.\n", classnr, class->script);
+ script_error_flag = script_debug_flag = 1;
+ return NULL_REG;
+ }
+ } else
+ if (caller.segment != class->reg.segment)
+ sm_increment_lockers(&s->seg_manager, class->reg.segment, SEG_ID);
+
+ return class->reg;
+ }
+}
+
+/* Operating on the stack */
+/* 16 bit: */
+#define PUSH(v) PUSH32(make_reg(0, v))
+#define POP() (validate_arithmetic(POP32()))
+/* 32 bit: */
+#define PUSH32(a) (*(validate_stack_addr(s, (xs->sp)++)) = (a))
+#define POP32() (*(validate_stack_addr(s, --(xs->sp))))
+
+/* Getting instruction parameters */
+#define GET_OP_BYTE() ((guint8) code_buf[(xs->addr.pc.offset)++])
+#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())
+#define GET_OP_SIGNED_BYTE() ((gint8)(code_buf[(xs->addr.pc.offset)++]))
+#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())
+
+#define SEG_GET_HEAP( s, reg ) sm_get_heap( &s->seg_manager, reg )
+#define OBJ_SPECIES(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SPECIES_OFFSET))
+/* Returns an object's species */
+
+#define OBJ_SUPERCLASS(s, reg) SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + SCRIPT_SUPERCLASS_OFFSET))
+/* Returns an object's superclass */
+
+inline exec_stack_t *
+execute_method(state_t *s, word script, word pubfunct, stack_ptr_t sp,
+ reg_t calling_obj, word argc, stack_ptr_t argp)
+{
+ int seg;
+ guint16 temp;
+
+ if (!sm_script_is_loaded (&s->seg_manager, script, SCRIPT_ID)) /* Script not present yet? */
+ script_instantiate(s, script);
+ else
+ sm_unmark_script_deleted(&s->seg_manager, script);
+
+ seg = sm_seg_get( &s->seg_manager, script );
+
+ temp = sm_validate_export_func( &s->seg_manager, pubfunct, seg );
+ VERIFY( temp, "Invalid pubfunct in export table" );
+ if( !temp ) {
+ sciprintf("Request for invalid exported function 0x%x of script 0x%x\n", pubfunct, script);
+ script_error_flag = script_debug_flag = 1;
+ return NULL;
+ }
+
+ /* Check if a breakpoint is set on this method */
+ if (s->have_bp & BREAK_EXPORT)
+ {
+ breakpoint_t *bp;
+ guint32 bpaddress;
+
+ 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;
+ }
+ }
+
+ return add_exec_stack_entry(s, make_reg( seg, temp ),
+ sp, calling_obj, argc, argp, -1, calling_obj,
+ s->execution_stack_pos, seg);
+}
+
+
+static void
+_exec_varselectors(state_t *s)
+{ /* Executes all varselector read/write ops on the TOS */
+ /* Now check the TOS to execute all varselector entries */
+ if (s->execution_stack_pos >= 0)
+ while (s->execution_stack[s->execution_stack_pos].type == EXEC_STACK_TYPE_VARSELECTOR) {
+ /* varselector access? */
+ if (s->execution_stack[s->execution_stack_pos].argc) { /* write? */
+ reg_t temp = s->execution_stack[s->execution_stack_pos].variables_argp[1];
+ *(s->execution_stack[s->execution_stack_pos].addr.varp) = temp;
+
+ } else /* No, read */
+ s->r_acc = *(s->execution_stack[s->execution_stack_pos].addr.varp);
+
+ --(s->execution_stack_pos);
+ }
+}
+
+exec_stack_t *
+send_selector(state_t *s, reg_t send_obj, reg_t work_obj,
+ stack_ptr_t sp, int framesize, stack_ptr_t argp)
+ /* send_obj and work_obj are equal for anything but 'super' */
+ /* Returns a pointer to the TOS exec_stack element */
+{
+#ifdef VM_DEBUG_SEND
+ int i;
+#endif
+ reg_t *varp;
+ reg_t funcp;
+ int selector;
+ int argc;
+ int origin = s->execution_stack_pos; /* Origin: Used for debugging */
+ exec_stack_t *retval = s->execution_stack + s->execution_stack_pos;
+ int print_send_action = 0;
+ /* We return a pointer to the new active exec_stack_t */
+
+ /* The selector calls we catch are stored below: */
+ int send_calls_nr = -1;
+
+ if (NULL == s) {
+ sciprintf("vm.c: exec_stack_t(): NULL passed for \"s\"\n");
+ return NULL;
+ }
+
+ while (framesize > 0) {
+
+ selector = validate_arithmetic(*argp++);
+ argc = validate_arithmetic(*argp);
+
+ if (argc > 0x800){ /* More arguments than the stack could possibly accomodate for */
+ CORE_ERROR("SEND", "More than 0x800 arguments to function call\n");
+ return NULL;
+ }
+
+ /* Check if a breakpoint is set on this method */
+ if (s->have_bp & BREAK_SELECTOR) {
+ breakpoint_t *bp;
+ char method_name [256];
+
+ sprintf (method_name, "%s::%s",
+ obj_get_name(s, send_obj),
+ s->selector_names [selector]);
+
+ bp = s->bp_list;
+ while (bp) {
+ int cmplen = strlen(bp->data.name);
+ if (bp->data.name[cmplen - 1] != ':')
+ cmplen = 256;
+
+ if (bp->type == BREAK_SELECTOR && !strncmp (bp->data.name, method_name, cmplen)) {
+ sciprintf ("Break on %s (in ["PREG"])\n", method_name,
+ PRINT_REG(send_obj));
+ script_debug_flag = print_send_action = 1;
+ bp_flag = 1;
+ break;
+ }
+ bp = bp->next;
+ }
+ }
+
+#ifdef VM_DEBUG_SEND
+ sciprintf("Send to "PREG", selector %04x (%s):",
+ PRINT_REG(send_obj), selector, s->selector_names[selector]);
+#endif /* VM_DEBUG_SEND */
+
+ if (++send_calls_nr == (send_calls_allocated - 1))
+ send_calls = (calls_struct_t*)sci_realloc(send_calls, sizeof(calls_struct_t)
+ * (send_calls_allocated *= 2));
+
+
+ switch (lookup_selector(s, send_obj, selector, &varp, &funcp)) {
+
+ case SELECTOR_NONE:
+ sciprintf("Send to invalid selector 0x%x of object at "PREG"\n",
+ 0xffff & selector, PRINT_REG(send_obj));
+ script_error_flag = script_debug_flag = 1;
+ --send_calls_nr;
+ break;
+
+ case SELECTOR_VARIABLE:
+
+#ifdef VM_DEBUG_SEND
+ sciprintf("Varselector: ");
+ if (argc)
+ sciprintf("Write "PREG"\n", PRINT_REG(argp[1]));
+ else
+ sciprintf("Read\n");
+#endif /* VM_DEBUG_SEND */
+
+ switch (argc) {
+ case 0: /* Read selector */
+ if (print_send_action) {
+ sciprintf("[read selector]\n");
+ print_send_action = 0;
+ }
+ /* fallthrough */
+ case 1:
+#ifndef STRICT_SEND
+ default:
+#endif
+ { /* Argument is supplied -> Selector should be set */
+
+ if (print_send_action) {
+ reg_t val = *varp;
+ reg_t new = argp[1];
+
+ sciprintf("[write to selector: change "PREG" to "PREG"]\n",
+ PRINT_REG(val), PRINT_REG(new));
+ print_send_action = 0;
+ }
+ send_calls[send_calls_nr].address.var = varp; /* register the call */
+ 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_VARSELECTOR; /* Register as a varselector */
+
+ } break;
+#ifdef STRICT_SEND
+ default:
+ --send_calls_nr;
+ 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 */
+ _debug_seeking = _debug_step_running = 0;
+
+#endif
+ }
+ break;
+
+ case SELECTOR_METHOD:
+
+#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));
+#endif /* VM_DEBUG_SEND */
+ if (print_send_action) {
+ sciprintf("[invoke selector]\n");
+ print_send_action = 0;
+ }
+
+ send_calls[send_calls_nr].address.func = funcp; /* register call */
+ 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;
+ sp = CALL_SP_CARRY; /* Destroy sp, as it will be carried over */
+
+ break;
+ } /* switch(lookup_selector()) */
+
+
+ framesize -= (2 + argc);
+ argp += argc + 1;
+ }
+
+ /* 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.
+ */
+ for (; send_calls_nr >= 0; send_calls_nr--)
+ if (send_calls[send_calls_nr].type == EXEC_STACK_TYPE_VARSELECTOR) /* Write/read variable? */
+ retval = add_exec_stack_varselector(s, work_obj, send_calls[send_calls_nr].argc,
+ send_calls[send_calls_nr].argp,
+ send_calls[send_calls_nr].selector,
+ send_calls[send_calls_nr].address.var, origin);
+
+ else
+ 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);
+
+ _exec_varselectors(s);
+
+ retval = s->execution_stack + s->execution_stack_pos;
+ return retval;
+}
+
+
+exec_stack_t *
+add_exec_stack_varselector(state_t *s, reg_t objp, int argc, stack_ptr_t argp,
+ selector_t selector, reg_t *address, int origin)
+{
+ exec_stack_t *xstack = add_exec_stack_entry(s, NULL_REG, address, objp, argc, argp,
+ selector, objp, origin, SCI_XS_CALLEE_LOCALS);
+ /* Store selector address in sp */
+
+ xstack->addr.varp = address;
+ xstack->type = EXEC_STACK_TYPE_VARSELECTOR;
+
+ return xstack;
+}
+
+
+exec_stack_t *
+add_exec_stack_entry(state_t *s, reg_t pc, stack_ptr_t sp, reg_t objp, int argc,
+ stack_ptr_t argp, selector_t selector, reg_t sendp, int origin,
+ seg_id_t locals_segment)
+/* Returns new TOS element for the execution stack*/
+/* locals_segment may be -1 if derived from the called object */
+{
+ exec_stack_t *xstack = NULL;
+
+ if (!s->execution_stack)
+ s->execution_stack =
+ (exec_stack_t*)sci_malloc(sizeof(exec_stack_t) * (s->execution_stack_size = 16));
+
+ if (++(s->execution_stack_pos) == s->execution_stack_size) /* Out of stack space? */
+ s->execution_stack = (exec_stack_t*)sci_realloc(s->execution_stack,
+ sizeof(exec_stack_t) * (s->execution_stack_size += 8));
+
+ /* sciprintf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos,
+ s->execution_stack_size, origin, s->execution_stack); */
+
+ 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;
+
+ xstack->variables_argp = argp; /* Parameters */
+
+ *argp = make_reg(0, argc); /* SCI code relies on the zeroeth argument to equal argc */
+
+ /* Additional debug information */
+ xstack->selector = selector;
+ xstack->origin = origin;
+
+ xstack->type = EXEC_STACK_TYPE_CALL; /* Normal call */
+
+ return xstack;
+}
+
+
+#ifdef DISABLE_VALIDATONS
+# define kernel_matches_signature(a, b, c, d) 1
+#endif
+
+
+void
+vm_handle_fatal_error(state_t *s, int line, const char *file)
+{
+ fprintf(stderr, "Fatal VM error in %s, L%d; aborting...\n", file, line);
+#ifdef HAVE_SETJMP_H
+ if (jump_initialized)
+ longjmp(vm_error_address, 0);
+#endif
+ fprintf(stderr, "Could not recover, exitting...\n");
+ exit(1);
+}
+
+static inline script_t *
+script_locate_by_segment(state_t *s, seg_id_t seg)
+{
+ mem_obj_t *memobj = GET_SEGMENT(s->seg_manager, seg, MEM_OBJ_SCRIPT);
+ if (memobj)
+ return &(memobj->data.script);
+
+ return NULL;
+}
+
+
+static reg_t
+pointer_add(state_t *s, reg_t base, int offset)
+{
+ mem_obj_t *mobj = GET_SEGMENT_ANY(s->seg_manager, base.segment);
+
+ 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:
+ base.offset += 2*offset;
+ 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;
+
+ }
+}
+
+static inline void
+gc_countdown(state_t *s)
+{
+ 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};
+
+void
+run_vm(state_t *s, int restoring)
+{
+ reg_t *variables[4]; /* global, local, temp, param, as immediate pointers */
+ reg_t *variables_base[4]; /* Used for referencing VM ops */
+ seg_id_t variables_seg[4]; /* Same as above, contains segment IDs */
+#ifndef DISABLE_VALIDATIONS
+ int variables_max[4]; /* Max. values for all variables */
+ int code_buf_size = 0 /* (Avoid spurious warning) */;
+#endif
+ int temp;
+ gint16 aux_acc; /* Auxiliary 16 bit accumulator */
+ reg_t r_temp; /* Temporary register */
+ stack_ptr_t s_temp; /* Temporary stack pointer */
+ gint16 opparams[4]; /* opcode parameters */
+
+ int restadjust = s->r_amp_rest; /* &rest adjusts the parameter count
+ ** by this value */
+ /* Current execution data: */
+ exec_stack_t *xs = s->execution_stack + s->execution_stack_pos;
+ exec_stack_t *xs_new = NULL /* (Avoid spurious warning) */; /* Used during some operations */
+ object_t *obj = obj_get(s, xs->objp);
+ script_t *local_script = script_locate_by_segment(s, xs->local_segment);
+ int old_execution_stack_base = s->execution_stack_base; /* Used to detect the
+ ** stack bottom, for "physical"
+ ** returns */
+ byte *code_buf = NULL /* (Avoid spurious warning) */;
+
+ if (!local_script) {
+ script_error(s, __FILE__, __LINE__, "Program Counter gone astray");
+ return;
+ }
+
+ if (NULL == s) {
+ sciprintf("vm.c: run_vm(): NULL passed for \"s\"\n");
+ return;
+ }
+
+#ifdef HAVE_SETJMP_H
+ setjmp(vm_error_address);
+ jump_initialized = 1;
+#endif
+
+ if (!restoring)
+ s->execution_stack_base = s->execution_stack_pos;
+
+#ifndef DISABLE_VALIDATIONS
+ /* Initialize maximum variable count */
+ 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;
+
+ /* SCI code reads the zeroeth argument to determine argc */
+ if (s->script_000->locals_block)
+ variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL]
+ = s->script_000->locals_block->locals;
+ else
+ variables_base[VAR_GLOBAL] = variables[VAR_GLOBAL] = NULL;
+
+
+
+ s->execution_stack_pos_changed = 1; /* Force initialization */
+
+ while (1) {
+ byte opcode;
+ int old_pc_offset;
+ stack_ptr_t old_sp = xs->sp;
+ byte opnumber;
+ int var_type; /* See description below */
+ int var_number;
+
+ old_pc_offset = xs->addr.pc.offset;
+
+ if (s->execution_stack_pos_changed) {
+ script_t *scr;
+ 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) {
+ /* No script? Implicit return via fake instruction buffer */
+ SCIkdebug(SCIkWARNING, "Running on non-existant script in segment %x!\n", xs->addr.pc.segment);
+ 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
+ /* if (!obj) {
+ SCIkdebug(SCIkWARNING, "Running with non-existant self= "PREG"\n", PRINT_REG(xs->objp));
+ }*/
+
+ local_script = script_locate_by_segment(s, xs->local_segment);
+ if (!local_script) {
+ SCIkdebug(SCIkWARNING, "Could not find local script from segment %x!\n", xs->local_segment);
+ 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)
+ variables_base[VAR_LOCAL] = variables[VAR_LOCAL]
+ = local_script->locals_block->locals;
+ else
+ variables_base[VAR_LOCAL] = variables[VAR_LOCAL]
+ = NULL;
+#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;
+ }
+
+ }
+
+ script_error_flag = 0; /* Set error condition to false */
+
+ if (script_abort_flag)
+ return; /* Emergency */
+
+ /* Debug if this has been requested: */
+ if (script_debug_flag || sci_debug_flags) {
+ script_debug(s, &(xs->addr.pc), &(xs->sp), &(xs->fp),
+ &(xs->objp), &restadjust,
+ variables_seg, variables, variables_base,
+#ifdef DISABLE_VALIDATIONS
+ NULL,
+#else
+ variables_max,
+#endif
+ bp_flag);
+ 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
+
+ opcode = GET_OP_BYTE(); /* Get opcode */
+
+ opnumber = opcode >> 1;
+
+ for (temp = 0; formats[opnumber][temp]; temp++) /* formats comes from script.c */
+ switch(formats[opnumber][temp]) {
+
+ case Script_Byte: opparams[temp] = GET_OP_BYTE(); break;
+ case Script_SByte: opparams[temp] = GET_OP_SIGNED_BYTE(); break;
+
+ case Script_Word: opparams[temp] = GET_OP_WORD(); break;
+ case Script_SWord: opparams[temp] = GET_OP_SIGNED_WORD(); break;
+
+ case Script_Variable:
+ case Script_Property:
+
+ case Script_Local:
+ case Script_Temp:
+ case Script_Global:
+ case Script_Param:
+ opparams[temp] = GET_OP_FLEX(); break;
+
+ case Script_SVariable:
+ case Script_SRelative:
+ opparams[temp] = GET_OP_SIGNED_FLEX(); break;
+
+ case Script_Offset:
+ opparams[temp] = GET_OP_FLEX(); break;
+
+ case Script_None:
+ case Script_End:
+ break;
+
+ case Script_Invalid:
+ default:
+ sciprintf("opcode %02x: Invalid!", opcode);
+ script_debug_flag = script_error_flag = 1;
+
+ }
+
+
+ switch (opnumber) {
+
+ case 0x00: /* bnot */
+ s->r_acc = ACC_ARITHMETIC_L (0xffff ^ /*acc*/);
+ break;
+
+ case 0x01: /* add */
+ r_temp = POP32();
+ if (r_temp.segment || s->r_acc.segment) {
+ reg_t r_ptr;
+ int offset;
+ /* Pointer arithmetics! */
+ if (s->r_acc.segment) {
+ if (r_temp.segment) {
+ sciprintf("Error: Attempt to add two pointers, stack="PREG" and acc="PREG"!\n",
+ PRINT_REG(r_temp), PRINT_REG(s->r_acc));
+ script_debug_flag = script_error_flag = 1;
+ offset = 0;
+ } else {
+ r_ptr = s->r_acc;
+ 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;
+
+ case 0x02: /* sub */
+ r_temp = POP32();
+ if (r_temp.segment || s->r_acc.segment) {
+ reg_t r_ptr;
+ int offset;
+ /* Pointer arithmetics! */
+ if (s->r_acc.segment) {
+ if (r_temp.segment) {
+ sciprintf("Error: Attempt to subtract two pointers, stack="PREG" and acc="PREG"!\n",
+ PRINT_REG(r_temp), PRINT_REG(s->r_acc));
+ script_debug_flag = script_error_flag = 1;
+ offset = 0;
+ } else {
+ r_ptr = s->r_acc;
+ 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;
+
+ case 0x03: /* mul */
+ s->r_acc = ACC_ARITHMETIC_L (((gint16)POP()) * (gint16)/*acc*/);
+ break;
+
+ case 0x04: /* div */
+ ACC_AUX_LOAD();
+ aux_acc = aux_acc != 0 ? ((gint16)POP()) / aux_acc : 0;
+ ACC_AUX_STORE();
+ break;
+
+ case 0x05: /* mod */
+ ACC_AUX_LOAD();
+ aux_acc = aux_acc != 0 ? ((gint16)POP()) % aux_acc : 0;
+ ACC_AUX_STORE();
+ break;
+
+ case 0x06: /* shr */
+ s->r_acc = ACC_ARITHMETIC_L(((guint16) POP()) >> /*acc*/);
+ break;
+
+ case 0x07: /* shl */
+ s->r_acc = ACC_ARITHMETIC_L(((guint16) POP()) << /*acc*/);
+ break;
+
+ case 0x08: /* xor */
+ s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/);
+ break;
+
+ case 0x09: /* and */
+ s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/);
+ break;
+
+ case 0x0a: /* or */
+ s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/);
+ break;
+
+ case 0x0b: /* neg */
+ s->r_acc = ACC_ARITHMETIC_L(-/*acc*/);
+ break;
+
+ case 0x0c: /* not */
+ s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
+ /* Must allow pointers to be negated, as this is used for
+ ** checking whether objects exist */
+ break;
+
+ case 0x0d: /* eq? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, REG_EQ(r_temp, s->r_acc));
+ /* Explicitly allow pointers to be compared */
+ break;
+
+ case 0x0e: /* ne? */
+ s->r_prev = s->r_acc;
+ r_temp = POP32();
+ s->r_acc = make_reg(0, !REG_EQ(r_temp, s->r_acc));
+ /* Explicitly allow pointers to be compared */
+ break;
+
+ case 0x0f: /* gt? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() > (gint16)/*acc*/);
+ break;
+
+ case 0x10: /* ge? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() >= (gint16)/*acc*/);
+ break;
+
+ case 0x11: /* lt? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() < (gint16)/*acc*/);
+ break;
+
+ case 0x12: /* le? */
+ s->r_prev = s->r_acc;
+ s->r_acc = ACC_ARITHMETIC_L((gint16)POP() <= (gint16)/*acc*/);
+ break;
+
+ case 0x13: /* ugt? */
+ 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;
+
+ case 0x14: /* uge? */
+ 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;
+
+ case 0x15: /* ult? */
+ 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;
+
+ case 0x16: /* ule? */
+ 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;
+
+ case 0x17: /* bt */
+ if (s->r_acc.offset || s->r_acc.segment)
+ xs->addr.pc.offset += opparams[0];
+ break;
+
+ case 0x18: /* bnt */
+ if (!(s->r_acc.offset || s->r_acc.segment))
+ xs->addr.pc.offset += opparams[0];
+ break;
+
+ case 0x19: /* jmp */
+ xs->addr.pc.offset += opparams[0];
+ break;
+
+ case 0x1a: /* ldi */
+ s->r_acc = make_reg(0, opparams[0]);
+ break;
+
+ case 0x1b: /* push */
+ PUSH32(s->r_acc);
+ break;
+
+ case 0x1c: /* pushi */
+ PUSH(opparams[0]);
+ break;
+
+ case 0x1d: /* toss */
+ xs->sp--;
+ break;
+
+ case 0x1e: /* dup */
+ r_temp = xs->sp[-1];
+ PUSH32(r_temp);
+ break;
+
+ case 0x1f: { /* link */
+ int i;
+ for (i = 0; i < opparams[0]; i++)
+ xs->sp[i] = NULL_REG;
+ xs->sp += opparams[0];
+ break;
+ }
+
+ case 0x20: { /* call */
+ int argc = (opparams[1] >> 1) /* Given as offset, but we need count */
+ + 1 + restadjust;
+ stack_ptr_t call_base = xs->sp - argc;
+
+ xs->sp[1].offset += restadjust;
+ 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);
+ restadjust = 0; /* Used up the &rest adjustment */
+ xs->sp = call_base;
+
+ s->execution_stack_pos_changed = 1;
+ break;
+ }
+
+ case 0x21: /* callk */
+ gc_countdown(s);
+
+ xs->sp -= (opparams[1] >> 1)+1;
+ if (s->version >= SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
+ xs->sp -= restadjust;
+ s->r_amp_rest = 0; /* We just used up the restadjust, remember? */
+ }
+
+ 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
+ && !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]);
+ script_debug_flag = script_error_flag = 1;
+ } else {
+ s->r_acc = s->kfunct_table[opparams[0]]
+ .fun(s, opparams[0], argc, xs->sp + 1);
+ }
+ /* Call kernel function */
+
+ /* Calculate xs again: The kernel function might
+ ** have spawned a new VM */
+
+ xs_new = s->execution_stack + s->execution_stack_pos;
+ s->execution_stack_pos_changed = 1;
+
+ if (s->version>=SCI_VERSION_FTU_NEW_SCRIPT_HEADER)
+ restadjust = s->r_amp_rest;
+
+ }
+ break;
+
+ case 0x22: /* callb */
+ temp = ((opparams[1] >> 1) + restadjust + 1);
+ s_temp = xs->sp;
+ xs->sp -= temp;
+
+
+ xs->sp[0].offset += restadjust;
+ xs_new = execute_method(s, 0, opparams[0], s_temp, xs->objp,
+ xs->sp[0].offset, xs->sp);
+ restadjust = 0; /* Used up the &rest adjustment */
+ if (xs_new) /* in case of error, keep old stack */
+ s->execution_stack_pos_changed = 1;
+ break;
+
+ case 0x23: /* calle */
+ temp = ((opparams[2] >> 1) + restadjust + 1);
+ s_temp = xs->sp;
+ xs->sp -= temp;
+
+ xs->sp[0].offset += restadjust;
+ xs_new = execute_method(s, opparams[0], opparams[1], s_temp, xs->objp,
+ xs->sp[0].offset, xs->sp);
+ restadjust = 0; /* Used up the &rest adjustment */
+
+ if (xs_new) /* in case of error, keep old stack */
+ s->execution_stack_pos_changed = 1;
+ break;
+
+ case 0x24: /* ret */
+ do {
+ stack_ptr_t old_sp = xs->sp;
+ stack_ptr_t old_fp = xs->fp;
+ exec_stack_t *old_xs = s->execution_stack + s->execution_stack_pos;
+
+ 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 */
+
+ --(s->execution_stack_pos);
+
+ s->execution_stack_pos_changed = 1;
+ s->r_amp_rest = restadjust; /* Update &rest */
+ return; /* "Hard" return */
+ }
+
+ if (old_xs->type == EXEC_STACK_TYPE_VARSELECTOR) {
+ /* varselector access? */
+ if (old_xs->argc) /* write? */
+ *(old_xs->addr.varp) = old_xs->variables_argp[1];
+ else /* No, read */
+ s->r_acc = *(old_xs->addr.varp);
+ }
+
+ /* Not reached the base, so let's do a soft return */
+ --(s->execution_stack_pos);
+ xs = old_xs - 1;
+ s->execution_stack_pos_changed = 1;
+ xs = s->execution_stack + s->execution_stack_pos;
+
+ if (xs->sp == CALL_SP_CARRY /* Used in sends to 'carry' the stack pointer */
+ || xs->type != EXEC_STACK_TYPE_CALL) {
+ xs->sp = old_sp;
+ xs->fp = old_fp;
+ }
+
+ } while (xs->type == EXEC_STACK_TYPE_VARSELECTOR);
+ /* Iterate over all varselector accesses */
+ s->execution_stack_pos_changed = 1;
+ xs_new = xs;
+
+ break;
+
+ case 0x25: /* send */
+ s_temp = xs->sp;
+ xs->sp -= ((opparams[0] >> 1) + restadjust); /* Adjust stack */
+
+ xs->sp[1].offset += restadjust;
+ xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp,
+ (int)(opparams[0]>>1) + (word)restadjust,
+ xs->sp);
+
+ if (xs_new && xs_new != xs)
+ s->execution_stack_pos_changed = 1;
+
+ restadjust = 0;
+
+ break;
+
+ case 0x28: /* class */
+ s->r_acc = get_class_address(s, (unsigned) opparams[0], SCRIPT_GET_LOCK, xs->addr.pc);
+ break;
+
+ case 0x2a: /* self */
+ s_temp = xs->sp;
+ xs->sp -= ((opparams[0] >> 1) + restadjust); /* Adjust stack */
+
+ xs->sp[1].offset += restadjust;
+ xs_new = send_selector(s, xs->objp, xs->objp, s_temp,
+ (int)(opparams[0]>>1) + (word)restadjust,
+ xs->sp);
+
+ if (xs_new && xs_new != xs)
+ s->execution_stack_pos_changed = 1;
+
+ restadjust = 0;
+ break;
+
+ case 0x2b: /* super */
+ 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;
+ xs->sp -= ((opparams[1] >> 1) + restadjust); /* Adjust stack */
+
+ xs->sp[1].offset += restadjust;
+ xs_new = send_selector(s, r_temp, xs->objp, s_temp,
+ (int)(opparams[1]>>1) + (word)restadjust,
+ xs->sp);
+
+ if (xs_new && xs_new != xs)
+ s->execution_stack_pos_changed = 1;
+
+ restadjust = 0;
+ }
+
+ break;
+
+ case 0x2c: /* &rest */
+ temp = (guint16) opparams[0]; /* First argument */
+ restadjust = xs->argc - temp + 1; /* +1 because temp counts the paramcount while argc doesn't */
+ if (restadjust < 0)
+ restadjust = 0;
+
+ for (; temp <= xs->argc; temp++)
+ PUSH32(xs->variables_argp[temp]);
+
+ break;
+
+ case 0x2d: /* lea */
+ temp = (guint16) opparams[0] >> 1;
+ var_number = temp & 0x03; /* Get variable type */
+
+ /* Get variable block offset */
+ r_temp.segment = variables_seg[var_number];
+ r_temp.offset = variables[var_number] - variables_base[var_number];
+
+ if (temp & 0x08) /* Add accumulator offset if requested */
+ r_temp.offset += signed_validate_arithmetic(s->r_acc);
+
+ r_temp.offset += opparams[1]; /* Add index */
+ r_temp.offset *= sizeof(reg_t);
+ /* That's the immediate address now */
+ s->r_acc = r_temp;
+ break;
+
+
+ case 0x2e: /* selfID */
+ s->r_acc = xs->objp;
+ break;
+
+ case 0x30: /* pprev */
+ PUSH32(s->r_prev);
+ break;
+
+ case 0x31: /* pToa */
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ break;
+
+ case 0x32: /* aTop */
+ OBJ_PROPERTY(obj, (opparams[0] >> 1)) = s->r_acc;
+ break;
+
+ case 0x33: /* pTos */
+ PUSH32(OBJ_PROPERTY(obj, opparams[0] >> 1));
+ break;
+
+ case 0x34: /* sTop */
+ OBJ_PROPERTY(obj, (opparams[0] >> 1)) = POP32();
+ break;
+
+ case 0x35: /* ipToa */
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) =
+ ACC_ARITHMETIC_L( 1 + /*acc*/);
+ break;
+
+ case 0x36: /* dpToa */
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) =
+ ACC_ARITHMETIC_L(-1 + /*acc*/);
+ break;
+
+ case 0x37: /* ipTos */
+ ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
+ temp = ++OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
+ PUSH(temp);
+ break;
+
+ case 0x38: /* dpTos */
+ ASSERT_ARITHMETIC(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
+ temp = --OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
+ PUSH(temp);
+ break;
+
+
+ case 0x39: /* lofsa */
+ s->r_acc.segment = xs->addr.pc.segment;
+
+ if (s->version >= SCI_VERSION(1,001,000))
+ s->r_acc.offset = opparams[0]+local_script->script_size;
+ else
+ 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];
+#ifndef DISABLE_VALIDATIONS
+ if (s->r_acc.offset >= code_buf_size) {
+ sciprintf("VM: lofsa operation overflowed: "PREG" beyond end"
+ " of script (at %04x)\n", PRINT_REG(s->r_acc),
+ code_buf_size);
+ script_error_flag = script_debug_flag = 1;
+ }
+#endif
+ break;
+
+
+ case 0x3a: /* lofss */
+ r_temp.segment = xs->addr.pc.segment;
+
+ if (s->version >= SCI_VERSION_FTU_LOFS_ABSOLUTE)
+ r_temp.offset = opparams[0]; else
+ 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"
+ " of script (at %04x)\n", PRINT_REG(r_temp),
+ code_buf_size);
+ script_error_flag = script_debug_flag = 1;
+ }
+#endif
+ PUSH32(r_temp);
+ break;
+
+ case 0x3b: /* push0 */
+ PUSH(0);
+ break;
+
+ case 0x3c: /* push1 */
+ PUSH(1);
+ break;
+
+ case 0x3d: /* push2 */
+ PUSH(2);
+ break;
+
+ case 0x3e: /* pushSelf */
+ PUSH32(xs->objp);
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ WRITE_VAR(var_type, var_number, POP32());
+ break;
+
+ case 0x58: /* sagi */
+ case 0x59: /* sali */
+ case 0x5a: /* sati */
+ case 0x5b: /* sapi */
+ /* Special semantics because it wouldn't really make a whole lot
+ ** of sense otherwise, with acc being used for two things
+ ** simultaneously... */
+ var_type = (opcode >> 1) & 0x3; /* Gets the variable type: g, l, t or p */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ WRITE_VAR(var_type, var_number, s->r_acc = POP32());
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ WRITE_VAR(var_type, var_number, POP32());
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ s->r_acc = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ r_temp = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ s->r_acc = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ r_temp = make_reg(0,
+ 1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ s->r_acc = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number, s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0];
+ r_temp = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number, s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ s->r_acc = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ WRITE_VAR(var_type, var_number, s->r_acc);
+ break;
+
+ 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 */
+ var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
+ r_temp = make_reg(0,
+ -1 + validate_arithmetic(READ_VAR(var_type,
+ var_number,
+ s->r_acc)));
+ PUSH32(r_temp);
+ WRITE_VAR(var_type, var_number, r_temp);
+ break;
+
+ default:
+ script_error(s, __FILE__, __LINE__, "Illegal opcode");
+
+ } /* switch(opcode >> 1) */
+
+
+ if (s->execution_stack_pos_changed) /* Force initialization */
+ xs = xs_new;
+
+#ifndef DISABLE_VALIDATIONS
+ if (xs != s->execution_stack + s->execution_stack_pos) {
+ sciprintf("Error: xs is stale (%d vs %d); last command was %02x\n",
+ xs-s->execution_stack, s->execution_stack_pos, opnumber);
+ }
+#endif
+
+ if (script_error_flag) {
+ _debug_step_running = 0; /* Stop multiple execution */
+ _debug_seeking = 0; /* Stop special seeks */
+ xs->addr.pc.offset = old_pc_offset;
+ xs->sp = old_sp;
+ } else
+ ++script_step_counter;
+ }
+}
+
+
+
+static inline int
+_obj_locate_varselector(state_t *s, object_t *obj, selector_t slc)
+{ /* Determines if obj explicitly defines slc as a varselector */
+ /* Returns -1 if not found */
+
+ if (s->version < SCI_VERSION(1,001,000))
+ {
+ 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;
+
+ obj->base_vars = (guint16 *) buf;
+
+ for (i = 0; i < varnum; i++)
+ if (getUInt16(buf + (i << 1)) == slc) /* Found it? */
+ return i; /* report success */
+
+ return -1; /* Failed */
+ }
+ else
+ {
+ 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++)
+ if (getUInt16(buf + (i << 1)) == slc) /* Found it? */
+ return i; /* report success */
+
+ return -1; /* Failed */
+ }
+}
+
+
+static inline int
+_class_locate_funcselector(state_t *s, object_t *obj, selector_t slc)
+{ /* Determines if obj is a class and explicitly defines slc as a funcselector */
+ /* Does NOT say anything about obj's superclasses, i.e. failure may be
+ ** returned even if one of the superclasses defines the funcselector. */
+ int funcnum = obj->methods_nr;
+ int i;
+
+ for (i = 0; i < funcnum; i++)
+ if (VM_OBJECT_GET_FUNCSELECTOR(obj, i) == slc) /* Found it? */
+ return i; /* report success */
+
+ return -1; /* Failed */
+}
+
+
+static inline int
+_lookup_selector_function(state_t *s, int seg_id, object_t *obj, selector_t selector_id, reg_t *fptr)
+{
+ int index;
+
+ /* "recursive" lookup */
+
+ while (obj) {
+ index = _class_locate_funcselector(s, obj, selector_id);
+
+ if (index >= 0) {
+ if (fptr)
+ {
+ if (s->version < SCI_VERSION(1,001,000))
+ *fptr = make_reg(obj->pos.segment,
+ getUInt16((byte *)
+ (obj->base_method + index
+ + obj->methods_nr + 1)));
+ else
+ *fptr = make_reg(obj->pos.segment,
+ getUInt16((byte *)
+ (obj->base_method + index * 2 + 2)));
+ }
+
+ return SELECTOR_METHOD;
+ } else {
+ seg_id = obj->variables[SCRIPT_SUPERCLASS_SELECTOR].segment;
+ obj = obj_get(s, obj->variables[SCRIPT_SUPERCLASS_SELECTOR]);
+ }
+ }
+
+ return SELECTOR_NONE;
+}
+
+int
+lookup_selector(state_t *s, reg_t obj_location, selector_t selector_id, reg_t **vptr, reg_t *fptr)
+{
+ object_t *obj = obj_get(s, obj_location);
+ object_t *species;
+ int index;
+
+ /* Early SCI versions used the LSB in the selector ID as a read/write
+ ** toggle, meaning that we must remove it for selector lookup. */
+ 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));
+ return SELECTOR_NONE;
+ }
+
+ 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]));
+ return SELECTOR_NONE;
+ }
+
+ index = _obj_locate_varselector(s, obj, selector_id);
+
+ if (index >= 0) {
+ /* Found it as a variable */
+ if (vptr)
+ *vptr = obj->variables + index;
+ return SELECTOR_VARIABLE;
+ } return
+ _lookup_selector_function(s, obj_location.segment, obj, selector_id, fptr);
+
+}
+
+
+/* Detects SCI versions by their different script header */
+void script_detect_versions(state_t *s)
+{
+ int c;
+ resource_t *script = {0};
+
+
+ if (scir_find_resource(s->resmgr, sci_heap, 0, 0))
+ {
+ version_require_later_than(s, SCI_VERSION(1,001,000));
+ return;
+ }
+
+ for (c = 0; c < 1000; c++) {
+ if ((script = scir_find_resource(s->resmgr, sci_script, c, 0))) {
+
+ int id = getInt16(script->data);
+
+ if (id > 15) {
+ version_require_earlier_than(s, SCI_VERSION_FTU_NEW_SCRIPT_HEADER);
+ return;
+ }
+ }
+ }
+}
+
+
+seg_id_t
+script_get_segment(state_t *s, int script_nr, int load)
+{
+ seg_id_t segment;
+
+ if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD)
+ script_instantiate(s, script_nr);
+
+ segment = sm_seg_get(&s->seg_manager, script_nr);
+
+ if (segment > 0) {
+ if ((load & SCRIPT_GET_LOCK) == SCRIPT_GET_LOCK)
+ sm_increment_lockers(&s->seg_manager, segment, SEG_ID);
+
+ return segment;
+ } else
+ return 0;
+}
+
+reg_t
+script_lookup_export(state_t *s, int script_nr, int export_index)
+{
+ seg_id_t seg = script_get_segment(s, script_nr, SCRIPT_GET_DONT_LOAD);
+ mem_obj_t *memobj;
+ script_t *script = NULL;
+
+#ifndef DISABLE_VALIDATIONS
+ if (!seg) {
+ CORE_ERROR("EXPORTS", "Script invalid or not loaded");
+ sciprintf("Script was script.03d (0x%x)\n",
+ script_nr, script_nr);
+ return NULL_REG;
+ }
+#endif
+
+ memobj = GET_SEGMENT(s->seg_manager, seg, MEM_OBJ_SCRIPT);
+
+ if (memobj)
+ script = &(memobj->data.script);
+
+#ifndef DISABLE_VALIDATIONS
+ if (script
+ && export_index < script->exports_nr
+ && export_index >= 0)
+#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",
+ script_nr, export_index, script->exports_nr);
+ return NULL_REG;
+ }
+#endif
+}
+
+#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, reg))
+
+int sm_script_marked_deleted(seg_manager_t* self, int script_nr);
+int sm_initialise_script(mem_obj_t *mem, struct _state *s, int script_nr);
+
+int
+script_instantiate_common(state_t *s, int script_nr, resource_t **script, resource_t **heap, int *was_new)
+{
+ int seg;
+ int seg_id;
+ int marked_for_deletion;
+ mem_obj_t *mem;
+ reg_t reg;
+
+ *was_new = 1;
+
+ *script = scir_find_resource(s->resmgr, sci_script, script_nr, 0);
+ if (s->version >= SCI_VERSION(1,001,000))
+ *heap = scir_find_resource(s->resmgr, sci_heap, script_nr, 0);
+
+ if (!*script || (s->version >= SCI_VERSION(1,001,000) && !heap)) {
+ sciprintf("Script 0x%x requested but not found\n", script_nr);
+ /* script_debug_flag = script_error_flag = 1; */
+ if (s->version >= SCI_VERSION(1,001,000))
+ {
+ if (*heap)
+ sciprintf("Inconsistency: heap resource WAS found\n");
+ else if (*script)
+ sciprintf("Inconsistency: script resource WAS found\n");
+ }
+ return 0;
+ }
+
+ if (NULL == s) {
+ sciprintf("vm.c: script_instantiate(): NULL passed for \"s\"\n");
+ return 0;
+ }
+
+ seg = sm_seg_get ( &s->seg_manager, script_nr );
+ if (sm_script_is_loaded (&s->seg_manager, script_nr, SCRIPT_ID)) {
+ marked_for_deletion = sm_script_marked_deleted(&s->seg_manager, script_nr);
+
+ if (!marked_for_deletion) {
+ sm_increment_lockers( &s->seg_manager, seg, SEG_ID );
+ return seg;
+ }
+ else
+ {
+ seg_id = seg;
+ mem = s->seg_manager.heap[seg];
+ sm_free_script(mem);
+ }
+ }
+ else if (!(mem = sm_allocate_script( &s->seg_manager, s, script_nr, &seg_id ))) { /* ALL YOUR SCRIPT BASE ARE BELONG TO US */
+ sciprintf("Not enough heap space for script size 0x%x of script 0x%x,"
+ " should this happen?`\n",
+ (*script)->size, script_nr);
+ script_debug_flag = script_error_flag = 1;
+ return 0;
+ }
+
+ sm_initialise_script(mem, s, script_nr);
+
+ reg.segment = seg_id;
+ reg.offset = 0;
+
+ /* Set heap position (beyond the size word) */
+ sm_set_lockers( &s->seg_manager, 1, reg.segment, SEG_ID );
+ sm_set_export_table_offset( &s->seg_manager, 0, reg.segment, SEG_ID );
+ sm_set_synonyms_offset( &s->seg_manager, 0, reg.segment, SEG_ID );
+ sm_set_synonyms_nr( &s->seg_manager, 0, reg.segment, SEG_ID );
+
+ *was_new = 0;
+
+ return seg_id;
+}
+
+int
+script_instantiate_sci0(state_t *s, int script_nr)
+{
+ int objtype;
+ unsigned int objlength;
+ reg_t reg, reg_tmp;
+ int seg_id;
+ int relocation = -1;
+ int magic_pos_adder; /* Usually 0; 2 for older SCI versions */
+ resource_t *script;
+ int was_new;
+
+ seg_id = script_instantiate_common(s, script_nr, &script, NULL, &was_new);
+
+ if (was_new) return seg_id;
+
+ reg.segment = seg_id;
+ reg.offset = 0;
+
+ if (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER) {
+ /*
+ int locals_size = getUInt16(script->data)*2;
+ int locals = (locals_size)? script->size : 0;
+ */
+ int locals_nr = getUInt16(script->data);
+
+ /* Old script block */
+ /* There won't be a localvar block in this case */
+ /* Instead, the script starts with a 16 bit int specifying the
+ ** number of locals we need; these are then allocated and zeroed. */
+
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, reg.segment, SEG_ID);
+ magic_pos_adder = 2; /* Step over the funny prefix */
+
+ if (locals_nr)
+ sm_script_initialise_locals_zero( &s->seg_manager,
+ reg.segment, locals_nr);
+
+ } else {
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, reg.segment, SEG_ID);
+ magic_pos_adder = 0;
+ }
+
+ /* Now do a first pass through the script objects to find the
+ ** export table and local variable block
+ */
+
+ objlength = 0;
+ reg_tmp = reg;
+ reg.offset = magic_pos_adder;
+
+ do {
+ reg_t data_base;
+ reg_t addr;
+ reg.offset += objlength; /* Step over the last checked object */
+ objtype = SEG_GET_HEAP(s, reg);
+ if( !objtype ) break;
+
+ objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2));
+
+ data_base = reg;
+ data_base.offset += 4;
+
+ addr = data_base;
+
+ switch( objtype ) {
+ case sci_obj_exports: {
+ sm_set_export_table_offset( &s->seg_manager, data_base.offset,
+ reg.segment, SEG_ID );
+ }
+ break;
+
+ case sci_obj_synonyms:
+ sm_set_synonyms_offset( &s->seg_manager, addr.offset, reg.segment, SEG_ID ); /* +4 is to step over the header */
+ sm_set_synonyms_nr( &s->seg_manager, (objlength) / 4, reg.segment, SEG_ID );
+ break;
+
+ case sci_obj_localvars:
+ sm_script_initialise_locals( &s->seg_manager, data_base);
+ 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 "
+ "[0,%d) while instantiating script %d\n",
+ species, species, s->classtable_size,
+ script_nr);
+ 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;
+ /* Set technical class position-- into the block allocated for it */
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ } while (objtype != 0);
+ /* And now a second pass to adjust objects and class pointers, and the general pointers */
+
+ objlength = 0;
+ reg.offset = magic_pos_adder; /* Reset counter */
+
+ do {
+ reg_t addr;
+ reg.offset += objlength; /* Step over the last checked object */
+ objtype = SEG_GET_HEAP(s, reg);
+ if( !objtype ) break;
+ objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2));
+ reg.offset += 4; /* Step over header */
+
+ addr = reg;
+
+ switch (objtype) {
+ case sci_obj_code:
+ sm_script_add_code_block(&s->seg_manager, addr);
+ break;
+ case sci_obj_object:
+ case sci_obj_class:
+ { /* object or class? */
+ object_t *obj = sm_script_obj_init(&s->seg_manager, s, addr);
+ object_t *base_obj;
+
+ /* Instantiate the superclass, if neccessary */
+ obj->variables[SCRIPT_SPECIES_SELECTOR] =
+ INST_LOOKUP_CLASS(obj->variables[SCRIPT_SPECIES_SELECTOR].offset);
+
+ 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;
+ /* Copy base from species class, as we need its selector IDs */
+
+ obj->variables[SCRIPT_SUPERCLASS_SELECTOR] =
+ INST_LOOKUP_CLASS(obj->variables[SCRIPT_SUPERCLASS_SELECTOR].offset);
+
+ } /* if object or class */
+ break;
+ case sci_obj_pointers: /* A relocation table */
+ relocation = addr.offset;
+ break;
+
+ default:
+ break;
+ }
+
+ reg.offset -= 4; /* Step back on header */
+
+ } while ((objtype != 0) && (((unsigned)reg.offset) < script->size - 2));
+
+ if (relocation >= 0)
+ sm_script_relocate(&s->seg_manager, make_reg(reg.segment, relocation));
+
+ sm_script_free_unused_objects(&s->seg_manager, reg.segment);
+
+ return reg.segment; /* instantiation successful */
+}
+
+void
+sm_script_relocate_exports_sci11(seg_manager_t *self, int seg);
+void
+sm_script_initialise_objects_sci11(seg_manager_t *self, state_t *s, int seg);
+void
+sm_heap_relocate(seg_manager_t *self, state_t *s, reg_t block);
+
+int
+script_instantiate_sci11(state_t *s, int script_nr)
+{
+ 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);
+
+ if (was_new) return seg_id;
+
+ heap_start = script->size;
+ if (script->size & 2)
+ heap_start ++;
+
+ sm_mcpy_in_out( &s->seg_manager, 0, script->data, script->size, seg_id, SEG_ID);
+ sm_mcpy_in_out( &s->seg_manager, heap_start, heap->data, heap->size, seg_id, SEG_ID);
+
+ if (getUInt16(script->data+6) > 0)
+ sm_set_export_table_offset(&s->seg_manager, 6,
+ seg_id, SEG_ID);
+
+ reg.segment = seg_id;
+ reg.offset = heap_start+4;
+ sm_script_initialise_locals(&s->seg_manager, reg);
+
+ sm_script_relocate_exports_sci11(&s->seg_manager, seg_id);
+ sm_script_initialise_objects_sci11(&s->seg_manager, s, seg_id);
+
+ reg.offset = getUInt16(heap->data);
+ sm_heap_relocate(&s->seg_manager, s, reg);
+
+ return seg_id;
+}
+
+int
+script_instantiate(state_t *s, int script_nr)
+{
+ if (s->version >= SCI_VERSION(1,001,000))
+ return script_instantiate_sci11(s, script_nr);
+ else
+ return script_instantiate_sci0(s, script_nr);
+}
+
+void sm_mark_script_deleted(seg_manager_t* self, int script_nr);
+
+void
+script_uninstantiate_sci0(state_t *s, int script_nr, seg_id_t seg)
+{
+ reg_t reg = make_reg( seg, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 2 : 0 );
+ int objtype, objlength;
+
+ /* Make a pass over the object in order uninstantiate all superclasses */
+ objlength = 0;
+
+ do {
+ reg.offset += objlength; /* Step over the last checked object */
+
+ objtype = SEG_GET_HEAP(s, reg);
+ if( !objtype ) break;
+ objlength = SEG_GET_HEAP(s, make_reg(reg.segment, reg.offset + 2)); /* use SEG_UGET_HEAP ?? */
+
+ reg.offset += 4; /* Step over header */
+
+ if ((objtype == sci_obj_object) || (objtype == sci_obj_class)) { /* object or class? */
+ int superclass;
+
+ reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET;
+
+ superclass = OBJ_SUPERCLASS(s, reg); /* Get superclass... */
+
+ if (superclass >= 0) {
+ int superclass_script = s->classtable[superclass].script;
+
+ if (superclass_script == script_nr) {
+ if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) )
+ sm_decrement_lockers( &s->seg_manager, reg.segment, SEG_ID); /* Decrease lockers if this is us ourselves */
+ } else
+ script_uninstantiate(s, superclass_script);
+ /* Recurse to assure that the superclass lockers number gets decreased */
+ }
+
+ reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
+ } /* if object or class */
+
+ reg.offset -= 4; /* Step back on header */
+
+ } while (objtype != 0);
+}
+
+void
+script_uninstantiate(state_t *s, int script_nr)
+{
+ reg_t reg = make_reg( 0, (s->version < SCI_VERSION_FTU_NEW_SCRIPT_HEADER)? 2 : 0 );
+ int i;
+
+ reg.segment = sm_seg_get( &s->seg_manager, script_nr);
+
+ if (!sm_script_is_loaded (&s->seg_manager, script_nr, SCRIPT_ID) || reg.segment <= 0 ) { /* Is it already loaded? */
+ /* sciprintf("Warning: unloading script 0x%x requested although not loaded\n", script_nr); */
+ /* This is perfectly valid SCI behaviour */
+ return;
+ }
+
+ sm_decrement_lockers( &s->seg_manager, reg.segment, SEG_ID); /* One less locker */
+
+ if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) > 0 )
+ return;
+
+ /* Free all classtable references to this script */
+ for (i = 0; i < s->classtable_size; i++)
+ if (s->classtable[i].reg.segment == reg.segment)
+ s->classtable[i].reg = NULL_REG;
+
+ if (s->version < SCI_VERSION(1,001,000))
+ script_uninstantiate_sci0(s, script_nr, reg.segment);
+ else
+ sciprintf("FIXME: Add proper script uninstantiation for SCI 1.1\n");
+
+ if( sm_get_lockers( &s->seg_manager, reg.segment, SEG_ID) )
+ return; /* if xxx.lockers > 0 */
+
+ /* Otherwise unload it completely */
+ /* Explanation: I'm starting to believe that this work is done by SCI itself. */
+ sm_mark_script_deleted( &s->seg_manager, script_nr );
+
+ if (script_checkloads_flag)
+ sciprintf("Unloaded script 0x%x.\n", script_nr);
+
+ return;
+}
+
+
+static void
+_init_stack_base_with_selector(state_t *s, selector_t selector)
+{
+ s->stack_base[0] = make_reg(0, (word) selector);
+ s->stack_base[1] = NULL_REG;
+}
+
+static state_t *
+_game_run(state_t *s, int restoring)
+{
+ state_t *successor = NULL;
+ int game_is_finished = 0;
+ do {
+ s->execution_stack_pos_changed = 0;
+ run_vm(s, (successor || restoring)? 1 : 0);
+ if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { /* Restart was requested? */
+
+ sci_free(s->execution_stack);
+ 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);
+ /* Call the play selector */
+
+ send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2, s->stack_base);
+
+ script_abort_flag = 0;
+ s->restarting_flags = SCI_GAME_WAS_RESTARTED |
+ SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE;
+
+ }
+ else {
+ successor = s->successor;
+ if (successor) {
+ game_exit(s);
+ script_free_vm_memory(s);
+ sci_free(s);
+ s = successor;
+
+ if (!send_calls_allocated)
+ send_calls = (calls_struct_t*)sci_calloc(sizeof(calls_struct_t),
+ send_calls_allocated = 16);
+
+ if (script_abort_flag == SCRIPT_ABORT_WITH_REPLAY) {
+ sciprintf("Restarting with replay()\n");
+ s->execution_stack_pos = -1; /* Resatart with replay */
+
+ _init_stack_base_with_selector(s, s->selector_map.replay);
+ /* Call the replay selector */
+
+ send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2,
+ s->stack_base);
+ }
+
+ script_abort_flag = 0;
+
+ } else
+ game_is_finished = 1;
+ }
+ } while (!game_is_finished);
+
+ return s;
+}
+
+int objinfo(state_t *s, reg_t pos);
+
+int
+game_run(state_t **_s)
+{
+ state_t *s = *_s;
+
+ sciprintf(" Calling %s::play()\n", s->game_name);
+ _init_stack_base_with_selector(s, s->selector_map.play); /* Call the play selector */
+
+
+ /* 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) {
+ objinfo(s, s->game_obj);
+ sciprintf("Failed to run the game! Aborting...\n");
+ return 1;
+ }
+ /* and ENGAGE! */
+ *_s = s = _game_run(s, 0);
+
+ sciprintf(" Game::play() finished.\n");
+ return 0;
+}
+
+int
+game_restore(state_t **_s, char *game_name)
+{
+ state_t *s;
+ 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;
+
+ s->execution_stack_pos = -1; /* Resatart with replay */
+
+ _init_stack_base_with_selector(s, s->selector_map.replay);
+ /* Call the replay selector */
+
+ send_selector(s, s->game_obj, s->game_obj,
+ s->stack_base, 2,
+ s->stack_base);
+
+ *_s = s = _game_run(s, 1);
+
+ sciprintf(" Game::play() finished.\n");
+ return 0;
+}
+
+
+object_t *
+obj_get(state_t *s, reg_t offset)
+{
+ mem_obj_t *memobj = GET_OBJECT_SEGMENT(s->seg_manager, offset.segment);
+ object_t *obj = NULL;
+ int idx;
+
+ if (memobj != NULL) {
+ if (memobj->type == MEM_OBJ_CLONES
+ && ENTRY_IS_VALID(&memobj->data.clones, offset.offset))
+ obj = &(memobj->data.clones.table[offset.offset].entry);
+ else if (memobj->type == MEM_OBJ_SCRIPT) {
+ if (offset.offset <= memobj->data.script.buf_size
+ && offset.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
+ && RAW_IS_OBJECT(memobj->data.script.buf + offset.offset)) {
+ 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;
+}
+
+const char *
+obj_get_name(struct _state *s, reg_t pos)
+{
+ object_t *obj = obj_get(s, pos);
+
+ if (!obj)
+ return "<no such object>";
+
+ return
+ (const char*)(obj->base + obj->variables[SCRIPT_NAME_SELECTOR].offset);
+}
+
+
+void
+quit_vm()
+{
+ script_abort_flag = 1; /* Terminate VM */
+ _debugstate_valid = 0;
+ _debug_seeking = 0;
+ _debug_step_running = 0;
+}
+
+