Files
nunuhara_xsystem4/vm.c
T
Nunuhara Cabbage 49586506d0 Fix memory managment of objects on stack
A few things.

The S_REF instruction creates a _copy_ of the referenced string. Proof:

    FUNC foo
    // string local_string = "foo"
    SH_LOCALREF local_string
    S_PUSH "foo"
    S_ASSIGN
    S_POP

    // prepare argument for bar()
    PUSHLOCALPAGE
    PUSH local_string
    S_REF

    // stealthily alter local_string
    SH_LOCALREF local_string
    S_PUSH "bar"
    S_ASSIGN
    S_POP

    // bar(local_string)
    CALLFUNC bar // local_string is still "foo" in the body of bar()!

Thus, it is not CALLFUNC's responsibility to copy by-value string
arguments. CALLFUNC need only copy the index from the stack into the
local page.

Objects pushed to the stack with SH_LOCALREF, SH_GLOBALREF and REF do
not gain a reference. They are already referenced by the page they
belong to.

I believe the implementation of S_REF is still not quite right. What
I've noticed is that System40.exe is able to distinguish between strings
pushed with S_REF/S_PUSH and strings pushed with
SH_LOCALREF/SH_GLOBALREF/REF. It seems the former are r-values and the
latter are l-values (i.e. the latter can be assigned to, but not the
former), and the VM will throw an error if you try to assign to an
r-value.
The underlying implementation is not clear to me. Both types appear to
be indices into the heap, which suggests that the distinguishing factor
must be stored either in the object itself, or as part of the pointer to
an object. Perhaps its as simple as a flag marking an object as an
r-value?
2019-10-19 21:36:52 -07:00

909 lines
20 KiB
C

