#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) static struct line { const struct mnemonic *mnem; uint16_t value; int label; } lines[0x7fff]; static size_t maxpc; #define LINE(addr) &lines[(uint16_t)(addr) >> 1] 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 struct line *get_pcoff_target(uint16_t pc, int argidx) { struct line *l = LINE(pc + get_argvalue(LINE(pc), argidx)); return l - lines >= 2*maxpc ? NULL : l; } static void assign_labels(void) { /* mark those addresses that need a label */ for (size_t pc = 0; pc < maxpc; pc += 2) { const struct line *l = LINE(pc); if (!l->mnem) continue; const struct operand *ops = l->mnem->operands; for (int j = 0; j < 3 && ops[j].type; j++) { if (ops[j].type == PCOFF) { struct line *target = get_pcoff_target(pc, j); if (target) target->label = 1; } } } /* assign labels in ascending order */ int label = 0; for (size_t pc = 0; pc < maxpc; pc += 2) { struct line *l = LINE(pc); l->label = l->label ? label++ : -1; } } static void print_operand(FILE *out, size_t pc, size_t argidx) { const struct line *inst = LINE(pc); 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(pc, 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 read_lines(FILE *in) { unsigned bits; for (maxpc = 0; fscanf(in, " %x\n", &bits) > 0; maxpc += 2) { if (maxpc > 0xffff) { ERROR("Input exceeds 16-bit address space"); return 1; } *LINE(maxpc) = (struct line) { .mnem = match_instruction(bits), .value = bits, }; } return 0; } static void disassemble(FILE *in, FILE *out) { if (read_lines(in)) return; assign_labels(); for (size_t pc = 0; pc < maxpc; pc += 2) { const struct line *line = LINE(pc); 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, pc, j); } fprintf(out, "\n"); } } 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; }