/** * Assembler for the ULNAv2-C instruction set. * * Generates an image file suitable to be read by Logisim and Logisim-evolution. * * Copyright(c) 2019-2021,2024 Jason Tang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define _GNU_SOURCE #include #include #include #include #include #include "ulnav2-c-as.h" extern FILE *yyin; extern int yylineno; extern int yyparse(); enum inst_class { INST_A, INST_B, INST_C, INST_D }; struct ops_a { unsigned int rw; unsigned int ra; unsigned int rb; }; struct ops_b { unsigned int rw; unsigned int ra; unsigned int rb; unsigned long imm5; char *target; }; struct ops_c { unsigned int rw; unsigned long imm8; char *target; }; struct ops_d { unsigned long imm11; char *target; }; struct instruction { enum inst_class inst_class; unsigned int inst_num; union { struct ops_a a; struct ops_b b; struct ops_c c; struct ops_d d; } ops; unsigned int address; unsigned int lineno; char *str; struct instruction *next; }; struct label { char *label; unsigned int address; unsigned int lineno; struct label *next; }; struct instruction *inst_list, *inst_last; struct label *label_list; unsigned next_addr; bool input_parsed = true; static const char *INST_STR[] = { "or.", "add.", "and.", "stw", "br", "cmp.", "mul", "ldw", "ash", "addi.", "lsh", "stwi", "negi.", "brl", "rot", "ldwi", "ori.", "undef-17", "andi.", "undef-19", "movi", "cmpi.", "movis", "bl", "b", "b.gt", "b.eq", "b.ge", "b.lt", "b.ne", "b.le", "halt" }; static struct label *find_label(const char *label) { struct label *l = label_list; while (l != NULL) { if (strcmp(l->label, label) == 0) { return l; } l = l->next; } return l; } static void append_inst(struct instruction *inst) { inst->address = next_addr; inst->lineno = yylineno; inst->next = NULL; if (!inst_list) { inst_list = inst_last = inst; } else { inst_last->next = inst; inst_last = inst; } next_addr++; } void add_inst_a(unsigned int inst_num, unsigned int rw, unsigned int ra, unsigned int rb) { struct instruction *inst; inst = malloc(sizeof(*inst)); if (!inst) { perror("Allocate instruction"); exit(EXIT_FAILURE); } inst->inst_class = INST_A; inst->inst_num = inst_num; inst->ops.a.rw = rw; inst->ops.a.ra = ra; inst->ops.a.rb = rb; append_inst(inst); } void add_inst_b(unsigned int inst_num, unsigned int rw, unsigned int ra, unsigned long imm5, char *target) { struct instruction *inst; inst = malloc(sizeof(*inst)); if (!inst) { perror("Allocate instruction"); exit(EXIT_FAILURE); } inst->inst_class = INST_B; inst->inst_num = inst_num; inst->ops.b.rw = rw; inst->ops.b.ra = ra; if (target) { inst->ops.b.imm5 = 0; inst->ops.b.target = target; } else { inst->ops.b.imm5 = imm5 & 0x1f; inst->ops.b.target = NULL; } append_inst(inst); } void add_inst_b_simm(unsigned int inst_num, unsigned int rw, unsigned int ra, long imm5, char *target) { struct instruction *inst; inst = malloc(sizeof(*inst)); if (!inst) { perror("Allocate instruction"); exit(EXIT_FAILURE); } inst->inst_class = INST_B; inst->inst_num = inst_num; inst->ops.b.rw = rw; inst->ops.b.ra = ra; if (target) { inst->ops.b.imm5 = 0; inst->ops.b.target = target; } else if (imm5 >= 0) { inst->ops.b.imm5 = imm5 & 0x0f; inst->ops.b.target = NULL; } else { inst->ops.b.imm5 = ((-1 * imm5) & 0x0f) | (1 << 4); inst->ops.b.target = NULL; } append_inst(inst); } void add_inst_c(unsigned int inst_num, unsigned int rw, unsigned long imm8, char *target) { struct instruction *inst; inst = malloc(sizeof(*inst)); if (!inst) { perror("Allocate instruction"); exit(EXIT_FAILURE); } inst->inst_class = INST_C; inst->inst_num = inst_num; inst->ops.c.rw = rw; if (target) { inst->ops.c.imm8 = 0; inst->ops.c.target = target; } else { inst->ops.c.imm8 = imm8 & 0xff; inst->ops.c.target = NULL; } append_inst(inst); } void add_inst_d(unsigned int inst_num, unsigned long imm11, char *target) { struct instruction *inst; inst = malloc(sizeof(*inst)); if (!inst) { perror("Allocate instruction"); exit(EXIT_FAILURE); } inst->inst_class = INST_D; inst->inst_num = inst_num; if (target) { inst->ops.d.imm11 = 0; inst->ops.d.target = target; } else { inst->ops.d.imm11 = imm11 & 0x7ff; inst->ops.d.target = NULL; } append_inst(inst); } void add_label(char *label) { struct label *l; l = find_label(label); if (l) { yyerrorv(false, "duplicate label \"%s\": previously declared on line %u\n", label, l->lineno); input_parsed = false; return; } l = malloc(sizeof(*l)); if (!l) { perror("Allocate label"); exit(EXIT_FAILURE); } l->label = label; l->address = next_addr; l->lineno = yylineno; l->next = label_list; label_list = l; } static void link_symbols(void) { struct instruction *inst = inst_list; for (inst = inst_list; inst != NULL; inst = inst->next) { char *target; switch (inst->inst_class) { case INST_B: { target = inst->ops.b.target; break; } case INST_C: { target = inst->ops.c.target; break; } case INST_D: { target = inst->ops.d.target; break; } default: { continue; } } if (target == NULL) { continue; } struct label *l = find_label(target); if (!l) { fprintf(stderr, "Line %d: undefined label \"%s\"\n", inst->lineno, target); input_parsed = false; continue; } unsigned long new_imm = l->address - inst->address; switch (inst->inst_class) { case INST_B: { inst->ops.b.imm5 = new_imm & 0x1f; break; } case INST_C: { inst->ops.c.imm8 = new_imm & 0xff; break; } case INST_D: { inst->ops.d.imm11 = new_imm & 0x7ff; break; } default: { break; } } } } static void gen_debug(void) { struct instruction *inst = inst_list; for (inst = inst_list; inst != NULL; inst = inst->next) { int retval = 0; switch (inst->inst_class) { case INST_A: { retval = asprintf(&inst->str, "%8s r%u, r%u, r%u", INST_STR[inst->inst_num], inst->ops.a.rw, inst->ops.a.ra, inst->ops.a.rb); break; } case INST_B: { uint8_t u = (uint8_t) inst->ops.b.imm5; if (u & (1 << 4)) { int8_t d = (int8_t) (0xe0 | u); retval = asprintf(&inst->str, "%8s r%u, r%u, 0x%02x (+%u/%d)", INST_STR[inst->inst_num], inst->ops.b.rw, inst->ops.b.ra, u, u, d); } else { retval = asprintf(&inst->str, "%8s r%u, r%u, 0x%02x (+%u)", INST_STR[inst->inst_num], inst->ops.b.rw, inst->ops.b.ra, u, u); } break; } case INST_C: { uint8_t u = (uint8_t) inst->ops.c.imm8; if (u & (1 << 7)) { int8_t d = (int8_t) (u); retval = asprintf(&inst->str, "%8s r%u, 0x%02x (+%u/%d)", INST_STR[inst->inst_num], inst->ops.c.rw, u, u, d); } else { retval = asprintf(&inst->str, "%8s r%u, 0x%02x (+%u)", INST_STR[inst->inst_num], inst->ops.c.rw, u, u); } break; } case INST_D: { uint16_t u = (uint16_t) inst->ops.d.imm11; if (u & (1 << 10)) { int16_t d = (int16_t) (0xf800 | u); retval = asprintf(&inst->str, "%8s 0x%02x (+%u/%d)", INST_STR[inst->inst_num], u, u, d); } else { retval = asprintf(&inst->str, "%8s 0x%02x (+%u)", INST_STR[inst->inst_num], u, u); } break; } } if (retval == -1) { perror("Allocate string"); exit(EXIT_FAILURE); } } } static void generate_image(FILE * f, bool debug) { struct instruction *inst = inst_list; fprintf(f, "v2.0 raw\n"); for (inst = inst_list; inst != NULL; inst = inst->next) { uint16_t u = 0; u |= (inst->inst_num & 0x1f) << 11; switch (inst->inst_class) { case INST_A: { u |= (inst->ops.a.rw & 0x7) << 8; u |= (inst->ops.a.ra & 0x7) << 5; u |= (inst->ops.a.rb & 0x7) << 2; break; } case INST_B: { u |= (inst->ops.b.rw & 0x7) << 8; u |= (inst->ops.b.ra & 0x7) << 5; u |= (inst->ops.b.imm5 & 0x1f) << 0; break; } case INST_C: { u |= (inst->ops.c.rw & 0x7) << 8; u |= (inst->ops.c.imm8 & 0xff) << 0; break; } case INST_D: { u |= (inst->ops.d.imm11 & 0x7ff) << 0; break; } default: { continue; } } fprintf(f, "%04x", u); if (debug) { fprintf(f, " # addr 0x%02x, line %-3u: ", inst->address, inst->lineno); fprintf(f, "%s", inst->str); } fprintf(f, "\n"); } } int main(int argc, char *argv[]) { bool debug = false; int optind = 1; if (argc >= 2 && strcmp(argv[1], "-g") == 0) { debug = true; optind++; } if (argc < optind + 2) { fprintf(stderr, "Usage: %s [-g] INPUT.S OUTPUT.IMG\n", argv[0]); exit(EXIT_FAILURE); } FILE *infd = fopen(argv[optind], "r"); if (!infd) { perror("Could not open input file"); exit(EXIT_FAILURE); } yyin = infd; if (yyparse()) { input_parsed = false; } if (!input_parsed) { fprintf(stderr, "Aborted.\n"); exit(EXIT_FAILURE); } link_symbols(); if (!input_parsed) { fprintf(stderr, "Aborted.\n"); exit(EXIT_FAILURE); } if (debug) { gen_debug(); } FILE *outfd = fopen(argv[optind + 1], "w"); if (!outfd) { perror("Could not open output file"); exit(EXIT_FAILURE); } generate_image(outfd, debug); exit(EXIT_SUCCESS); }