diff options
author | Thomas Voss <mail@thomasvoss.com> | 2024-02-13 14:00:32 +0100 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2024-02-13 14:00:32 +0100 |
commit | c23acb820f8698361c65911649f97a9320c2c2b8 (patch) | |
tree | b4b083ff111d8bbac63e666c60c2625118d648f9 /src/c8asm | |
parent | 6dca08dfe5e1c1cfc6f40b2e421027e13821bdb6 (diff) |
Complete the assembler
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, |