do not edit — generated by btf.
git.druid.rocksindexdruid520jurysrc/jury.c

src/jury.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include "isa.h"
 
#define F_Z 1
#define F_C 2
#define F_N 4
 
typedef struct {
	unsigned char ram[RAM_SIZE];
	unsigned char r[NREGS];
	unsigned int sp;
	unsigned int pc;
	unsigned char flags;
	int running;
	const char* halt;
	long lastst;  /* last address ST/STR wrote, -1 if none yet */
	ins in;       /* the instruction being run */
} vm;
 
typedef struct {
	int id;
	void (*fn)(vm* m);
} handler;
 
void vminit(vm* m);
void vmload(vm* m, const unsigned char* prog, int len);
void vmstep(vm* m);
void vmdump(vm* m);
void vmtrace(vm* m);
int main(int argc, char** argv);
 
/* ---- machine state helpers ---- */
 
static void
stop(vm* m, const char* why)
{
	m->halt = why;
	m->running = 0;
}
 
/* the register named by the low nibble */
static unsigned char*
reg(vm* m)
{
	return &m->r[m->in.nib];
}
 
/* the x:y register pair as a 16 bit value */
static unsigned int
pairget(vm* m)
{
	return ((unsigned int)m->r[m->in.x] << 8) | m->r[m->in.y];
}
 
static void
pairset(vm* m, unsigned int v)
{
	m->r[m->in.x] = (v >> 8) & 0xFF;
	m->r[m->in.y] = v & 0xFF;
}
 
static int
carry(vm* m)
{
	return (m->flags & F_C) != 0;
}
 
/* ---- flags ---- */
 
/* z and n off an 8 bit value, c left alone */
static void
flagszn(vm* m, unsigned int v)
{
	m->flags = m->flags & F_C;
	if((v & 0xFF) == 0)
	{
		m->flags = m->flags | F_Z;
	}
	if((v & 0x80) != 0)
	{
		m->flags = m->flags | F_N;
	}
}
 
static void
flagsc(vm* m, int c)
{
	m->flags = m->flags & ~F_C;
	if(c != 0)
	{
		m->flags = m->flags | F_C;
	}
}
 
/* result is the untruncated int, c means it didnt fit in a byte */
static unsigned char
add8(vm* m, int a, int b, int cin)
{
	int r = a + b + cin;
 
	flagszn(m, r);
	flagsc(m, r > 0xFF);
	return r & 0xFF;
}
 
static unsigned char
sub8(vm* m, int a, int b, int bin)
{
	int r = a - b - bin;
 
	flagszn(m, r);
	flagsc(m, r < 0);
	return r & 0xFF;
}
 
static unsigned char
logic8(vm* m, int v)
{
	flagszn(m, v);
	return v & 0xFF;
}
 
/* ---- memory and stack ---- */
 
static int
addrok(vm* m, unsigned int a)
{
	if(a >= RAM_SIZE)
	{
		stop(m, "bad address.");
		return 0;
	}
	return 1;
}
 
static void
load(vm* m, unsigned int a, unsigned char* dst)
{
	if(addrok(m, a) != 0)
	{
		*dst = m->ram[a];
	}
}
 
static void
store(vm* m, unsigned int a, unsigned char v)
{
	if(addrok(m, a) != 0)
	{
		m->ram[a] = v;
		m->lastst = (long)a;
	}
}
 
/* the stack grows down from STACK_TOP. sp points at the last byte
 * pushed, so an empty stack has sp == STACK_TOP. every push/pop checks
 * room for all its bytes before touching anything. */
static int
stackroom(vm* m, unsigned int n)
{
	if(m->sp - STACK_BASE < n)
	{
		stop(m, "stack overflow.");
		return 0;
	}
	return 1;
}
 
static int
stackhas(vm* m, unsigned int n)
{
	if(STACK_TOP - m->sp < n)
	{
		stop(m, "stack underflow.");
		return 0;
	}
	return 1;
}
 
static void
push8(vm* m, unsigned char v)
{
	m->sp = m->sp - 1;
	m->ram[m->sp] = v;
}
 
static unsigned char
pop8(vm* m)
{
	m->sp = m->sp + 1;
	return m->ram[m->sp - 1];
}
 
/* return address goes on big endian, same as everywhere else */
static void
call(vm* m, unsigned int to)
{
	if(stackroom(m, 2) != 0)
	{
		push8(m, m->pc & 0xFF);
		push8(m, (m->pc >> 8) & 0xFF);
		m->pc = to;
	}
}
 
/* ---- normal ops ---- */
 
