/* * dis34.c - TMS34010 disassembler * * Produces output matching as34 assembler syntax: * lowercase mnemonics, 0x hex prefix, sp register name * * Input: raw binary (little-endian 16-bit words) * Output: disassembly to stdout * * Usage: dis34 [-a] [-b base_addr] [-s skip] [file] * dis34 [-a] -e file1 [file2 ...] * dis34 [-a] -E lo1 hi1 [lo2 hi2 ...] * -a analyze mode: heuristic code/data separation * -b base bit-address (hex, default 0) * -e EPROM mode: concatenate 16-bit files, last byte at top of address space * -E 8-bit EPROM mode: interleave pairs (lo,hi), last byte at top * -s skip bytes at start of file (e.g. 80 for g.out header) * reads stdin if no file given */ #include #include #include #include static uint8_t *buf; static size_t bufsz; static size_t pos; /* byte offset into buf */ static uint32_t baseaddr; static size_t skip; static int analyze_mode; static int eprom_mode; static int eprom8_mode; static uint8_t *code_map; /* per 16-bit word: 0=data, 1=code */ static uint8_t *entry_map; /* per 16-bit word: 1=code entry point */ static uint8_t *label_map; /* per 16-bit word: 1=branch/call target */ static const char *import_file; /* -p: import entry points */ static const char *export_file; /* -P: export entry points */ static int show_addresses; /* -l: show address prefix on each line */ static int show_opbytes; /* -o: show opcode bytes as comment */ static uint16_t r16 (void) { uint16_t v; if (pos + 1 >= bufsz) { fprintf (stderr, "dis34: unexpected end of input at byte %zu\n", pos); exit (1); } v = buf[pos] | (buf[pos + 1] << 8); pos += 2; return v; } static uint32_t r32 (void) { uint16_t lo = r16 (); uint16_t hi = r16 (); return (uint32_t) hi << 16 | lo; } /* register name - uses 8 rotating static buffers */ static const char * regname (int reg, int file) { static char s[8][8]; static int idx; char *p = s[idx++ & 7]; if (reg == 15) { strcpy (p, "sp"); return p; } snprintf (p, 8, "%c%d", file ? 'b' : 'a', reg); return p; } /* format a hex value with 0x prefix - 8 rotating buffers */ static const char * fmthex (uint32_t v) { static char s[8][20]; static int idx; char *p = s[idx++ & 7]; snprintf (p, 20, "0x%x", v); return p; } /* format a branch/call target: label if referenced, else hex */ static const char * fmt_target (uint32_t addr) { static char s[8][20]; static int idx; char *p = s[idx++ & 7]; if (label_map && addr >= baseaddr) { uint32_t bit_off = addr - baseaddr; if ((bit_off & 7) == 0) { size_t w = bit_off / 16; size_t total_words = (bufsz - skip) / 2; if (w < total_words && label_map[w]) { snprintf (p, 20, "L%X", addr); return p; } } } snprintf (p, 20, "0x%x", addr); return p; } static const char *condnames[16] = { "uc", "p", "ls", "hi", "lt", "ge", "le", "gt", "c", "nc", "eq", "ne", "v", "nv", "n", "nn" }; /* TMS34010 I/O register names, indexed by (bit_addr - 0xC0000000) >> 4 */ static const char *ioreg_names[32] = { "HESYNC", "HEBLNK", "HSBLNK", "HTOTAL", "VESYNC", "VEBLNK", "VSBLNK", "VTOTAL", "DPYCTL", "DPYSTRT", "DPYINT", "CONTROL", "HSTDATA", "HSTADRL", "HSTADRH", "HSTCTLL", "HSTCTLH", "INTENB", "INTPEND", "CONVSP", "CONVDP", "PSIZE", "PMASK", NULL, NULL, NULL, NULL, "DPYTAP", "HCOUNT", "VCOUNT", "DPYADR", "REFCNT" }; /* return I/O register name for a bit-address, or NULL */ static const char * ioreg_name (uint32_t addr) { if (addr < 0xC0000000 || addr > 0xC00001F0) return NULL; uint32_t off = addr - 0xC0000000; if (off & 0xF) return NULL; return ioreg_names[off >> 4]; } static const char *vector_names[32] = { "RESET", "INT1", "INT2", "TRAP3", "TRAP4", "TRAP5", "TRAP6", "TRAP7", "TRAP8", "TRAP9", "TRAP10", "ILLOP", "TRAP12", "TRAP13", "TRAP14", "TRAP15", "TRAP16", "TRAP17", "TRAP18", "TRAP19", "TRAP20", "TRAP21", "TRAP22", "TRAP23", "TRAP24", "TRAP25", "TRAP26", "TRAP27", "TRAP28", "TRAP29", "TRAP30", "TRAP31" }; /* returns vector index (0=RESET..31=TRAP31) if pc is a vector address, -1 otherwise */ static int get_vector_idx (uint32_t pc) { if (pc < 0xFFFFFC00 || pc > 0xFFFFFFE0) return -1; uint32_t off = 0xFFFFFFE0 - pc; if (off & 0x1F) return -1; return (int) (off >> 5); } /* print register list for MMTM (rev=1: MSB=reg0) or MMFM (rev=0: LSB=reg0) */ static void print_reglist (uint16_t reglist, int rf, int rev) { int first = 1; for (int i = 0; i < 16; i++) { int bit; if (rev) bit = (reglist >> (15 - i)) & 1; else bit = (reglist >> i) & 1; if (bit) { printf ("%s%s", first ? ", " : ",", regname (i, rf)); first = 0; } } } /* read a 16-bit word at byte offset without advancing pos */ static uint16_t peek16 (size_t off) { return buf[off] | (buf[off + 1] << 8); } /* read a 32-bit word at byte offset without advancing pos */ static uint32_t peek32 (size_t off) { uint16_t lo = buf[off] | (buf[off + 1] << 8); uint16_t hi = buf[off + 2] | (buf[off + 3] << 8); return (uint32_t) hi << 16 | lo; } /* return instruction length in bytes at file offset, or 0 if invalid */ static int get_insn_len (size_t off) { if (off + 1 >= bufsz) return 0; uint16_t op = peek16 (off); uint16_t subop = op & 0x01e0; int len = 0; switch (op & 0xfe00) { case 0x0000: switch (subop) { case 0x0020: case 0x0100: case 0x0120: case 0x0140: case 0x0160: case 0x0180: case 0x01a0: case 0x01c0: case 0x01e0: len = 2; break; } break; case 0x0200: switch (subop) { case 0x0100: case 0x0120: case 0x0160: case 0x0180: case 0x01a0: case 0x01c0: case 0x01e0: len = 2; break; case 0x0140: /* movb @,@ */ len = 10; break; } break; case 0x0400: case 0x0600: switch (subop) { case 0x0100: case 0x0120: /* sext, zext */ case 0x0140: case 0x0160: /* setf */ len = 2; break; case 0x0180: case 0x01a0: /* move r/@, move @/r */ len = 6; break; case 0x01c0: /* move @,@ */ len = 10; break; case 0x01e0: /* movb r/@ or movb @/r */ len = 6; break; } break; case 0x0800: switch (subop) { case 0x0100: case 0x0120: /* trap, call */ case 0x0140: case 0x0160: /* reti, rets */ len = 2; break; case 0x0180: case 0x01a0: /* mmtm, mmfm */ case 0x01c0: /* movi word */ len = 4; break; case 0x01e0: /* movi long */ len = 6; break; } break; case 0x0a00: switch (subop) { case 0x0100: /* addiw */ case 0x0140: /* cmpiw */ case 0x01e0: /* subiw */ len = 4; break; case 0x0120: /* addi long */ case 0x0160: /* cmpi long */ case 0x0180: /* andi */ case 0x01a0: /* ori */ case 0x01c0: /* xori */ len = 6; break; } break; case 0x0c00: switch (subop) { case 0x0160: /* eint */ case 0x01e0: /* setc */ len = 2; break; case 0x0120: /* callr */ case 0x0180: case 0x01a0: case 0x01c0: /* dsj, dsjeq, dsjne */ len = 4; break; case 0x0100: /* subi long */ case 0x0140: /* calla */ len = 6; break; } break; case 0x0e00: switch (subop) { case 0x0100: case 0x0120: case 0x0140: case 0x0160: case 0x0180: case 0x01a0: case 0x01c0: case 0x01e0: len = 2; break; } break; case 0x1000: case 0x1200: /* addk/inc */ case 0x1400: case 0x1600: /* subk/dec */ case 0x1800: case 0x1a00: /* movk */ case 0x1c00: case 0x1e00: /* btst K */ case 0x2000: case 0x2200: /* sla K */ case 0x2400: case 0x2600: /* sll K */ case 0x2800: case 0x2a00: /* sra K */ case 0x2c00: case 0x2e00: /* srl K */ case 0x3000: case 0x3200: /* rl K */ len = 2; break; case 0x3800: case 0x3a00: /* dsjs */ case 0x3c00: case 0x3e00: len = 2; break; case 0x4000: case 0x4200: case 0x4400: case 0x4600: /* add-subb */ case 0x4800: case 0x4a00: /* cmp, btst reg */ case 0x4c00: case 0x4e00: /* move reg */ case 0x5000: case 0x5200: case 0x5400: case 0x5600: /* and-xor */ case 0x5800: case 0x5a00: case 0x5c00: case 0x5e00: /* div/mul */ case 0x6000: case 0x6200: case 0x6400: case 0x6600: /* shifts reg */ case 0x6800: case 0x6a00: case 0x6c00: case 0x6e00: /* rl/lmo/mod */ len = 2; break; /* 0x7000-0x7e00: invalid gap */ case 0x8000: case 0x8200: /* move rs, *rd */ case 0x8400: case 0x8600: /* move *rs, rd */ case 0x8800: case 0x8a00: /* move *rs, *rd */ case 0x8c00: case 0x8e00: /* movb rs,*rd / *rs,rd */ case 0x9000: case 0x9200: /* move rs, *rd+ */ case 0x9400: case 0x9600: /* move *rs+, rd */ case 0x9800: case 0x9a00: /* move *rs+, *rd+ */ case 0x9c00: /* movb *rs, *rd */ case 0xa000: case 0xa200: /* move rs, -*rd */ case 0xa400: case 0xa600: /* move -*rs, rd */ case 0xa800: case 0xaa00: /* move -*rs, -*rd */ len = 2; break; case 0xac00: case 0xae00: /* movb with 1 disp */ case 0xb000: case 0xb200: /* move with 1 disp */ case 0xb400: case 0xb600: /* move with 1 disp */ len = 4; break; case 0xb800: case 0xba00: /* move with 2 disp */ case 0xbc00: /* movb with 2 disp */ len = 6; break; case 0xc000: case 0xc200: case 0xc400: case 0xc600: case 0xc800: case 0xca00: case 0xcc00: case 0xce00: { int lo8 = op & 0xff; if (lo8 == 0x80) len = 6; /* JA absolute */ else if (lo8 == 0x00) len = 4; /* JR 16-bit disp */ else len = 2; /* JR 8-bit disp */ } break; case 0xd000: case 0xd200: /* move *rs(d), *rd+ */ len = 4; break; case 0xd400: case 0xd600: switch (subop) { case 0x0000: /* move @abs, *rd+ */ len = 6; break; case 0x0100: /* exgf */ len = 2; break; } break; case 0xde00: switch (subop) { case 0x0100: case 0x0180: /* line 0, line 1 */ len = 2; break; } break; case 0xe000: case 0xe200: case 0xe400: case 0xe600: case 0xe800: /* addxy-cvxyl */ case 0xec00: case 0xee00: /* movx, movy */ len = 2; break; /* 0xea00: invalid gap */ case 0xf000: case 0xf200: case 0xf400: /* pixt xy */ case 0xf600: /* drav */ case 0xf800: case 0xfa00: case 0xfc00: /* pixt */ len = 2; break; } if (len && off + len > bufsz) return 0; return len; } /* returns 1 for RETS or RETI */ static int is_block_end (size_t off) { uint16_t op = peek16 (off); if ((op & 0xfe00) != 0x0800) return 0; uint16_t subop = op & 0x01e0; return subop == 0x0140 || subop == 0x0160; /* reti or rets */ } /* returns 1 for JUMP rd, JAUC/JRUC */ static int is_unconditional_xfer (size_t off) { uint16_t op = peek16 (off); /* JUMP rd */ if ((op & 0xfe00) == 0x0000 && (op & 0x01e0) == 0x0160) return 1; /* conditional jump range with cc==0 (unconditional) */ if ((op & 0xf000) == 0xc000 && ((op >> 8) & 0x0f) == 0) return 1; return 0; } /* returns 1 for conditional jumps (cc!=0), DSJ, DSJEQ, DSJNE, DSJS */ static int is_conditional_branch (size_t off) { uint16_t op = peek16 (off); /* conditional jump with cc != 0 */ if ((op & 0xf000) == 0xc000 && ((op >> 8) & 0x0f) != 0) return 1; /* DSJ/DSJEQ/DSJNE */ if ((op & 0xfe00) == 0x0c00) { uint16_t subop = op & 0x01e0; if (subop == 0x0180 || subop == 0x01a0 || subop == 0x01c0) return 1; } /* DSJS */ if ((op & 0xf800) == 0x3800) return 1; return 0; } /* return file byte offset of branch/call target, or (size_t)-1 */ static size_t get_branch_target_off (size_t off) { uint16_t op = peek16 (off); uint32_t pc = baseaddr + (uint32_t) (off - skip) * 8; uint32_t target_addr; /* CALLR */ if ((op & 0xfe00) == 0x0c00 && (op & 0x01e0) == 0x0120) { if (off + 3 >= bufsz) return (size_t) -1; int16_t disp = (int16_t) peek16 (off + 2); target_addr = pc + 32 + ((int32_t) disp << 4); } /* CALLA */ else if ((op & 0xfe00) == 0x0c00 && (op & 0x01e0) == 0x0140) { if (off + 5 >= bufsz) return (size_t) -1; target_addr = peek32 (off + 2); } /* conditional/unconditional jumps */ else if ((op & 0xf000) == 0xc000) { int lo8 = op & 0xff; if (lo8 == 0x80) { if (off + 5 >= bufsz) return (size_t) -1; target_addr = peek32 (off + 2); } else if (lo8 == 0x00) { if (off + 3 >= bufsz) return (size_t) -1; int16_t disp = (int16_t) peek16 (off + 2); target_addr = pc + 32 + ((int32_t) disp << 4); } else { int8_t disp = (int8_t) lo8; target_addr = pc + 16 + ((int32_t) disp << 4); } } /* DSJ/DSJEQ/DSJNE */ else if ((op & 0xfe00) == 0x0c00) { uint16_t subop = op & 0x01e0; if (subop == 0x0180 || subop == 0x01a0 || subop == 0x01c0) { if (off + 3 >= bufsz) return (size_t) -1; int16_t disp = (int16_t) peek16 (off + 2); target_addr = pc + 32 + ((int32_t) disp << 4); } else return (size_t) -1; } /* DSJS */ else if ((op & 0xf800) == 0x3800) { int k = (op >> 5) & 0x1f; if (op & 0x0400) k = -k; target_addr = pc + 16 + (k << 4); } else return (size_t) -1; /* convert bit-address to file byte offset */ if (target_addr < baseaddr) return (size_t) -1; uint32_t bit_off = target_addr - baseaddr; if (bit_off & 7) return (size_t) -1; size_t byte_off = skip + bit_off / 8; if (byte_off < skip || byte_off + 1 >= bufsz) return (size_t) -1; return byte_off; } /* extract ROM-address immediate from MOVI or ADDI long instruction at off. returns the immediate value if it falls within ROM, or 0. */ static uint32_t get_rom_addr_imm (size_t off) { if (off + 5 >= bufsz) return 0; uint16_t op = peek16 (off); uint32_t imm; /* MOVI long: (op & 0xfe00)==0x0800, subop==0x01e0 → 6 bytes */ if ((op & 0xfe00) == 0x0800 && (op & 0x01e0) == 0x01e0) imm = peek32 (off + 2); /* ADDI long: (op & 0xfe00)==0x0a00, subop==0x0120 → 6 bytes */ else if ((op & 0xfe00) == 0x0a00 && (op & 0x01e0) == 0x0120) imm = peek32 (off + 2); else return 0; /* check if within ROM address range and byte-aligned */ if (imm < baseaddr) return 0; uint32_t bit_off = imm - baseaddr; if (bit_off & 7) return 0; size_t byte_off = skip + bit_off / 8; if (byte_off + 1 >= bufsz) return 0; return imm; } /* check if instruction at off uses a register (not implied/setf/trap/rets) */ static int has_register (size_t off) { uint16_t op = peek16 (off); uint16_t hi = op & 0xfe00; uint16_t subop = op & 0x01e0; /* implied: nop, clrc, dint, popst, pushst, eint, setc, emu, reti, pixblt, fill, line */ if (hi == 0x0000 && (subop == 0x0100 || subop == 0x01c0 || subop == 0x01e0)) return 0; if (hi == 0x0200 && (subop == 0x0100 || subop == 0x0120 || subop == 0x0160)) return 0; if (hi == 0x0c00 && (subop == 0x0160 || subop == 0x01e0)) return 0; if (hi == 0x0e00) return 0; if (hi == 0xde00) return 0; /* setf: register field is fs, not a register */ if ((hi == 0x0400 || hi == 0x0600) && (subop == 0x0140 || subop == 0x0160)) return 0; /* trap, rets: low bits are trap number / pop count */ if (hi == 0x0800 && (subop == 0x0100 || subop == 0x0140 || subop == 0x0160)) return 0; /* conditional jumps: low bits are displacement, not register */ if ((op & 0xf000) == 0xc000) return 0; /* callr, calla: fixed opcodes, no register in rd */ if (hi == 0x0c00 && (subop == 0x0120 || subop == 0x0140)) return 0; return 1; } /* check if instruction at off references a TMS34010 I/O register address. checks MOVI/ADDI immediates and MOVE/MOVB @addr operands. */ static int has_ioreg_ref (size_t off) { if (off + 1 >= bufsz) return 0; uint16_t op = peek16 (off); uint16_t hi = op & 0xfe00; uint16_t subop = op & 0x01e0; /* MOVI long */ if (hi == 0x0800 && subop == 0x01e0 && off + 5 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL; /* MOVI word */ if (hi == 0x0800 && subop == 0x01c0 && off + 3 < bufsz) return ioreg_name (peek16 (off + 2)) != NULL; /* ADDI long */ if (hi == 0x0a00 && subop == 0x0120 && off + 5 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL; /* ADDI word */ if (hi == 0x0a00 && subop == 0x0100 && off + 3 < bufsz) return ioreg_name (peek16 (off + 2)) != NULL; /* move rd,@addr / move @addr,rd (0x0400/0x0600, subop 0x0180/0x01a0) */ if ((hi == 0x0400 || hi == 0x0600) && (subop == 0x0180 || subop == 0x01a0) && off + 5 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL; /* move @a1,@a2 (0x0400/0x0600, subop 0x01c0) */ if ((hi == 0x0400 || hi == 0x0600) && subop == 0x01c0 && off + 9 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL || ioreg_name (peek32 (off + 6)) != NULL; /* movb rd,@addr (0x0400 0x01e0) / movb @addr,rd (0x0600 0x01e0) */ if ((hi == 0x0400 || hi == 0x0600) && subop == 0x01e0 && off + 5 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL; /* movb @a1,@a2 (0x0200 0x0140) */ if (hi == 0x0200 && subop == 0x0140 && off + 9 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL || ioreg_name (peek32 (off + 6)) != NULL; /* move @addr,*rd+ (0xd400/0xd600 sub0) */ if ((hi == 0xd400 || hi == 0xd600) && subop == 0x0000 && off + 5 < bufsz) return ioreg_name (peek32 (off + 2)) != NULL; return 0; } /* validate a candidate code block: reject false positives */ static int validate_block (size_t start, size_t end) { int a_count = 0, b_count = 0; int has_branch = 0; int has_ioreg = 0; int n_insns = 0; for (size_t off = start; off < end;) { int len = get_insn_len (off); if (len == 0) break; n_insns++; /* count register file usage */ if (has_register (off)) { if (peek16 (off) & 0x10) b_count++; else a_count++; } /* track branches/calls */ if (is_conditional_branch (off)) has_branch = 1; if (get_branch_target_off (off) != (size_t) -1) has_branch = 1; /* track I/O register references */ if (has_ioreg_ref (off)) has_ioreg = 1; off += len; } /* require at least 4 instructions (2 if block references I/O registers) */ if (n_insns < (has_ioreg ? 2 : 4)) return 0; /* require at least one branch or call (waived if I/O register access) */ if (!has_branch && !has_ioreg) return 0; /* reject blocks that mix A and B register files (real code stays in one file; random data ~50/50) */ int total_reg = a_count + b_count; if (total_reg >= 3) { int minority = a_count < b_count ? a_count : b_count; if (minority * 4 >= total_reg) /* >=25% minority → likely data */ return 0; } /* validate that branch targets are within the ROM */ int good_targets = 0, bad_targets = 0; for (size_t off = start; off < end;) { int len = get_insn_len (off); if (len == 0) break; size_t target = get_branch_target_off (off); if (target != (size_t) -1) { if (target >= skip && target + 1 < bufsz) good_targets++; else bad_targets++; } off += len; } if (bad_targets > good_targets) return 0; return 1; } /* mark words from start to end as code in code_map */ static void mark_block (size_t start, size_t end) { for (size_t off = start; off < end;) { int len = get_insn_len (off); if (len == 0) break; for (int i = 0; i < len; i += 2) code_map[(off - skip + i) / 2] = 1; off += len; } } /* worklist for recursive descent */ static size_t *worklist; static size_t wl_count, wl_cap; static void wl_push (size_t off) { if (off < skip || off + 1 >= bufsz) return; if (entry_map) entry_map[(off - skip) / 2] = 1; if (code_map[(off - skip) / 2]) return; if (wl_count == wl_cap) { wl_cap = wl_cap ? wl_cap * 2 : 4096; worklist = realloc (worklist, wl_cap * sizeof *worklist); } worklist[wl_count++] = off; } /* trace execution from off, marking code and pushing branch targets. does a dry-run first to validate the block before committing. if force is set, skip heuristic checks (for known code entry points). */ static void trace_from (size_t off, int force) { /* dry run: record instruction offsets and lengths */ size_t trace_off[4096]; int trace_len[4096]; int n_trace = 0; size_t tpos = off; while (tpos + 1 < bufsz && n_trace < 4096) { if (code_map[(tpos - skip) / 2]) break; /* will merge with known code */ int ilen = get_insn_len (tpos); if (ilen == 0) break; trace_off[n_trace] = tpos; trace_len[n_trace] = ilen; n_trace++; if (is_block_end (tpos) || is_unconditional_xfer (tpos)) break; tpos += ilen; } if (n_trace == 0) return; if (!force) { /* check for I/O register references (strong code signal) */ int has_ioreg = 0; for (int i = 0; i < n_trace; i++) if (has_ioreg_ref (trace_off[i])) { has_ioreg = 1; break; } if (n_trace < 2 && !has_ioreg) return; /* single instructions are not credible blocks */ /* validate the traced block: check register file consistency */ int a_count = 0, b_count = 0; for (int i = 0; i < n_trace; i++) { if (has_register (trace_off[i])) { if (peek16 (trace_off[i]) & 0x10) b_count++; else a_count++; } } int total_reg = a_count + b_count; if (total_reg >= 4 && !has_ioreg) { int minority = a_count < b_count ? a_count : b_count; if (minority * 4 >= total_reg) return; /* mixed register files → likely data */ } } /* commit: mark as code and push branch targets */ for (int i = 0; i < n_trace; i++) { for (int j = 0; j < trace_len[i]; j += 2) code_map[(trace_off[i] - skip + j) / 2] = 1; size_t target = get_branch_target_off (trace_off[i]); if (target != (size_t) -1) wl_push (target); } /* follow ROM address references (MOVI/ADDI with ROM-range immediates) as potential jump tables: read consecutive 32-bit entries and push valid code pointers to the worklist */ for (int i = 0; i < n_trace; i++) { uint32_t ref = get_rom_addr_imm (trace_off[i]); if (!ref) continue; uint32_t rbit = ref - baseaddr; size_t ref_off = skip + rbit / 8; /* try reading consecutive 32-bit values as jump table entries */ for (int ent = 0; ent < 256 && ref_off + 3 < bufsz; ent++, ref_off += 4) { uint32_t entry = peek32 (ref_off); if (entry < baseaddr) break; uint32_t e_bit = entry - baseaddr; if (e_bit & 0xf) /* must be instruction-aligned (16 bits) */ break; size_t e_off = skip + e_bit / 8; if (e_off + 1 >= bufsz) break; wl_push (e_off); } } } #define MAX_BLOCK_SIZE 65536 #define MAX_BLOCKS 4096 /* analyze a single region [skip..bufsz) using the current globals */ static void analyze_pair (void) { size_t pair_words = (bufsz - skip) / 2; /* block recording */ struct { size_t start, end; } blocks[MAX_BLOCKS]; int num_blocks = 0; /* Pass 1: sequential scan */ size_t offset = skip; size_t block_start = 0; int in_block = 0; int has_cond_branch = 0; while (offset + 1 < bufsz) { int ilen = get_insn_len (offset); if (ilen == 0) { in_block = 0; offset += 2; continue; } if (in_block) { if (is_conditional_branch (offset)) has_cond_branch = 1; if (is_block_end (offset)) { size_t block_end = offset + ilen; if (validate_block (block_start, block_end) && num_blocks < MAX_BLOCKS) { blocks[num_blocks].start = block_start; blocks[num_blocks].end = block_end; num_blocks++; } in_block = 0; } else if (is_unconditional_xfer (offset)) { if (!has_cond_branch) in_block = 0; } } if (!in_block && is_unconditional_xfer (offset)) { size_t next = offset + ilen; if (next + 1 < bufsz) { block_start = next; in_block = 1; has_cond_branch = 0; } } offset += ilen; if (in_block && (offset - block_start) > MAX_BLOCK_SIZE) in_block = 0; } /* mark pass 1 blocks */ for (int i = 0; i < num_blocks; i++) mark_block (blocks[i].start, blocks[i].end); int seq_found = num_blocks; /* Pass 2: recursive descent from all known code + vector table */ /* seed worklist from branch/call targets and ROM address references in pass 1 code */ for (size_t off = skip; off + 1 < bufsz; off += 2) { if (!code_map[(off - skip) / 2]) continue; size_t target = get_branch_target_off (off); if (target != (size_t) -1) wl_push (target); /* follow ROM address references (MOVI/ADDI) as jump tables */ uint32_t ref = get_rom_addr_imm (off); if (ref) { uint32_t rbit = ref - baseaddr; size_t ref_off = skip + rbit / 8; for (int ent = 0; ent < 256 && ref_off + 3 < bufsz; ent++, ref_off += 4) { uint32_t entry = peek32 (ref_off); if (entry < baseaddr) break; uint32_t e_bit = entry - baseaddr; if (e_bit & 0xf) break; size_t e_off = skip + e_bit / 8; if (e_off + 1 >= bufsz) break; wl_push (e_off); } } } /* seed from TMS34010 hardware vector table (traps 0-31). each entry is a 32-bit handler address (not an instruction). */ for (int v = 0; v < 32; v++) { uint32_t vec_addr = 0xFFFFFFE0 - (uint32_t) v * 0x20; if (vec_addr < baseaddr) continue; uint32_t bit_off = vec_addr - baseaddr; if (bit_off & 7) continue; size_t vec_off = skip + bit_off / 8; if (vec_off + 3 >= bufsz) continue; /* read the 32-bit handler address */ uint32_t handler = peek32 (vec_off); if (handler < baseaddr) continue; uint32_t h_bit_off = handler - baseaddr; if (h_bit_off & 7) continue; size_t h_off = skip + h_bit_off / 8; wl_push (h_off); trace_from (h_off, 1); } /* seed from imported entry points file */ if (import_file) { FILE *fp = fopen (import_file, "r"); if (!fp) { perror (import_file); exit (1); } char line[256]; int imp_count = 0; while (fgets (line, sizeof line, fp)) { char *p = line; while (*p == ' ' || *p == '\t') p++; if (*p == '#' || *p == '\n' || *p == '\0') continue; uint32_t addr = strtoul (p, NULL, 16); if (addr < baseaddr) continue; uint32_t bit_off = addr - baseaddr; if (bit_off & 7) continue; size_t off = skip + bit_off / 8; wl_push (off); trace_from (off, 1); imp_count++; } fclose (fp); fprintf (stderr, "dis34: imported %d entry points from %s\n", imp_count, import_file); } /* process worklist: trace from each entry, following both paths */ while (wl_count > 0) { size_t entry = worklist[--wl_count]; if (code_map[(entry - skip) / 2]) continue; trace_from (entry, 0); } free (worklist); worklist = NULL; wl_count = wl_cap = 0; /* stats */ int code_words = 0; for (size_t i = 0; i < pair_words; i++) if (code_map[i]) code_words++; fprintf (stderr, "dis34: analyze: %d seq blocks + recursive descent, " "%d/%zu words marked as code\n", seq_found, code_words, pair_words); } static void analyze_code (void) { size_t total_words = (bufsz - skip) / 2; /* allocate code_map and entry_map covering the entire buffer */ code_map = calloc (total_words, 1); entry_map = calloc (total_words, 1); if (!code_map || !entry_map) { fprintf (stderr, "dis34: out of memory for code_map\n"); exit (1); } /* analyze the entire address space as one unified region */ analyze_pair (); } /* pre-scan to find all branch/call targets and populate label_map */ static void build_label_map (void) { size_t total_words = (bufsz - skip) / 2; label_map = calloc (total_words, 1); if (!label_map) return; for (size_t off = skip; off + 1 < bufsz;) { uint32_t pc = baseaddr + (uint32_t) (off - skip) * 8; /* vector table entries: mark the handler address */ int vidx = get_vector_idx (pc); if (vidx >= 0 && off + 3 < bufsz) { uint32_t handler = peek32 (off); if (handler >= baseaddr) { uint32_t h_bit_off = handler - baseaddr; if ((h_bit_off & 7) == 0) { size_t w = h_bit_off / 16; if (w < total_words) label_map[w] = 1; } } off += 4; continue; } /* in analyze mode, skip data words */ if (analyze_mode && code_map && !code_map[(off - skip) / 2]) { off += 2; continue; } int len = get_insn_len (off); if (len == 0) { off += 2; continue; } size_t target = get_branch_target_off (off); if (target != (size_t) -1) label_map[(target - skip) / 2] = 1; /* in analyze mode, mark ROM address references (MOVI/ADDI) as data labels so referenced tables get labeled in the output */ if (analyze_mode) { uint32_t ref = get_rom_addr_imm (off); if (ref) { uint32_t rbit = ref - baseaddr; size_t w = rbit / 16; if (w < total_words) label_map[w] = 1; } } off += len; } } int main (int argc, char **argv) { const char *fnames[64]; int nfiles = 0; int i; for (i = 1; i < argc; i++) { if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "--help")) { fprintf (stderr, "Usage: dis34 [-a] [-b base] [-s skip] [file]\n" " dis34 [-a] -e file1 [file2 ...]\n" " dis34 [-a] -E lo1 hi1 [lo2 hi2 ...]\n" "\n" "TMS34010 disassembler\n" "\n" "Options:\n" " -a analyze mode: heuristic code/data separation\n" " -b BASE base bit-address in hex (default 0)\n" " -s SKIP skip bytes at start of file\n" " -e EPROM mode: concatenate 16-bit files\n" " -E 8-bit EPROM mode: interleave lo/hi pairs\n" " -p FILE import code entry points from file\n" " -P FILE export discovered entry points to file\n" " -l show address prefix on each line\n" " -o show opcode bytes as comment\n" " -h show this help\n" "\n" "Reads stdin if no file given.\n"); return 0; } else if (!strcmp (argv[i], "-a")) analyze_mode = 1; else if (!strcmp (argv[i], "-e")) eprom_mode = 1; else if (!strcmp (argv[i], "-E")) eprom8_mode = 1; else if (!strcmp (argv[i], "-b") && i + 1 < argc) baseaddr = strtoul (argv[++i], NULL, 16); else if (!strcmp (argv[i], "-s") && i + 1 < argc) skip = strtoul (argv[++i], NULL, 0); else if (!strcmp (argv[i], "-p") && i + 1 < argc) import_file = argv[++i]; else if (!strcmp (argv[i], "-P") && i + 1 < argc) export_file = argv[++i]; else if (!strcmp (argv[i], "-l")) show_addresses = 1; else if (!strcmp (argv[i], "-o")) show_opbytes = 1; else if (nfiles < 64) fnames[nfiles++] = argv[i]; } if (eprom8_mode) { if (nfiles < 2 || (nfiles & 1)) { fprintf (stderr, "dis34: -E requires pairs of files (lo hi)\n"); return 1; } bufsz = 0; size_t cap = 65536; buf = malloc (cap); for (int f = 0; f < nfiles; f += 2) { /* read low-byte EPROM */ FILE *flo = fopen (fnames[f], "rb"); if (!flo) { perror (fnames[f]); return 1; } fseek (flo, 0, SEEK_END); size_t losz = ftell (flo); rewind (flo); uint8_t *lobuf = malloc (losz); if (fread (lobuf, 1, losz, flo) != losz) { fprintf (stderr, "dis34: short read: %s\n", fnames[f]); return 1; } fclose (flo); /* read high-byte EPROM */ FILE *fhi = fopen (fnames[f + 1], "rb"); if (!fhi) { perror (fnames[f + 1]); return 1; } fseek (fhi, 0, SEEK_END); size_t hisz = ftell (fhi); rewind (fhi); uint8_t *hibuf = malloc (hisz); if (fread (hibuf, 1, hisz, fhi) != hisz) { fprintf (stderr, "dis34: short read: %s\n", fnames[f + 1]); return 1; } fclose (fhi); if (losz != hisz) { fprintf (stderr, "dis34: EPROM size mismatch: %s (%zu) vs %s (%zu)\n", fnames[f], losz, fnames[f + 1], hisz); return 1; } /* interleave: lo[i] = even byte, hi[i] = odd byte */ size_t need = bufsz + losz * 2; while (cap < need) cap *= 2; buf = realloc (buf, cap); for (size_t j = 0; j < losz; j++) { buf[bufsz++] = lobuf[j]; buf[bufsz++] = hibuf[j]; } free (lobuf); free (hibuf); fprintf (stderr, "dis34: pair %s + %s: %zu bytes each, %zu interleaved\n", fnames[f], fnames[f + 1], losz, losz * 2); } skip = 0; baseaddr = (uint32_t) (0 - (uint32_t) (bufsz * 8)); fprintf (stderr, "dis34: 8-bit EPROM mode, %zu bytes total, base %08x\n", bufsz, baseaddr); } else if (eprom_mode) { if (nfiles == 0) { fprintf (stderr, "dis34: -e requires at least one file\n"); return 1; } size_t cap = 65536; buf = malloc (cap); bufsz = 0; for (int f = 0; f < nfiles; f++) { FILE *fp = fopen (fnames[f], "rb"); if (!fp) { perror (fnames[f]); return 1; } size_t n; while ((n = fread (buf + bufsz, 1, cap - bufsz, fp)) > 0) { bufsz += n; if (bufsz == cap) { cap *= 2; buf = realloc (buf, cap); } } fclose (fp); } skip = 0; baseaddr = (uint32_t) (0 - (uint32_t) (bufsz * 8)); fprintf (stderr, "dis34: %d EPROM%s, %zu bytes, base %08x\n", nfiles, nfiles > 1 ? "s" : "", bufsz, baseaddr); } else { const char *fname = nfiles > 0 ? fnames[nfiles - 1] : NULL; FILE *fp = fname ? fopen (fname, "rb") : stdin; if (!fp) { perror (fname); return 1; } /* read entire input */ { size_t cap = 65536, n; buf = malloc (cap); bufsz = 0; while ((n = fread (buf + bufsz, 1, cap - bufsz, fp)) > 0) { bufsz += n; if (bufsz == cap) { cap *= 2; buf = realloc (buf, cap); } } if (fname) fclose (fp); } if (skip >= bufsz) { fprintf (stderr, "dis34: skip %zu >= file size %zu\n", skip, bufsz); return 1; } } pos = skip; if (analyze_mode) { analyze_code (); if (export_file) { FILE *efp = fopen (export_file, "w"); if (!efp) { perror (export_file); return 1; } size_t total_words = (bufsz - skip) / 2; int ep_count = 0; for (size_t w = 0; w < total_words; w++) { if (entry_map[w]) { uint32_t addr = baseaddr + (uint32_t) w * 16; fprintf (efp, "%08X\n", addr); ep_count++; } } fclose (efp); fprintf (stderr, "dis34: exported %d entry points to %s\n", ep_count, export_file); } free (entry_map); entry_map = NULL; } build_label_map (); while (pos + 1 < bufsz) { size_t startpos = pos; uint32_t pc = baseaddr + (uint32_t) (startpos - skip) * 8; /* print label if this address is a branch/call target */ if (label_map && label_map[(startpos - skip) / 2]) printf ("L%X:\n", pc); /* vector table entries: show as labeled longwords */ int vidx = get_vector_idx (pc); if (vidx >= 0 && pos + 3 < bufsz) { uint32_t val = r32 (); if (show_addresses) printf ("%s:\n%08x:\t.long L%X\n", vector_names[vidx], pc, val); else printf ("%s:\n\t.long L%X\n", vector_names[vidx], val); continue; } uint16_t op = r16 (); if (analyze_mode && code_map && !code_map[(startpos - skip) / 2]) { if (show_addresses) printf ("%08x:\t.word 0x%04x\n", pc, op); else printf ("\t.word 0x%04x\n", op); continue; } uint16_t subop = op & 0x01e0; int rs = (op >> 5) & 0x0f; int rd = op & 0x0f; int rf = (op & 0x10) ? 1 : 0; /* 0=A, 1=B */ int field = (op >> 9) & 1; int bad = 0; uint32_t io_addrs[4]; int n_io = 0; if (show_addresses) printf ("%08x:\t", pc); else printf ("\t"); switch (op & 0xfe00) { case 0x0000: switch (subop) { case 0x0020: printf ("rev %s", regname (rd, rf)); break; case 0x0100: printf ("emu"); break; case 0x0120: printf ("exgpc %s", regname (rd, rf)); break; case 0x0140: printf ("getpc %s", regname (rd, rf)); break; case 0x0160: printf ("jump %s", regname (rd, rf)); break; case 0x0180: printf ("getst %s", regname (rd, rf)); break; case 0x01a0: printf ("putst %s", regname (rd, rf)); break; case 0x01c0: printf ("popst"); break; case 0x01e0: printf ("pushst"); break; default: bad = 1; } break; case 0x0200: switch (subop) { case 0x0100: printf ("nop"); break; case 0x0120: printf ("clrc"); break; case 0x0140: { uint32_t a1 = r32 (); uint32_t a2 = r32 (); printf ("movb @%s, @%s", fmthex (a1), fmthex (a2)); if (ioreg_name (a1)) io_addrs[n_io++] = a1; if (ioreg_name (a2)) io_addrs[n_io++] = a2; } break; case 0x0160: printf ("dint"); break; case 0x0180: printf ("abs %s", regname (rd, rf)); break; case 0x01a0: printf ("neg %s", regname (rd, rf)); break; case 0x01c0: printf ("negb %s", regname (rd, rf)); break; case 0x01e0: printf ("not %s", regname (rd, rf)); break; default: bad = 1; } break; case 0x0400: case 0x0600: switch (subop) { case 0x0100: printf ("sext %s, %d", regname (rd, rf), field); break; case 0x0120: printf ("zext %s, %d", regname (rd, rf), field); break; case 0x0140: case 0x0160: { int fs = op & 0x1f; int fe = (op >> 5) & 1; if (!fs) fs = 32; printf ("setf %d, %d, %d", fs, fe, field); } break; case 0x0180: { uint32_t addr = r32 (); printf ("move %s, @%s, %d", regname (rd, rf), fmthex (addr), field); if (ioreg_name (addr)) io_addrs[n_io++] = addr; } break; case 0x01a0: { uint32_t addr = r32 (); printf ("move @%s, %s, %d", fmthex (addr), regname (rd, rf), field); if (ioreg_name (addr)) io_addrs[n_io++] = addr; } break; case 0x01c0: { uint32_t a1 = r32 (); uint32_t a2 = r32 (); printf ("move @%s, @%s, %d", fmthex (a1), fmthex (a2), field); if (ioreg_name (a1)) io_addrs[n_io++] = a1; if (ioreg_name (a2)) io_addrs[n_io++] = a2; } break; case 0x01e0: if (op & 0x0200) { uint32_t addr = r32 (); printf ("move @%s, %s", fmthex (addr), regname (rd, rf)); if (ioreg_name (addr)) io_addrs[n_io++] = addr; } else { uint32_t addr = r32 (); printf ("movb %s, @%s", regname (rd, rf), fmthex (addr)); if (ioreg_name (addr)) io_addrs[n_io++] = addr; } break; default: bad = 1; } break; case 0x0800: switch (subop) { case 0x0100: printf ("trap %d", op & 0x1f); break; case 0x0120: printf ("call %s", regname (rd, rf)); break; case 0x0140: printf ("reti"); break; case 0x0160: if (op & 0x1f) printf ("rets %d", op & 0x1f); else printf ("rets"); break; case 0x0180: { uint16_t reglist = r16 (); printf ("mmtm %s", regname (rd, rf)); print_reglist (reglist, rf, 1); } break; case 0x01a0: { uint16_t reglist = r16 (); printf ("mmfm %s", regname (rd, rf)); print_reglist (reglist, rf, 0); } break; case 0x01c0: { uint16_t imm = r16 (); printf ("movi %s, %s", fmthex (imm), regname (rd, rf)); if (ioreg_name (imm)) io_addrs[n_io++] = imm; } break; case 0x01e0: { uint32_t imm = r32 (); printf ("movi %s, %s", fmthex (imm), regname (rd, rf)); if (ioreg_name (imm)) io_addrs[n_io++] = imm; } break; default: bad = 1; } break; case 0x0a00: switch (subop) { case 0x0100: { uint16_t imm = r16 (); printf ("addi %s, %s", fmthex (imm), regname (rd, rf)); if (ioreg_name (imm)) io_addrs[n_io++] = imm; } break; case 0x0120: { uint32_t imm = r32 (); printf ("addi %s, %s", fmthex (imm), regname (rd, rf)); if (ioreg_name (imm)) io_addrs[n_io++] = imm; } break; case 0x0140: { uint16_t raw = r16 (); printf ("cmpi %s, %s", fmthex ((uint16_t) ~raw), regname (rd, rf)); } break; case 0x0160: { uint32_t raw = r32 (); printf ("cmpi %s, %s", fmthex (~raw), regname (rd, rf)); } break; case 0x0180: { uint32_t raw = r32 (); printf ("andi %s, %s", fmthex (~raw), regname (rd, rf)); } break; case 0x01a0: { uint32_t imm = r32 (); printf ("ori %s, %s", fmthex (imm), regname (rd, rf)); } break; case 0x01c0: { uint32_t imm = r32 (); printf ("xori %s, %s", fmthex (imm), regname (rd, rf)); } break; case 0x01e0: { uint16_t raw = r16 (); printf ("subi %s, %s", fmthex ((uint16_t) ~raw), regname (rd, rf)); } break; default: bad = 1; } break; case 0x0c00: switch (subop) { case 0x0100: { uint32_t raw = r32 (); printf ("subi %s, %s", fmthex (~raw), regname (rd, rf)); } break; case 0x0120: { int16_t disp = (int16_t) r16 (); uint32_t target = pc + 32 + ((int32_t) disp << 4); printf ("callr %s", fmt_target (target)); } break; case 0x0140: { uint32_t addr = r32 (); printf ("calla %s", fmt_target (addr)); } break; case 0x0160: printf ("eint"); break; case 0x0180: { int16_t disp = (int16_t) r16 (); uint32_t target = pc + 32 + ((int32_t) disp << 4); printf ("dsj %s, %s", regname (rd, rf), fmt_target (target)); } break; case 0x01a0: { int16_t disp = (int16_t) r16 (); uint32_t target = pc + 32 + ((int32_t) disp << 4); printf ("dsjeq %s, %s", regname (rd, rf), fmt_target (target)); } break; case 0x01c0: { int16_t disp = (int16_t) r16 (); uint32_t target = pc + 32 + ((int32_t) disp << 4); printf ("dsjne %s, %s", regname (rd, rf), fmt_target (target)); } break; case 0x01e0: printf ("setc"); break; default: bad = 1; } break; case 0x0e00: switch (subop) { case 0x0100: printf ("pixblt l,l"); break; case 0x0120: printf ("pixblt l,xy"); break; case 0x0140: printf ("pixblt xy,l"); break; case 0x0160: printf ("pixblt xy,xy"); break; case 0x0180: printf ("pixblt b,l"); break; case 0x01a0: printf ("pixblt b,xy"); break; case 0x01c0: printf ("fill l"); break; case 0x01e0: printf ("fill xy"); break; default: bad = 1; } break; case 0x1000: case 0x1200: { int k = (op >> 5) & 0x1f; if (!k) k = 32; if (k == 1) printf ("inc %s", regname (rd, rf)); else printf ("addk %d, %s", k, regname (rd, rf)); } break; case 0x1400: case 0x1600: { int k = (op >> 5) & 0x1f; if (!k) k = 32; if (k == 1) printf ("dec %s", regname (rd, rf)); else printf ("subk %d, %s", k, regname (rd, rf)); } break; case 0x1800: case 0x1a00: { int k = (op >> 5) & 0x1f; if (!k) k = 32; printf ("movk %d, %s", k, regname (rd, rf)); } break; case 0x1c00: case 0x1e00: { int k = (~op >> 5) & 0x1f; printf ("btst %d, %s", k, regname (rd, rf)); } break; case 0x2000: case 0x2200: { int k = (op >> 5) & 0x1f; printf ("sla %s, %s", fmthex (k), regname (rd, rf)); } break; case 0x2400: case 0x2600: { int k = (op >> 5) & 0x1f; printf ("sll %s, %s", fmthex (k), regname (rd, rf)); } break; case 0x2800: case 0x2a00: { int k = 32 - ((op >> 5) & 0x1f); printf ("sra %s, %s", fmthex (k), regname (rd, rf)); } break; case 0x2c00: case 0x2e00: { int k = 32 - ((op >> 5) & 0x1f); printf ("srl %s, %s", fmthex (k), regname (rd, rf)); } break; case 0x3000: case 0x3200: { int k = (op >> 5) & 0x1f; printf ("rl %s, %s", fmthex (k), regname (rd, rf)); } break; case 0x3800: case 0x3a00: case 0x3c00: case 0x3e00: { int k = (op >> 5) & 0x1f; if (op & 0x0400) k = -k; uint32_t target = pc + 16 + (k << 4); printf ("dsjs %s, %s", regname (rd, rf), fmt_target (target)); } break; /* register-register ALU */ case 0x4000: printf ("add %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x4200: printf ("addc %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x4400: printf ("sub %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x4600: printf ("subb %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x4800: printf ("cmp %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x4a00: printf ("btst %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x4c00: case 0x4e00: if (!(op & 0x0200)) printf ("move %s, %s", regname (rs, rf), regname (rd, rf)); else printf ("move %s, %s", regname (rs, rf), regname (rd, rf ^ 1)); break; case 0x5000: printf ("and %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5200: printf ("andn %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5400: printf ("or %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5600: if (rs == rd) printf ("clr %s", regname (rd, rf)); else printf ("xor %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5800: printf ("divs %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5a00: printf ("divu %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5c00: printf ("mpys %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x5e00: printf ("mpyu %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6000: printf ("sla %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6200: printf ("sll %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6400: printf ("sra %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6600: printf ("srl %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6800: printf ("rl %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6a00: printf ("lmo %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6c00: printf ("mods %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x6e00: printf ("modu %s, %s", regname (rs, rf), regname (rd, rf)); break; /* MOVE with addressing modes */ case 0x8000: case 0x8200: printf ("move %s, *%s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0x8400: case 0x8600: printf ("move *%s, %s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0x8800: case 0x8a00: printf ("move *%s, *%s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0x8c00: printf ("movb %s, *%s", regname (rs, rf), regname (rd, rf)); break; case 0x8e00: printf ("movb *%s, %s", regname (rs, rf), regname (rd, rf)); break; case 0x9000: case 0x9200: printf ("move %s, *%s+, %d", regname (rs, rf), regname (rd, rf), field); break; case 0x9400: case 0x9600: printf ("move *%s+, %s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0x9800: case 0x9a00: printf ("move *%s+, *%s+, %d", regname (rs, rf), regname (rd, rf), field); break; case 0x9c00: printf ("movb *%s, *%s", regname (rs, rf), regname (rd, rf)); break; case 0xa000: case 0xa200: printf ("move %s, -*%s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0xa400: case 0xa600: printf ("move -*%s, %s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0xa800: case 0xaa00: printf ("move -*%s, -*%s, %d", regname (rs, rf), regname (rd, rf), field); break; case 0xac00: { uint16_t disp = r16 (); printf ("movb %s, *%s(%s)", regname (rs, rf), regname (rd, rf), fmthex (disp)); } break; case 0xae00: { uint16_t disp = r16 (); printf ("movb *%s(%s), %s", regname (rs, rf), fmthex (disp), regname (rd, rf)); } break; case 0xb000: case 0xb200: { uint16_t disp = r16 (); printf ("move %s, *%s(%s), %d", regname (rs, rf), regname (rd, rf), fmthex (disp), field); } break; case 0xb400: case 0xb600: { uint16_t disp = r16 (); printf ("move *%s(%s), %s, %d", regname (rs, rf), fmthex (disp), regname (rd, rf), field); } break; case 0xb800: case 0xba00: { uint16_t d1 = r16 (); uint16_t d2 = r16 (); printf ("move *%s(%s), *%s(%s), %d", regname (rs, rf), fmthex (d1), regname (rd, rf), fmthex (d2), field); } break; case 0xbc00: { uint16_t d1 = r16 (); uint16_t d2 = r16 (); printf ("movb *%s(%s), *%s(%s)", regname (rs, rf), fmthex (d1), regname (rd, rf), fmthex (d2)); } break; /* conditional jumps */ case 0xc000: case 0xc200: case 0xc400: case 0xc600: case 0xc800: case 0xca00: case 0xcc00: case 0xce00: { int cc = (op >> 8) & 0x0f; int lo8 = op & 0xff; if (lo8 == 0x80) { uint32_t addr = r32 (); printf ("ja%s %s", condnames[cc], fmt_target (addr)); } else if (lo8 == 0x00) { int16_t disp = (int16_t) r16 (); uint32_t target = pc + 32 + ((int32_t) disp << 4); printf ("ja%s %s", condnames[cc], fmt_target (target)); } else { int8_t disp = (int8_t) lo8; uint32_t target = pc + 16 + ((int32_t) disp << 4); printf ("jr%s %s", condnames[cc], fmt_target (target)); } } break; case 0xd000: case 0xd200: { uint16_t disp = r16 (); printf ("move *%s(%s), *%s+, %d", regname (rs, rf), fmthex (disp), regname (rd, rf), field); } break; case 0xd400: case 0xd600: switch (subop) { case 0x0000: { uint32_t addr = r32 (); printf ("move @%s, *%s+, %d", fmthex (addr), regname (rd, rf), field); if (ioreg_name (addr)) io_addrs[n_io++] = addr; } break; case 0x0100: printf ("exgf %s, %d", regname (rd, rf), field); break; default: bad = 1; } break; case 0xde00: switch (subop) { case 0x0100: printf ("line 0"); break; case 0x0180: printf ("line 1"); break; default: bad = 1; } break; /* graphics */ case 0xe000: printf ("addxy %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xe200: printf ("subxy %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xe400: printf ("cmpxy %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xe600: printf ("cpw %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xe800: printf ("cvxyl %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xec00: printf ("movx %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xee00: printf ("movy %s, %s", regname (rs, rf), regname (rd, rf)); break; /* PIXT */ case 0xf000: printf ("pixt %s, *%s, xy", regname (rs, rf), regname (rd, rf)); break; case 0xf200: printf ("pixt *%s, xy, %s", regname (rs, rf), regname (rd, rf)); break; case 0xf400: printf ("pixt *%s, xy, *%s, xy", regname (rs, rf), regname (rd, rf)); break; case 0xf600: printf ("drav %s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xf800: printf ("pixt %s, *%s", regname (rs, rf), regname (rd, rf)); break; case 0xfa00: printf ("pixt *%s, %s", regname (rs, rf), regname (rd, rf)); break; case 0xfc00: printf ("pixt *%s, *%s", regname (rs, rf), regname (rd, rf)); break; default: bad = 1; } if (bad) printf (".word 0x%04x", op); if (show_opbytes || n_io > 0) { printf ("\t;"); if (show_opbytes) for (size_t b = startpos; b < pos; b += 2) printf (" %04x", peek16 (b)); for (int j = 0; j < n_io; j++) { const char *name = ioreg_name (io_addrs[j]); if (name) printf (" %s", name); } } printf ("\n"); } free (code_map); free (label_map); free (buf); return 0; }