spl

systems programming language
git clone http://frotz.net/git/spl.git
Log | Files | Refs | README | LICENSE

assemble-sr32.c (16727B)


      1 // Copyright 2025, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 #include <assert.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <stdarg.h>
      8 #include <ctype.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 #include <fcntl.h>
     12 
     13 #include "sr32.h"
     14 
     15 #define RBUFSIZE 4096
     16 #define SMAXSIZE 1024
     17 
     18 static unsigned linenumber = 0;
     19 static char *filename;
     20 
     21 void die(const char *fmt, ...) {
     22 	va_list ap;
     23 	fprintf(stderr,"\n%s:%d: ", filename, linenumber);
     24 	va_start(ap, fmt);
     25 	vfprintf(stderr, fmt, ap);
     26 	va_end(ap);
     27 	fprintf(stderr,"\n");
     28 	exit(1);
     29 }
     30 
     31 int is_signed16(uint32_t n) {
     32 	n &= 0xFFFF0000;
     33 	return ((n == 0) || (n == 0xFFFF0000));
     34 }
     35 int is_signed21(uint32_t n) {
     36 	n &= 0xFFFFF800;
     37 	return ((n == 0) || (n == 0xFFFFF800));
     38 }
     39 int fits_in_signed16(uint32_t n) {
     40 	n &= 0xFFFF8000;
     41 	return ((n == 0) || (n == 0xFFFF8000));
     42 }
     43 
     44 uint8_t image[1*1024*1024];
     45 uint32_t image_base = 0;
     46 uint32_t image_size = 0;
     47 uint32_t PC = 0;
     48 
     49 void wr32(uint32_t addr, uint32_t val) {
     50 	addr &= ~3;
     51 	addr -= image_base;
     52 	if (addr < image_size) {
     53 		*((uint32_t*) (image + addr)) = val;
     54 	}
     55 }
     56 uint32_t rd32(uint32_t addr) {
     57 	addr &= ~3;
     58 	addr -= image_base;
     59 	if (addr < image_size) {
     60 		return *((uint32_t*) (image + addr));
     61 	}
     62 	return 0;
     63 }
     64 void wr8(uint32_t addr, uint32_t val) {
     65 	addr -= image_base;
     66 	if (addr < image_size) {
     67 		image[addr] = val;
     68 	}
     69 }
     70 
     71 #define TYPE_PCREL_S16	1
     72 #define TYPE_PCREL_S21	2
     73 #define TYPE_ABS_U32	3
     74 #define TYPE_ABS_HILO   4
     75 #define TYPE_PCREL_HILO 5
     76 
     77 struct fixup {
     78 	struct fixup *next;
     79 	unsigned pc;
     80 	unsigned type;
     81 };
     82 
     83 struct label {
     84 	struct label *next;
     85 	struct fixup *fixups;
     86 	const char *name;
     87 	unsigned pc;
     88 	unsigned defined;
     89 };
     90 
     91 struct label *labels;
     92 struct fixup *fixups;
     93 
     94 uint32_t do_fixup(const char *name, uint32_t addr, uint32_t tgt, int type) {
     95 	uint32_t n = tgt;
     96 	switch(type) {
     97 	case TYPE_PCREL_S16:
     98 		n = n - (addr + 4);
     99 		if (!is_signed16(n)) goto oops;
    100 		wr32(addr, rd32(addr) | (n << 16));
    101 		break;
    102 	case TYPE_PCREL_S21:
    103 		n = n - (addr + 4);
    104 		if (!is_signed21(n)) goto oops;
    105 		wr32(addr, rd32(addr) | (n << 11));
    106 		break;
    107 	case TYPE_ABS_U32:
    108 		wr32(addr, n);
    109 		break;
    110 	case TYPE_PCREL_HILO:
    111 		n = n - (addr + 4);
    112 	case TYPE_ABS_HILO:
    113 		uint32_t hi = n >> 16;
    114 		uint32_t lo = n & 0xffff;
    115 		if (lo & 0x8000) hi += 1;
    116 		n = (hi << 16) | lo;
    117 		wr32(addr + 0, rd32(addr + 0) | (hi << 16));
    118 		wr32(addr + 4, rd32(addr + 4) | (lo << 16));
    119 		break;
    120 	default:
    121 		die("unknown branch type %d\n", type);
    122 	}
    123 	return n;
    124 oops:
    125 	die("label '%s' at %08x is out of range of %08x\n", name, tgt, addr);
    126 	return 0;
    127 }
    128 
    129 void setlabel(const char *name, unsigned pc) {
    130 	struct label *l;
    131 	struct fixup *f;
    132 
    133 	for (l = labels; l; l = l->next) {
    134 		if (!strcasecmp(l->name, name)) {
    135 			if (l->defined) die("cannot redefine '%s'", name);
    136 			l->pc = pc;
    137 			l->defined = 1;
    138 			for (f = l->fixups; f; f = f->next) {
    139 				do_fixup(name, f->pc, l->pc, f->type);
    140 			}
    141 			return;
    142 		}
    143 	}
    144 	l = malloc(sizeof(*l));
    145 	l->name = name;
    146 	l->pc = pc;
    147 	l->fixups = 0;
    148 	l->defined = 1;
    149 	l->next = labels;
    150 	labels = l;
    151 }
    152 
    153 const char *getlabel(unsigned pc) {
    154 	struct label *l;
    155 	for (l = labels; l; l = l->next)
    156 		if (l->pc == pc)
    157 			return l->name;
    158 	return 0;
    159 }
    160 
    161 uint32_t uselabel(const char *name, unsigned pc, unsigned type) {
    162 	struct label *l;
    163 	struct fixup *f;
    164 
    165 	for (l = labels; l; l = l->next) {
    166 		if (!strcmp(l->name, name)) {
    167 			if (l->defined) {
    168 				return do_fixup(name, pc, l->pc, type);
    169 			} else {
    170 				goto add_fixup;
    171 			}
    172 		}
    173 	}
    174 	l = malloc(sizeof(*l));
    175 	l->name = strdup(name);
    176 	l->pc = 0;
    177 	l->fixups = 0;
    178 	l->defined = 0;
    179 	l->next = labels;
    180 	labels = l;
    181 add_fixup:
    182 	f = malloc(sizeof(*f));
    183 	f->pc = pc;
    184 	f->type = type;
    185 	f->next = l->fixups;
    186 	l->fixups = f;
    187 	return 0;
    188 }
    189 
    190 void checklabels(void) {
    191 	struct label *l;
    192 	for (l = labels; l; l = l->next) {
    193 		if (!l->defined) {
    194 			die("undefined label '%s'", l->name);
    195 		}
    196 	}
    197 }
    198 
    199 void sr32dis(uint32_t pc, uint32_t ins, char *out);
    200 
    201 void emit(uint32_t instr) {
    202 	if (PC & 3) {
    203 		PC = (PC + 3) & ~3;
    204 	}
    205 	//fprintf(stderr,"{%08x:%08x} ", PC, instr);
    206 	wr32(PC, instr);
    207 	PC += 4;
    208 }
    209 
    210 void save(const char *fn) {
    211 	const char *name;
    212 	uint32_t n;
    213 	char dis[128];
    214 
    215 	FILE *fp = fopen(fn, "w");
    216 	if (!fp) die("cannot write to '%s'", fn);
    217 	for (n = image_base; n < PC; n += 4) {
    218 		uint32_t ins = rd32(n);
    219 		sr32dis(n, ins, dis);
    220 		name = getlabel(n);
    221 		char bs[8] = "000000 ";
    222 		for (unsigned i = 0; i < 6; i++) {
    223 			if (ins & (1<<i)) bs[5-i] = '1';
    224 		}
    225 		if (name) {
    226 			fprintf(fp, "%08x: %08x // %s %-25s <- %s\n", n, ins, bs, dis, name);
    227 		} else {
    228 			fprintf(fp, "%08x: %08x // %s %s\n", n, ins, bs, dis);
    229 		}
    230 	}
    231 	fclose(fp);
    232 }
    233 
    234 enum tokens {
    235 	tEOF, tEOL, tIDENT, tREGISTER, tNUMBER, tSTRING,
    236 	tCOMMA, tCOLON, tOPAREN, tCPAREN, tAT, tDOT,
    237 	tADD, tSUB, tAND, tOR, tXOR, tSLL, tSRL, tSRA,
    238 	tSLT, tSLTU, tMUL, tDIV,
    239 	tADDI, tSUBI, tANDI, tORI, tXORI, tSLLI, tSRLI, tSRAI,
    240 	tSLTI, tSLTUI, tMULI, tDIVI,
    241 	tJALR,
    242 	tBEQ, tBNE, tBLT, tBLTU, tBGE, tBGEU,
    243 	tLDW, tLDH, tLDB, tLDX, tLUI, tLDHU, tLDBU, tAUIPC,
    244 	tSTW, tSTH, tSTB, tSTX,
    245 	tJAL, tSYSCALL, tBREAK, tSYSRET,
    246 	tNOP, tMV, tLI, tLA, tJ, tJR, tCALL, tRET,
    247 	tNOT, tNEG, tSEQZ, tSNEZ, tSLTZ, tSGTZ,
    248 	tBEQZ, tBNEZ, tBLEZ, tBGEZ, tBLTZ, tBGTZ,
    249 	tBGT, tBLE, tBGTU, tBLEU,
    250 	tEQU, tBYTE, tHALF, tWORD,
    251 	NUMTOKENS,
    252 };
    253 
    254 char *tnames[] = { "<EOF>", "<EOL>", "IDENT", "REGISTER", "NUMBER", "STRING",
    255 	",", ":", "(", ")", "@", ".",
    256 	"ADD", "SUB", "AND", "OR", "XOR", "SLL", "SRL", "SRA",
    257 	"SLT", "SLTU", "MUL", "DIV",
    258 	"ADDI", "SUBI", "ANDI", "ORI", "XORI", "SLLI", "SRLI", "SRAI",
    259 	"SLTI", "SLTUI", "MULI", "DIVI",
    260 	"JALR",
    261 	"BEQ", "BNE", "BLT", "BLTU", "BGE", "BGEU",
    262 	"LDW", "LDH", "LDB", "LDX", "LUI", "LDHU", "LDBU", "AUIPC",
    263 	"STW", "STH", "STB", "STX",
    264 	"JAL", "SYSCALL", "BREAK", "SYSRET",
    265 	// pseudo instructions
    266 	"NOP", "MV", "LI", "LA", "J", "JR", "CALL", "RET",
    267 	"NOT", "NEG", "SEQZ", "SNEZ", "SLTZ", "SGTZ",
    268 	"BEQZ", "BNEZ", "BLEZ", "BGEZ", "BLTZ", "BGTZ",
    269 	"BGT", "BLE", "BGTU", "BLEU",
    270 	".EQU", ".BYTE", ".HALF", ".WORD",
    271 };
    272 
    273 static_assert(NUMTOKENS == (sizeof(tnames) / sizeof(tnames[0])),
    274 	"length of tokens and tnames must be equal");
    275 
    276 #define FIRSTKEYWORD tDOT
    277 
    278 int is_stopchar(unsigned x) {
    279 	switch (x) {
    280 	case 0: case ' ': case '\t': case '\r': case '\n':
    281 	case '.': case ',': case ':': case ';': case '@':
    282 	case '#': case '"': case '/': case '(': case ')':
    283 		return 1;
    284 	default:
    285 		return 0;
    286 	}
    287 }
    288 
    289 typedef struct State {
    290 	char *next;
    291 	unsigned avail;
    292 	unsigned tok;
    293 	unsigned num;
    294 	char *str;
    295 	int fd;
    296 	char rbuf[RBUFSIZE];
    297 	char sbuf[SMAXSIZE + 1];
    298 } State;
    299 
    300 static char* rnames[64] = {
    301 	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
    302 	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
    303 	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
    304 	"x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31",
    305 	"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
    306 	"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
    307 	"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
    308 	"s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6",
    309 };
    310 
    311 // may be called once after nextchar
    312 void pushback(State *state, unsigned ch) {
    313 	state->avail++;
    314 	state->next--;
    315 	*(state->next) = ch;
    316 }
    317 
    318 unsigned nextchar(State *state) {
    319 	for (;;) {
    320 		if (state->avail) {
    321 			state->avail--;
    322 			return *(state->next)++;
    323 		} else {
    324 			if (state->fd < 0) {
    325 				return 0;
    326 			}
    327 			state->next = state->rbuf;
    328 			ssize_t n = read(state->fd, state->rbuf, RBUFSIZE);
    329 			if (n <= 0) {
    330 				close(state->fd);
    331 				state->fd = -1;
    332 				state->avail = 0;
    333 				return 0;
    334 			} else {
    335 				state->avail = n;
    336 			}
    337 		}
    338 	}
    339 }
    340 
    341 unsigned tokenize(State *state) {
    342 	char *sbuf = state->sbuf;
    343 	char *s = sbuf;
    344 	unsigned x, n;
    345 
    346 	for (;;) {
    347 		switch (x = nextchar(state)) {
    348 		case 0:
    349 			return tEOF;
    350 		case ' ': case '\t': case '\r':
    351 			continue;
    352 		case '/':
    353 			if (nextchar(state) != '/') {
    354 				die("stray /");
    355 			}
    356 		case '#':
    357 			for (;;) {
    358 				x = nextchar(state);
    359 				if ((x == '\n') || (x == 0)) break;
    360 			}
    361 		case '\n':
    362 			linenumber++;
    363 			return tEOL;
    364 		case ';': return tEOL;
    365 		case ',': return tCOMMA;
    366 		case ':': return tCOLON;
    367 		case '(': return tOPAREN;
    368 		case ')': return tCPAREN;
    369 		case '@': return tAT;
    370 		case '"':
    371 			while ((x = nextchar(state)) != '"') {
    372 				if ((x == '\n') || (x == 0)) {
    373 					die("unterminated string");
    374 				}
    375 				if ((s - sbuf) == SMAXSIZE) {
    376 					die("string too long");
    377 				}
    378 				*s++ = x;
    379 			}
    380 			*s++ = 0;
    381 			state->str = sbuf;
    382 			return tSTRING;
    383 		}
    384 		*s++ = x;
    385 		while (!is_stopchar(x = nextchar(state))) {
    386 			if ((s - sbuf) == SMAXSIZE) {
    387 				die("token too long");
    388 			}
    389 			*s++ = x;
    390 		}
    391 		*s = 0;
    392 		pushback(state, x);
    393 		state->str = sbuf;
    394 
    395 		char *end = sbuf;
    396 		n = strtoul(sbuf, &end, 0);
    397 		if (*end == '\0') {
    398 			state->num = n;
    399 			return tNUMBER;
    400 		}
    401 		for (n = 0; n < (sizeof(rnames)/sizeof(rnames[0])); n++) {
    402 			if (!strcasecmp(sbuf, rnames[n])) {
    403 				state->str = rnames[n];
    404 				state->num = n & 31;
    405 				return tREGISTER;
    406 			}
    407 		}
    408 		if (isalpha(sbuf[0]) || (sbuf[0] == '.') || (sbuf[0] == '_')) {
    409 			for (n = FIRSTKEYWORD; n < NUMTOKENS; n++) {
    410 				if (!strcasecmp(sbuf, tnames[n])) {
    411 					return n;
    412 				}
    413 			}
    414 			s = sbuf + 1;
    415 			while (*s) {
    416 				if (!isalnum(*s) && (*s != '_')) {
    417 					die("invalid character '%c' (%d) in identifier", *s, *s);
    418 				}
    419 				s++;
    420 			}
    421 			return tIDENT;
    422 		}
    423 		die("invalid character '%c' (%d)", s[0], s[0]);
    424 	}
    425 	// impossible
    426 	return tEOF;
    427 }
    428 
    429 unsigned next(State *state) {
    430 	state->num = 0;
    431 	state->str = 0;
    432 	state->tok = tokenize(state);
    433 	if (state->str == 0) {
    434 		state->str = tnames[state->tok];
    435 	}
    436 #if 0
    437 	if (state->tok == tNUMBER) {
    438 		fprintf(stderr, "#%08x ", state->num);
    439 	} else {
    440 		fprintf(stderr,"%s ", state->str);
    441 	}
    442 	if (state->tok == tEOL) fprintf(stderr,"\n");
    443 #endif
    444 	return state->tok;
    445 }
    446 
    447 void expect(State *s, unsigned expected) {
    448 	if (expected != s->tok) {
    449 		die("expected %s, got %s", tnames[expected], tnames[s->tok]);
    450 	}
    451 }
    452 void require(State *s, unsigned expected) {
    453 	expect(s, expected);
    454 	next(s);
    455 }
    456 
    457 void parse_reg(State *s, uint32_t *r) {
    458 	expect(s, tREGISTER);
    459 	*r = s->num;
    460 	next(s);
    461 }
    462 void parse_num(State *s, uint32_t *n) {
    463 	expect(s, tNUMBER);
    464 	*n = s->num;
    465 	next(s);
    466 }
    467 void parse_memref(State *s, uint32_t *r, uint32_t *i) {
    468 	if (s->tok == tNUMBER) {
    469 		*i = s->num;
    470 		if (next(s) != tOPAREN) {
    471 			*r = 0;
    472 			return;
    473 		}
    474 	} else {
    475 		*i = 0;
    476 	}
    477 	require(s, tOPAREN);
    478 	parse_reg(s, r);
    479 	require(s, tCPAREN);
    480 }
    481 void parse_rel(State *s, unsigned type, uint32_t *i) {
    482 	switch (s->tok) {
    483 	case tIDENT:
    484 		*i = uselabel(s->str, PC, type);
    485 		break;
    486 	case tDOT:
    487 		*i = -4;
    488 		break;
    489 	case tAT:
    490 		next(s);
    491 		if ((type == TYPE_PCREL_S16) || (type == TYPE_PCREL_S21)) {
    492 			expect(s, tNUMBER);
    493 			*i = (s->num * 4) - 4;
    494 			break;
    495 		}
    496 	default:
    497 		die("expected address");
    498 	}
    499 	next(s);
    500 }
    501 
    502 void parse_r_c(State *s, uint32_t *one) {
    503 	parse_reg(s, one);
    504 	require(s, tCOMMA);
    505 }
    506 void parse_2r(State *s, uint32_t *one, uint32_t *two) {
    507 	parse_r_c(s, one);
    508 	parse_reg(s, two);
    509 }
    510 void parse_2r_c(State *s, uint32_t *one, uint32_t *two) {
    511 	parse_2r(s, one, two);
    512 	require(s, tCOMMA);
    513 }
    514 
    515 int parse_line(State *s) {
    516 	uint32_t a, b, t, i, o;
    517 	char *name;
    518 	if (s->tok == tIDENT) {
    519 		name = strdup(s->str);
    520 		setlabel(name, PC);
    521 		if (next(s) != tCOLON) {
    522 			die("unexpected '%s'\n", name);
    523 		}
    524 		next(s);
    525 	}
    526 
    527 	unsigned tok = s->tok;
    528 	next(s);
    529 
    530 	switch (tok) {
    531 	case tADD: case tSUB: case tAND: case tOR:
    532 	case tXOR: case tSLL: case tSRL: case tSRA:
    533 	case tSLT: case tSLTU:
    534 		o = tok - tADD;
    535 		parse_2r_c(s, &t, &a);
    536 		parse_reg(s, &b);
    537 		emit(ins_r(o, t, a, b, 0));
    538 		break;
    539 	case tADDI: case tSUBI: case tANDI: case tORI:
    540 	case tXORI: case tSLLI: case tSRLI: case tSRAI:
    541 	case tSLTI: case tSLTUI:
    542 		o = tok - tADDI;
    543 		parse_2r_c(s, &t, &a);
    544 		parse_num(s, &i);
    545 		emit(ins_i(o, t, a, i));
    546 		break;
    547 	// todo: mul div
    548 	case tBEQ: case tBNE: case tBLT:
    549 	case tBLTU: case tBGE: case tBGEU:
    550 		o = tok - tBEQ;
    551 		parse_2r_c(s, &a, &b);
    552 		parse_rel(s, TYPE_PCREL_S16, &i);
    553 		emit(ins_b(o, a, b, i));
    554 		break;
    555 	case tLDW: case tLDH: case tLDB: case tLDX:
    556 	case tLDHU: case tLDBU:
    557 		o = tok - tLDW;
    558 		parse_r_c(s, &t);
    559 		parse_memref(s, &a, &i);
    560 		emit(ins_l(o, t, a, i));
    561 		break;
    562 	case tLUI:
    563  	case tAUIPC:
    564 		o = tok - tLDW;
    565 		parse_r_c(s, &t);
    566 		parse_num(s, &i);
    567 		emit(ins_l(o, t, 0, i >> 16));
    568 		break;
    569 	case tSTW: case tSTH: case tSTB: case tSTX:
    570 		o = tok - tSTW;
    571 		parse_r_c(s, &b);
    572 		parse_memref(s, &a, &i);
    573 		emit(ins_s(o, b, a, i));
    574 		break;
    575 	case tJAL:
    576 		parse_r_c(s, &t);
    577 		parse_rel(s, TYPE_PCREL_S21, &i);
    578 		emit(ins_j(J_JAL, t, i));
    579 		break;
    580 	case tSYSCALL:
    581 		parse_num(s, &i);
    582 		emit(ins_j(J_SYSCALL, 0, i));
    583 		break;
    584 	case tBREAK:
    585 		emit(ins_j(J_BREAK, 0, 0));
    586 		break;
    587 	case tSYSRET:
    588 		emit(ins_j(J_SYSRET, 0, 0));
    589 		break;
    590 	case tJALR:
    591 		parse_2r_c(s, &t, &a);
    592 		if (s->tok == tNUMBER) {
    593 			emit(ins_i(IR_JALR, t, a, s->num));
    594 		} else if (s->tok == tREGISTER) {
    595 			emit(ins_r(IR_JALR, t, a, s->num, 0));
    596 		} else {
    597 			die("expected register or immediate");
    598 		}
    599 		break;
    600 	case tNOT:
    601 		parse_2r(s, &t, &a);
    602 		emit(ins_i(IR_XOR, t, a, -1));
    603 		break;
    604 	case tNEG:
    605 		parse_2r(s, &t, &a);
    606 		emit(ins_r(IR_SUB, t, 0, a, 0));
    607 		break;
    608 	case tSEQZ:
    609 		parse_2r(s, &t, &a);
    610 		emit(ins_i(IR_SLTU, t, a, 1));
    611 		break;
    612 	case tSNEZ:
    613 		parse_2r(s, &t, &a);
    614 		emit(ins_r(IR_SLTU, t, 0, a, 0));
    615 		break;
    616 	case tSLTZ:
    617 		parse_2r(s, &t, &a);
    618 		emit(ins_r(IR_SLT, t, a, 0, 0));
    619 		break;
    620 	case tSGTZ:
    621 		parse_2r(s, &t, &a);
    622 		emit(ins_r(IR_SLT, t, 0, a, 0));
    623 		break;
    624 	case tBEQZ:
    625 		parse_r_c(s, &a);
    626 		parse_rel(s, TYPE_PCREL_S16, &i);
    627 		emit(ins_b(B_BEQ, a, 0, i));
    628 		break;
    629 	case tBNEZ:
    630 		parse_r_c(s, &a);
    631 		parse_rel(s, TYPE_PCREL_S16, &i);
    632 		emit(ins_b(B_BNE, a, 0, i));
    633 		break;
    634 	case tBLEZ:
    635 		parse_r_c(s, &a);
    636 		parse_rel(s, TYPE_PCREL_S16, &i);
    637 		emit(ins_b(B_BGE, 0, a, i));
    638 		break;
    639 	case tBGEZ:
    640 		parse_r_c(s, &a);
    641 		parse_rel(s, TYPE_PCREL_S16, &i);
    642 		emit(ins_b(B_BGE, 0, a, i));
    643 		break;
    644 	case tBLTZ:
    645 		parse_r_c(s, &a);
    646 		parse_rel(s, TYPE_PCREL_S16, &i);
    647 		emit(ins_b(B_BLT, a, 0, i));
    648 		break;
    649 	case tBGTZ:
    650 		parse_r_c(s, &a);
    651 		parse_rel(s, TYPE_PCREL_S16, &i);
    652 		emit(ins_b(B_BLT, 0, a, i));
    653 		break;
    654 	case tBGT:
    655 		parse_2r_c(s, &a, &b);
    656 		parse_rel(s, TYPE_PCREL_S16, &i);
    657 		emit(ins_b(B_BLT, b, a, i));
    658 		break;
    659 	case tBLE:
    660 		parse_2r_c(s, &a, &b);
    661 		parse_rel(s, TYPE_PCREL_S16, &i);
    662 		emit(ins_b(B_BGE, b, a, i));
    663 		break;
    664 	case tBGTU:
    665 		parse_2r_c(s, &a, &b);
    666 		parse_rel(s, TYPE_PCREL_S16, &i);
    667 		emit(ins_b(B_BLTU, b, a, i));
    668 		break;
    669 	case tBLEU:
    670 		parse_2r_c(s, &a, &b);
    671 		parse_rel(s, TYPE_PCREL_S16, &i);
    672 		emit(ins_b(B_BGEU, b, a, i));
    673 		break;
    674 	case tJR:
    675 		parse_reg(s, &a);
    676 		emit(ins_r(IR_JALR, 0, a, 0, 0));
    677 		break;
    678 	case tRET:
    679 		emit(ins_r(IR_JALR, 0, 1, 0, 0));
    680 		break;
    681 	case tNOP:
    682 		emit(ins_i(IR_ADD, 0, 0, 0));
    683 		break;
    684 	case tMV:
    685 		parse_2r(s, &t, &a);
    686 		emit(ins_i(IR_ADD, t, a, 0));
    687 		break;
    688 	case tLI:
    689 		parse_r_c(s, &t);
    690 		if (s->tok == tIDENT) {
    691 			parse_rel(s, TYPE_ABS_HILO, &i);
    692 		} else {
    693 			parse_num(s, &i);
    694 			if (fits_in_signed16(i)) {
    695 				emit(ins_i(IR_ADD, t, 0, i));
    696 			} else {
    697 				uint32_t hi = i >> 16;
    698 				uint32_t lo = i & 0xffff;
    699 				if (lo & 0x8000) hi += 1;
    700 				emit(ins_l(L_LUI, t, 0, hi));
    701 				emit(ins_i(IR_ADD, t, t, lo));
    702 			}
    703 		}
    704 		break;
    705 	case tLA:
    706 		parse_r_c(s, &t);
    707 		parse_rel(s, TYPE_PCREL_HILO, &i);
    708 		emit(ins_l(L_AUIPC, t, 0, i >> 16));
    709 		emit(ins_i(IR_ADD, t, t, i & 0xFFFF));
    710 		break;
    711 	case tJ:
    712 		parse_rel(s, TYPE_PCREL_S21, &i);
    713 		emit(ins_j(J_JAL, 0, i));
    714 		break;
    715 	case tCALL:
    716 		parse_rel(s, TYPE_PCREL_S21, &i);
    717 		emit(ins_j(J_JAL, 1, i));
    718 		break;
    719 	case tEQU:
    720 		require(s, tIDENT);
    721 		name = strdup(s->str);
    722 		parse_num(s, &i);
    723 		setlabel(name, i);
    724 		break;
    725 	case tWORD:
    726 		for (;;) {
    727 			switch (s->tok) {
    728 			case tNUMBER:
    729 				emit(s->num);
    730 				break;
    731 			case tIDENT:
    732 				emit(0);
    733 				uselabel(s->str, PC - 4, TYPE_ABS_U32);
    734 				break;
    735 			default:
    736 				die("expected constant or symbol");
    737 			}
    738 			if (next(s) != tCOMMA) break;
    739 			next(s);
    740 		}
    741 		break;
    742 	case tBYTE:
    743 		for (;;) {
    744 			switch (s->tok) {
    745 			case tNUMBER:
    746 				wr8(PC++, s->num);
    747 				break;
    748 			case tSTRING:
    749 				for (char *p = s->str; *p != 0; p++) {
    750 					wr8(PC++, *p);
    751 				}
    752 				break;
    753 			default:
    754 				die("expected numeric or string data");
    755 			}
    756 			if (next(s) != tCOMMA) break;
    757 			next(s);
    758 		}
    759 		break;
    760 
    761 	// todo: HALF
    762 	case tEOL:
    763 		return 1;
    764 	case tEOF:
    765 		return 0;
    766 	}
    767 
    768 	require(s, tEOL);
    769 	return 1;
    770 }
    771 
    772 void assemble(const char *fn) {
    773 	State state;
    774 	memset(&state, 0, sizeof(state));
    775 	state.fd = open(fn, O_RDONLY);
    776 	if (state.fd < 0) {
    777 		die("cannot open '%s'", fn);
    778 	}
    779 	state.next = state.sbuf;
    780 	linenumber = 1;
    781 	next(&state);
    782 	while (parse_line(&state)) ;
    783 }
    784 
    785 int main(int argc, char **argv) {
    786 	const char *outname = "out.hex";
    787 	filename = argv[1];
    788 
    789 	image_base = 0x100000;
    790 	image_size = sizeof(image);
    791 	PC = image_base;
    792 
    793 	if (argc < 2) {
    794 		die("no file specified");
    795 	}
    796 	if (argc == 3) {
    797 		outname = argv[2];
    798 	}
    799 
    800 	assemble(filename);
    801 	checklabels();
    802 	save(outname);
    803 	return 0;
    804 }