/* This file is in the public domain. */ /* * Super-H support. For the moment, this is for the SH-4, such as used * in the Dreamcast; in particular, Hitachi document ADE-602-156A rev * 2.0, documenting the SH7750, is my main reference. */ #include #include extern const char *__progname; #include "machine.h" #include "fnprintf.h" typedef struct instr INSTR; struct instr { const char *init; const char *fmt; unsigned short int mask; unsigned short int value; } ; /* * In fmt: * %n = 0x0f00 bits * %N3 = like %n, but masked with 0xe * %N2 = like %n, but masked with 0xc * %m = 0x00f0 bits * %M3 = like %m, but masked with 0xe * %M2 = 0x0300 bits, *4 * %i = 0x00ff bits, signed * %b8 = 0x00ff bits, branch offset * %bc = 0x0fff bits, branch offset * %r = 0x0070 bits * %p2 = 0x00ff bits, pc-relative offset ×2 * %p4 = 0x00ff bits, (pc&~3)-relative offset ×4 * %d1 = 0x00ff bits * %d2 = 0x00ff bits ×2 * %d4 = 0x00ff bits ×4 * %D1 = 0x000f bits * %D2 = 0x000f bits ×2 * %D4 = 0x000f bits ×4 * * %w also exists in the code (dis_fmt), but it is not for use by this * table; it's for the "unrecognized" format. */ static INSTR instrs[] = { { "0011nnnnmmmm1100", "add r%m,r%n" }, { "0111nnnniiiiiiii", "add #%i,r%n" }, { "0011nnnnmmmm1110", "addc r%m,r%n" }, { "0011nnnnmmmm1111", "addv r%m,r%n" }, { "0010nnnnmmmm1001", "and r%m,r%n" }, { "11001001iiiiiiii", "and #%i,r0" }, { "11001101iiiiiiii", "and.b #%i,@(r0,gbr)" }, { "10001011dddddddd", "bf %b8" }, { "10001111dddddddd", "bf/s %b8" }, { "1010dddddddddddd", "bra %bc" }, { "0000nnnn00100011", "braf r%n" }, { "1011dddddddddddd", "bsr %bc" }, { "0000nnnn00000011", "bsrf r%n" }, { "10001001dddddddd", "bt %b8" }, { "10001101dddddddd", "bt/s %b8" }, { "0000000000101000", "clrmac" }, { "0000000001001000", "clrs" }, { "0000000000001000", "clrt" }, { "0011nnnnmmmm0000", "cmp/eq r%m,r%n" }, { "0011nnnnmmmm0011", "cmp/ge r%m,r%n" }, { "0011nnnnmmmm0111", "cmp/gt r%m,r%n" }, { "0011nnnnmmmm0110", "cmp/hi r%m,r%n" }, { "0011nnnnmmmm0010", "cmp/hs r%m,r%n" }, { "0100nnnn00010101", "cmp/pl r%n" }, { "0100nnnn00010001", "cmp/pz r%n" }, { "0010nnnnmmmm1100", "cmp/str r%m,r%n" }, { "10001000iiiiiiii", "cmp/eq #%i,r0" }, { "0010nnnnmmmm0111", "div0s r%m,r%n" }, { "0000000000011001", "div0u" }, { "0011nnnnmmmm0100", "div1 r%m,r%n" }, { "0011nnnnmmmm1101", "dmuls.l r%m,r%n" }, { "0011nnnnmmmm0101", "dmulu.l r%m,r%n" }, { "0100nnnn00010000", "dt r%n" }, { "0110nnnnmmmm1110", "exts.b r%m,r%n" }, { "0110nnnnmmmm1111", "exts.w r%m,r%n" }, { "0110nnnnmmmm1100", "extu.b r%m,r%n" }, { "0110nnnnmmmm1101", "extu.w r%m,r%n" }, /* There are several cases here where 1111nnnnmmmmXXXX ... 1111nnn0mmm0XXXX ... gets turned into 1111nnn0mmm0XXXX ... 1111nnn1mmm0XXXX ... 1111nnn0mmm1XXXX ... 1111nnn1mmm1XXXX ... with the same format for the last three. These are marked with comments "expand00", "expand10", etc. */ { "1111nnn101011101", "fabs fr%n" }, { "1111nnn001011101", "fabs fr%n/dr%N3" }, { "1111nnn0mmm00000", "fadd fr%m/dr%M3,fr%n/dr%N3" }, /* expand00 */ { "1111nnn1mmm00000", "fadd fr%m,fr%n" }, /* expand10 */ { "1111nnn0mmm10000", "fadd fr%m,fr%n" }, /* expand01 */ { "1111nnn1mmm10000", "fadd fr%m,fr%n" }, /* expand11 */ { "1111nnn0mmm00100", "fcmp/eq fr%m/dr%M3,fr%n/dr%N3" }, /* expand00 */ { "1111nnn1mmm00100", "fcmp/eq fr%m,fr%n" }, /* expand10 */ { "1111nnn0mmm10100", "fcmp/eq fr%m,fr%n" }, /* expand01 */ { "1111nnn1mmm10100", "fcmp/eq fr%m,fr%n" }, /* expand11 */ { "1111nnn0mmm00101", "fcmp/gt fr%m/dr%M3,fr%n/dr%N3" }, /* expand00 */ { "1111nnn1mmm00101", "fcmp/gt fr%m,fr%n" }, /* expand10 */ { "1111nnn0mmm10101", "fcmp/gt fr%m,fr%n" }, /* expand01 */ { "1111nnn1mmm10101", "fcmp/gt fr%m,fr%n" }, /* expand11 */ { "1111nnn010111101", "fcnvds dr%N3,fpul" }, { "1111nnn010101101", "fcnvsd fpul,dr%N3" }, { "1111nnn0mmm00011", "fdiv fr%m/dr%M3,fr%n/dr%N3" }, /* expand00 */ { "1111nnn1mmm00011", "fdiv fr%m,fr%n" }, /* expand10 */ { "1111nnn0mmm10011", "fdiv fr%m,fr%n" }, /* expand01 */ { "1111nnn1mmm10011", "fdiv fr%m,fr%n" }, /* expand11 */ { "1111nnmm11101101", "fipr fv%M2,fv%M2" }, { "1111nnnn10001101", "fldi0 fr%n" }, { "1111nnnn10011101", "fldi1 fr%n" }, { "1111nnnn00011101", "flds fr%n,fpul" }, { "1111nnn100101101", "float fpul,fr%n" }, { "1111nnn000101101", "float fpul,fr%n/dr%N3" }, { "1111nnnnmmmm1110", "fmac fr0,fr%m,fr%n" }, { "1111nnn0mmm01100", "fmov fr%m/dr%M3,fr%n/dr%N3" }, { "1111nnn1mmm11100", "fmov fr%m/xd%M3,fr%n/xd%N3" }, { "1111nnn0mmm11100", "fmov fr%m/xd%M3,fr%n/dr%N3" }, { "1111nnn1mmm01100", "fmov fr%m/dr%M3,fr%n/xd%N3" }, { "1111nnnnmmm01010", "fmov fr%m/dr%M3,@r%n" }, { "1111nnnnmmm11010", "fmov fr%m/xd%M3,@r%n" }, { "1111nnn0mmmm1000", "fmov @r%m,fr%n/dr%N3" }, { "1111nnn1mmmm1000", "fmov @r%m,fr%n/xd%N3" }, { "1111nnn0mmmm1001", "fmov @r%m+,fr%n/dr%N3" }, { "1111nnnnmmm01011", "fmov fr%m/dr%M3,@-r%n" }, { "1111nnnnmmm11011", "fmov fr%m/xd%M3,@-r%n" }, { "1111nnn0mmmm0110", "fmov @(r0,r%m),fr%n/dr%N3" }, { "1111nnn1mmmm0110", "fmov @(r0,r%m),fr%n/xd%N3" }, { "1111nnnnmmm00111", "fmov fr%m/dr%M3,@(r0,r%n)" }, { "1111nnnnmmm10111", "fmov fr%m/xd%M3,@(r0,r%n)" }, { "1111nnn1mmmm1001", "fmov @r%m+,fr%n/xd%N3" }, { "1111nnn0mmm00010", "fmul fr%m/dr%M3,fr%n/dr%N3" }, /* expand00 */ { "1111nnn1mmm00010", "fmul fr%m,fr%n" }, /* expand10 */ { "1111nnn0mmm10010", "fmul fr%m,fr%n" }, /* expand01 */ { "1111nnn1mmm10010", "fmul fr%m,fr%n" }, /* expand11 */ { "1111nnn101001101", "fneg fr%n" }, { "1111nnn001001101", "fneg fr%n/dr%N3" }, { "1111101111111101", "frchg" }, { "1111001111111101", "fschg" }, { "1111nnn101101101", "fsqrt fr%n" }, { "1111nnn001101101", "fsqrt fr%n/dr%N3" }, { "1111nnnn00001101", "fsts fpul,fr%n" }, { "1111nnn0mmm00001", "fsub fr%m/dr%M3,fr%m/dr%N3" }, /* expand00 */ { "1111nnn1mmm00001", "fsub fr%m,fr%n" }, /* expand10 */ { "1111nnn0mmm10001", "fsub fr%m,fr%n" }, /* expand01 */ { "1111nnn1mmm10001", "fsub fr%m,fr%n" }, /* expand11 */ { "1111nnn100111101", "ftrc fr%n,fpul" }, { "1111nnn000111101", "ftrc fr%n/dr%N3,fpul" }, { "1111nn0111111101", "ftrv xmtrx,fv%N2" }, { "0100nnnn00101011", "jmp @r%n" }, { "0100nnnn00001011", "jsr @r%n" }, { "0100nnnn00001110", "ldc r%n,sr" }, { "0100nnnn00011110", "ldc r%n,gbr" }, { "0100nnnn00101110", "ldc r%n,vbr" }, { "0100nnnn00111110", "ldc r%n,ssr" }, { "0100nnnn01001110", "ldc r%n,spc" }, { "0100nnnn11111010", "ldc r%n,dbr" }, { "0100nnnn1rrr1110", "ldc r%n,r%r_bank" }, { "0100nnnn00000111", "ldc.l @r%n+,sr" }, { "0100nnnn00010111", "ldc.l @r%n+,gbr" }, { "0100nnnn00100111", "ldc.l @r%n+,vbr" }, { "0100nnnn00110111", "ldc.l @r%n+,ssr" }, { "0100nnnn01000111", "ldc.l @r%n+,spc" }, { "0100nnnn11110110", "ldc.l @r%n+,dbr" }, { "0100nnnn1rrr0111", "ldc.l @r%n+,r%r_bank" }, { "0100nnnn01011010", "lds r%n,fpul" }, { "0100nnnn01010110", "lds.l @r%n+,fpul" }, { "0100nnnn01101010", "lds r%n,fpscr" }, { "0100nnnn01100110", "lds.l @r%n+,fpscr" }, { "0100nnnn00001010", "lds r%n,mach" }, { "0100nnnn00011010", "lds r%n,macl" }, { "0100nnnn00101010", "lds r%n,pr" }, { "0100nnnn00000110", "lds.l @r%n+,mach" }, { "0100nnnn00010110", "lds.l @r%n+,macl" }, { "0100nnnn00100110", "lds.l @r%n+,pr" }, { "0000000000111000", "ldtlb" }, { "0000nnnnmmmm1111", "mac.l @r%m+,@r%n+" }, { "0100nnnnmmmm1111", "mac.w @r%m+,@r%n+" }, { "0110nnnnmmmm0011", "mov r%m,r%n" }, { "0010nnnnmmmm0000", "mov.b r%m,@r%n" }, { "0010nnnnmmmm0001", "mov.w r%m,@r%n" }, { "0010nnnnmmmm0010", "mov.l r%m,@r%n" }, { "0110nnnnmmmm0000", "mov.b @r%m,r%n" }, { "0110nnnnmmmm0001", "mov.w @r%m,r%n" }, { "0110nnnnmmmm0010", "mov.l @r%m,r%n" }, { "0010nnnnmmmm0100", "mov.b r%m,@-r%n" }, { "0010nnnnmmmm0101", "mov.w r%m,@-r%n" }, { "0010nnnnmmmm0110", "mov.l r%m,@-r%n" }, { "0110nnnnmmmm0100", "mov.b @r%m+,r%n" }, { "0110nnnnmmmm0101", "mov.w @r%m+,r%n" }, { "0110nnnnmmmm0110", "mov.l @r%m+,r%n" }, { "0000nnnnmmmm0100", "mov.b r%m,@(r0,r%n)" }, { "0000nnnnmmmm0101", "mov.w r%m,@(r0,r%n)" }, { "0000nnnnmmmm0110", "mov.l r%m,@(r0,r%n)" }, { "0000nnnnmmmm1100", "mov.b @(r0,r%m),r%n" }, { "0000nnnnmmmm1101", "mov.w @(r0,r%m),r%n" }, { "0000nnnnmmmm1110", "mov.l @(r0,r%m),r%n" }, { "1110nnnniiiiiiii", "mov #%i,r%n" }, { "1001nnnnoooooooo", "mov.w %p2,r%n" }, { "1101nnnnoooooooo", "mov.l %p4,r%n" }, { "11000100oooooooo", "mov.b @(%d1,gbr),r0" }, { "11000101oooooooo", "mov.w @(%d2,gbr),r0" }, { "11000110oooooooo", "mov.l @(%d4,gbr),r0" }, { "11000000oooooooo", "mov.b r0,@(%d1,gbr)" }, { "11000001oooooooo", "mov.w r0,@(%d2,gbr)" }, { "11000010oooooooo", "mov.l r0,@(%d4,gbr)" }, { "10000000mmmmoooo", "mov.b r0,@(%D1,r%m)" }, { "10000001mmmmoooo", "mov.w r0,@(%D2,r%m)" }, { "0001nnnnmmmmoooo", "mov.l r%m,@(%D4,r%n)" }, { "10000100mmmmoooo", "mov.b @(%D1,r%m),r0" }, { "10000101mmmmoooo", "mov.w @(%D2,r%m),r0" }, { "0101nnnnmmmmoooo", "mov.l @(%D4,r%m),r%n" }, { "11000111oooooooo", "mova %p4,r0" }, { "0000nnnn11000011", "movca.l r0,@r%n" }, { "0000nnnn00101001", "movt r%n" }, { "0000nnnnmmmm0111", "mul.l r%m,r%n" }, { "0010nnnnmmmm1111", "muls.w r%m,r%n" }, { "0010nnnnmmmm1110", "mulu.w r%m,r%n" }, { "0110nnnnmmmm1011", "neg r%m,r%n" }, { "0110nnnnmmmm1010", "negc r%m,r%n" }, { "0000000000001001", "nop" }, { "0110nnnnmmmm0111", "not r%m,r%n" }, { "0000nnnn10010011", "ocbi @r%n" }, { "0000nnnn10100011", "ocbp @r%n" }, { "0000nnnn10110011", "ocbwb @r%n" }, { "0010nnnnmmmm1011", "or r%m,r%n" }, { "11001011iiiiiiii", "or #%i,r0" }, { "11001111iiiiiiii", "or.b #%i,@(r0,gbr)" }, { "0000nnnn10000011", "pref @r%n" }, { "0100nnnn00100100", "rotcl r%n" }, { "0100nnnn00100101", "rotcr r%n" }, { "0100nnnn00000100", "rotl r%n" }, { "0100nnnn00000101", "rotr r%n" }, { "0000000000101011", "rte" }, { "0000000000001011", "rts" }, { "0000000001011000", "sets" }, { "0000000000011000", "sett" }, { "0100nnnnmmmm1100", "shad r%m,r%n" }, { "0100nnnn00100000", "shal r%n" }, { "0100nnnn00100001", "shar r%n" }, { "0100nnnnmmmm1101", "shld r%m,r%n" }, { "0100nnnn00000000", "shll r%n" }, { "0100nnnn00001000", "shll2 r%n" }, { "0100nnnn00011000", "shll8 r%n" }, { "0100nnnn00101000", "shll16 r%n" }, { "0100nnnn00000001", "shlr r%n" }, { "0100nnnn00001001", "shlr2 r%n" }, { "0100nnnn00011001", "shlr8 r%n" }, { "0100nnnn00101001", "shlr16 r%n" }, { "0000000000011011", "sleep" }, { "0000nnnn00000010", "stc sr,r%n" }, { "0000nnnn00010010", "stc gbr,r%n" }, { "0000nnnn00100010", "stc vbr,r%n" }, { "0000nnnn00110010", "stc ssr,r%n" }, { "0000nnnn01000010", "stc spc,r%n" }, { "0000nnnn00111010", "stc sgr,r%n" }, { "0000nnnn11111010", "stc dbr,r%n" }, { "0000nnnn1rrr0010", "stc r%r_bank,r%n" }, { "0100nnnn00000011", "stc.l sr,@-r%n" }, { "0100nnnn00010011", "stc.l gbr,@-r%n" }, { "0100nnnn00100011", "stc.l vbr,@-r%n" }, { "0100nnnn00110011", "stc.l ssr,@-r%n" }, { "0100nnnn01000011", "stc.l spc,@-r%n" }, { "0100nnnn00110010", "stc.l sgr,@-r%n" }, { "0100nnnn11110010", "stc.l dbr,@-r%n" }, { "0100nnnn1rrr0011", "stc.l r%r_bank,@-r%n" }, { "0000nnnn00001010", "sts mach,r%n" }, { "0000nnnn00011010", "sts macl,r%n" }, { "0000nnnn00101010", "sts pr,r%n" }, { "0100nnnn00000010", "sts.l mach,@-r%n" }, { "0100nnnn00010010", "sts.l macl,@-r%n" }, { "0100nnnn00100010", "sts.l pr,@-r%n" }, { "0000nnnn01011010", "sts fpul,r%n" }, { "0000nnnn01101010", "sts fpscr,r%n" }, { "0100nnnn01010010", "sts.l fpul,@-r%n" }, { "0100nnnn01100010", "sts.l fpscr,@-r%n" }, { "0011nnnnmmmm1000", "sub r%m,r%n" }, { "0011nnnnmmmm1010", "subc r%m,r%n" }, { "0011nnnnmmmm1011", "subv r%m,r%n" }, { "0110nnnnmmmm1000", "swap.b r%m,r%n" }, { "0110nnnnmmmm1001", "swap.w r%m,r%n" }, { "0100nnnn00011011", "tas.b @r%n" }, { "11000011iiiiiiii", "trapa #%i" }, { "0010nnnnmmmm1000", "tst r%m,r%n" }, { "11001000iiiiiiii", "tst #%i,r0" }, { "11001100iiiiiiii", "tst.b #%i,@(r0,gbr)" }, { "0010nnnnmmmm1010", "xor r%m,r%n" }, { "11001010iiiiiiii", "xor #%i,r0" }, { "11001110iiiiiiii", "xor.b #%i,@(r0,gbr)" }, { "0010nnnnmmmm1101", "xtrct r%m,r%n" }, { 0, 0 } }; static void dis_fmt(const char *fmt, unsigned short int inst, ADDR at, void (*fn)(char)) { int v; while (*fmt) { if (*fmt++ != '%') { if (fn) (*fn)(fmt[-1]); continue; } switch (*fmt++) { case 'n': v = (inst >> 8) & 0xf; break; case 'N': switch (*fmt++) { case '3': v = (inst >> 8) & 0xe; break; case '2': v = (inst >> 8) & 0xc; break; default: fnprintf(fn,"",(unsigned char)fmt[-1]); return; break; } break; case 'm': v = (inst >> 4) & 0xf; break; case 'M': switch (*fmt++) { case '3': v = (inst >> 4) & 0xe; break; case '2': v = (inst >> 6) & 0xc; break; default: fnprintf(fn,"",(unsigned char)fmt[-1]); return; break; } break; case 'i': v = (inst & 0x0080) ? (int)(inst&0xff) - (int)0x100 : (inst & 0xff); break; case 'b': switch (*fmt++) { case '8': v = (inst & 0x0080) ? (int)(inst&0xff) - (int)0x100 : (inst & 0xff); break; case 'c': v = (inst & 0x0800) ? (int)(inst&0xfff) - (int)0x1000 : (inst & 0xfff); break; default: fnprintf(fn,"",(unsigned char)fmt[-1]); return; break; } print_symbol_or_hex(fn,at+4+(v*2)); continue; break; case 'r': v = (inst >> 4) & 0x7; break; case 'p': v = inst & 0x00ff; switch (*fmt++) { case '2': print_symbol_or_hex(fn,at+4+(v*2)); break; case '4': print_symbol_or_hex(fn,(at&~(ADDR)3)+4+(v*4)); break; default: fnprintf(fn,"",(unsigned char)fmt[-1]); return; break; } continue; break; case 'd': switch (*fmt++) { case '1': v = inst & 0xff; break; case '2': v = (inst & 0xff) << 1; break; case '4': v = (inst & 0xff) << 2; break; default: fnprintf(fn,"",(unsigned char)fmt[-1]); return; break; } break; case 'D': switch (*fmt++) { case '1': v = inst & 0xf; break; case '2': v = (inst & 0xf) << 1; break; case '4': v = (inst & 0xf) << 2; break; default: fnprintf(fn,"",(unsigned char)fmt[-1]); return; break; } break; case 'w': fnprintf(fn,"%04x",inst); continue; break; } fnprintf(fn,"%d",v); } } static int do_dis_inst(ADDR addr, void (*fn)(char), CFLAG flg) { unsigned short int inst; const char *fmt; int i; const INSTR *ip; int found; addr -= corebase; if (addr+2 > coresize) return(-1); inst = corefetchu(addr,2); found = -1; for (i=0;instrs[i].init;i++) { ip = &instrs[i]; if ((inst & ip->mask) == ip->value) { if (found == -1) { found = i; } else { found = -2; } } } switch (found) { case -1: fmt = ""; break; case -2: fmt = ""; break; default: if (found < 0) abort(); fmt = instrs[found].fmt; break; } dis_fmt(fmt,inst,addr+corebase,fn); if (flg & CFF_INST_PARALLEL) { return(1); } else { flags[addr+1] = CF_PREV; return(2); } } static int init_instr(INSTR *i) { int j; unsigned short int m; unsigned short int v; if (! i->init) return(0); m = 0; v = 0; for (j=0;j<16;j++) { switch (i->init[j]) { case '0': m = (m << 1) | 1; v = (v << 1) | 0; break; case '1': m = (m << 1) | 1; v = (v << 1) | 1; break; case 'd': case 'i': case 'm': case 'n': case 'o': case 'r': m = (m << 1) | 0; v = (v << 1) | 0; break; default: fprintf(stderr,"%s: invalid init (%02x at %d): %s %s\n",__progname,(unsigned char)i->init[j],j,i->init,i->fmt); exit(1); } i->mask = m; i->value = v; } return(1); } static void dreamcast_init(void) { int i; for (i=0;init_instr(&instrs[i]);i++) ; } static int dreamcast_dis(ADDR addr, void (*fn)(char)) { int rv; if (ISINST(flags[addr-corebase])) { rv = do_dis_inst(addr,fn,flags[addr-corebase]); } else { fnprintf(fn,"",(unsigned long int)flags[addr-corebase]); rv = 1; } return(rv); } static unsigned long int dreamcast_fetch(const void *base, int size) #define bp(n) ((unsigned long int)(((const unsigned char *)base)[(n)])) { switch (size) { case 1: return(bp(0)); break; case 2: return((bp(1)<<8)|bp(0)); break; case 4: return((bp(3)<<24)|(bp(2)<<16)|(bp(1)<<8)|bp(0)); break; } abort(); } #undef bp static const char *dreamcast_names[] = { "DREAMCAST", "DreamCast", "Dreamcast", "DC", "dc", 0 }; MACHINE machine_dreamcast = { &dreamcast_names[0], 4, &dreamcast_init, 0, &dreamcast_dis, &dreamcast_fetch, 0 };