rizin/libr/asm/arch/xap/dis.c

563 lines
9.5 KiB
C

/* sorbo '07 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
//#include <err.h>
#include <string.h>
//#include <assert.h>
#define assert(x) if (!(x)) { eprintf("assert ##x##\n"); return; }
#include <stdarg.h>
#include <stdint.h>
#include "dis.h"
static struct state _state;
#include <r_types.h>
static inline struct state *get_state(void) {
memset (&_state, 0, sizeof (struct state));
return &_state;
}
static uint16_t i2u16(struct instruction *in) {
return *((uint16_t*)in);
}
#if 0
static void output(struct state *s, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
cons_printf(fmt, ap);
//vfprintf(s->s_out, fmt, ap);
va_end(ap);
}
#endif
static void decode_unknown(struct state *s, struct directive *d) {
#if 0
printf("Opcode 0x%x reg %d mode %d operand 0x%x",
in->in_opcode, in->in_reg, in->in_mode, in->in_operand);
#endif
sprintf(d->d_asm, "DC 0x%4x", i2u16(&d->d_inst));
}
static int decode_fixed(struct state *s, struct directive *d) {
*d->d_asm='\0';
switch (i2u16 (&d->d_inst)) {
case INST_NOP:
if (s->s_prefix)
return 0;
s->s_nop++;
strcpy(d->d_asm, "nop");
break;
case INST_BRK: strcpy(d->d_asm, "brk"); break;
case INST_SLEEP: strcpy(d->d_asm, "sleep"); break;
case INST_SIF: strcpy(d->d_asm, "sif"); break;
case INST_BC: strcpy(d->d_asm, "bc"); break;
case INST_BRXL: strcpy(d->d_asm, "brxl"); break;
case INST_U: strcpy(d->d_asm, ""); s->s_u = 1; break;
case INST_RTS: strcpy(d->d_asm, "rts"); break;
}
return d->d_asm[0]!=0;
}
static char *regname(int reg) {
switch (reg) {
case REG_AH: return "AH";
case REG_AL: return "AL";
case REG_X: return "X";
case REG_Y: return "Y";
}
return NULL;
}
static int get_num(int num, int shift) {
char x = (char) ((num >> shift) & 0xff);
return (int)(x<<shift);
}
static int get_operand(struct state *s, struct directive *d) {
int total = get_num(d->d_inst.in_operand, 0);
if (s->s_prefix)
total += get_num(s->s_prefix_val, 8);
if (s->s_prefix == 2)
total += get_num(s->s_prefix_val, 16);
return total;
}
static int decode_known(struct state *s, struct directive *d) {
char *op = NULL;
char *regn = NULL;
int reg = 0;
int ptr = 1;
int idx = 1;
int imm = 0;
int rel = 0;
char fmt[16];
char tmp[128];
int fmtsz;
int branch = 0;
struct instruction *in = &d->d_inst;
// int operand;
char *sign = "";
int rti = 0;
switch (in->in_opcode) {
case 0:
if (in->in_reg == 0 && in->in_mode == 0) {
if (s->s_prefix == 0)
s->s_prefix_val = 0;
s->s_prefix++;
if (s->s_prefix == 2)
s->s_prefix_val <<= 8;
#if 0
/* XXX we need to look ahead more to see if we're
* getting a branch instruction */
if (s->s_nopd && in->in_operand == 0x80)
strcpy(s->s_nopd->d_asm, "");
#endif
s->s_prefix_val |= in->in_operand << 8;
strcpy(d->d_asm, "");
return 1;
}
switch (i2u16(in) & 0xf) {
case 1:
op = "st";
regn = "FLAGS";
break;
case 2:
op = "st";
regn = "UX";
break;
case 3:
op = "st";
regn = "UY";
break;
case 5:
op = "ld";
regn = "FLAGS";
break;
case 6:
op = "ld";
regn = "UX";
break;
case 7:
op = "ld";
regn = "UY";
break;
case 0xa:
op = "st";
regn = "XH";
break;
case 0xd:
op = "rti";
regn = "";
rti = 1;
break;
case 0xe:
op = "ld";
regn = "XH";
break;
}
break;
case 1:
op = "ld";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 2:
if (in->in_mode == DATA_MODE_IMMEDIATE) {
op = "print";
imm = 1;
reg = 1;
} else {
op = "st";
reg = 1;
ptr = 1;
idx = 1;
}
break;
case 3:
op = "add";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 4:
op = "addc";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 5:
op = "sub";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 6:
op = "subc";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 7:
op = "nadd";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 8:
op = "cmp";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 0x9:
switch (in->in_reg) {
case 0:
if (s->s_u) op = "umult";
else op = "smult";
imm = 1;
s->s_u = 0;
idx = 1;
ptr = 1;
break;
case 1:
if (s->s_u) op = "udiv";
else op = "sdiv";
s->s_u = 0;
imm = 1;
break;
case 2:
op = "tst";
imm = 1;
ptr = 1;
idx = 1;
break;
case 3:
branch = 1;
op = "bsr";
ptr = 1;
idx = 1;
if (in->in_mode == ADDR_MODE_RELATIVE)
rel = 1;
break;
}
break;
case 0xa:
switch (in->in_reg) {
case 0:
op = "asl";
imm = 1;
break;
case 1:
if (s->s_u) op = "lsr";
else op = "asr";
s->s_u = 0;
imm = 1;
idx = 1;
ptr = 1;
break;
case 2:
op = "rol";
imm = 1;
break;
case 3:
op = "ror";
imm = 1;
break;
}
break;
case 0xb:
op = "or";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 0xc:
op = "and";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 0xd:
op = "xor";
reg = 1;
ptr = 1;
idx = 1;
imm = 1;
break;
case 0xe:
branch = 1;
if (in->in_mode == ADDR_MODE_RELATIVE)
rel = 1;
switch (in->in_reg) {
case 0:
op = "bra";
ptr = 1;
idx = 1;
#if 0
if (s->s_nopd) {
op = "bra2"; /* XXX need bra3 support */
strcpy(s->s_nopd->d_asm, "");
}
#endif
break;
case 1:
op = "blt"; /* yummy */
break;
case 2:
op = "bpl";
break;
case 3:
op = "bmi";
break;
}
break;
case 0xf:
branch = 1;
if (in->in_mode == ADDR_MODE_RELATIVE)
rel = 1;
switch (in->in_reg) {
case 0: op = "bne"; break;
case 1: op = "beq"; break;
case 2: op = "bcc"; break;
case 3: op = "bcs"; break;
}
break;
}
if (!op) return 0;
if (ptr && in->in_mode == DATA_MODE_IMMEDIATE)
ptr = 0;
if (branch && in->in_mode == ADDR_MODE_X_RELATIVE)
ptr = 0;
if (idx && (!(in->in_mode & 2)))
idx = 0;
if (regn) {
ptr = 1;
idx = 1;
reg = 1;
}
sprintf (d->d_asm, "%s ", op);
if (reg) {
char *r = regn;
if (!r) r = regname(in->in_reg);
if (r && !rti) {
strcat (d->d_asm, r);
strcat (d->d_asm, ", ");
}
}
if (ptr) {
strcat(d->d_asm, "@");
rel = 0;
} else if (imm) strcat(d->d_asm, "#");
if (idx && ptr) strcat(d->d_asm, "(");
d->d_prefix = s->s_prefix;
// d->d_operand = get_operand(s, d);
#if 1
if ((branch && idx) || rti) {
d->d_operand = get_operand(s, d);
if (d->d_operand < 0) {
d->d_operand *= -1;
sign = "-";
}
} else {
d->d_operand = s->s_prefix_val | in->in_operand;
if (d->d_operand & 0x80) {
if (d->d_prefix) {
if (!rel) d->d_operand -= 0x100;
} else d->d_operand |= 0xff00;
}
}
#endif
#if 0
operand = d->d_operand;
if (operand < 0)
operand *= -1;
#endif
fmtsz = 4;
if (d->d_operand & 0xff0000)
fmtsz += 2;
// can be cleaned, no need to fmtsz
snprintf (fmt, sizeof (fmt), "%s0x%%.%dX", sign, fmtsz);
snprintf (tmp, sizeof (tmp), fmt, d->d_operand);
strcat (d->d_asm, tmp);
if (idx) {
char *r = in->in_mode == DATA_MODE_INDEXED_X ? "X" : "Y";
if (regn) r = "Y";
snprintf(tmp, sizeof(tmp), ", %s", r);
strcat(d->d_asm, tmp);
if (ptr)
strcat(d->d_asm, ")");
}
#if 0
/* XXX quirks */
if (!rel && in->in_mode == DATA_MODE_IMMEDIATE
&& ((d->d_operand & 0xff00) == 0x7F00) && d->d_operand & 0x80)
s->s_ff_quirk = 1;
if (rel && !s->s_prefix && d->d_operand == 0x7F) {
if (s->s_nopd) {
printf("w00t\n");
strcpy(s->s_nopd->d_asm, "nop");
}
printf("Warning: fucking up a branch %x\n", d->d_off);
decode_unknown(s, d);
}
#endif
return 1;
}
static void xap_decode(struct state *s, struct directive *d) {
int prefix = s->s_prefix;
if (!decode_fixed (s, d))
if (!decode_known (s, d))
decode_unknown (s, d);
if (s->s_prefix == prefix)
s->s_prefix_val = s->s_prefix = 0;
}
static int read_bin(struct state *s, struct directive *d) {
memcpy(&d->d_inst, s->s_buf, sizeof(d->d_inst));
d->d_off = s->s_off++;
return 1;
}
static inline struct directive *next_inst(struct state *s) {
int rd;
struct directive *d = malloc (sizeof (*d));
if (!d) {
perror ("malloc()");
return NULL;
}
memset (d, 0, sizeof (*d));
#if 0
if (s->s_format)
rd = read_text(s, d);
else
#endif
rd = read_bin (s, d);
if (!rd) {
free (d);
return NULL;
}
return d;
}
#if 0
static void own(struct state *s)
{
struct directive *d;
struct directive *last = &s->s_dirs;
struct label *l;
int flush = 0;
char fname[128];
char *fnamep;
snprintf(fname, sizeof(fname), "%s", s->s_fname);
fnamep = strchr(fname, '.');
if (fnamep)
*fnamep = 0;
output(s, "\tMODULE %s\n"
"\t.CODE\n"
"\t.LARGE\n"
"\n", fname);
/* decode instructions */
s->s_off = 0;
while ((d = next_inst(s))) {
xap_decode(s, d);
if (s->s_ff_quirk) {
strcpy(last->d_asm, "DC\t0x8000");
sprintf(d->d_asm, "DC\t0x%.4x", i2u16(&d->d_inst));
s->s_ff_quirk = 0;
}
if (s->s_nopd) {
last->d_next = s->s_nopd;
last = s->s_nopd;
s->s_nopd = NULL;
s->s_nop = 0;
}
if (s->s_nop) {
assert(s->s_nopd == NULL);
s->s_nopd = d;
} else {
last->d_next = d;
last = d;
}
#if 1
if (flush++ > 10000) {
printf("@0x%.6x\r", d->d_off);
fflush(stdout);
flush = 0;
}
#endif
}
if (s->s_nopd)
last->d_next = s->s_nopd;
printf("\n");
/* print them */
d = s->s_dirs.d_next;
l = s->s_labels.l_next;
while (d) {
/* print any labels first */
while (l) {
if (l->l_off > d->d_off)
break;
print_label(s, l);
l = l->l_next;
}
add_comment(s, d);
output(s, "\t%s\n", d->d_asm);
d = d->d_next;
}
if (l) {
print_label(s, l);
assert(l->l_next == NULL);
}
output(s, "\n\tENDMOD\n");
}
#endif