/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include "system4.h"
#include "vm_string.h"
#include "ain.h"
#include "instructions.h"
#include "little_endian.h"
#include "utfsjis.h"
#define INITIAL_STACK_SIZE 1024
#define INITIAL_HEAP_SIZE 4096
#define INITIAL_PAGES_SIZE 4096
#define INSTR (instructions[get_opcode(instr_ptr)])
#define EXECUTION_ERROR(msg, ...) \
ERROR("%s (0x%X): " msg, INSTR.name, instr_ptr, ##__VA_ARGS__)
/*
* NOTE: The current implementation is a simple bytecode interpreter.
* System40.exe uses a JIT compiler, and we should too.
*/
// Non-heap values. Stored in pages and on the stack.
union vm_value {
int32_t i;
int64_t i64;
float f;
};
enum vm_pointer_type {
VM_FRAME,
VM_PAGE,
VM_STRING
};
// Heap-backed objects. Reference counted.
struct vm_pointer {
int ref;
enum vm_pointer_type type;
union {
struct string *s;
union vm_value *page;
};
};
struct function_call {
int32_t fno;
int32_t return_address;
int32_t page_slot;
int32_t page_ptr;
};
// The stack
static union vm_value *stack = NULL; // the stack
static int32_t stack_ptr = 0; // pointer to the top of the stack
static size_t stack_size; // current size of the stack
// The heap
// An array of pointers to heap-allocated objects, plus reference counts.
static struct vm_pointer *heap;
static size_t heap_size;
// Heap free list
// This is a list of unused indices into the 'heap' array.
static int32_t *heap_free_stack;
static int32_t heap_free_ptr = 0;
// Memory for global page + local pages
static union vm_value *page_stack;
static int32_t page_ptr = 0; // points to start of current local page
static int32_t pages_size;
// Stack of function call frames
static struct function_call call_stack[4096];
static int32_t call_stack_ptr = 0;
static struct ain *ain;
static size_t instr_ptr = 0;
// Read the opcode at ADDR.
static int16_t get_opcode(size_t addr)
{
return LittleEndian_getW(ain->code, addr);
}
// Read argument N for the current instruction.
static int32_t get_argument(int n)
{
return LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4);
}
// XXX: not strictly portable
static float get_argument_float(int n)
{
union vm_value v;
v.i = LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4);
return v.f;
}
static int32_t heap_alloc_slot(enum vm_pointer_type type)
{
int32_t slot = heap_free_stack[heap_free_ptr++];
heap[slot].ref = 1;
heap[slot].type = type;
return slot;
}
static void heap_free_slot(int32_t slot)
{
heap_free_stack[--heap_free_ptr] = slot;
}
//static void heap_ref(int32_t slot)
//{
// heap[slot].ref++;
//}
static void heap_unref(int32_t slot)
{
if (heap[slot].ref <= 0) {
EXECUTION_ERROR("double free (slot %d)", slot);
}
if (--heap[slot].ref <= 0) {
switch (heap[slot].type) {
case VM_FRAME:
break;
case VM_PAGE:
free(heap[slot].page);
break;
case VM_STRING:
free_string(heap[slot].s);
break;
}
heap_free_slot(slot);
}
}
static union vm_value _vm_id(union vm_value v)
{
return v;
}
static union vm_value vm_int(int32_t v)
{
return (union vm_value) { .i = v };
}
static union vm_value vm_long(int64_t v)
{
return (union vm_value) { .i64 = v };
}
static union vm_value vm_float(float v)
{
return (union vm_value) { .f = v };
}
#define vm_value_cast(v) _Generic((v), \
union vm_value: _vm_id, \
int32_t: vm_int, \
int64_t: vm_long, \
float: vm_float)(v)
static int32_t local_get(int varno)
{
return page_stack[page_ptr + varno].i;
}
static void local_set(int varno, int32_t value)
{
page_stack[page_ptr + varno].i = value;
}
static int32_t global_get(int varno)
{
return heap[0].page[varno].i;
}
static union vm_value stack_peek(int n)
{
return stack[stack_ptr - (1 + n)];
}
// Set the Nth value from the top of the stack to V.
#define stack_set(n, v) (stack[stack_ptr - (1 + (n))] = vm_value_cast(v))
#define stack_push(v) (stack[stack_ptr++] = vm_value_cast(v))
static union vm_value stack_pop(void)
{
stack_ptr--;
return stack[stack_ptr];
}
// Pop a reference off the stack, returning the address of the referenced object.
static union vm_value *stack_pop_var(void)
{
int32_t page_index = stack_pop().i;
int32_t heap_index = stack_pop().i;
return &heap[heap_index].page[page_index];
}
static void stack_push_string(struct string *s)
{
int32_t heap_slot = heap_alloc_slot(VM_STRING);
heap[heap_slot].s = s;
stack[stack_ptr++].i = heap_slot;
}
static struct string *stack_peek_string(int n)
{
return heap[stack_peek(n).i].s;
}
static struct string *stack_pop_string(void)
{
return heap[stack[--stack_ptr].i].s;
}
static int32_t alloc_page(int nr_vars)
{
union vm_value *page = xmalloc(sizeof(union vm_value) * nr_vars);
int32_t slot = heap_alloc_slot(VM_PAGE);
heap[slot].page = page;
return slot;
}
static void delete_page(union vm_value *page, struct ain_variable *vars, int nr_vars)
{
for (int i = 0; i < nr_vars; i++) {
struct ain_struct *s;
switch (vars[i].data_type) {
case AIN_STRING:
heap_unref(page[i].i);
break;
case AIN_STRUCT:
s = &ain->structures[vars[i].struct_type];
delete_page(heap[page[i].i].page, s->members, s->nr_members);
heap_unref(page[i].i);
break;
default:
break;
}
}
}
static struct string EMPTY_STRING = {
.literal = true,
.size = 0,
.text = ""
};
static int create_struct(int no)
{
struct ain_struct *s = &ain->structures[no];
int slot = alloc_page(s->nr_members);
for (int i = 0; i < s->nr_members; i++) {
int memb;
switch (s->members[i].data_type) {
case AIN_STRING:
memb = heap_alloc_slot(VM_STRING);
heap[memb].s = &EMPTY_STRING;
heap[slot].page[i].i = memb;
break;
case AIN_STRUCT:
memb = create_struct(s->members[i].struct_type);
heap[slot].page[i].i = memb;
break;
default:
heap[slot].page[i].i = 0;
break;
}
}
// TODO: call constructor
return slot;
}
/*
* System 4 calling convention:
* - caller pushes arguments, in order
* - CALLFUNC creates stack frame, pops arguments into local page
* - callee pushes return value on the stack
* - RETURN jumps to return address (saved in stack frame)
*/
static void function_call(int32_t no)
{
struct ain_function *f = &ain->functions[no];
int32_t cur_fno = call_stack[call_stack_ptr-1].fno;
int32_t page_slot = heap_alloc_slot(VM_FRAME);
int32_t new_pp = page_ptr + ain->functions[cur_fno].nr_vars;
// create new stack frame
call_stack[call_stack_ptr++] = (struct function_call) {
.fno = no,
.return_address = instr_ptr + instruction_width(CALLFUNC),
.page_slot = page_slot,
.page_ptr = page_ptr
};
// create local page
heap[page_slot].page = page_stack + new_pp;
// pop arguments, store in local page
for (int i = f->nr_args - 1; i >= 0; i--) {
page_stack[new_pp + i] = stack_pop();
}
// initialize local variables
for (int i = f->nr_args; i < f->nr_vars; i++) {
int slot;
switch (f->vars[i].data_type) {
case AIN_STRING:
slot = heap_alloc_slot(VM_STRING);
heap[slot].s = NULL;
page_stack[new_pp + i].i = slot;
break;
case AIN_STRUCT:
page_stack[new_pp + i].i = -1;
break;
default:
page_stack[new_pp + i].i = 0;
break;
}
}
// update stack/instruction pointers
page_ptr = new_pp;
instr_ptr = ain->functions[no].address;
}
static void function_return(void)
{
call_stack_ptr--;
// unref slots for heap-backed variables
struct ain_function *f = &ain->functions[call_stack[call_stack_ptr].fno];
delete_page(page_stack + page_ptr, f->vars, f->nr_vars);
heap_free_slot(call_stack[call_stack_ptr].page_slot);
instr_ptr = call_stack[call_stack_ptr].return_address;
page_ptr = call_stack[call_stack_ptr].page_ptr;
}
static void system_call(int32_t code)
{
char *utf;
struct string *str;
switch (code) {
case 0x0: // system.Exit(int nResult)
sys_exit(stack_pop().i);
break;
case 0x6: // system.Output(string szText)
str = stack_peek_string(0);
utf = sjis2utf(str->text, str->size);
sys_message("%s", utf);
free(utf);
// XXX: caller S_POPs
break;
case 0x14: // system.Peek()
break;
case 0x15: // system.Sleep(int nSleep)
stack_pop();
break;
default:
WARNING("Unimplemented syscall: 0x%X", code);
}
}
static void execute_instruction(int16_t opcode)
{
int32_t index, a, b, c, v;
float f;
struct string *s;
union vm_value val;
union vm_value *ref;
const char *opcode_name = "UNKNOWN";
switch (opcode) {
//
// --- Stack Management ---
//
case PUSH:
stack_push(get_argument(0));
break;
case POP:
stack_pop();
break;
case F_PUSH:
stack_push(get_argument_float(0));
break;
case S_PUSH:
stack_push_string(ain->strings[get_argument(0)]);
break;
case S_POP:
index = stack_pop().i;
heap_unref(index);
break;
case REF:
// Dereference a reference to a value.
index = stack_pop_var()[0].i;
stack_push(index);
break;
case REFREF:
//case S_REFREF: // ???
// Dereference a reference to a reference.
ref = stack_pop_var();
stack_push(ref[0].i);
stack_push(ref[1].i);
break;
case S_REF:
// Dereference a reference to a string
index = stack_pop_var()->i;
stack_push_string(string_dup(heap[index].s));
break;
case DUP:
// A -> AA
stack_push(stack_peek(0).i);
break;
case DUP2:
// AB -> ABAB
a = stack_peek(1).i;
b = stack_peek(0).i;
stack_push(a);
stack_push(b);
break;
case DUP_X2:
// ABC -> CABC
a = stack_peek(2).i;
b = stack_peek(1).i;
c = stack_peek(0).i;
stack_set(2, c);
stack_set(1, a);
stack_set(0, b);
stack_push(c);
break;
case DUP2_X1:
// ABC -> BCABC
a = stack_peek(2).i;
b = stack_peek(1).i;
c = stack_peek(0).i;
stack_set(2, b);
stack_set(1, c);
stack_set(0, a);
stack_push(b);
stack_push(c);
break;
case PUSHGLOBALPAGE:
stack_push(0);
break;
case PUSHLOCALPAGE:
stack_push(call_stack[call_stack_ptr-1].page_slot);
break;
case ASSIGN:
case F_ASSIGN:
val = stack_pop();
stack_pop_var()[0] = val;
stack_push(val);
break;
case SH_GLOBALREF: // VARNO
stack_push(global_get(get_argument(0)));
break;
case SH_LOCALREF: // VARNO
stack_push(local_get(get_argument(0)));
break;
case SH_LOCALASSIGN: // VARNO, VALUE
local_set(get_argument(0), get_argument(1));
break;
case SH_LOCALINC: // VARNO
index = get_argument(0);
local_set(index, local_get(index)+1);
break;
case SH_LOCALDEC: // VARNO
index = get_argument(0);
local_set(index, local_get(index)-1);
break;
case SH_LOCALDELETE:
if ((index = local_get(get_argument(0))) != -1) {
heap_unref(index);
local_set(get_argument(0), -1);
}
break;
case SH_LOCALCREATE: // VARNO, STRUCTNO
index = create_struct(get_argument(1));
local_set(get_argument(0), index);
break;
//
// --- Function Calls ---
//
case CALLFUNC:
function_call(get_argument(0));
break;
case RETURN:
function_return();
break;
case CALLSYS:
system_call(get_argument(0));
break;
//
// --- Control Flow ---
//
case JUMP: // ADDR
instr_ptr = get_argument(0);
break;
case IFZ: // ADDR
if (!stack_pop().i)
instr_ptr = get_argument(0);
else
instr_ptr += instruction_width(IFZ);
break;
case IFNZ: // ADDR
if (stack_pop().i)
instr_ptr = get_argument(0);
else
instr_ptr += instruction_width(IFNZ);
break;
//
// --- Arithmetic ---
//
case INV:
stack[stack_ptr-1].i = -stack[stack_ptr-1].i;
break;
case NOT:
stack[stack_ptr-1].i = !stack[stack_ptr-1].i;
break;
case COMPL:
stack[stack_ptr-1].i = ~stack[stack_ptr-1].i;
break;
case ADD:
stack[stack_ptr-2].i += stack[stack_ptr-1].i;
stack_ptr--;
break;
case SUB:
stack[stack_ptr-2].i -= stack[stack_ptr-1].i;
stack_ptr--;
break;
case MUL:
stack[stack_ptr-2].i *= stack[stack_ptr-1].i;
stack_ptr--;
break;
case DIV:
stack[stack_ptr-2].i /= stack[stack_ptr-1].i;
stack_ptr--;
break;
case MOD:
stack[stack_ptr-2].i %= stack[stack_ptr-1].i;
stack_ptr--;
break;
case AND:
stack[stack_ptr-2].i &= stack[stack_ptr-1].i;
stack_ptr--;
break;
case OR:
stack[stack_ptr-2].i |= stack[stack_ptr-1].i;
stack_ptr--;
break;
case XOR:
stack[stack_ptr-2].i ^= stack[stack_ptr-1].i;
stack_ptr--;
break;
case LSHIFT:
stack[stack_ptr-2].i <<= stack[stack_ptr-1].i;
stack_ptr--;
break;
case RSHIFT:
stack[stack_ptr-2].i >>= stack[stack_ptr-1].i;
stack_ptr--;
break;
// Numeric Comparisons
case LT:
b = stack_pop().i;
a = stack_pop().i;
stack_push(a < b ? 1 : 0);
break;
case GT:
b = stack_pop().i;
a = stack_pop().i;
stack_push(a > b ? 1 : 0);
break;
case LTE:
b = stack_pop().i;
a = stack_pop().i;
stack_push(a <= b ? 1 : 0);
break;
case GTE:
b = stack_pop().i;
a = stack_pop().i;
stack_push(a >= b ? 1 : 0);
break;
case NOTE:
b = stack_pop().i;
a = stack_pop().i;
stack_push(a != b ? 1 : 0);
break;
case EQUALE:
b = stack_pop().i;
a = stack_pop().i;
stack_push(a == b ? 1 : 0);
break;
// +=, -=, etc.
case PLUSA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i += v);
break;
case MINUSA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i -= v);
break;
case MULA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i *= v);
break;
case DIVA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i /= v);
break;
case MODA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i %= v);
break;
case ANDA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i &= v);
break;
case ORA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i |= v);
break;
case XORA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i ^= v);
break;
case LSHIFTA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i <<= v);
break;
case RSHIFTA:
v = stack_pop().i;
stack_push(stack_pop_var()[0].i >>= v);
break;
case INC:
stack_pop_var()[0].i++;
break;
case DEC:
stack_pop_var()[0].i--;
break;
case ITOB:
stack_set(0, !!stack_peek(0).i);
break;
//
// --- Floating Point Arithmetic ---
//
case FTOI:
stack_set(0, (int32_t)stack_peek(0).f);
break;
case ITOF:
stack_set(0, (float)stack_peek(0).i);
break;
case F_INV:
stack_set(0, -stack_peek(0).f);
break;
case F_ADD:
f = stack_pop().f;
stack_set(0, stack_peek(0).f + f);
break;
case F_SUB:
f = stack_pop().f;
stack_set(0, stack_peek(0).f - f);
break;
case F_MUL:
f = stack_pop().f;
stack_set(0, stack_peek(0).f * f);
break;
case F_DIV:
f = stack_pop().f;
stack_set(0, stack_peek(0).f / f);
break;
// floating point comparison
case F_LT:
f = stack_pop().f;
stack_set(0, stack_peek(0).f < f ? 1 : 0);
break;
case F_GT:
f = stack_pop().f;
stack_set(0, stack_peek(0).f > f ? 1 : 0);
break;
case F_LTE:
f = stack_pop().f;
stack_set(0, stack_peek(0).f <= f ? 1 : 0);
break;
case F_GTE:
f = stack_pop().f;
stack_set(0, stack_peek(0).f >= f ? 1 : 0);
break;
case F_NOTE:
f = stack_pop().f;
stack_set(0, stack_peek(0).f != f ? 1 : 0);
break;
case F_EQUALE:
f = stack_pop().f;
stack_set(0, stack_peek(0).f == f ? 1 : 0);
break;
//
// --- Strings ---
//
case S_ASSIGN: // A = B
b = stack_peek(0).i;
a = stack_peek(1).i;
if (heap[a].s) {
free_string(heap[a].s);
}
heap[a].s = string_dup(heap[b].s);
// remove A from the stack, but leave B
stack_set(1, b);
stack_pop();
break;
case S_PLUSA2:
b = stack_peek(0).i;
a = stack_peek(1).i;
string_append(&heap[a].s, heap[b].s);
heap_unref(b);
stack_pop();
stack_pop();
stack_push_string(string_dup(heap[a].s));
break;
case S_ADD:
b = stack_pop().i;
a = stack_pop().i;
stack_push_string(string_concatenate(heap[a].s, heap[b].s));
heap_unref(a);
heap_unref(b);
break;
case S_LT:
v = strcmp(stack_peek_string(1)->text, stack_peek_string(0)->text) < 0;
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_GT:
v = strcmp(stack_peek_string(1)->text, stack_peek_string(0)->text) > 0;
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_LTE:
v = strcmp(stack_peek_string(1)->text, stack_peek_string(0)->text) <= 0;
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_GTE:
v = strcmp(stack_peek_string(1)->text, stack_peek_string(0)->text) >= 0;
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_NOTE:
v = !!strcmp(stack_peek_string(1)->text, stack_peek_string(0)->text);
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_EQUALE:
v = !strcmp(stack_peek_string(1)->text, stack_peek_string(0)->text);
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_LENGTH:
a = stack_pop_var()->i;
stack_push(sjis_count_char(heap[a].s->text));
break;
case S_LENGTHBYTE:
a = stack_pop_var()->i;
stack_push(heap[a].s->size);
break;
case S_EMPTY:
v = !stack_peek_string(0)->size;
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_FIND:
v = string_find(stack_peek_string(1), stack_peek_string(0));
heap_unref(stack_pop().i);
heap_unref(stack_pop().i);
stack_push(v);
break;
case S_GETPART:
b = stack_pop().i; // length
a = stack_pop().i; // index
s = string_copy(stack_peek_string(0), a, b);
heap_unref(stack_pop().i);
stack_push_string(s);
break;
//case S_PUSHBACK: // ???
case S_PUSHBACK2:
v = stack_pop().i;
string_push_back(&heap[stack_pop().i].s, v);
break;
//case S_POPBACK: // ???
case S_POPBACK2:
string_pop_back(stack_pop_string());
break;
//case S_ERASE: // ???
case S_ERASE2:
b = stack_pop().i; // ???
a = stack_pop().i; // index
string_erase(stack_pop_string(), a);
break;
case I_STRING:
stack_push_string(integer_to_string(stack_pop().i));
break;
case FTOS:
v = stack_pop().i; // precision
stack_push_string(float_to_string(stack_pop().f, v));
break;
// -- NOOPs ---
case FUNC:
break;
default:
if (opcode >= 0 && opcode < NR_OPCODES && instructions[opcode].name) {
opcode_name = instructions[opcode].name;
}
WARNING("Unimplemented instruction: 0x%X(%s)", opcode, opcode_name);
}
}
void vm_execute(struct ain *program)
{
// initialize VM state
stack_size = INITIAL_STACK_SIZE;
stack = xmalloc(INITIAL_STACK_SIZE * sizeof(union vm_value));
stack_ptr = 0;
heap_size = INITIAL_HEAP_SIZE;
heap = xmalloc(INITIAL_HEAP_SIZE * sizeof(struct vm_pointer));
heap_free_stack = xmalloc(INITIAL_HEAP_SIZE * sizeof(int32_t));
for (size_t i = 0; i < INITIAL_HEAP_SIZE; i++) {
heap_free_stack[i] = i;
}
heap_free_ptr = 1; // global page at index 0
pages_size = INITIAL_PAGES_SIZE;
page_stack = xmalloc(INITIAL_PAGES_SIZE * sizeof(union vm_value));
page_ptr = 0;
ain = program;
// Initialize globals
heap[0].page = xmalloc(sizeof(union vm_value) * ain->nr_globals);
for (int i = 0; i < ain->nr_globals; i++) {
switch (ain->globals[i].data_type) {
case AIN_STRING:
heap[0].page[i].i = heap_alloc_slot(VM_STRING);
break;
default:
break;
}
}
for (int i = 0; i < ain->nr_initvals; i++) {
int32_t index;
struct ain_initval *v = &ain->global_initvals[i];
switch (v->data_type) {
case AIN_STRING:
index = heap_alloc_slot(VM_STRING);
heap[0].page[v->global_index].i = index;
heap[index].s = make_string(v->string_value, strlen(v->string_value));
break;
default:
heap[0].page[v->global_index].i = v->int_value;
break;
}
}
// Jump to main. We set up a stack frame so that when main returns,
// the first instruction past the end of the code section is executed.
// (When we read the AIN file, CALLSYS 0x0 was placed there.)
instr_ptr = ain->code_size - instruction_width(CALLFUNC);
function_call(ain->main);
// fetch/decode/execute loop
for (;;)
{
if (instr_ptr >= ain->code_size + 6) {
ERROR("Illegal instruction pointer: 0x%lX", instr_ptr);
}
int16_t opcode = get_opcode(instr_ptr);
execute_instruction(opcode);
instr_ptr += instructions[opcode].ip_inc;
}
}