summaryrefslogtreecommitdiff
path: root/libinterp/das-arm.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /libinterp/das-arm.c
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'libinterp/das-arm.c')
-rw-r--r--libinterp/das-arm.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/libinterp/das-arm.c b/libinterp/das-arm.c
new file mode 100644
index 00000000..9ba90112
--- /dev/null
+++ b/libinterp/das-arm.c
@@ -0,0 +1,535 @@
+#include <lib9.h>
+
+typedef struct Instr Instr;
+struct Instr
+{
+ ulong w;
+ ulong addr;
+ uchar op; /* super opcode */
+
+ uchar cond; /* bits 28-31 */
+ uchar store; /* bit 20 */
+
+ uchar rd; /* bits 12-15 */
+ uchar rn; /* bits 16-19 */
+ uchar rs; /* bits 0-11 */
+
+ long imm; /* rotated imm */
+ char* curr; /* fill point in buffer */
+ char* end; /* end of buffer */
+ char* err; /* error message */
+};
+
+typedef struct Opcode Opcode;
+struct Opcode
+{
+ char* o;
+ void (*f)(Opcode*, Instr*);
+ char* a;
+};
+
+static void format(char*, Instr*, char*);
+static int arminst(ulong, char, char*, int);
+static int armdas(ulong, char*, int);
+
+static
+char* cond[16] =
+{
+ "EQ", "NE", "CS", "CC",
+ "MI", "PL", "VS", "VC",
+ "HI", "LS", "GE", "LT",
+ "GT", "LE", 0, "NV"
+};
+
+static
+char* shtype[4] =
+{
+ "<<", ">>", "->", "@>"
+};
+
+static int
+get4(ulong addr, long *v)
+{
+ *v = *(ulong*)addr;
+ return 1;
+}
+
+static char *
+_hexify(char *buf, ulong p, int zeros)
+{
+ ulong d;
+
+ d = p/16;
+ if(d)
+ buf = _hexify(buf, d, zeros-1);
+ else
+ while(zeros--)
+ *buf++ = '0';
+ *buf++ = "0123456789abcdef"[p&0x0f];
+ return buf;
+}
+
+int
+armclass(long w)
+{
+ int op;
+
+ op = (w >> 25) & 0x7;
+ switch(op) {
+ case 0: /* data processing r,r,r */
+ op = ((w >> 4) & 0xf);
+ if(op == 0x9) {
+ op = 48+16; /* mul */
+ if(w & (1<<24)) {
+ op += 2;
+ if(w & (1<<22))
+ op++; /* swap */
+ break;
+ }
+ if(w & (1<<21))
+ op++; /* mla */
+ break;
+ }
+ op = (w >> 21) & 0xf;
+ if(w & (1<<4))
+ op += 32;
+ else
+ if(w & (31<<7))
+ op += 16;
+ break;
+ case 1: /* data processing i,r,r */
+ op = (48) + ((w >> 21) & 0xf);
+ break;
+ case 2: /* load/store byte/word i(r) */
+ op = (48+20) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
+ break;
+ case 3: /* load/store byte/word (r)(r) */
+ op = (48+20+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
+ break;
+ case 4: /* block data transfer (r)(r) */
+ op = (48+20+4+4) + ((w >> 20) & 0x1);
+ break;
+ case 5: /* branch / branch link */
+ op = (48+20+4+4+2) + ((w >> 24) & 0x1);
+ break;
+ case 7: /* coprocessor crap */
+ op = (48+20+4+4+2+2) + ((w >> 3) & 0x2) + ((w >> 20) & 0x1);
+ break;
+ default:
+ op = (48+20+4+4+2+2+4);
+ break;
+ }
+ return op;
+}
+
+static int
+decode(ulong pc, Instr *i)
+{
+ long w;
+
+ get4(pc, &w);
+ i->w = w;
+ i->addr = pc;
+ i->cond = (w >> 28) & 0xF;
+ i->op = armclass(w);
+ return 1;
+}
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ i->curr = vseprint(i->curr, i->end, fmt, arg);
+ va_end(arg);
+}
+
+static void
+armdps(Opcode *o, Instr *i)
+{
+ i->store = (i->w >> 20) & 1;
+ i->rn = (i->w >> 16) & 0xf;
+ i->rd = (i->w >> 12) & 0xf;
+ i->rs = (i->w >> 0) & 0xf;
+ if(i->rn == 15 && i->rs == 0) {
+ if(i->op == 8) {
+ format("MOVW", i,"CPSR, R%d");
+ return;
+ } else
+ if(i->op == 10) {
+ format("MOVW", i,"SPSR, R%d");
+ return;
+ }
+ } else
+ if(i->rn == 9 && i->rd == 15) {
+ if(i->op == 9) {
+ format("MOVW", i, "R%s, CPSR");
+ return;
+ } else
+ if(i->op == 11) {
+ format("MOVW", i, "R%s, SPSR");
+ return;
+ }
+ }
+ format(o->o, i, o->a);
+}
+
+static void
+armdpi(Opcode *o, Instr *i)
+{
+ ulong v;
+ int c;
+
+ v = (i->w >> 0) & 0xff;
+ c = (i->w >> 8) & 0xf;
+ while(c) {
+ v = (v<<30) | (v>>2);
+ c--;
+ }
+ i->imm = v;
+ i->store = (i->w >> 20) & 1;
+ i->rn = (i->w >> 16) & 0xf;
+ i->rd = (i->w >> 12) & 0xf;
+ i->rs = i->w&0x0f;
+
+ /* RET is encoded as ADD #0,R14,R15 */
+ if(i->w == 0xe282f000){
+ format("RET", i, "");
+ return;
+ } else
+ format(o->o, i, o->a);
+}
+
+static void
+armsdti(Opcode *o, Instr *i)
+{
+ ulong v;
+
+ v = (i->w >> 0) & 0xfff;
+ if(!(i->w & (1<<23)))
+ v = -v;
+ i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
+ i->imm = v;
+ i->rn = (i->w >> 16) & 0xf;
+ i->rd = (i->w >> 12) & 0xf;
+ /* convert load of offset(PC) to a load immediate */
+ if(i->rn == 15 && (i->w & (1<<20)) && get4(i->addr+v+8, &i->imm) > 0)
+ format(o->o, i, "$#%i,R%d");
+ else
+ format(o->o, i, o->a);
+}
+
+static void
+armsdts(Opcode *o, Instr *i)
+{
+ i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
+ i->rs = (i->w >> 0) & 0xf;
+ i->rn = (i->w >> 16) & 0xf;
+ i->rd = (i->w >> 12) & 0xf;
+ format(o->o, i, o->a);
+}
+
+static void
+armbdt(Opcode *o, Instr *i)
+{
+ i->store = (i->w >> 21) & 0x3; /* S & W bits */
+ i->rn = (i->w >> 16) & 0xf;
+ i->imm = i->w & 0xffff;
+ if(i->w == 0xe8fd8000)
+ format("RFE", i, "");
+ else
+ format(o->o, i, o->a);
+}
+
+static void
+armund(Opcode *o, Instr *i)
+{
+ format(o->o, i, o->a);
+}
+
+static void
+armcdt(Opcode *o, Instr *i)
+{
+ format(o->o, i, o->a);
+}
+
+static void
+armunk(Opcode *o, Instr *i)
+{
+ format(o->o, i, o->a);
+}
+
+static void
+armb(Opcode *o, Instr *i)
+{
+ ulong v;
+
+ v = i->w & 0xffffff;
+ if(v & 0x800000)
+ v |= ~0xffffff;
+ i->imm = (v<<2) + i->addr + 8;
+ format(o->o, i, o->a);
+}
+
+static void
+armco(Opcode *o, Instr *i) /* coprocessor instructions */
+{
+ int op, p, cp;
+
+ char buf[1024];
+
+ i->rn = (i->w >> 16) & 0xf;
+ i->rd = (i->w >> 12) & 0xf;
+ i->rs = i->w&0xf;
+ cp = (i->w >> 8) & 0xf;
+ p = (i->w >> 5) & 0x7;
+ if(i->w&0x10) {
+ op = (i->w >> 20) & 0x0f;
+ snprint(buf, sizeof(buf), "#%x, #%x, R%d, C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p);
+ } else {
+ op = (i->w >> 21) & 0x07;
+ snprint(buf, sizeof(buf), "#%x, #%x, C(%d), C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p);
+ }
+ format(o->o, i, buf);
+}
+
+static Opcode opcodes[] =
+{
+ "AND%C%S", armdps, "R%s,R%n,R%d",
+ "EOR%C%S", armdps, "R%s,R%n,R%d",
+ "SUB%C%S", armdps, "R%s,R%n,R%d",
+ "RSB%C%S", armdps, "R%s,R%n,R%d",
+ "ADD%C%S", armdps, "R%s,R%n,R%d",
+ "ADC%C%S", armdps, "R%s,R%n,R%d",
+ "SBC%C%S", armdps, "R%s,R%n,R%d",
+ "RSC%C%S", armdps, "R%s,R%n,R%d",
+ "TST%C%S", armdps, "R%s,R%n,",
+ "TEQ%C%S", armdps, "R%s,R%n,",
+ "CMP%C%S", armdps, "R%s,R%n,",
+ "CMN%C%S", armdps, "R%s,R%n,",
+ "ORR%C%S", armdps, "R%s,R%n,R%d",
+ "MOVW%C%S", armdps, "R%s,R%d",
+ "BIC%C%S", armdps, "R%s,R%n,R%d",
+ "MVN%C%S", armdps, "R%s,R%d",
+
+ "AND%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "EOR%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "SUB%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "RSB%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "ADD%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "ADC%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "SBC%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "RSC%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "TST%C%S", armdps, "(R%s%h#%m),R%n,",
+ "TEQ%C%S", armdps, "(R%s%h#%m),R%n,",
+ "CMP%C%S", armdps, "(R%s%h#%m),R%n,",
+ "CMN%C%S", armdps, "(R%s%h#%m),R%n,",
+ "ORR%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "MOVW%C%S", armdps, "(R%s%h#%m),R%d",
+ "BIC%C%S", armdps, "(R%s%h#%m),R%n,R%d",
+ "MVN%C%S", armdps, "(R%s%h#%m),R%d",
+
+ "AND%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "EOR%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "SUB%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "RSB%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "ADD%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "ADC%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "SBC%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "RSC%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "TST%C%S", armdps, "(R%s%hR%m),R%n,",
+ "TEQ%C%S", armdps, "(R%s%hR%m),R%n,",
+ "CMP%C%S", armdps, "(R%s%hR%m),R%n,",
+ "CMN%C%S", armdps, "(R%s%hR%m),R%n,",
+ "ORR%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "MOVW%C%S", armdps, "(R%s%hR%m),R%d",
+ "BIC%C%S", armdps, "(R%s%hR%m),R%n,R%d",
+ "MVN%C%S", armdps, "(R%s%hR%m),R%d",
+
+ "AND%C%S", armdpi, "$#%i,R%n,R%d",
+ "EOR%C%S", armdpi, "$#%i,R%n,R%d",
+ "SUB%C%S", armdpi, "$#%i,R%n,R%d",
+ "RSB%C%S", armdpi, "$#%i,R%n,R%d",
+ "ADD%C%S", armdpi, "$#%i,R%n,R%d",
+ "ADC%C%S", armdpi, "$#%i,R%n,R%d",
+ "SBC%C%S", armdpi, "$#%i,R%n,R%d",
+ "RSC%C%S", armdpi, "$#%i,R%n,R%d",
+ "TST%C%S", armdpi, "$#%i,R%n,",
+ "TEQ%C%S", armdpi, "$#%i,R%n,",
+ "CMP%C%S", armdpi, "$#%i,R%n,",
+ "CMN%C%S", armdpi, "$#%i,R%n,",
+ "ORR%C%S", armdpi, "$#%i,R%n,R%d",
+ "MOVW%C%S", armdpi, "$#%i,,R%d",
+ "BIC%C%S", armdpi, "$#%i,R%n,R%d",
+ "MVN%C%S", armdpi, "$#%i,,R%d",
+
+ "MUL%C%S", armdpi, "R%s,R%m,R%n",
+ "MULA%C%S", armdpi, "R%s,R%m,R%n,R%d",
+ "SWPW", armdpi, "R%s,(R%n),R%d",
+ "SWPB", armdpi, "R%s,(R%n),R%d",
+
+ "MOVW%C%p", armsdti,"R%d,#%i(R%n)",
+ "MOVB%C%p", armsdti,"R%d,#%i(R%n)",
+ "MOVW%C%p", armsdti,"#%i(R%n),R%d",
+ "MOVB%C%p", armsdti,"#%i(R%n),R%d",
+
+ "MOVW%C%p", armsdts,"R%d,%D(R%s%h#%m)(R%n)",
+ "MOVB%C%p", armsdts,"R%d,%D(R%s%h#%m)(R%n)",
+ "MOVW%C%p", armsdts,"%D(R%s%h#%m)(R%n),R%d",
+ "MOVB%C%p", armsdts,"%D(R%s%h#%m)(R%n),R%d",
+
+ "MOVM%C%P%a", armbdt, "R%n,[%r]",
+ "MOVM%C%P%a", armbdt, "[%r],R%n",
+
+ "B%C", armb, "%b",
+ "BL%C", armb, "%b",
+
+ "CDP%C", armco, "",
+ "CDP%C", armco, "",
+ "MCR%C", armco, "",
+ "MRC%C", armco, "",
+
+ "UNK", armunk, "",
+};
+
+static char *mode[] = { 0, "IA", "DB", "IB" };
+static char *pw[] = { "P", "PW", 0, "W" };
+static char *sw[] = { 0, "W", "S", "SW" };
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+ int j, k, m, n;
+
+ if(mnemonic)
+ format(0, i, mnemonic);
+ if(f == 0)
+ return;
+ if(mnemonic)
+ if(i->curr < i->end)
+ *i->curr++ = '\t';
+ for ( ; *f && i->curr < i->end; f++) {
+ if(*f != '%') {
+ *i->curr++ = *f;
+ continue;
+ }
+ switch (*++f) {
+
+ case 'C': /* .CONDITION */
+ if(cond[i->cond])
+ bprint(i, ".%s", cond[i->cond]);
+ break;
+
+ case 'S': /* .STORE */
+ if(i->store)
+ bprint(i, ".S");
+ break;
+
+ case 'P': /* P & U bits for block move */
+ n = (i->w >>23) & 0x3;
+ if (mode[n])
+ bprint(i, ".%s", mode[n]);
+ break;
+
+ case 'D': /* ~U bit for single data xfer */
+ if((i->w & (1<<23)) == 0)
+ bprint(i, "-");
+ break;
+
+ case 'p': /* P & W bits for single data xfer*/
+ if (pw[i->store])
+ bprint(i, ".%s", pw[i->store]);
+ break;
+
+ case 'a': /* S & W bits for single data xfer*/
+ if (sw[i->store])
+ bprint(i, ".%s", sw[i->store]);
+ break;
+
+ case 's':
+ bprint(i, "%d", i->rs & 0xf);
+ break;
+
+ case 'm':
+ bprint(i, "%d", (i->w>>7) & 0x1f);
+ break;
+
+ case 'h':
+ bprint(i, "%s", shtype[(i->w>>5) & 0x3]);
+ break;
+
+ case 'n':
+ bprint(i, "%d", i->rn);
+ break;
+
+ case 'd':
+ bprint(i, "%d", i->rd);
+ break;
+
+ case 'i':
+ bprint(i, "%lux", i->imm);
+ break;
+
+ case 'b':
+ bprint(i, "%lux", i->imm);
+ break;
+
+ case 'r':
+ n = i->imm&0xffff;
+ j = 0;
+ k = 0;
+ while(n) {
+ m = j;
+ while(n&0x1) {
+ j++;
+ n >>= 1;
+ }
+ if(j != m) {
+ if(k)
+ bprint(i, ",");
+ if(j == m+1)
+ bprint(i, "R%d", m);
+ else
+ bprint(i, "R%d-R%d", m, j-1);
+ k = 1;
+ }
+ j++;
+ n >>= 1;
+ }
+ break;
+
+ case '\0':
+ *i->curr++ = '%';
+ return;
+
+ default:
+ bprint(i, "%%%c", *f);
+ break;
+ }
+ }
+ *i->curr = 0;
+}
+
+void
+das(ulong *x, int n)
+{
+ ulong pc;
+ Instr i;
+ char buf[128];
+
+ pc = (ulong)x;
+ while(n > 0) {
+ i.curr = buf;
+ i.end = buf+sizeof(buf)-1;
+
+ if(decode(pc, &i) < 0)
+ sprint(buf, "???");
+ else
+ (*opcodes[i.op].f)(&opcodes[i.op], &i);
+
+ print("%.8lux %.8lux\t%s\n", pc, i.w, buf);
+ pc += 4;
+ n--;
+ }
+}