/* * TR8 Assembler * Copyright (C) 2023 by Juan J. Martinez * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include #include #include #include #include #include #include "stb_image.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[9]; uint8_t (*parse)(As *, char **); } InstParse; /* used to map RGB values to palette index when including a PNG image */ static uint8_t palette[16][3] = { { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xaa }, { 0x00, 0xaa, 0x00 }, { 0x00, 0xaa, 0xaa }, { 0xaa, 0x00, 0x00 }, { 0xaa, 0x00, 0xaa }, { 0xaa, 0x55, 0x00 }, { 0xaa, 0xaa, 0xaa }, { 0x55, 0x55, 0x55 }, { 0x55, 0x55, 0xff }, { 0x55, 0xff, 0x55 }, { 0x55, 0xff, 0xff }, { 0xff, 0x55, 0x55 }, { 0xff, 0x55, 0xff }, { 0xff, 0xff, 0x55 }, { 0xff, 0xff, 0xff }, }; 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 next_string(As *as, char **c, char *word, uint8_t *wlen) { *c = skip_whitespace(*c); *wlen = 0; if (**c != '"') return 0; (*c)++; while (**c && **c != '"') { word[(*wlen)++] = **c; (*c)++; if (*wlen == MAX_ID) { word[*wlen - 1] = 0; *wlen = 0; return error_l("String is too long", &as->loc, word); } } word[*wlen] = 0; /* the closing quote */ (*c)++; return 1; } static uint8_t isspecial(char c) { return c == '$' || c == '_' || c == '.' || c == '#' || c == '<' || c == '>'; } static uint8_t next_word(As *as, char **c, char *word, uint8_t *wlen) { *c = skip_whitespace(*c); *wlen = 0; while (**c && (isalnum(**c) || isspecial(**c))) { word[(*wlen)++] = **c; (*c)++; if (*wlen == MAX_ID) { word[*wlen - 1] = 0; *wlen = 0; return error_l("Invalid input (too long)", &as->loc, word); } } word[*wlen] = 0; return 1; } 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_label_imm(As *as, char *word, uint16_t imm) { if (!new_label(as, word)) return 1; as->labels[as->lcnt - 1].addr = imm; 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 uint8_t emit(As *as, uint8_t instr, uint8_t p1, uint8_t p2, uint8_t p3) { if (as->addr + 2 > UINT16_MAX) return error_l("Memory overflow", &as->loc, "output is more than 65535 bytes"); as->out[as->addr++] = p3; as->out[as->addr++] = (instr << 4) | (p1 << 2) | p2; if (as->addr > as->size) as->size = as->addr; return 1; } static uint8_t emit_imm(As *as, uint16_t imm) { if (as->addr + 2 > UINT16_MAX) return error_l("Memory overflow", &as->loc, "output is more than 65535 bytes"); as->out[as->addr++] = imm & 0xff; as->out[as->addr++] = imm >> 8; if (as->addr > as->size) as->size = as->addr; return 1; } static uint8_t parse_org(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; /* .org imm */ if (!next_word(as, c, word, &wlen)) return 0; 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(As *as, char *c); static uint8_t parse_include(As *as, char **c) { /* XXX: may be support longer filenames */ char word[MAX_ID + 1]; uint8_t wlen; char line[MAX_LINE]; Location old; FILE *fd; uint8_t rc; /* .include "filename" */ if (!next_string(as, c, word, &wlen)) return 0; if (wlen == 0) return error_l("Syntax error", &as->loc, "expected string"); fd = fopen(word, "rb"); if (!fd) { error_l("Failed to open include", &as->loc, word); return 1; } old.filename = as->loc.filename; old.line = as->loc.line; as->loc.filename = word; as->loc.line = 0; while (fgets(line, MAX_LINE - 1, fd)) { as->loc.line++; if ((rc = parse(as, line)) == 0) break; } fclose(fd); as->loc.filename = old.filename; as->loc.line = old.line; if (!rc) return error_l("Error in include", &as->loc, word); return 1; } static uint8_t parse_incpng(As *as, char **c) { /* XXX: may be support longer filenames */ char word[MAX_ID + 1]; uint8_t wlen; /* .incpng "filename" */ if (!next_string(as, c, word, &wlen)) return 0; if (wlen == 0) return error_l("Syntax error", &as->loc, "expected string"); if (strcasecmp(word + wlen - 4, ".png")) return error_l("Invalid input", &as->loc, "expected PNG file"); int x, y, n; uint8_t rc = 1; unsigned char *data = stbi_load(word, &x, &y, &n, 3); if (!data) return error_l("Invalid input", &as->loc, "expected PNG file"); if (x * y + as->addr > UINT16_MAX) { rc = error_l("Overflow in incpng", &as->loc, word); goto exit_parseinc; } uint16_t addr = as->addr; uint8_t index; /* map RGB values to the palette indexes; * any unmatched value is "transparent" with index 128 */ for (int i = 0; i < x * y * 3; i += 3) { for (index = 0; index < 16; index++) if (data[i] == palette[index][0] && data[i + 1] == palette[index][1] && data[i + 2] == palette[index][2]) { as->out[addr++] = index; break; } if (index == 16) as->out[addr++] = 128; } as->addr = addr; if (as->addr > as->size) as->size = as->addr; exit_parseinc: stbi_image_free(data); return rc; } static uint8_t parse_incbin(As *as, char **c) { /* XXX: may be support longer filenames */ char word[MAX_ID + 1]; uint8_t wlen; FILE *fd; size_t size; /* .incbin "filename" */ if (!next_string(as, c, word, &wlen)) return 0; if (wlen == 0) return error_l("Syntax error", &as->loc, "expected string"); fd = fopen(word, "rb"); if (!fd) { error_l("Failed to open incbin", &as->loc, word); return 1; } fseek(fd, 0, SEEK_END); size = ftell(fd); if (size + as->addr > UINT16_MAX) { fclose(fd); error_l("Overflow in incbin", &as->loc, word); return 1; } fseek(fd, 0, SEEK_SET); if (fread(as->out + as->addr, 1, size, fd) != size) { fclose(fd); error_l("Read error in incbin", &as->loc, word); return 1; } fclose(fd); as->addr += size; if (as->addr > as->size) as->size = as->addr; return 1; } static uint8_t parse_equ(As *as, char **c) { char label[MAX_ID + 1]; char word[MAX_ID + 1]; uint8_t wlen; uint16_t imm; /* .equ label imm */ if (!next_word(as, c, label, &wlen)) return 0; if (wlen == 0) return error_l("Syntax error", &as->loc, "expected label"); if (!next_word(as, c, word, &wlen)) return 0; if (wlen == 0) return error_l("Syntax error", &as->loc, "expected immediate"); if (next_imm(word, &imm)) return error_l("Syntax error", &as->loc, word); return new_label_imm(as, label, imm); } static uint8_t parse_db(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint16_t imm = 0xff; /* .db imm [, imm] */ while (1) { if (!next_word(as, c, word, &wlen)) return 0; 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 (**c == ',') { (*c)++; continue; } break; } return 1; } static uint8_t parse_dw(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint16_t imm; /* .dw imm [, imm] */ while (1) { if (!next_word(as, c, word, &wlen)) return 0; 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 (**c == ',') { (*c)++; continue; } break; } return 1; } static uint8_t parse_nop(As *as, char **c) { /* NOP */ return emit(as, 0, 0, 0, 0); } static uint8_t parse_sif(As *as, char **c) { /* SIF */ return emit(as, 11, 0, FHL, 0); } static uint8_t parse_cif(As *as, char **c) { /* CIF */ return emit(as, 11, 0, 0, 0); } static uint8_t parse_ccf(As *as, char **c) { /* CCF */ return emit(as, 13, 0, FHH | FHL, 0); } static uint8_t parse_scf(As *as, char **c) { /* SCF */ return emit(as, 13, 0, FHH, 0); } static uint8_t parse_sof(As *as, char **c) { /* SOF */ return emit(as, 13, 0, FHL, 0); } static uint8_t parse_cof(As *as, char **c) { /* COF */ return emit(as, 13, 0, 0, 0); } static uint8_t parse_halt(As *as, char **c) { /* HALT */ return emit(as, 0, 0, FHH | FHL, 0); } static uint8_t parse_iret(As *as, char **c) { /* IRET */ return emit(as, 0, 0, FHL, 0); } static uint8_t parse_ret(As *as, char **c) { /* RET */ return emit(as, 0, 0, 0, FL); } 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]; uint8_t wlen; /* ? r1, ? */ if (!next_word(as, c, word, &wlen)) return 0; 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); *c = skip_whitespace(*c); if (**c != ',') return error_l("Syntax error", &as->loc, "expected ,"); (*c)++; if (!next_word(as, c, word, &wlen)) return 0; 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); } /* ? r1, label */ else if (!new_ref(as, word, 0xff, as->addr)) 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]; uint8_t wlen; /* ? r1, imm */ if (!next_word(as, c, word, &wlen)) return 0; 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); *c = skip_whitespace(*c); if (**c != ',') return error_l("Syntax error", &as->loc, "expected ,"); (*c)++; if (!next_word(as, c, word, &wlen)) return 0; 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 */ return emit(as, 4, r1, FHH | FHL, (r2 << 6)); /* AND r1, imm */ return emit(as, 4, r1, FHH, imm); } 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 */ return emit(as, 4, r1, FHL, (r2 << 6)); /* OR r1, imm */ return emit(as, 4, r1, 0, imm); } 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 */ return emit(as, 5, r1, FHH | FHL, (r2 << 6)); /* XOR r1, imm */ return emit(as, 5, r1, FHH, imm); } 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 */ return emit(as, 5, r1, FHL, (r2 << 6)); /* CMP r1, imm */ return emit(as, 5, r1, 0, imm); } 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 */ return emit(as, 6, r1, FHH | FHL, (r2 << 6)); /* ADD r1, imm */ return emit(as, 6, r1, FHH, imm); } 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 */ return emit(as, 6, r1, FHL, (r2 << 6)); /* SUB r1, imm */ return emit(as, 6, r1, 0, imm); } 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)"); return emit(as, 7, r1, FHH, imm); } 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)"); return emit(as, 7, r1, FHL, imm); } 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)"); return emit(as, 7, r1, 0, imm); } 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)"); return emit(as, 8, r1, 0, imm); } 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)"); return emit(as, 8, r1, FHH, imm); } static uint8_t parse_push(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint8_t r1; /* PUSH r1 */ if (!next_word(as, c, word, &wlen)) return 0; 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') return emit(as, 3, 0, FHH | FHL, 0); return error_l("Syntax error", &as->loc, word); } return emit(as, 3, r1, FHL, 0); } static uint8_t parse_port(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint8_t r1, r2; /* PORT r1, r2 */ if (!next_word(as, c, word, &wlen)) return 0; 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); *c = skip_whitespace(*c); if (**c != ',') return error_l("Syntax error", &as->loc, "expected ,"); (*c)++; if (!next_word(as, c, word, &wlen)) return 0; 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); return emit(as, 0, r1, FHH, r2 << 6); } static uint8_t parse_pop(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint8_t r1; /* POP r1 */ if (!next_word(as, c, word, &wlen)) return 0; 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') return emit(as, 3, 0, FHH, 0); return error_l("Syntax error", &as->loc, word); } return emit(as, 3, r1, 0, 0); } static uint8_t parse_xsp(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint8_t r1; /* XSP r1 */ if (!next_word(as, c, word, &wlen)) return 0; 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); return emit(as, 3, r1, 0, FL); } static uint8_t parse_inc(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint8_t r1; /* INC r1 */ if (!next_word(as, c, word, &wlen)) return 0; 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); return emit(as, 11, r1, FHH | FHL, 0); } static uint8_t parse_dec(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint8_t r1; /* INC r1 */ if (!next_word(as, c, word, &wlen)) return 0; 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); return emit(as, 11, r1, FHH, 0); } static uint8_t parse_bz(As *as, char **c) { /* BZ */ return emit(as, 10, 0, 0, 0); } static uint8_t parse_bnz(As *as, char **c) { /* BNZ */ return emit(as, 10, 0, FHH, 0); } static uint8_t parse_bc(As *as, char **c) { /* BC */ return emit(as, 10, 0, 0, 1); } static uint8_t parse_bnc(As *as, char **c) { /* BNC */ return emit(as, 10, 0, FHH, 1); } static uint8_t parse_bo(As *as, char **c) { /* BO */ return emit(as, 10, 0, 0, 2); } static uint8_t parse_bno(As *as, char **c) { /* BNO */ return emit(as, 10, 0, FHH, 2); } static uint8_t parse_bs(As *as, char **c) { /* BS */ return emit(as, 10, 0, 0, 3); } static uint8_t parse_bns(As *as, char **c) { /* BNS */ return emit(as, 10, 0, FHH, 3); } static uint8_t parse_bi(As *as, char **c) { /* BI */ return emit(as, 10, 0, 0, 4); } static uint8_t parse_bni(As *as, char **c) { /* BNI */ return emit(as, 10, 0, FHH, 4); } 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] */ if (!next_word(as, c, word, &wlen)) return 0; 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)++; if (!next_word(as, c, word, &wlen)) return 0; 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)++; if (!next_word(as, c, word, &wlen)) return 0; 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]; uint8_t wlen; uint16_t imm = 0xffff; uint8_t r1, r2; *c = skip_whitespace(*c); /* JMP [r1:r2] */ if (**c == '[') { (*c)++; if (!parse_indirect(as, c, &r1, &r2, NULL)) return 0; return emit(as, 9, r1, 0, (r2 << 6) | FL); } if (!next_word(as, c, word, &wlen)) return 0; 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; return (emit(as, 9, 0, FHH, FL) && emit_imm(as, imm)); } static uint8_t parse_call(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint16_t imm = 0xffff; uint8_t r1, r2; *c = skip_whitespace(*c); /* CALL [r1:r2] */ if (**c == '[') { (*c)++; if (!parse_indirect(as, c, &r1, &r2, NULL)) return 0; return emit(as, 9, r1, 0, (r2 << 6)); } if (!next_word(as, c, word, &wlen)) return 0; 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; return (emit(as, 9, 0, FHH, 0) && emit_imm(as, imm)); } static uint8_t parse_ld(As *as, char **c) { char word[MAX_ID + 1]; uint8_t wlen; uint16_t imm = 0xff; uint8_t r1, r2, r3; *c = skip_whitespace(*c); /* LD [r1:r2], r3 */ /* LD [sp + imm], r2 */ if (**c == '[') { (*c)++; if (!parse_indirect(as, c, &r1, &r2, &imm)) return 0; /* expected , */ *c = skip_whitespace(*c); if (**c != ',') return error_l("Syntax error", &as->loc, "expected ,"); (*c)++; if (!next_word(as, c, word, &wlen)) return 0; 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) return emit(as, 12, r3, FHH, imm); return emit(as, 2, r1, r2, (r3 << 6) | FL); } /* LD r1, ? */ if (!next_word(as, c, word, &wlen)) return 0; 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); *c = skip_whitespace(*c); if (**c != ',') return error_l("Syntax error", &as->loc, "expected ,"); (*c)++; *c = skip_whitespace(*c); /* LD r1, [r2:r3] */ /* LD r1, [sp + imm] */ if (**c == '[') { (*c)++; if (!parse_indirect(as, c, &r2, &r3, &imm)) return 0; if (r2 == 0xff) return emit(as, 12, r1, 0, imm); return emit(as, 2, r2, r3, r1 << 6); } if (!next_word(as, c, word, &wlen)) return 0; if (wlen == 0) return error_l("Syntax error", &as->loc, "expected register or immediate"); /* LD r1, r2 */ r2 = parse_register(word); if (r2 != 0xff) return emit(as, 1, r1, FHH, (r2 << 6)); /* 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)) return 0; return emit(as, 1, r1, 0, imm); } static InstParse insts[] = { { ".include", parse_include }, { ".incpng", parse_incpng }, { ".incbin", parse_incbin }, { ".org", parse_org }, { ".equ", parse_equ }, { ".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]; uint8_t wlen; uint8_t i; if (!next_word(as, &c, word, &wlen)) return 0; if (wlen == 0) return 1; /* comment */ if (word[0] == ';') return 1; /* new label */ if (*c == ':') { c++; if (!new_label(as, word)) return 0; } else { /* instructions */ for (i = 0; insts[i].parse; i++) if (!strcasecmp(insts[i].id, word)) { if (!insts[i].parse(as, &c)) return 0; break; } if (!insts[i].parse) return error_l("Parse error", &as->loc, word); } /* EOL */ if (!next_word(as, &c, word, &wlen)) return 0; 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 */