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

src/asm.c


#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include "isa.h"
 
#define LINE_LEN 1024
#define TOK_LEN 32
#define TOKS_MAX 64
#define LABELS_MAX 1024
#define FIXUPS_MAX 8192
 
/* how a value operand gets used */
#define VAL_IMM  1  /* one byte */
#define VAL_ADDR 2  /* two bytes, big endian, must be inside ram */
 
/* which part of a value: all of it, or <x / >x for the low/high byte */
#define PART_ALL 0
#define PART_LO  1
#define PART_HI  2
 
typedef struct {
	char name[TOK_LEN];
	unsigned int val;
} label;
 
typedef struct {
	char name[TOK_LEN];
	unsigned int at;  /* where in out[] it goes */
	int kind;
	int part;
	int line;
} fixup;
 
/* a "name;" label: its bytes, recorded for the .vars file */
typedef struct {
	char name[TOK_LEN];
	unsigned int at;
	unsigned int len;
	int line;
} var;
 
unsigned char out[RAM_SIZE];
unsigned int nout = 0;
 
label labels[LABELS_MAX];
int nlabels = 0;
 
fixup fixups[FIXUPS_MAX];
int nfixups = 0;
 
var vars[VARS_MAX];
int nvars = 0;
 
int lineno = 0;
 
void fail(const char* fmt, ...);
int hexval(int c);
int isregname(const char* s);
int islabelname(const char* s);
int labelfind(const char* name);
void labeldef(const char* name);
void vardef(const char* name);
void varmore(void);
void emit(unsigned int v);
int parsereg(const char* s);
void parsepair(const char* s, int* hi, int* lo);
void putval(unsigned int at, unsigned int v, int kind, int part, const char* what);
void parseval(const char* s, int kind);
int splitline(char* line, char toks[][TOK_LEN]);
void parseins(char toks[][TOK_LEN], int ntok, int mode, int op, int nib);
void parsestream(FILE* in);
void resolve(void);
int writevars(const char* binfile);
int main(int argc, char** argv);
 
void
fail(const char* fmt, ...)
{
	va_list ap;
 
	fprintf(stderr, "err: ");
	if(lineno > 0)
	{
		fprintf(stderr, "line %d: ", lineno);
	}
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	fprintf(stderr, "\n");
	exit(1);
}
 
int
hexval(int c)
{
	if(c >= '0' && c <= '9')
	{
		return c - '0';
	}
	return (toupper(c) - 'A') + 10;
}
 
int
isregname(const char* s)
{
	return (s[0] == 'r' || s[0] == 'R') && isdigit((unsigned char)s[1]) && s[2] == '\0';
}
 
int
islabelname(const char* s)
{
	int i;
 
	if(isalpha((unsigned char)s[0]) == 0 && s[0] != '_')
	{
		return 0;
	}
	for(i = 1; s[i] != '\0'; i = i + 1)
	{
		if(isalnum((unsigned char)s[i]) == 0 && s[i] != '_')
		{
			return 0;
		}
	}
	return isregname(s) == 0;
}
 
