#include #include #include #include #include #include #include "mnemonics.h" const char *progname; #define ERROR(...) \ do { \ fprintf(stderr, "%s: ", progname); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) #define PERROR(str) \ do { \ int err = errno; \ fprintf(stderr, "%s: ", progname); \ errno = err; \ perror(str); \ } while (0) struct line { const struct mnemonic *mnem; uint16_t addr; uint16_t value; int label; }; static const struct mnemonic *match_instruction(unsigned bits) { for (int i = 0; i < MNEM_LAST; i++) { const struct mnemonic *m = mnemonics + i; if ((bits & m->mask) == m->bits) return m; } return NULL; } static inline int get_argvalue(const struct line *line, int argidx) { const struct operand *op = line->mnem->operands + argidx; switch (op->type) { case REG: case REGPTR: return 7 & (line->value >> op->shift); case IMM: return (uint8_t) (line->value >> op->shift); case PCOFF: return (int8_t) (line->value >> op->shift); } assert(0); } static int cmp_line_addr(const void *a, const void *b) { const struct line *la = (const struct line*)a; const struct line *lb = (const struct line*)b; return la->addr < lb->addr ? -1 : la->addr == lb->addr ? 0 : 1; } static struct line *get_pcoff_target(const struct line *lines, size_t count, const struct line *inst, int argidx) { uint16_t targetaddr = inst->addr + get_argvalue(inst, argidx); return bsearch(&(struct line){ .addr = targetaddr }, lines, count, sizeof *lines, cmp_line_addr); } static void assign_labels(struct line *lines, size_t count) { /* mark those addresses that need a label */ for (size_t i = 0; i < count; i++) { if (!lines[i].mnem) continue; const struct operand *ops = lines[i].mnem->operands; for (int j = 0; j < 3 && ops[j].type; j++) { if (ops[j].type == PCOFF) { struct line *target = get_pcoff_target(lines, count, lines + i, j); if (target) target->label = 1; } } } /* assign labels in ascending order */ int label = 0; for (size_t i = 0; i < count; i++) { lines[i].label = lines[i].label ? label++ : -1; } } static void print_operand(FILE *out, const struct line *lines, size_t count, size_t lineidx, size_t argidx) { const struct line *inst = lines + lineidx; const struct operand *op = inst->mnem->operands + argidx; switch (op->type) { case REG: fprintf(out, "r%d", get_argvalue(inst, argidx)); break; case REGPTR: fprintf(out, "@r%d", get_argvalue(inst, argidx)); break; case IMM: fprintf(out, "%d", get_argvalue(inst, argidx)); break; case PCOFF: { const struct line *target = get_pcoff_target(lines, count, inst, argidx); if (target && target->label >= 0) fprintf(out, "L%d", target->label); else fprintf(out, "%d", get_argvalue(inst, argidx)); break; } default: assert(0); } } static int resize_lines(struct line **lines, size_t alloc) { struct line *result = realloc(*lines, alloc * sizeof **lines); if (result) *lines = result; else PERROR("Error allocating memory"); return !result; } static struct line *read_lines(FILE *in, size_t *count) { size_t alloc = 16; struct line *lines = NULL; *count = 0; if (resize_lines(&lines, alloc)) goto fail; unsigned bits, addr = 0; while (fscanf(in, " %x\n", &bits) > 0) { if (addr > 0xffff) { ERROR("Input exceeds 16-bit address space"); goto fail; } if (*count >= alloc && resize_lines(&lines, alloc *= 2)) { goto fail; } lines[*count] = (struct line) { .mnem = match_instruction(bits), .value = bits, .addr = addr, }; addr += 2; *count += 1; } return lines; fail: free(lines); return NULL; } static void disassemble(FILE *in, FILE *out) { size_t count; struct line *lines = read_lines(in, &count); if (!lines) return; assign_labels(lines, count); for (size_t i = 0; i < count; i++) { const struct line *line = lines + i; unsigned bits = line->value; const struct mnemonic *m = line->mnem; if (line->label >= 0) fprintf(out, "L%d:", line->label); if (!m) { fprintf(out, "\t.word 0x%04x\n", bits); continue; } fprintf(out, "\t%s", m->mnem); for (int j = 0; j < 3 && m->operands[j].type; j++) { if (j) fprintf(out, ", "); else fprintf(out, "%*s", 8 - (int)strlen(m->mnem), ""); print_operand(out, lines, count, i, j); } fprintf(out, "\n"); } free(lines); } static void usage(const char *progname) { printf("usage: %s [-o output] [input]\n", progname); printf(" %s -h\n", progname); printf("\n"); printf("\t%-16s %s\n", "-h", "display this help text and exit"); printf("\t%-16s %s\n", "-o output", "write output to 'output' (default=stdout)"); printf("\t%-16s %s\n", "input", "read input from 'input' (default=stdin)"); } int main(int argc, char **argv) { const char *outname = NULL; int error = 0; extern int optind; extern char *optarg; progname = argv[0]; for (int opt; (opt = getopt(argc, argv, "+ho:")) != -1;) { switch (opt) { case 'h': usage(argv[0]); return 0; case 'o': outname = optarg; break; default: error = 1; } } if (optind < argc - 1) { ERROR("Too many input files"); return 1; } if (error) return 1; FILE *in = stdin; FILE *out = stdout; if (optind < argc && !(in = fopen(argv[optind], "r"))) { PERROR("Error opening input file"); return 1; } if (outname && !(out = fopen(outname, "w"))) { PERROR("Error opening output file"); fclose(in); return 1; } disassemble(in, out); fclose(in); fclose(out); return 0; }