320 lines
7.2 KiB
C
320 lines
7.2 KiB
C
// SPDX-FileCopyrightText: 2012-2018 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_types.h>
|
|
#include <rz_util.h>
|
|
|
|
static const char *const regs[33] = {
|
|
"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
|
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
|
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
|
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
|
|
NULL
|
|
};
|
|
|
|
static struct {
|
|
const char *name;
|
|
int type;
|
|
int args;
|
|
int n;
|
|
int x;
|
|
} ops[] = {
|
|
{ "nop", 'N', 0, 0, 0 },
|
|
{ "lui", 'I', 2, 15, 0 },
|
|
{ "sw", 'I', 3, 43, 0 },
|
|
{ "sh", 'I', 3, 41, 0 },
|
|
{ "sb", 'I', 3, 40, 0 },
|
|
{ "lw", 'I', 3, 35, 0 },
|
|
{ "lh", 'I', 3, 33, 0 },
|
|
{ "lb", 'I', 3, 32, 0 },
|
|
{ "ori", 'I', 3, 13, 0 },
|
|
{ "andi", 'I', 3, 12, 0 },
|
|
{ "xori", 'I', 3, 14, 0 },
|
|
{ "addi", 'I', 3, 8, 0 },
|
|
{ "addiu", 'I', 3, 9, 0 },
|
|
{ "b", 'B', -1, 4, 0 },
|
|
{ "bnez", 'B', 2, 5, 0 },
|
|
{ "bal", 'B', -1, -1, 17 },
|
|
{ "bne", 'B', 3, 5, 0 },
|
|
{ "beq", 'B', 3, 4, 0 },
|
|
{ "bgez", 'B', -2, -1, 1 },
|
|
{ "bgezal", 'B', -2, -1, 17 },
|
|
{ "bltzal", 'B', -2, -1, 16 },
|
|
{ "bgtz", 'B', -2, 7, 0 },
|
|
{ "blez", 'B', -2, 6, 0 },
|
|
{ "bltz", 'B', -2, 1, 0 },
|
|
{ "syscall", 'R', 0, 12, 0 },
|
|
{ "break", 'R', 0, 13, 0 },
|
|
{ "nor", 'R', 3, 39, 0 },
|
|
{ "or", 'R', 3, 37, 0 },
|
|
{ "xor", 'R', 3, 38, 0 },
|
|
{ "and", 'R', 3, 36, 0 },
|
|
{ "sll", 'R', -3, 0, 0 },
|
|
{ "sllv", 'R', 3, 4, 0 },
|
|
{ "slt", 'R', 3, 42, 0 },
|
|
{ "sltu", 'R', 3, 43, 0 },
|
|
{ "sra", 'R', -3, 3, 0 },
|
|
{ "srl", 'R', -3, 2, 0 },
|
|
{ "srlv", 'R', 3, 6, 0 },
|
|
{ "srav", 'R', 3, 7, 0 },
|
|
{ "add", 'R', 3, 32, 0 },
|
|
{ "move", 'R', -2, 32, 0 },
|
|
{ "addu", 'R', 3, 33, 0 },
|
|
{ "sub", 'R', 3, 34, 0 },
|
|
{ "subu", 'R', 3, 35, 0 },
|
|
{ "mult", 'R', 2, 24, 0 },
|
|
{ "multu", 'R', 2, 25, 0 },
|
|
{ "div", 'R', 2, 26, 0 },
|
|
{ "divu", 'R', 2, 27, 0 },
|
|
{ "mfhi", 'R', 1, 16, 0 },
|
|
{ "mflo", 'R', 1, 18, 0 },
|
|
{ "mthi", 'R', 1, 17, 0 },
|
|
{ "mtlo", 'R', 1, 19, 0 },
|
|
{ "jalr", 'R', -2, 9, 0 },
|
|
{ "jr", 'R', 1, 8, 0 },
|
|
{ "jal", 'J', 1, 3, 0 },
|
|
{ "j", 'J', 1, 2, 0 },
|
|
{ NULL }
|
|
};
|
|
|
|
static int mips_r(ut8 *b, int op, int rs, int rt, int rd, int sa, int fun) {
|
|
//^this will keep the below mips_r fuctions working
|
|
// diff instructions use a diff arg order (add is rd, rs, rt - sll is rd, rt, sa - sllv is rd, rt, rs
|
|
// static int mips_r (ut8 *b, int op, int rd, int rs, int rt, int sa, int fun) {
|
|
if (rs < 0 || rt < 0 || rd < 0 || sa < 0) {
|
|
return -1;
|
|
}
|
|
b[3] = ((op << 2) & 0xfc) | ((rs >> 3) & 3); // 2
|
|
b[2] = (rs << 5) | (rt & 0x1f); // 1
|
|
b[1] = ((rd << 3) & 0xff) | (sa >> 2); // 0
|
|
b[0] = (fun & 0x3f) | ((sa & 3) << 6);
|
|
return 4;
|
|
}
|
|
|
|
static int mips_i(ut8 *b, int op, int rs, int rt, int imm, int is_branch) {
|
|
if (rs < 0 || rt < 0) {
|
|
return -1;
|
|
}
|
|
if (is_branch) {
|
|
if (imm > 4) {
|
|
imm /= 4;
|
|
imm--;
|
|
} else {
|
|
imm = 0;
|
|
}
|
|
}
|
|
b[3] = ((op << 2) & 0xfc) | ((rs >> 3) & 3);
|
|
b[2] = (rs << 5) | (rt);
|
|
b[1] = (imm >> 8) & 0xff;
|
|
b[0] = imm & 0xff;
|
|
return 4;
|
|
}
|
|
|
|
static int mips_j(ut8 *b, int op, int addr) {
|
|
addr /= 4;
|
|
b[3] = ((op << 2) & 0xfc) | ((addr >> 24) & 3);
|
|
b[2] = (addr >> 16) & 0xff;
|
|
b[1] = (addr >> 8) & 0xff;
|
|
b[0] = addr & 0xff;
|
|
return 4;
|
|
}
|
|
|
|
static int getreg(const char *p) {
|
|
int n;
|
|
if (RZ_STR_ISEMPTY(p)) {
|
|
RZ_LOG_ERROR("assembler: mips: invalid assembly (missing an argument).\n");
|
|
return -1;
|
|
}
|
|
/* check if it's a register */
|
|
for (n = 0; regs[n]; n++) {
|
|
if (!strcmp(p, regs[n])) {
|
|
return n;
|
|
}
|
|
}
|
|
/* try to convert it into a number */
|
|
if (p[0] == '-') {
|
|
n = (int)rz_num_get(NULL, &p[1]);
|
|
n = -n;
|
|
} else {
|
|
n = (int)rz_num_get(NULL, p);
|
|
}
|
|
if (n != 0 || p[0] == '0') {
|
|
return n;
|
|
}
|
|
RZ_LOG_ERROR("assembler: mips: invalid reg name (%s) at pos %d.\n", p, n);
|
|
return -1;
|
|
}
|
|
|
|
RZ_IPI int mips_assemble(const char *str, ut64 pc, ut8 *out) {
|
|
int i, hasp;
|
|
char w0[32], w1[32], w2[32], w3[32];
|
|
char *s = strdup(str);
|
|
if (!s) {
|
|
return -1;
|
|
}
|
|
|
|
rz_str_replace_char(s, ',', ' ');
|
|
hasp = rz_str_replace_char(s, '(', ' ');
|
|
rz_str_replace_char(s, ')', ' ');
|
|
|
|
*out = 0;
|
|
*w0 = 0;
|
|
*w1 = 0;
|
|
*w2 = 0;
|
|
*w3 = 0;
|
|
|
|
if (!strncmp(s, "jalr", 4) && !strchr(s, ',')) {
|
|
char opstr[32];
|
|
const char *arg = strchr(s, ' ');
|
|
if (arg) {
|
|
snprintf(opstr, sizeof(opstr), "jalr ra ra %s", arg + 1);
|
|
free(s);
|
|
s = strdup(opstr);
|
|
if (!s) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
sscanf(s, "%31s", w0);
|
|
if (*w0) {
|
|
for (i = 0; ops[i].name; i++) {
|
|
if (!strcmp(ops[i].name, w0)) {
|
|
switch (ops[i].args) {
|
|
case 3: sscanf(s, "%31s %31s %31s %31s", w0, w1, w2, w3); break;
|
|
case -3: sscanf(s, "%31s %31s %31s %31s", w0, w1, w2, w3); break;
|
|
case 2: sscanf(s, "%31s %31s %31s", w0, w1, w2); break;
|
|
case -2: sscanf(s, "%31s %31s %31s", w0, w1, w2); break;
|
|
case 1: sscanf(s, "%31s %31s", w0, w1); break;
|
|
case -1: sscanf(s, "%31s %31s", w0, w1); break;
|
|
case 0: sscanf(s, "%31s", w0); break;
|
|
}
|
|
if (hasp) {
|
|
char tmp[32];
|
|
strcpy(tmp, w2);
|
|
strcpy(w2, w3);
|
|
strcpy(w3, tmp);
|
|
}
|
|
switch (ops[i].type) {
|
|
case 'R': {
|
|
// reg order diff per instruction 'group' - ordered to number of likelyhood to call (add > mfhi)
|
|
int op = 0, rs = 0, rt = 0, rd = 0, sa = 0, fn = 0;
|
|
bool invalid = false;
|
|
switch (ops[i].args) {
|
|
case 3:
|
|
rs = getreg(w2);
|
|
rt = getreg(w3);
|
|
rd = getreg(w1);
|
|
fn = ops[i].n;
|
|
break;
|
|
case -3:
|
|
if (ops[i].n > -1) {
|
|
rt = getreg(w2);
|
|
rd = getreg(w1);
|
|
sa = getreg(w3);
|
|
fn = ops[i].n;
|
|
} else {
|
|
rs = getreg(w3);
|
|
rt = getreg(w2);
|
|
rd = getreg(w1);
|
|
fn = (-1 * ops[i].n);
|
|
}
|
|
break;
|
|
case 2:
|
|
rs = getreg(w1);
|
|
rt = getreg(w2);
|
|
fn = ops[i].n;
|
|
break;
|
|
case 1:
|
|
rs = getreg(w1);
|
|
fn = ops[i].n;
|
|
break;
|
|
case -2:
|
|
rs = getreg(w2);
|
|
rd = getreg(w1);
|
|
fn = ops[i].n;
|
|
break;
|
|
case -1:
|
|
rd = getreg(w1);
|
|
fn = ops[i].n;
|
|
break;
|
|
case 0:
|
|
fn = ops[i].n;
|
|
break;
|
|
default:
|
|
invalid = true;
|
|
break;
|
|
}
|
|
if (!invalid) {
|
|
free(s);
|
|
return mips_r(out, op, rs, rt, rd, sa, fn);
|
|
}
|
|
break;
|
|
}
|
|
case 'I':
|
|
case 'B': {
|
|
bool invalid = false;
|
|
int op = 0, rs = 0, rt = 0, imm = 0, is_branch = ops[i].type == 'B';
|
|
switch (ops[i].args) {
|
|
case 2:
|
|
op = ops[i].n;
|
|
rt = getreg(w1);
|
|
imm = getreg(w2);
|
|
break;
|
|
case 3:
|
|
op = ops[i].n;
|
|
rs = getreg(w2);
|
|
rt = getreg(w1);
|
|
imm = getreg(w3);
|
|
break;
|
|
case -2:
|
|
if (ops[i].n > 0) {
|
|
op = ops[i].n;
|
|
rs = getreg(w1);
|
|
imm = getreg(w2);
|
|
} else {
|
|
op = (-1 * ops[i].n);
|
|
rs = getreg(w1);
|
|
rt = ops[i].x;
|
|
imm = getreg(w2);
|
|
}
|
|
break;
|
|
case -1:
|
|
if (ops[i].n > 0) {
|
|
op = ops[i].n;
|
|
imm = getreg(w1);
|
|
} else {
|
|
op = (-1 * ops[i].n);
|
|
rt = ops[i].x;
|
|
imm = getreg(w1);
|
|
}
|
|
break;
|
|
default:
|
|
invalid = true;
|
|
break;
|
|
}
|
|
if (!invalid) {
|
|
free(s);
|
|
return mips_i(out, op, rs, rt, imm, is_branch);
|
|
}
|
|
break;
|
|
}
|
|
case 'J':
|
|
if (ops[i].args == 1) {
|
|
free(s);
|
|
return mips_j(out, ops[i].n, getreg(w1));
|
|
}
|
|
break;
|
|
case 'N': // nop
|
|
memset(out, 0, 4);
|
|
free(s);
|
|
return 4;
|
|
}
|
|
free(s);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
free(s);
|
|
return -1;
|
|
}
|