static void op_halt(vm* m) { m->running = 0; }
static void op_ldi(vm* m) { *reg(m) = m->in.val; }
static void op_add(vm* m) { *reg(m) = add8(m, *reg(m), m->in.val, 0); }
static void op_sub(vm* m) { *reg(m) = sub8(m, *reg(m), m->in.val, 0); }
static void op_and(vm* m) { *reg(m) = logic8(m, *reg(m) & m->in.val); }
static void op_or(vm* m) { *reg(m) = logic8(m, *reg(m) | m->in.val); }
static void op_xor(vm* m) { *reg(m) = logic8(m, *reg(m) ^ m->in.val); }
static void op_cmp(vm* m) { sub8(m, *reg(m), m->in.val, 0); }
static void op_ld(vm* m) { load(m, m->in.val, reg(m)); }
static void op_st(vm* m) { store(m, m->in.val, *reg(m)); }
static void op_call(vm* m) { call(m, m->in.val); }
static void op_mode(vm* m) { stop(m, "bad opcode."); }  /* decoder never lets this through */
 
static void
op_jcc(vm* m)
{
	int f = m->flags;
	int go = 0;
 
	switch(m->in.nib)
	{
		case CC_ALWAYS: go = 1; break;
		case CC_Z: go = (f & F_Z) != 0; break;
		case CC_NZ: go = (f & F_Z) == 0; break;
		case CC_C: go = (f & F_C) != 0; break;
		case CC_NC: go = (f & F_C) == 0; break;
		case CC_N: go = (f & F_N) != 0; break;
		case CC_NN: go = (f & F_N) == 0; break;
	}
	if(go != 0)
	{
		m->pc = m->in.val;
	}
}
 
static void
op_push(vm* m)
{
	if(stackroom(m, 1) != 0)
	{
		push8(m, *reg(m));
	}
}
 
static void
op_pop(vm* m)
{
	if(stackhas(m, 1) != 0)
	{
		*reg(m) = pop8(m);
	}
}
 
static void
op_ret(vm* m)
{
	unsigned int hi;
 
	if(stackhas(m, 2) != 0)
	{
		hi = pop8(m);
		m->pc = (hi << 8) | pop8(m);
	}
}
 
/* ---- mode 1, through a register pair ---- */
 
static void op_ldr(vm* m) { load(m, pairget(m), reg(m)); }
static void op_str(vm* m) { store(m, pairget(m), *reg(m)); }
static void op_jmpr(vm* m) { m->pc = pairget(m); }
static void op_callr(vm* m) { call(m, pairget(m)); }
 
/* 16 bit: z if the pair hit 0, n off bit 15, c if it wrapped */
static void
pairstep(vm* m, long d)
{
	long r = (long)pairget(m) + d;
 
	m->flags = 0;
	if((r & 0xFFFF) == 0)
	{
		m->flags = m->flags | F_Z;
	}
	if((r & 0x8000) != 0)
	{
		m->flags = m->flags | F_N;
	}
	if(r < 0 || r > 0xFFFF)
	{
		m->flags = m->flags | F_C;
	}
	pairset(m, (unsigned int)(r & 0xFFFF));
}
 
static void op_incp(vm* m) { pairstep(m, 1); }
static void op_decp(vm* m) { pairstep(m, -1); }
 
/* ---- mode 2, one register ---- */
 
static void
shift(vm* m, int left, int cin)
{
	unsigned char v = *reg(m);
	int cout;
 
	if(left != 0)
	{
		cout = (v >> 7) & 1;
		v = ((v << 1) | cin) & 0xFF;
	}
	else
	{
		cout = v & 1;
		v = ((v >> 1) | (cin << 7)) & 0xFF;
	}
	*reg(m) = v;
	flagszn(m, v);
	flagsc(m, cout);
}
 
static void op_shl(vm* m) { shift(m, 1, 0); }
static void op_shr(vm* m) { shift(m, 0, 0); }
static void op_rol(vm* m) { shift(m, 1, carry(m)); }
static void op_ror(vm* m) { shift(m, 0, carry(m)); }
static void op_not(vm* m) { *reg(m) = logic8(m, ~*reg(m)); }
static void op_neg(vm* m) { *reg(m) = sub8(m, 0, *reg(m), 0); }
static void op_inc(vm* m) { *reg(m) = add8(m, *reg(m), 1, 0); }
static void op_dec(vm* m) { *reg(m) = sub8(m, *reg(m), 1, 0); }
static void op_out(vm* m) { printf("%02x\n", *reg(m)); }
static void op_outc(vm* m) { putchar(*reg(m)); }
 
/* ---- mode 3, register/register, x is dest y is source (written "y, x") ---- */
 
#define RX (m->r[m->in.x])
#define RY (m->r[m->in.y])
 
