aboutsummaryrefslogtreecommitdiff
path: root/tr8as.c
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2023-05-01 13:50:52 +0100
committerJuan J. Martinez <jjm@usebox.net>2023-05-01 13:58:09 +0100
commit8998bd04c94da08dc49ab62007da5604d53895c3 (patch)
tree43a588f7a372ce17d035536a56fd71691fa6a73f /tr8as.c
downloadtr8vm-8998bd04c94da08dc49ab62007da5604d53895c3.tar.gz
tr8vm-8998bd04c94da08dc49ab62007da5604d53895c3.zip
Initial import
Diffstat (limited to 'tr8as.c')
-rw-r--r--tr8as.c1341
1 files changed, 1341 insertions, 0 deletions
diff --git a/tr8as.c b/tr8as.c
new file mode 100644
index 0000000..3f5aba2
--- /dev/null
+++ b/tr8as.c
@@ -0,0 +1,1341 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+
+#define MAX_LINE 1024
+#define MAX_ID 0x40
+#define MAX_LABELS 0x200
+#define MAX_REFS 0x1000
+
+#define FHH 2
+#define FHL 1
+#define FL 32
+
+typedef struct
+{
+ const char *filename;
+ uint16_t line;
+} Location;
+
+typedef struct
+{
+ char id[MAX_ID + 1];
+ uint16_t addr;
+ Location loc;
+} Label;
+
+typedef enum
+{
+ ModNone = 0,
+ ModLow,
+ ModHigh
+} Mod;
+
+typedef struct
+{
+ char id[MAX_ID + 1];
+ Mod mod;
+ uint16_t mask;
+ uint16_t addr;
+ Location loc;
+} Reference;
+
+typedef struct
+{
+ uint8_t out[UINT16_MAX + 1];
+ size_t size;
+
+ uint16_t addr;
+ Location loc;
+
+ Label labels[MAX_LABELS];
+ uint16_t lcnt;
+
+ Reference refs[MAX_REFS];
+ uint16_t rcnt;
+} As;
+
+typedef struct
+{
+ char id[5];
+ uint8_t (*parse)(As *, char *);
+} InstParse;
+
+static uint8_t error_l(const char *msg, Location *loc, const char *reason)
+{
+ fprintf(stderr, "%s (%s:%d): %s\n", msg, loc->filename, loc->line, reason);
+ return 0;
+}
+
+static uint8_t error(const char *msg, const char *reason)
+{
+ if (reason == NULL)
+ fprintf(stderr, "error: %s\n", msg);
+ else
+ fprintf(stderr, "%s: %s\n", msg, reason);
+ return 0;
+}
+
+static char * skip_whitespace(char *c)
+{
+ while (*c && isspace(*c))
+ c++;
+ return c;
+}
+
+static uint8_t isspecial(char c)
+{
+ return c == '$' || c == '_' || c == '.' || c == '#' || c == '<' || c == '>';
+}
+
+static char * next_word(char *c, char *word, uint8_t *wlen)
+{
+ c = skip_whitespace(c);
+
+ *wlen = 0;
+ while (*c && (isalnum(*c) || isspecial(*c)))
+ {
+ word[(*wlen)++] = *c++;
+ if (*wlen == MAX_ID)
+ {
+ /* XXX: should be an error? */
+ break;
+ }
+ }
+ word[*wlen] = 0;
+
+ return c;
+}
+
+static uint8_t next_imm(char *word, uint16_t *n)
+{
+ *n = 0;
+
+ if (*word == '0')
+ {
+ /* hex */
+ if (word[1] == 'x')
+ {
+ word += 2;
+ while (*word)
+ {
+ if (*word >= '0' && *word <= '9')
+ *n = (*n) * 16 + (*word - '0');
+ else if (*word >= 'a' && *word <= 'f')
+ *n = (*n) * 16 + 10 + (*word - 'a');
+ else if (*word >= 'A' && *word <= 'F')
+ *n = (*n) * 16 + 10 + (*word - 'A');
+ else
+ break;
+
+ word++;
+ }
+ return *word;
+ }
+ /* bin */
+ else if (word[1] == 'b')
+ {
+ word += 2;
+ while (*word)
+ {
+ if (*word == '0' || *word == '1')
+ *n = (*n) * 2 + (*word - '0');
+ else
+ break;
+
+ word++;
+ }
+ return *word;
+ }
+ }
+
+ /* dec */
+ while (*word)
+ {
+ if (*word >= '0' && *word <= '9')
+ *n = (*n) * 10 + (*word - '0');
+ else
+ break;
+
+ word++;
+ }
+
+ return *word;
+}
+
+static uint8_t parse_register(char *word)
+{
+ if (!strcasecmp(word, "a"))
+ return 0;
+ if (!strcasecmp(word, "b"))
+ return 1;
+ if (!strcasecmp(word, "x"))
+ return 2;
+ if (!strcasecmp(word, "y"))
+ return 3;
+
+ return 0xff;
+}
+
+static Label * find_label(As *as, char *word)
+{
+ uint8_t i;
+
+ for (i = 0; i < as->lcnt; i++)
+ if (!strcmp(word, as->labels[i].id))
+ return &as->labels[i];
+
+ return NULL;
+}
+
+static uint8_t new_label(As *as, char *word)
+{
+ if (find_label(as, word))
+ return error_l("Label redefined", &as->loc, word);
+
+ strcpy(as->labels[as->lcnt].id, word);
+
+ as->labels[as->lcnt].loc.filename = as->loc.filename;
+ as->labels[as->lcnt].loc.line = as->loc.line;
+ as->labels[as->lcnt++].addr = as->addr;
+
+ if (as->lcnt == MAX_LABELS)
+ return error("Too many labels", NULL);
+
+ return 1;
+}
+
+static uint8_t new_ref(As *as, char *word, uint16_t mask, uint16_t addr)
+{
+ Mod mod = ModNone;
+ char *pt = word;
+
+ if (*pt == '<')
+ {
+ mod = ModLow;
+ pt++;
+ }
+ else if (*pt == '>')
+ {
+ mod = ModHigh;
+ pt++;
+ }
+
+ if (!*pt)
+ return error_l("Syntax error", &as->loc, "expected label");
+
+ if (mask == 0xff && mod == ModNone)
+ return error_l("Overflow in immediate", &as->loc, word);
+
+ strcpy(as->refs[as->rcnt].id, pt);
+ as->refs[as->rcnt].mod = mod;
+ as->refs[as->rcnt].mask = mask;
+ as->refs[as->rcnt].loc = as->loc;
+ as->refs[as->rcnt++].addr = addr;
+
+ if (as->rcnt == MAX_REFS)
+ return error("Too many references", NULL);
+
+ return 1;
+}
+
+static uint8_t resolve(As *as)
+{
+ uint16_t i;
+ uint16_t v;
+ Label *label;
+
+ for (i = 0; i < as->rcnt; i++)
+ {
+ label = find_label(as, as->refs[i].id);
+ if (!label)
+ return error_l("Unresolved reference", &as->refs[i].loc, as->refs[i].id);
+
+ v = label->addr;
+ switch (as->refs[i].mod)
+ {
+ case ModLow:
+ v &= 0xff;
+ break;
+ case ModHigh:
+ v >>= 8;
+ break;
+ default:
+ break;
+ }
+ as->out[as->refs[i].addr] = v & 0xff;
+ if (as->refs[i].mask == 0xffff)
+ as->out[as->refs[i].addr + 1] = v >> 8;
+ }
+
+ return 1;
+}
+
+/*
+ * inst: instruction opcode
+ * p1: r1
+ * p2: either FHx or r2
+ * p3: either FL, r3 or immediate
+ */
+static void emit(As *as, uint8_t instr, uint8_t p1, uint8_t p2, uint8_t p3)
+{
+ as->out[as->addr++] = p3;
+ as->out[as->addr++] = (instr << 4) | (p1 << 2) | p2;
+ if (as->addr > as->size)
+ as->size = as->addr;
+}
+
+static void emit_imm(As *as, uint16_t imm)
+{
+ as->out[as->addr++] = imm & 0xff;
+ as->out[as->addr++] = imm >> 8;
+ if (as->addr > as->size)
+ as->size = as->addr;
+}
+
+static uint8_t parse_org(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+
+ /* .org imm */
+
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (next_imm(word, &as->addr))
+ return error_l("Syntax error", &as->loc, word);
+
+ if (as->addr > as->size)
+ as->size = as->addr;
+
+ return 1;
+}
+
+static uint8_t parse_db(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+ uint16_t imm = 0xff;
+
+ /* .db imm [, imm] */
+
+ pt = c;
+ while (1)
+ {
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (isdigit(*word))
+ {
+ if (next_imm(word, &imm))
+ return error_l("Syntax error", &as->loc, word);
+
+ if (imm > 0xff)
+ return error_l("Overflow in immediate", &as->loc, word);
+ }
+ else if (!new_ref(as, word, 0xff, as->addr))
+ return 0;
+
+ as->out[as->addr++] = imm & 0xff;
+ if (as->addr > as->size)
+ as->size = as->addr;
+
+ if (*pt == ',')
+ {
+ pt++;
+ continue;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+static uint8_t parse_dw(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+ uint16_t imm;
+
+ /* .dw imm [, imm] */
+
+ pt = c;
+ while (1)
+ {
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (isdigit(*word))
+ {
+ if (next_imm(word, &imm))
+ return error_l("Syntax error", &as->loc, word);
+ }
+ else if (!new_ref(as, word, 0xffff, as->addr))
+ return 0;
+
+ as->out[as->addr++] = imm & 0xff;
+ as->out[as->addr++] = imm >> 8;
+ if (as->addr > as->size)
+ as->size = as->addr;
+
+ if (*pt == ',')
+ {
+ pt++;
+ continue;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+static uint8_t parse_nop(As *as, char *c)
+{
+ /* NOP */
+ emit(as, 0, 0, 0, 0);
+ return 1;
+}
+
+static uint8_t parse_sif(As *as, char *c)
+{
+ /* SIF */
+ emit(as, 11, 0, FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_cif(As *as, char *c)
+{
+ /* CIF */
+ emit(as, 11, 0, 0, 0);
+ return 1;
+}
+
+static uint8_t parse_ccf(As *as, char *c)
+{
+ /* CCF */
+ emit(as, 13, 0, FHH | FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_scf(As *as, char *c)
+{
+ /* SCF */
+ emit(as, 13, 0, FHH, 0);
+ return 1;
+}
+
+static uint8_t parse_sof(As *as, char *c)
+{
+ /* SOF */
+ emit(as, 13, 0, FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_cof(As *as, char *c)
+{
+ /* COF */
+ emit(as, 13, 0, 0, 0);
+ return 1;
+}
+
+static uint8_t parse_halt(As *as, char *c)
+{
+ /* HALT */
+ emit(as, 0, 0, FHH | FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_iret(As *as, char *c)
+{
+ /* IRET */
+ emit(as, 0, 0, FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_ret(As *as, char *c)
+{
+ /* RET */
+ emit(as, 0, 0, 0, FL);
+ return 1;
+}
+
+static uint8_t parse_r1_r2_or_imm(As *as, char *c, uint8_t *r1, uint8_t *r2, uint16_t *imm)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+
+ /* ? r1, ? */
+ pt = next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ *r1 = parse_register(word);
+ if (*r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ pt = skip_whitespace(pt);
+ if (*pt != ',')
+ return error_l("Syntax error", &as->loc, "expected ,");
+ pt++;
+
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register or immediate");
+
+ /* ? r1, r2 */
+ *r2 = parse_register(word);
+ if (*r2 != 0xff)
+ return 1;
+
+ /* ? r1, imm */
+ if (isdigit(*word))
+ {
+ if (next_imm(word, imm))
+ return error_l("Syntax error", &as->loc, word);
+
+ if (*imm > 0xff)
+ return error_l("Overflow in immediate", &as->loc, word);
+ }
+ /* AND r1, label */
+ else if (!new_ref(as, word, 0xff, as->addr + 1))
+ return 0;
+
+ return 1;
+}
+
+static uint8_t parse_r1_imm(As *as, char *c, uint8_t *r1, uint16_t *imm)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+
+ /* ? r1, imm */
+ pt = next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ *r1 = parse_register(word);
+ if (*r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ pt = skip_whitespace(pt);
+ if (*pt != ',')
+ return error_l("Syntax error", &as->loc, "expected ,");
+ pt++;
+
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (!isdigit(*word))
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (next_imm(word, imm))
+ return error_l("Syntax error", &as->loc, word);
+
+ return 1;
+}
+
+static uint8_t parse_and(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1, r2;
+
+ if (!parse_r1_r2_or_imm(as, c, &r1, &r2, &imm))
+ return 0;
+
+ if (r2 != 0xff)
+ /* AND r1, r2 */
+ emit(as, 4, r1, FHH | FHL, (r2 << 6));
+ else
+ /* AND r1, imm */
+ emit(as, 4, r1, FHH, imm);
+
+ return 1;
+}
+
+static uint8_t parse_or(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1, r2;
+
+ if (!parse_r1_r2_or_imm(as, c, &r1, &r2, &imm))
+ return 0;
+
+ if (r2 != 0xff)
+ /* OR r1, r2 */
+ emit(as, 4, r1, FHL, (r2 << 6));
+ else
+ /* OR r1, imm */
+ emit(as, 4, r1, 0, imm);
+
+ return 1;
+}
+
+static uint8_t parse_xor(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1, r2;
+
+ if (!parse_r1_r2_or_imm(as, c, &r1, &r2, &imm))
+ return 0;
+
+ if (r2 != 0xff)
+ /* XOR r1, r2 */
+ emit(as, 5, r1, FHH | FHL, (r2 << 6));
+ else
+ /* XOR r1, imm */
+ emit(as, 5, r1, FHH, imm);
+
+ return 1;
+}
+
+static uint8_t parse_cmp(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1, r2;
+
+ if (!parse_r1_r2_or_imm(as, c, &r1, &r2, &imm))
+ return 0;
+
+ if (r2 != 0xff)
+ /* CMP r1, r2 */
+ emit(as, 5, r1, FHL, (r2 << 6));
+ else
+ /* CMP r1, imm */
+ emit(as, 5, r1, 0, imm);
+
+ return 1;
+}
+
+static uint8_t parse_add(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1, r2;
+
+ if (!parse_r1_r2_or_imm(as, c, &r1, &r2, &imm))
+ return 0;
+
+ if (r2 != 0xff)
+ /* ADD r1, r2 */
+ emit(as, 6, r1, FHH | FHL, (r2 << 6));
+ else
+ /* ADD r1, imm */
+ emit(as, 6, r1, FHH, imm);
+
+ return 1;
+}
+
+static uint8_t parse_sub(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1, r2;
+
+ if (!parse_r1_r2_or_imm(as, c, &r1, &r2, &imm))
+ return 0;
+
+ if (r2 != 0xff)
+ /* SUB r1, r2 */
+ emit(as, 6, r1, FHL, (r2 << 6));
+ else
+ /* SUB r1, imm */
+ emit(as, 6, r1, 0, imm);
+
+ return 1;
+}
+
+static uint8_t parse_bit(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1;
+
+ /* BIT r1, imm */
+ if (!parse_r1_imm(as, c, &r1, &imm))
+ return 0;
+
+ if (imm > 7)
+ return error_l("Immediate out of range", &as->loc, "expected bit (0-7)");
+
+ emit(as, 7, r1, FHH, imm);
+ return 1;
+}
+
+static uint8_t parse_shl(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1;
+
+ /* SHL r1, imm */
+ if (!parse_r1_imm(as, c, &r1, &imm))
+ return 0;
+
+ if (imm > 7)
+ return error_l("Immediate out of range", &as->loc, "expected bit (0-7)");
+
+ emit(as, 7, r1, FHL, imm);
+ return 1;
+}
+
+static uint8_t parse_shr(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1;
+
+ /* SHR r1, imm */
+ if (!parse_r1_imm(as, c, &r1, &imm))
+ return 0;
+
+ if (imm > 7)
+ return error_l("Immediate out of range", &as->loc, "expected bit (0-7)");
+
+ emit(as, 7, r1, 0, imm);
+ return 1;
+}
+
+static uint8_t parse_ror(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1;
+
+ /* ROR r1, imm */
+ if (!parse_r1_imm(as, c, &r1, &imm))
+ return 0;
+
+ if (imm > 7)
+ return error_l("Immediate out of range", &as->loc, "expected bit (0-7)");
+
+ emit(as, 8, r1, 0, imm);
+ return 1;
+}
+
+static uint8_t parse_rol(As *as, char *c)
+{
+ uint16_t imm = 0xff;
+ uint8_t r1;
+
+ /* ROL r1, imm */
+ if (!parse_r1_imm(as, c, &r1, &imm))
+ return 0;
+
+ if (imm > 7)
+ return error_l("Immediate out of range", &as->loc, "expected bit (0-7)");
+
+ emit(as, 8, r1, FHH, imm);
+ return 1;
+}
+
+static uint8_t parse_push(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+ uint8_t r1;
+
+ /* PUSH r1 */
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ {
+ /* PUSH F */
+ if (*word == 'f' || *word == 'F')
+ {
+ emit(as, 3, 0, FHH | FHL, 0);
+ return 1;
+ }
+ return error_l("Syntax error", &as->loc, word);
+ }
+
+ emit(as, 3, r1, FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_port(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+ uint8_t r1, r2;
+
+ /* PORT r1, r2 */
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r2 = parse_register(word);
+ if (r2 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ emit(as, 0, r1, FHH, r2);
+ return 1;
+}
+
+static uint8_t parse_pop(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+ uint8_t r1;
+
+ /* POP r1 */
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ {
+ /* POP F */
+ if (*word == 'f' || *word == 'F')
+ {
+ emit(as, 3, 0, FHH, 0);
+ return 1;
+ }
+ return error_l("Syntax error", &as->loc, word);
+ }
+
+ emit(as, 3, r1, 0, 0);
+ return 1;
+}
+
+static uint8_t parse_xsp(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+ uint8_t r1;
+
+ /* XSP r1 */
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ emit(as, 3, r1, 0, FL);
+ return 1;
+}
+
+static uint8_t parse_inc(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+ uint8_t r1;
+
+ /* INC r1 */
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ emit(as, 11, r1, FHH | FHL, 0);
+ return 1;
+}
+
+static uint8_t parse_dec(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+ uint8_t r1;
+
+ /* INC r1 */
+ next_word(c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ emit(as, 11, r1, FHH, 0);
+ return 1;
+}
+
+static uint8_t parse_bz(As *as, char *c)
+{
+ /* BZ */
+ emit(as, 10, 0, 0, 0);
+ return 1;
+}
+
+static uint8_t parse_bnz(As *as, char *c)
+{
+ /* BNZ */
+ emit(as, 10, 0, FHH, 0);
+ return 1;
+}
+
+static uint8_t parse_bc(As *as, char *c)
+{
+ /* BC */
+ emit(as, 10, 0, 0, 1);
+ return 1;
+}
+
+static uint8_t parse_bnc(As *as, char *c)
+{
+ /* BNC */
+ emit(as, 10, 0, FHH, 1);
+ return 1;
+}
+
+static uint8_t parse_bo(As *as, char *c)
+{
+ /* BO */
+ emit(as, 10, 0, 0, 2);
+ return 1;
+}
+
+static uint8_t parse_bno(As *as, char *c)
+{
+ /* BNO */
+ emit(as, 10, 0, FHH, 2);
+ return 1;
+}
+
+static uint8_t parse_bs(As *as, char *c)
+{
+ /* BS */
+ emit(as, 10, 0, 0, 3);
+ return 1;
+}
+
+static uint8_t parse_bns(As *as, char *c)
+{
+ /* BNS */
+ emit(as, 10, 0, FHH, 3);
+ return 1;
+}
+
+static uint8_t parse_bi(As *as, char *c)
+{
+ /* BI */
+ emit(as, 10, 0, 0, 4);
+ return 1;
+}
+
+static uint8_t parse_bni(As *as, char *c)
+{
+ /* BNI */
+ emit(as, 10, 0, FHH, 4);
+ return 1;
+}
+
+static uint8_t parse_indirect(As *as, char **c, uint8_t *r1, uint8_t *r2, uint16_t *imm)
+{
+ char word[MAX_ID + 1];
+ uint8_t wlen;
+
+ /* [r1:r2] */
+ *c = next_word(*c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ *r1 = parse_register(word);
+ if (*r1 == 0xff)
+ {
+ if (imm == NULL)
+ return error_l("Syntax error", &as->loc, word);
+
+ /* [SP + imm] */
+ if (!strcasecmp(word, "sp"))
+ {
+ *c = skip_whitespace(*c);
+ if (**c != '+')
+ return error_l("Syntax error", &as->loc, "expected +");
+ (*c)++;
+
+ *c = next_word(*c, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (!isdigit(*word))
+ return error_l("Syntax error", &as->loc, "expected immediate");
+
+ if (next_imm(word, imm))
+ return error_l("Syntax error", &as->loc, word);
+
+ if (*imm > 0xff)
+ return error_l("Overflow in immediate", &as->loc, word);
+
+ *c = skip_whitespace(*c);
+ if (**c != ']')
+ return error_l("Syntax error", &as->loc, "expected ]");
+ (*c)++;
+
+ return 1;
+ }
+ else
+ return error_l("Syntax error", &as->loc, word);
+ }
+
+ *c = skip_whitespace(*c);
+ if (**c != ':')
+ return error_l("Syntax error", &as->loc, "expected :");
+
+ *c = next_word(*c + 1, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ *r2 = parse_register(word);
+ if (*r2 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ *c = skip_whitespace(*c);
+ if (**c != ']')
+ return error_l("Syntax error", &as->loc, "expected ]");
+
+ (*c)++;
+ return 1;
+}
+
+static uint8_t parse_jmp(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+ uint16_t imm = 0xffff;
+ uint8_t r1, r2;
+
+ pt = skip_whitespace(c);
+
+ /* JMP [r1:r2] */
+ if (*pt == '[')
+ {
+ pt++;
+ if (!parse_indirect(as, &pt, &r1, &r2, NULL))
+ return 0;
+
+ emit(as, 9, r1, 0, (r2 << 6) | FL);
+ return 1;
+ }
+
+ next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected label or immediate");
+
+ /* JMP imm */
+ if (isdigit(*word))
+ {
+ if (next_imm(word, &imm))
+ return error_l("Syntax error", &as->loc, word);
+ }
+ /* JMP label */
+ else if (!new_ref(as, word, 0xffff, as->addr + 2))
+ return 0;
+
+ emit(as, 9, 0, FHH, FL);
+ emit_imm(as, imm);
+ return 1;
+}
+
+static uint8_t parse_call(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+ uint16_t imm = 0xffff;
+ uint8_t r1, r2;
+
+ pt = skip_whitespace(c);
+
+ /* CALL [r1:r2] */
+ if (*pt == '[')
+ {
+ pt++;
+ if (!parse_indirect(as, &pt, &r1, &r2, NULL))
+ return 0;
+
+ emit(as, 9, r1, 0, (r2 << 6));
+ return 1;
+ }
+
+ next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected label or immediate");
+
+ /* CALL imm */
+ if (isdigit(*word))
+ {
+ if (next_imm(word, &imm))
+ return error_l("Syntax error", &as->loc, word);
+ }
+ /* CALL label */
+ else if (!new_ref(as, word, 0xffff, as->addr + 2))
+ return 0;
+
+ emit(as, 9, 0, FHH, 0);
+ emit_imm(as, imm);
+ return 1;
+}
+
+static uint8_t parse_ld(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+ uint16_t imm = 0xff;
+
+ uint8_t r1, r2, r3;
+
+ pt = skip_whitespace(c);
+ /* LD [r1:r2], r3 */
+ /* LD [sp + imm], r2 */
+ if (*pt == '[')
+ {
+ pt++;
+ if (!parse_indirect(as, &pt, &r1, &r2, &imm))
+ return 0;
+
+ /* expected , */
+ pt = skip_whitespace(pt);
+ if (*pt != ',')
+ return error_l("Syntax error", &as->loc, "expected ,");
+
+ pt = next_word(pt + 1, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r3 = parse_register(word);
+ if (r3 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ if (r1 == 0xff)
+ emit(as, 12, r3, FHH, imm);
+ else
+ emit(as, 2, r1, r2, (r3 << 6) | FL);
+ return 1;
+ }
+
+ /* LD r1, ? */
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register");
+
+ r1 = parse_register(word);
+ if (r1 == 0xff)
+ return error_l("Syntax error", &as->loc, word);
+
+ pt = skip_whitespace(pt);
+ if (*pt != ',')
+ return error_l("Syntax error", &as->loc, "expected ,");
+ pt++;
+
+ pt = skip_whitespace(pt);
+ /* LD r1, [r2:r3] */
+ /* LD r1, [sp + imm] */
+ if (*pt == '[')
+ {
+ pt++;
+ if (!parse_indirect(as, &pt, &r2, &r3, &imm))
+ return 0;
+
+ if (r2 == 0xff)
+ emit(as, 12, r1, 0, imm);
+ else
+ emit(as, 2, r2, r3, r1 << 6);
+ return 1;
+ }
+
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return error_l("Syntax error", &as->loc, "expected register or immediate");
+
+ /* LD r1, r2 */
+ r2 = parse_register(word);
+ if (r2 != 0xff)
+ {
+ emit(as, 1, r1, FHH, (r2 << 6));
+ return 1;
+ }
+
+ /* LD r1, imm */
+ if (isdigit(*word))
+ {
+ if (next_imm(word, &imm))
+ return error_l("Syntax error", &as->loc, word);
+
+ if (imm > 0xff)
+ return error_l("Overflow in immediate", &as->loc, word);
+ }
+ /* LD r1, label */
+ else if (!new_ref(as, word, 0xff, as->addr + 1))
+ return 0;
+
+ emit(as, 1, r1, 0, imm);
+ return 1;
+}
+
+static InstParse insts[] = {
+ { ".org", parse_org },
+ { ".db", parse_db },
+ { ".dw", parse_dw },
+ { "halt", parse_halt },
+ { "push", parse_push },
+ { "port", parse_port },
+ { "iret", parse_iret },
+ { "call", parse_call },
+ { "xsp", parse_xsp },
+ { "and", parse_and },
+ { "cmp", parse_cmp },
+ { "add", parse_add },
+ { "sub", parse_sub },
+ { "bit", parse_bit },
+ { "shl", parse_shl },
+ { "shr", parse_shr },
+ { "ror", parse_ror },
+ { "rol", parse_rol },
+ { "xor", parse_xor },
+ { "ret", parse_ret },
+ { "pop", parse_pop },
+ { "nop", parse_nop },
+ { "inc", parse_inc },
+ { "dec", parse_dec },
+ { "bnz", parse_bnz },
+ { "bnc", parse_bnc },
+ { "bno", parse_bno },
+ { "bns", parse_bns },
+ { "bni", parse_bni },
+ { "jmp", parse_jmp },
+ { "sif", parse_sif },
+ { "cif", parse_cif },
+ { "ccf", parse_ccf },
+ { "scf", parse_scf },
+ { "sof", parse_sof },
+ { "cof", parse_cof },
+ { "or", parse_or },
+ { "bz", parse_bz },
+ { "bc", parse_bc },
+ { "bo", parse_bo },
+ { "bs", parse_bs },
+ { "bi", parse_bi },
+ { "ld", parse_ld },
+ { "", NULL },
+};
+
+static uint8_t parse(As *as, char *c)
+{
+ char word[MAX_ID + 1];
+ char *pt;
+ uint8_t wlen;
+ uint8_t i;
+
+ pt = next_word(c, word, &wlen);
+ if (wlen == 0)
+ return 1;
+
+ /* comment */
+ if (word[0] == ';')
+ return 1;
+
+ /* new label */
+ if (*pt == ':')
+ {
+ pt++;
+
+ if (!new_label(as, word))
+ return 0;
+ }
+ else
+ {
+ /* instructions */
+ for (i = 0; insts[i].parse; i++)
+ if (!strcasecmp(insts[i].id, word))
+ return insts[i].parse(as, pt);
+ printf("NOPE %s\n", word);
+
+ return error_l("Parse error", &as->loc, word);
+ }
+
+ /* EOL */
+ pt = next_word(pt, word, &wlen);
+ if (wlen == 0)
+ return 1;
+
+ /* or comment */
+ if (word[0] == ';')
+ return 1;
+
+ return error_l("Parse error", &as->loc, word);
+}
+
+static uint8_t asm(As *as, const char *filename, FILE *in)
+{
+ char line[MAX_LINE];
+ memset(as, 0, sizeof(As));
+
+ as->loc.filename = filename;
+
+ while (fgets(line, MAX_LINE - 1, in))
+ {
+ as->loc.line++;
+ if (!parse(as, line))
+ return 0;
+ }
+
+ return resolve(as);
+}
+
+#ifdef DO_MAIN
+
+int main(int argc, char *argv[])
+{
+ int rc = 0;
+ FILE *src, *out;
+ As as;
+
+ if (argc < 3)
+ {
+ error("usage", "input.asm output.tr8");
+ return 1;
+ }
+ src = fopen(argv[1], "rt");
+ if (!src)
+ {
+ error("Failed to open input", argv[1]);
+ return 1;
+ }
+
+ if (asm(&as, argv[1], src))
+ {
+ out = fopen(argv[2], "wb");
+ if (!out)
+ {
+ fclose(src);
+ error("Failed to open output", argv[2]);
+ return 1;
+ }
+ fwrite(as.out, as.size, 1, out);
+ fclose(out);
+
+ fprintf(stderr, "%s: %lu bytes, OK\n", argv[2], as.size);
+ }
+ else
+ rc = 11;
+
+ fclose(src);
+ return rc;
+}
+
+#endif /* DO_MAIN */