Newer
Older
Tardis / lang / bytecode.c
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 John Watts and the LuminaSensum contributors

#include "bytecode.h"
#include "error.h"
#include "number.h"
#include <stddef.h>

enum opcodes {
	OP_NUM = 0x01,
	OP_RET = 0x03,
	OP_CALL = 0x04,
	OP_NULL = 0x05,
	OP_GET = 0x06,
	OP_SET = 0x07,
	OP_DROP = 0x08,
};

// clang-format off
static const unsigned char bytecode[] = {
	OP_NUM, 0xaa, 0xaa, 0xaa, 0xaa,
	OP_NUM, 0xbb, 0xbb, 0xbb, 0xbb,
	OP_NUM, 0xcc, 0xcc, 0xcc, 0xcc,
	OP_NUM, 0x69, 0x02, 0x00, 0x00,
	OP_NULL,
	OP_GET, 0x4,
	OP_GET, 0x4,
	OP_CALL, 'A', 'd', 'd', '\0',
	OP_DROP, 0x1,
	OP_SET, 0x0,
	OP_RET
};
// clang-format on

void bytecode_run(struct object **stack, int arg_count) {
	abort_if(!stack, "bytecode_run has no stack");
	const unsigned char *pos_code = &bytecode[0];
	// We assume one argument: A return value
	struct object **pos_stack = stack + arg_count;
	unsigned char op = OP_RET;
	while ((op = *pos_code++) != OP_RET) {
		// Skip over NOP, ASCII and unknown OPs
		switch (op) {
		case OP_NUM: {
			int num = 0;
			num |= *pos_code++ << 0;
			num |= *pos_code++ << 8;
			num |= *pos_code++ << 16;
			num |= *pos_code++ << 24;
			*pos_stack++ = number_create(num);
			break;
		}
		case OP_CALL: {
			const char *dispatch = (const char *)pos_code;
			struct object *obj = *(--pos_stack);
			struct object **args = pos_stack - 2;
			dispatch_call(obj, dispatch, 2, args);
			break;
		}
		case OP_NULL: {
			*pos_stack++ = NULL;
			break;
		}
		case OP_GET: {
			*pos_stack++ = stack[*pos_code++];
			break;
		}
		case OP_SET: {
			stack[*pos_code++] = *--pos_stack;
			break;
		}
		case OP_DROP: {
			pos_stack -= *pos_code++;
			break;
		}
		}
	}
	// Clean up the stack
	struct object **pos_cleanup = pos_stack - arg_count;
	while (pos_cleanup != stack) {
		object_drop(pos_cleanup--);
	}
}