diff options
Diffstat (limited to 'src/c8asm')
| -rw-r--r-- | src/c8asm/assembler.c | 174 | ||||
| -rw-r--r-- | src/c8asm/parser.c | 6 | ||||
| -rw-r--r-- | src/c8asm/parser.h | 6 | 
3 files changed, 178 insertions, 8 deletions
| diff --git a/src/c8asm/assembler.c b/src/c8asm/assembler.c index 0d02555..6cdebe7 100644 --- a/src/c8asm/assembler.c +++ b/src/c8asm/assembler.c @@ -1,3 +1,4 @@ +#include <arpa/inet.h>  #include <stddef.h>  #include <stdint.h>  #include <stdio.h> @@ -16,7 +17,8 @@  #	define unreachable() __builtin_unreachable()  #endif -#define E_LEXISTS "label ‘%.*s’ has already been declared" +#define E_LEXISTS  "label ‘%.*s’ has already been declared" +#define E_LNEXISTS "label ‘%.*s’ hasn’t been declared"  struct label {  	uint16_t addr; @@ -30,8 +32,11 @@ struct labels {  static bool u8eq(struct u8view, struct u8view);  static void pushlabel(struct labels *, struct label); +static uint16_t getaddr(struct raw_addr); +static struct label *getlabel(struct u8view);  static size_t i; +static struct labels locals, globals;  bool  u8eq(struct u8view x, struct u8view y) @@ -39,6 +44,29 @@ u8eq(struct u8view x, struct u8view y)  	return x.len == y.len && memcmp(x.p, y.p, x.len) == 0;  } +struct label * +getlabel(struct u8view sv) +{ +	struct labels *xs = sv.p[0] == '.' ? &locals : &globals; +	da_foreach (xs, x) { +		if (u8eq(x->sv, sv)) +			return x; +	} +	return nullptr; +} + +uint16_t +getaddr(struct raw_addr a) +{ +	if (a.label) { +		struct label *lbl = getlabel(a.sv); +		if (lbl) +			return lbl->addr; +		die_with_off(filename, a.sv.p - baseptr, E_LNEXISTS, U8_PRI_ARGS(a.sv)); +	} +	return a.val; +} +  void  pushlabel(struct labels *dst, struct label lbl)  { @@ -55,7 +83,11 @@ pushlabel(struct labels *dst, struct label lbl)  void  assemble([[maybe_unused]] FILE *stream, struct ast ast)  { -	static struct labels locals, globals; +#define PUT(X) \ +	do { \ +		uint16_t __x = htons(X); \ +		fwrite(&__x, 1, sizeof(__x), stream); \ +	} while (false)  	da_foreach (&ast, node) {  		if (node->kind == D_LABEL) { @@ -70,5 +102,143 @@ assemble([[maybe_unused]] FILE *stream, struct ast ast)  			unreachable();  	} +	da_foreach (&ast, node) { +		if (node->kind == D_LABEL) +			continue; +		if (node->kind != D_INSTR) +			unreachable(); + +		switch (node->instr.kind) { +		case I_ADD_I_VX: +			PUT(0xF01E | (node->instr.args[0].val << 8)); +			break; +		case I_ADD_VX_B: +			PUT(0x7000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val); +			break; +		case I_ADD_VX_VY: +			PUT(0x8004 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_AND: +			PUT(0x8002 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_BCD: +			PUT(0xF033 | (node->instr.args[0].val << 8)); +			break; +		case I_CALL: +			PUT(0x2000 | getaddr(node->instr.args[0])); +			break; +		case I_CLS: +			PUT(0x00E0); +			break; +		case I_DB: +			da_foreach (&node->instr, byte) +				fputc(*byte, stream); +			break; +		case I_DRW: +			PUT(0xD000 | (node->instr.args[0].val << 8) +			    | (node->instr.args[1].val << 4) | node->instr.args[2].val); +			break; +		case I_HEX: +			PUT(0xF029 | (node->instr.args[0].val << 8)); +			break; +		case I_JP_A: +			PUT(0x1000 | getaddr(node->instr.args[0])); +			break; +		case I_JP_V0_A: +			PUT(0xB000 | getaddr(node->instr.args[0])); +			break; +		case I_LD_DT: +			PUT(0xF015 | (node->instr.args[0].val << 8)); +			break; +		case I_LD_I: +			PUT(0xA000 | getaddr(node->instr.args[0])); +			break; +		case I_LD_ST: +			PUT(0xF018 | (node->instr.args[0].val << 8)); +			break; +		case I_LD_VX_B: +			PUT(0x6000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val); +			break; +		case I_LD_VX_DT: +			PUT(0xF007 | (node->instr.args[0].val << 8)); +			break; +		case I_LD_VX_K: +			PUT(0xF00A | (node->instr.args[0].val << 8)); +			break; +		case I_LD_VX_VY: +			PUT(0x8000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_OR: +			PUT(0x8001 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_RET: +			PUT(0x00EE); +			break; +		case I_RND: +			PUT(0xC000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val); +			break; +		case I_RSTR: +			PUT(0xF065 | (node->instr.args[0].val << 8)); +			break; +		case I_SE_VX_B: +			PUT(0x3000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val); +			break; +		case I_SE_VX_VY: +			PUT(0x5000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_SHL: +			PUT(0x800E | (node->instr.args[0].val << 8)); +			break; +		case I_SHR: +			PUT(0x8006 | (node->instr.args[0].val << 8)); +			break; +		case I_SKNP: +			PUT(0xE0A1 | (node->instr.args[0].val << 8)); +			break; +		case I_SKP: +			PUT(0xE09E | (node->instr.args[0].val << 8)); +			break; +		case I_SNE_VX_B: +			PUT(0x4000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val); +			break; +		case I_SNE_VX_VY: +			PUT(0x9000 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_STOR: +			PUT(0xF055 | (node->instr.args[0].val << 8)); +			break; +		case I_SUB: +			PUT(0x8005 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_SUBN: +			PUT(0x8007 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		case I_SYS: +			PUT(getaddr(node->instr.args[0])); +			break; +		case I_XOR: +			PUT(0x8003 | (node->instr.args[0].val << 8) +			    | node->instr.args[1].val << 4); +			break; +		default: +			unreachable(); +		} +	} +  	locals.len = 0; + +#undef PUT  } diff --git a/src/c8asm/parser.c b/src/c8asm/parser.c index b746bea..03d190d 100644 --- a/src/c8asm/parser.c +++ b/src/c8asm/parser.c @@ -383,10 +383,10 @@ parseop_jp(void)  	if (op.kind == T_IDENT)  		rt = regtype(op.sv);  	if (op.kind == T_NUMBER || (op.kind == T_IDENT && rt == RT_NONE)) { -		ins.kind = I_JP_ADDR; +		ins.kind = I_JP_A;  		ins.args[ins.len++] = parseaddr(op);  	} else if (op.kind == T_IDENT) { -		ins.kind = I_JP_V0_ADDR; +		ins.kind = I_JP_V0_A;  		if (op.sv.len != 2 || !memeq(op.sv.p, "v0", 2)) {  			die_with_off(op.sv.p, E_EXPECTED, "v0-register or address",  			             tokrepr(op.kind), U8_PRI_ARGS(op.sv)); @@ -447,7 +447,7 @@ parseop_ld(void)  			break;  		case T_NUMBER: -			ins.kind = I_LD_VX_BYTE; +			ins.kind = I_LD_VX_B;  			ins.args[ins.len++].val = parsenum(op, NS_BYTE);  			break;  		default: diff --git a/src/c8asm/parser.h b/src/c8asm/parser.h index 392b003..a8cc03c 100644 --- a/src/c8asm/parser.h +++ b/src/c8asm/parser.h @@ -19,12 +19,12 @@ typedef enum {  	I_DB,  	I_DRW,  	I_HEX, -	I_JP_ADDR, -	I_JP_V0_ADDR, +	I_JP_A, +	I_JP_V0_A,  	I_LD_DT,  	I_LD_I,  	I_LD_ST, -	I_LD_VX_BYTE, +	I_LD_VX_B,  	I_LD_VX_DT,  	I_LD_VX_K,  	I_LD_VX_VY, |