mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
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).
2319 lines
64 KiB
C
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;
|
|
}
|