Implement CALLONJUMP and SJUMP instructions

Add support for scenario functions (functions which clear the call stack
upon entry and never return).
This commit is contained in:
Nunuhara Cabbage
2019-11-19 19:56:18 -08:00
parent 419da7889c
commit 7ec7124d01
2 changed files with 56 additions and 2 deletions
+2 -2
View File
@@ -146,8 +146,8 @@ const struct instruction instructions[NR_OPCODES] = {
OP(FUNC, 1, INSTR_FUN),
OP(_EOF, 1, INSTR_FILE),
OP(CALLSYS, 1, INSTR_INT),
OP(SJUMP, 0), // TODO
OP(CALLONJUMP, 0), // TODO
JMP(SJUMP, 0),
OP(CALLONJUMP, 0),
OP(SWAP, 0),
OP(SH_STRUCTREF, 1, INSTR_INT),
OP(S_LENGTH, 0),
+54
View File
@@ -253,6 +253,49 @@ union vm_value vm_copy(union vm_value v, enum ain_data_type type)
}
}
static int get_function_by_name(const char *name)
{
for (int i = 0; i < ain->nr_functions; i++) {
if (!strcmp(name, ain->functions[i].name))
return i;
}
return -1;
}
static int alloc_scenario_page(const char *fname)
{
int fno, slot;
struct ain_function *f;
if ((fno = get_function_by_name(fname)) < 0)
VM_ERROR("Invalid scenario function: %s", fname);
f = &ain->functions[fno];
slot = heap_alloc_slot(VM_PAGE);
heap[slot].page = alloc_page(LOCAL_PAGE, fno, f->nr_vars);
for (int i = 0; i < f->nr_vars; i++) {
heap[slot].page->values[i] = variable_initval(f->vars[i].data_type);
}
return slot;
}
static void scenario_call(int slot)
{
int fno = heap[slot].page->index;
// flush call stack
for (int i = call_stack_ptr - 1; i >= 0; i--) {
heap_unref(call_stack[i].page_slot);
}
call_stack[0] = (struct function_call) {
.fno = fno,
.return_address = VM_RETURN,
.page_slot = slot,
.struct_page = -1
};
call_stack_ptr = 1;
instr_ptr = ain->functions[fno].address;
}
/*
* System 4 calling convention:
* - caller pushes arguments, in order
@@ -572,6 +615,17 @@ static void execute_instruction(int16_t opcode)
case CALLSYS:
system_call(get_argument(0));
break;
case CALLONJUMP:
slot = stack_pop().i;
// XXX: I am GUESSING that the VM pre-allocates the scenario function's
// local page here. It certainly pushes what appears to be a page
// index to the stack.
stack_push(alloc_scenario_page(heap[slot].s->text));
heap_unref(slot);
break;
case SJUMP:
scenario_call(stack_pop().i);
break;
case JUMP: // ADDR
instr_ptr = get_argument(0);
break;