shithub: apl10

ref: 7b65afc1ad13f3859eca6eadaa2c45d864320304
dir: /vm.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "dat.h"
#include "fns.h"

static VM *vm;

static void _Noreturn
usage(void)
{
	fprint(2, "usage: apl/vm objfile\n");
	exits("usage");
}

static void
initvm(void)
{
	vm = mallocz(sizeof(VM), 1);
}

static void
loadmod(char *file)
{
	Biobuf *b = Bopen(file, OREAD);
	if(b == nil)
		sysfatal("open: %r");

	Module *m = mallocz(sizeof(Module), 1);
	for(int n = 0; n < nelem(objparts); n++)
		objparts[n].read(m, b, 0);
	Bterm(b);

	vm->nmods++;
	vm->mods = realloc(vm->mods, vm->nmods * sizeof(*vm->mods));
	vm->mods[vm->nmods-1] = m;
}

static void
stackgrow(uvlong n)
{
	vm->regs[RegSp].u64 += n;
	if(vm->regs[RegSp].u64 > nelem(vm->stack))
		sysfatal("APL stack overflow");
}

static uvlong
findlabel(char *name)
{
	Module *m = vm->mods[vm->regs[RegMod].u64];
	for(int i = 0; i < m->nlabels; i++){
		if(strcmp(name, m->labels[i].name) == 0)
			return m->labels[i].coffset;
	}
	sysfatal("Failed to find label %s", name);
}

static Word *
getaddr(OpArg *a)
{
	switch(a->tag){
	case OAlabel:
	case OAnum1:
	case OAnum2:
	case OAnum4:
	case OAnum8:
		return (Word*)&a->u64;
	case OAreg:
		return &vm->regs[a->u64];
	case OAlocal1:
	case OAlocal2:
	case OAlocal4:
	case OAlocal8:
		if((a->u64+1) > (vm->regs[RegSp].u64 - vm->regs[RegFp].u64))
			sysfatal("Use of unallocated local: %%%ulld\n", a->u64);
		return &vm->stack[vm->regs[RegFp].u64+a->u64];
	default:
		sysfatal("unhandled case in getaddr: %d", a->tag);
	}
}

static void
interpret(void)
{
	ParsedInstr instr;

	vm->regs[RegMod].u64 = 0;
	u8int *code = vm->mods[vm->regs[RegMod].u64]->code;
	vm->regs[RegIp].u64 = findlabel("main");

	Word *src, *dst;
	u64int sp;
	for(;;){
		decodeinstr(code + vm->regs[RegIp].u64, &instr, nil, 0);
		vm->regs[RegIp].u64 += instr.len;

		switch(instr.opcode){
		case Onop:
			break;
		case Oexit:
			exits(nil);
			break;
		case Ocall:
			sp = vm->regs[RegSp].u64;
			stackgrow(Reg_save);
			memcpy(vm->stack+sp, vm->regs, Reg_save * sizeof(*vm->regs));
			vm->regs[RegIp].u64 = instr.args[0].u64;
			vm->regs[RegFp].u64 = vm->regs[RegSp].u64;
			break;
		case Oreturn:
			if(vm->regs[RegFp].u64 == 0)
				sysfatal("APL stack underflow");

			sp = vm->regs[RegFp].u64 - Reg_save;
			memcpy(vm->regs, vm->stack + sp, Reg_save * sizeof(*vm->regs));
			vm->regs[RegSp].u64 = sp;
			break;
		case Omov:
			src = getaddr(&instr.args[0]);
			dst = getaddr(&instr.args[1]);
			memcpy(dst, src, sizeof(Word));
			break;
		case Olocals:
			stackgrow(instr.args[0].s64);
			break;
		case Oscalnum:
		case Odisplay:
		default:
			sysfatal("missing case in interpret: %s", optab[instr.opcode].name);
		}
	}
}

void
main(int argc, char *argv[])
{
	char *objfile;

	ARGBEGIN{
	default:
		usage();
	}ARGEND;

	if(argc != 1)
		usage();
	objfile = *argv;

	initvm();
	loadmod(objfile);

	interpret();
}