int
labelfind(const char* name)
{
	int i;
 
	for(i = 0; i < nlabels; i = i + 1)
	{
		if(strcmp(labels[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}
 
void
labeldef(const char* name)
{
	if(islabelname(name) == 0)
	{
		fail("bad label name '%s'.", name);
	}
	if(labelfind(name) >= 0)
	{
		fail("duplicate label '%s'.", name);
	}
	if(nlabels == LABELS_MAX)
	{
		fail("too many labels.");
	}
	strcpy(labels[nlabels].name, name);
	labels[nlabels].val = nout;
	nlabels = nlabels + 1;
}
 
/* name was just defined as a label, it starts a variable too */
void
vardef(const char* name)
{
	if(nvars == VARS_MAX)
	{
		fail("too many variables.");
	}
	strcpy(vars[nvars].name, name);
	vars[nvars].at = nout;
	vars[nvars].len = 0;
	vars[nvars].line = lineno;
	nvars = nvars + 1;
}
 
/* a lone ";" carries on the variable right before it */
void
varmore(void)
{
	if(nvars == 0 || vars[nvars - 1].at + vars[nvars - 1].len != nout)
	{
		fail("';' with no variable right before it to carry on.");
	}
}
 
void
emit(unsigned int v)
{
	if(nout == RAM_SIZE)
	{
		fail("program is bigger than ram (%d bytes).", RAM_SIZE);
	}
	out[nout] = v & 0xFF;
	nout = nout + 1;
}
 
int
parsereg(const char* s)
{
	if(isregname(s) == 0 || s[1] - '0' >= NREGS)
	{
		fail("bad register '%s', want r0-r%d.", s, NREGS - 1);
	}
	return s[1] - '0';
}
 
/* "r1:r2" */
void
parsepair(const char* s, int* hi, int* lo)
{
	char h[TOK_LEN];
	const char* c = strchr(s, ':');
 
	if(c == NULL || c - s >= TOK_LEN)
	{
		fail("bad register pair '%s', want like r1:r2.", s);
	}
	memcpy(h, s, c - s);
	h[c - s] = '\0';
	*hi = parsereg(h);
	*lo = parsereg(c + 1);
}
 
/* number or label value v, filled in at out[at] */
void
putval(unsigned int at, unsigned int v, int kind, int part, const char* what)
{
	if(part == PART_LO)
	{
		v = v & 0xFF;
	}
	if(part == PART_HI)
	{
		v = (v >> 8) & 0xFF;
	}
	if(kind == VAL_IMM)
	{
		if(v > 0xFF)
		{
			fail("'%s' is %u, doesnt fit in a byte (try <%s or >%s).", what, v, what, what);
		}
		out[at] = v;
		return;
	}
	if(v >= RAM_SIZE)
	{
		fail("address '%s' is %u, past the end of ram.", what, v);
	}
	out[at] = (v >> 8) & 0xFF;
	out[at + 1] = v & 0xFF;
}
 
/* decimal, 0x hex, or a label, optionally prefixed < (low byte) or >
 * (high byte). emits the operand bytes, label values get patched in by
 * resolve() once every label is known. */
void
parseval(const char* s, int kind)
{
	const char* p = s;
	int part = PART_ALL;
	unsigned long v = 0;
	unsigned int at = nout;
	int base = 10;
 
	if(*p == '<' || *p == '>')
	{
		if(kind != VAL_IMM)
		{
			fail("'%s': < and > only work on byte operands.", s);
		}
		part = *p == '<' ? PART_LO : PART_HI;
		p = p + 1;
	}
 
	emit(0);
	if(kind == VAL_ADDR)
	{
		emit(0);
	}
 
	if(isregname(p) != 0)
	{
		fail("wanted a value, got register '%s'.", p);
	}
	if(islabelname(p) != 0)
	{
		if(nfixups == FIXUPS_MAX)
		{
			fail("too many label references.");
		}
		strcpy(fixups[nfixups].name, p);
		fixups[nfixups].at = at;
		fixups[nfixups].kind = kind;
		fixups[nfixups].part = part;
		fixups[nfixups].line = lineno;
		nfixups = nfixups + 1;
		return;
	}
 
	if(p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
	{
		base = 16;
		p = p + 2;
	}
	if(*p == '\0')
	{
		fail("bad operand '%s'.", s);
	}
	for(; *p != '\0'; p = p + 1)
	{
		if((base == 16 && isxdigit((unsigned char)*p) == 0) ||
		   (base == 10 && isdigit((unsigned char)*p) == 0))
		{
			fail("bad operand '%s'.", s);
		}
		v = v * base + hexval((unsigned char)*p);
		if(v > 0xFFFF)
		{
			fail("operand '%s' out of range.", s);
		}
	}
	putval(at, (unsigned int)v, kind, part, s);
}
 
/* whitespace and commas both separate tokens */
int
splitline(char* line, char toks[][TOK_LEN])
{
	int n = 0;
	int j;
	char* p = line;
 
	for(;;)
	{
		while(*p != '\0' && (isspace((unsigned char)*p) || *p == ','))
		{
			p = p + 1;
		}
		if(*p == '\0')
		{
			return n;
		}
		if(n == TOKS_MAX)
		{
			fail("too many tokens.");
		}
		j = 0;
		while(*p != '\0' && isspace((unsigned char)*p) == 0 && *p != ',')
		{
			if(j == TOK_LEN - 1)
			{
				fail("token too long.");
			}
			toks[n][j] = *p;
			j = j + 1;
			p = p + 1;
		}
		toks[n][j] = '\0';
		n = n + 1;
	}
}
 
/* one instruction, toks[0] is the mnemonic. operands are source first,
 * destination last, and have to match the order isafmt prints them in. */
void
parseins(char toks[][TOK_LEN], int ntok, int mode, int op, int nib)
{
	/* operand count, indexed by format, same order as the enum in isa.h */
	static const int NARGS[] = {0, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 0};
	const opdef* d = &MODES[mode][op];
	int x = 0;
	int y = 0;
 
	if(ntok - 1 != NARGS[d->fmt])
	{
		fail("%s takes %d operand(s), got %d.", toks[0], NARGS[d->fmt], ntok - 1);
	}
 
	if(mode != 0)
	{
		emit(0xC0 | mode);
	}
 
	switch(d->fmt)
	{
		case FMT_NONE:
			emit(op << 4);
			break;
		case FMT_R:
			emit((op << 4) | parsereg(toks[1]));
			break;
		case FMT_IR:
			emit((op << 4) | parsereg(toks[2]));
			parseval(toks[1], VAL_IMM);
			break;
		case FMT_AR:
			emit((op << 4) | parsereg(toks[2]));
			parseval(toks[1], VAL_ADDR);
			break;
		case FMT_RA:
			emit((op << 4) | parsereg(toks[1]));
			parseval(toks[2], VAL_ADDR);
			break;
		case FMT_CA:
			emit((op << 4) | nib);
			parseval(toks[1], VAL_ADDR);
			break;
		case FMT_A:
			emit(op << 4);
			parseval(toks[1], VAL_ADDR);
			break;
		case FMT_P:
			parsepair(toks[1], &x, &y);
			emit(op << 4);
			emit((x << 4) | y);
			break;
		case FMT_PR:
			emit((op << 4) | parsereg(toks[2]));
			parsepair(toks[1], &x, &y);
			emit((x << 4) | y);
			break;
		case FMT_RP:
			emit((op << 4) | parsereg(toks[1]));
			parsepair(toks[2], &x, &y);
			emit((x << 4) | y);
			break;
		case FMT_RR:
			/* written "s, d", encoded d:s */
			y = parsereg(toks[1]);
			x = parsereg(toks[2]);
			emit(op << 4);
			emit((x << 4) | y);
			break;
	}
}
 
void
parsestream(FILE* in)
{
	char line[LINE_LEN];
	static char toks[TOKS_MAX][TOK_LEN];
	int ntok;
	int t;
	int len;
	int mode;
	int op;
	int nib;
	int isvar;
	char* h;
 
	while(fgets(line, sizeof(line), in) != NULL)
	{
		lineno = lineno + 1;
		if(strchr(line, '\n') == NULL && !feof(in))
		{
			fail("line too long.");
		}
		h = strchr(line, '#');
		if(h != NULL)
		{
			*h = '\0';
		}
 
		ntok = splitline(line, toks);
 
		/* leading "name:" tokens are labels. "name;" is a label that
		 * starts a variable, a lone ";" carries on the last one - either
		 * way the rest of the line is the variable's bytes. */
		t = 0;
		isvar = 0;
		while(t < ntok && isvar == 0)
		{
			len = strlen(toks[t]);
			if(strcmp(toks[t], ";") == 0)
			{
				varmore();
				isvar = 1;
			}
			else if(len >= 2 && (toks[t][len - 1] == ':' || toks[t][len - 1] == ';'))
			{
				isvar = toks[t][len - 1] == ';';
				toks[t][len - 1] = '\0';
				labeldef(toks[t]);
				if(isvar != 0)
				{
					vardef(toks[t]);
				}
			}
			else
			{
				break;
			}
			t = t + 1;
		}
		if(isvar != 0)
		{
			/* same values as any byte operand: decimal or 0x hex, never
			 * bare two digit hex, "62" here is sixty two. */
			for(; t < ntok; t = t + 1)
			{
				parseval(toks[t], VAL_IMM);
			}
			vars[nvars - 1].len = nout - vars[nvars - 1].at;
			continue;
		}
		if(t == ntok)
		{
			continue;
		}
 
		if(isafind(toks[t], &mode, &op, &nib) != 0)
		{
			parseins(toks + t, ntok - t, mode, op, nib);
			continue;
		}
		if(sameci(toks[t], "MODE") != 0)
		{
			fail("dont write MODE, asm puts it in front of extended ops for you.");
		}
 
		/* otherwise the whole line is raw bytes */
		for(; t < ntok; t = t + 1)
		{
			if(strlen(toks[t]) != 2 ||
			   isxdigit((unsigned char)toks[t][0]) == 0 ||
			   isxdigit((unsigned char)toks[t][1]) == 0)
			{
				fail("bad token '%s'.", toks[t]);
			}
			emit((hexval((unsigned char)toks[t][0]) << 4) | hexval((unsigned char)toks[t][1]));
		}
	}
	for(t = 0; t < nvars; t = t + 1)
	{
		if(vars[t].len == 0)
		{
			lineno = vars[t].line;
			fail("variable '%s' has no bytes.", vars[t].name);
		}
	}
	lineno = 0;
}
 
void
resolve(void)
{
	int i;
	int li;
 
	for(i = 0; i < nfixups; i = i + 1)
	{
		lineno = fixups[i].line;
		li = labelfind(fixups[i].name);
		if(li < 0)
		{
			fail("undefined label '%s'.", fixups[i].name);
		}
		putval(fixups[i].at, labels[li].val, fixups[i].kind, fixups[i].part, fixups[i].name);
	}
	lineno = 0;
}
 
/* binfile + ".vars", or no file at all if there are no variables, so
 * a stale one from an older build cant describe the wrong binary. */
int
writevars(const char* binfile)
{
	char* name;
	FILE* f;
	int i;
 
	name = malloc(strlen(binfile) + strlen(VARS_EXT) + 1);
	if(name == NULL)
	{
		fprintf(stderr, "err: out of memory.\n");
		return 1;
	}
	strcpy(name, binfile);
	strcat(name, VARS_EXT);
 
	if(nvars == 0)
	{
		remove(name);
		free(name);
		return 0;
	}
 
	f = fopen(name, "wb");
	if(f == NULL)
	{
		fprintf(stderr, "err: cant create %s.\n", name);
		free(name);
		return 1;
	}
	for(i = 0; i < nvars; i = i + 1)
	{
		fprintf(f, "%s %04x %04x\n", vars[i].name, vars[i].at, vars[i].len);
	}
	fclose(f);
 
	printf("ok: wrote %s with %d variable(s).\n", name, nvars);
	free(name);
	return 0;
}
 
int
main(int argc, char** argv)
{
	const char* infile = "src.s";
	const char* outfile = "prog.bin";
	FILE* in;
	FILE* f;
 
	if(argc > 3)
	{
		fprintf(stderr, "err: bad usage.\n");
		fprintf(stderr, "usage: %s [in.s|-] [out.bin]\n", argv[0]);
		return 1;
	}
	if(argc >= 2)
	{
		infile = argv[1];
	}
	if(argc >= 3)
	{
		outfile = argv[2];
	}
 
	if(strcmp(infile, "-") == 0)
	{
		in = stdin;
	}
	else
	{
		in = fopen(infile, "rb");
		if(in == NULL)
		{
			fprintf(stderr, "err: cant open %s.\n", infile);
			return 1;
		}
	}
 
	parsestream(in);
 
	if(in != stdin)
	{
		fclose(in);
	}
 
	resolve();
 
	f = fopen(outfile, "wb");
	if(f == NULL)
	{
		fprintf(stderr, "err: cant create %s.\n", outfile);
		return 1;
	}
	fwrite(out, 1, nout, f);
	fclose(f);
 
	printf("ok: wrote %s with %u bytes.\n", outfile, nout);
 
	return writevars(outfile);
}
powered by btf.