Files
SDD af7d3565a5 dis34: print long relative JR as jr and surface B-file SP as b15
The 16-bit-displacement jump (low byte 0x00, two words) is JRcc, not the
three-word JAcc (low byte 0x80).  Printing it as ja made linear
disassembly report the wrong control flow and made every such branch grow
by a word when reassembled.

Register 15 is the stack pointer in either file: 0x0F is A15 and 0x1F is
B15.  The disassembler printed both as sp, hiding B-file stack-pointer
code.  Print b15 when the R bit selects the B file.

Validated with the 1987 TI GSPA/GSPLNK/GSPROM round trip: the 32 KB region
at bit 3F208000 still reassembles byte-for-byte (md5
43adb6669571041985f3e26fd9ff7cb9), as do the 4 KB and 16 KB spans and the
0x8000/3F240000 region.  The converter no longer needs its ja->jr or
sp->b15 rewrites (5 fixups -> 3); a whole-ROM diff contains only those two
output categories.  The ROM is unchanged
(b46a3e442ccb86134d2356bd70d22fd2).
2026-10-18 00:54:34 +03:00

2319 lines
64 KiB
C

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
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)
{
/* Register 15 is the stack pointer in *either* file: the R bit
(bit 4 of the register field) selects A15 or B15. Printing both
as "sp" hides which file the instruction actually names. GSPA
spells the B-file one "b15"; it rejects "a15", and bare "sp" is
the A-file register. */
strcpy (p, file ? "b15" : "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)
{
/* 16-bit displacement form: still JRcc (2 words), not
the 3-word absolute JAcc (lo8 == 0x80). */
int16_t disp = (int16_t) r16 ();
uint32_t target = pc + 32 + ((int32_t) disp << 4);
printf ("jr%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;
}