static void op_mov(vm* m) { RX = RY; }
static void op_addr(vm* m) { RX = add8(m, RX, RY, 0); }
static void op_adcr(vm* m) { RX = add8(m, RX, RY, carry(m)); }
static void op_subr(vm* m) { RX = sub8(m, RX, RY, 0); }
static void op_sbcr(vm* m) { RX = sub8(m, RX, RY, carry(m)); }
static void op_cmpr(vm* m) { sub8(m, RX, RY, 0); }
static void op_andr(vm* m) { RX = logic8(m, RX & RY); }
static void op_orr(vm* m) { RX = logic8(m, RX | RY); }
static void op_xorr(vm* m) { RX = logic8(m, RX ^ RY); }
 
static void
op_swap(vm* m)
{
	unsigned char t = RX;
 
	RX = RY;
	RY = t;
}
 
#undef RX
#undef RY
 
/* ---- mode 4, more register/immediate ---- */
 
static void op_adc(vm* m) { *reg(m) = add8(m, *reg(m), m->in.val, carry(m)); }
static void op_sbc(vm* m) { *reg(m) = sub8(m, *reg(m), m->in.val, carry(m)); }
static void op_tst(vm* m) { logic8(m, *reg(m) & m->in.val); }
 
/* ---- dispatch, indexed by op id. order has to match the enum in
 * isa.h, main checks that before running anything. ---- */
 
static const handler EXEC[OP_COUNT] =
{
	{OP_HALT, op_halt}, {OP_LDI, op_ldi}, {OP_ADD, op_add},
	{OP_SUB, op_sub}, {OP_AND, op_and}, {OP_OR, op_or},
	{OP_XOR, op_xor}, {OP_CMP, op_cmp}, {OP_JCC, op_jcc},
	{OP_LD, op_ld}, {OP_ST, op_st}, {OP_CALL, op_call},
	{OP_MODE, op_mode}, {OP_PUSH, op_push}, {OP_POP, op_pop},
	{OP_RET, op_ret},
 
	{OP_LDR, op_ldr}, {OP_STR, op_str}, {OP_JMPR, op_jmpr},
	{OP_CALLR, op_callr}, {OP_INCP, op_incp}, {OP_DECP, op_decp},
 
	{OP_SHL, op_shl}, {OP_SHR, op_shr}, {OP_ROL, op_rol},
	{OP_ROR, op_ror}, {OP_NOT, op_not}, {OP_NEG, op_neg},
	{OP_INC, op_inc}, {OP_DEC, op_dec}, {OP_OUT, op_out},
	{OP_OUTC, op_outc},
 
	{OP_MOV, op_mov}, {OP_SWAP, op_swap}, {OP_ADDR, op_addr},
	{OP_ADCR, op_adcr}, {OP_SUBR, op_subr}, {OP_SBCR, op_sbcr},
	{OP_CMPR, op_cmpr}, {OP_ANDR, op_andr}, {OP_ORR, op_orr},
	{OP_XORR, op_xorr},
 
	{OP_ADC, op_adc}, {OP_SBC, op_sbc}, {OP_TST, op_tst}
};
 
/* ---- the vm ---- */
 
void
vminit(vm* m)
{
	memset(m, 0, sizeof(*m));
	m->sp = STACK_TOP;
	m->running = 1;
	m->halt = "ok.";
	m->lastst = -1;
}
 
void
vmload(vm* m, const unsigned char* prog, int len)
{
	memcpy(m->ram, prog, len);
}
 
void
vmstep(vm* m)
{
	int st = isadecode(m->ram, RAM_SIZE, m->pc, &m->in);
 
	if(st != DEC_OK)
	{
		stop(m, DECMSG[st]);
		return;
	}
	m->pc = m->pc + m->in.len;
	EXEC[m->in.def->id].fn(m);
}
 
/* ---- looking at it ---- */
 
static void
flagstr(vm* m, char* buf)
{
	buf[0] = (m->flags & F_Z) != 0 ? 'z' : '-';
	buf[1] = (m->flags & F_C) != 0 ? 'c' : '-';
	buf[2] = (m->flags & F_N) != 0 ? 'n' : '-';
	buf[3] = '\0';
}
 
/* "c3 20 12     ADDR r2, r1" for the instruction at a */
static void
insstr(vm* m, unsigned int a, char* buf)
{
	ins in;
	char txt[INS_TXT];
	char hex[INS_MAX * 3 + 1];
	int st;
	int i;
 
	st = isadecode(m->ram, RAM_SIZE, a, &in);
	if(st != DEC_OK)
	{
		if(a < RAM_SIZE)
		{
			sprintf(buf, "%02x           ?? %s", m->ram[a], DECMSG[st]);
		}
		else
		{
			sprintf(buf, "             ?? %s", DECMSG[st]);
		}
		return;
	}
	hex[0] = '\0';
	for(i = 0; i < in.len; i = i + 1)
	{
		sprintf(hex + i * 3, "%02x ", m->ram[a + i]);
	}
	isafmt(&in, txt);
	sprintf(buf, "%-12s %s", hex, txt);
}
 
/* 16 bytes of ram from the row holding a, mk marks one byte */
static void
dumprow(vm* m, const char* tag, unsigned int a, unsigned int mk, int mc)
{
	unsigned int row = a & ~0xFu;
	unsigned int i;
 
	printf("%-6s %04x:", tag, row);
	for(i = row; i < row + 16 && i < RAM_SIZE; i = i + 1)
	{
		printf("%c%02x", i == mk ? mc : ' ', m->ram[i]);
	}
	printf("\n");
}
 
void
vmdump(vm* m)
{
	char fl[4];
	char txt[64];
	unsigned int i;
	int k;
 
	flagstr(m, fl);
	printf("pc=%04x sp=%04x flags=%s ", m->pc, m->sp, fl);
	for(k = 0; k < NREGS; k = k + 1)
	{
		printf(" r%d=%02x", k, m->r[k]);
	}
	printf("\n");
 
	insstr(m, m->pc, txt);
	printf("next   %04x: %s\n", m->pc, txt);
 
	/* the row pc is in and the one after it, pc marked with > */
	if(m->pc < RAM_SIZE)
	{
		dumprow(m, "ram", m->pc, m->pc, '>');
		if((m->pc | 0xF) + 1 < RAM_SIZE)
		{
			dumprow(m, "", (m->pc | 0xF) + 1, m->pc, '>');
		}
	}
 
	/* stack top first, at most 16 bytes of it */
	if(m->sp == STACK_TOP)
	{
		printf("stack  empty\n");
	}
	else
	{
		printf("stack  %04x:", m->sp);
		for(i = m->sp; i < STACK_TOP && i < m->sp + 16; i = i + 1)
		{
			printf(" %02x", m->ram[i]);
		}
		printf("%s  (%u/%d)\n", STACK_TOP - m->sp > 16 ? " .." : "", STACK_TOP - m->sp, STACK_SIZE);
	}
 
	/* where the last store went, marked with * */
	if(m->lastst >= 0)
	{
		dumprow(m, "stored", (unsigned int)m->lastst, (unsigned int)m->lastst, '*');
	}
}
 
/* one line per step for -r */
void
vmtrace(vm* m)
{
	char fl[4];
	char txt[64];
	int k;
 
	flagstr(m, fl);
	insstr(m, m->pc, txt);
	printf("%04x  %-32s", m->pc, txt);
	for(k = 0; k < NREGS; k = k + 1)
	{
		printf(" r%d=%02x", k, m->r[k]);
	}
	printf(" sp=%04x %s\n", m->sp, fl);
}
 
/* default (no flag): straight through, no output but [start]/[stop] --
 * quiet is the plain thing to do with a program you already trust,
 * dumping every step is the exception you have to ask for. -r traces
 * one line per step; -d single-steps and shows the full state (regs,
 * ram window, stack) each time, pausing for enter -- what running
 * with no flag used to do unconditionally, before dumping had a flag
 * of its own to hide behind. */
static int
usage(const char* me)
{
	fprintf(stderr, "err: bad usage.\n");
	fprintf(stderr, "usage: %s prog.bin [-r|-d]\n", me);
	return 1;
}
 
int
main(int argc, char** argv)
{
	static vm m;
	static unsigned char prog[RAM_SIZE + 1];
	FILE* f;
	int len;
	int run = 0;
	int k;
 
	for(k = 0; k < OP_COUNT; k = k + 1)
	{
		if(EXEC[k].id != k)
		{
			fprintf(stderr, "err: EXEC table out of order at %d.\n", k);
			return 1;
		}
	}
 
	if(argc < 2 || argc > 3)
	{
		return usage(argv[0]);
	}
	if(argc == 3)
	{
		if(strcmp(argv[2], "-r") == 0)
		{
			run = 1;
		}
		else if(strcmp(argv[2], "-d") == 0)
		{
			run = 2;
		}
		else
		{
			return usage(argv[0]);
		}
	}
 
	f = fopen(argv[1], "rb");
	if(f == NULL)
	{
		perror("fopen");
		return 1;
	}
	len = fread(prog, 1, RAM_SIZE + 1, f);
	fclose(f);
	if(len > RAM_SIZE)
	{
		fprintf(stderr, "err: %s is bigger than ram (%d bytes).\n", argv[1], RAM_SIZE);
		return 1;
	}
 
	vminit(&m);
	vmload(&m, prog, len);
 
	printf("[start]\n");
	while(m.running != 0)
	{
		if(run == 2)
		{
			vmdump(&m);
		}
		if(run == 1)
		{
			vmtrace(&m);
		}
		vmstep(&m);
		if(run == 2)
		{
			getchar();
		}
	}
	if(run == 2)
	{
		vmdump(&m);
	}
	printf("[stop] %s\n", m.halt);
 
	return strcmp(m.halt, "ok.") == 0 ? 0 : 1;
}
powered by btf.