- Add LICENSES directory - Download additional licenses used - Add .reuse directory - Add doc about SPDX/reuse Co-authored-by: Florian Märkl <info@florianmaerkl.de>
1293 lines
28 KiB
C
1293 lines
28 KiB
C
// SPDX-FileCopyrightText: 2013-2019 pancake <pancake@nopcode.org>
|
|
// SPDX-FileCopyrightText: 2013-2019 dkreuter <dkreuter@gmail.com>
|
|
// SPDX-FileCopyrightText: 2013-2019 astuder <github@adrianstuder.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <string.h>
|
|
#include <rz_types.h>
|
|
#include <rz_lib.h>
|
|
#include <rz_asm.h>
|
|
#include <rz_analysis.h>
|
|
|
|
#include <8051_ops.h>
|
|
#include "../asm/arch/8051/8051_disas.c"
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
ut32 map_code;
|
|
ut32 map_idata;
|
|
ut32 map_sfr;
|
|
ut32 map_xdata;
|
|
ut32 map_pdata;
|
|
} i8051_cpu_model;
|
|
|
|
static i8051_cpu_model cpu_models[] = {
|
|
{ .name = "8051-generic",
|
|
.map_code = 0,
|
|
.map_idata = 0x10000000,
|
|
.map_sfr = 0x10000180,
|
|
.map_xdata = 0x20000000,
|
|
.map_pdata = 0x00000000 },
|
|
{ .name = "8051-shared-code-xdata",
|
|
.map_code = 0,
|
|
.map_idata = 0x10000000,
|
|
.map_sfr = 0x10000180,
|
|
.map_xdata = 0x00000000,
|
|
.map_pdata = 0x00000000 },
|
|
{
|
|
.name = NULL // last entry
|
|
}
|
|
};
|
|
|
|
static bool i8051_is_init = false;
|
|
static const i8051_cpu_model *cpu_curr_model = NULL;
|
|
|
|
static bool i8051_reg_write(RzReg *reg, const char *regname, ut32 num) {
|
|
if (reg) {
|
|
RzRegItem *item = rz_reg_get(reg, regname, RZ_REG_TYPE_GPR);
|
|
if (item) {
|
|
rz_reg_set_value(reg, item, num);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static ut32 i8051_reg_read(RzReg *reg, const char *regname) {
|
|
if (reg) {
|
|
RzRegItem *item = rz_reg_get(reg, regname, RZ_REG_TYPE_GPR);
|
|
if (item) {
|
|
return rz_reg_get_value(reg, item);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
typedef struct {
|
|
RzIODesc *desc;
|
|
ut32 addr;
|
|
const char *name;
|
|
} i8051_map_entry;
|
|
|
|
static const int I8051_IDATA = 0;
|
|
static const int I8051_SFR = 1;
|
|
static const int I8051_XDATA = 2;
|
|
|
|
static i8051_map_entry mem_map[3] = {
|
|
{ NULL, UT32_MAX, "idata" },
|
|
{ NULL, UT32_MAX, "sfr" },
|
|
{ NULL, UT32_MAX, "xdata" }
|
|
};
|
|
|
|
static void map_cpu_memory(RzAnalysis *analysis, int entry, ut32 addr, ut32 size, bool force) {
|
|
RzIODesc *desc = mem_map[entry].desc;
|
|
if (desc && analysis->iob.fd_get_name(analysis->iob.io, desc->fd)) {
|
|
if (force || addr != mem_map[entry].addr) {
|
|
// reallocate mapped memory if address changed
|
|
analysis->iob.fd_remap(analysis->iob.io, desc->fd, addr);
|
|
}
|
|
} else {
|
|
// allocate memory for address space
|
|
char *mstr = rz_str_newf("malloc://%d", size);
|
|
desc = analysis->iob.open_at(analysis->iob.io, mstr, RZ_PERM_RW, 0, addr);
|
|
free(mstr);
|
|
// set 8051 address space as name of mapped memory
|
|
if (desc && analysis->iob.fd_get_name(analysis->iob.io, desc->fd)) {
|
|
RzList *maps = analysis->iob.fd_get_map(analysis->iob.io, desc->fd);
|
|
RzIOMap *current_map;
|
|
RzListIter *iter;
|
|
rz_list_foreach (maps, iter, current_map) {
|
|
char *cmdstr = rz_str_newf("omni %d %s", current_map->id, mem_map[entry].name);
|
|
analysis->coreb.cmd(analysis->coreb.core, cmdstr);
|
|
free(cmdstr);
|
|
}
|
|
rz_list_free(maps);
|
|
}
|
|
}
|
|
mem_map[entry].desc = desc;
|
|
mem_map[entry].addr = addr;
|
|
}
|
|
|
|
static void set_cpu_model(RzAnalysis *analysis, bool force) {
|
|
ut32 addr_idata, addr_sfr, addr_xdata;
|
|
|
|
if (!analysis->reg) {
|
|
return;
|
|
}
|
|
|
|
const char *cpu = analysis->cpu;
|
|
if (!cpu || !cpu[0]) {
|
|
cpu = cpu_models[0].name;
|
|
}
|
|
|
|
// if cpu model changed, reinitialize emulation
|
|
if (force || !cpu_curr_model || rz_str_casecmp(cpu, cpu_curr_model->name)) {
|
|
// find model by name
|
|
int i = 0;
|
|
while (cpu_models[i].name && rz_str_casecmp(cpu, cpu_models[i].name)) {
|
|
i++;
|
|
}
|
|
if (!cpu_models[i].name) {
|
|
i = 0; // if not found, default to generic 8051
|
|
}
|
|
cpu_curr_model = &cpu_models[i];
|
|
|
|
// TODO: Add flags as needed - seek using pseudo registers works w/o flags
|
|
|
|
// set memory map registers
|
|
addr_idata = cpu_models[i].map_idata;
|
|
addr_sfr = cpu_models[i].map_sfr;
|
|
addr_xdata = cpu_models[i].map_xdata;
|
|
i8051_reg_write(analysis->reg, "_code", cpu_models[i].map_code);
|
|
i8051_reg_write(analysis->reg, "_idata", addr_idata);
|
|
i8051_reg_write(analysis->reg, "_sfr", addr_sfr - 0x80);
|
|
i8051_reg_write(analysis->reg, "_xdata", addr_xdata);
|
|
i8051_reg_write(analysis->reg, "_pdata", cpu_models[i].map_pdata);
|
|
} else {
|
|
addr_idata = i8051_reg_read(analysis->reg, "_idata");
|
|
addr_sfr = i8051_reg_read(analysis->reg, "_sfr") + 0x80;
|
|
addr_xdata = i8051_reg_read(analysis->reg, "_xdata");
|
|
}
|
|
|
|
// (Re)allocate memory as needed.
|
|
// We assume that code is allocated with firmware image
|
|
if (analysis->iob.fd_get_name && analysis->coreb.cmd) {
|
|
map_cpu_memory(analysis, I8051_IDATA, addr_idata, 0x100, force);
|
|
map_cpu_memory(analysis, I8051_SFR, addr_sfr, 0x80, force);
|
|
map_cpu_memory(analysis, I8051_XDATA, addr_xdata, 0x10000, force);
|
|
}
|
|
}
|
|
|
|
static ut8 bitindex[] = {
|
|
// bit 'i' can be found in (ram[bitindex[i>>3]] >> (i&7)) & 1
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // 0x00
|
|
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 0x40
|
|
0x80, 0x88, 0x90, 0x98, 0xA0, 0xA8, 0xB0, 0xB8, // 0x80
|
|
0xC0, 0xC8, 0xD0, 0xD8, 0xE0, 0xE8, 0xF0, 0xF8 // 0xC0
|
|
};
|
|
|
|
#if 0
|
|
typedef struct {
|
|
const char *name;
|
|
ut8 offset; // offset into memory, where the value is held
|
|
ut8 resetvalue; // value the register takes in case of a reset
|
|
ut8 num_bytes; // no more than sizeof(ut64)
|
|
ut8 banked : 1;
|
|
ut8 isdptr : 1;
|
|
} RI8051Reg;
|
|
|
|
// custom reg read/write temporarily disabled - see r2 issue #9242
|
|
static RI8051Reg registers[] = {
|
|
// keep these sorted
|
|
{"a", 0xE0, 0x00, 1, 0},
|
|
{"b", 0xF0, 0x00, 1, 0},
|
|
{"dph", 0x83, 0x00, 1, 0},
|
|
{"dpl", 0x82, 0x00, 1, 0},
|
|
{"dptr", 0x82, 0x00, 2, 0, 1},
|
|
{"ie", 0xA8, 0x00, 1, 0},
|
|
{"ip", 0xB8, 0x00, 1, 0},
|
|
{"p0", 0x80, 0xFF, 1, 0},
|
|
{"p1", 0x90, 0xFF, 1, 0},
|
|
{"p2", 0xA0, 0xFF, 1, 0},
|
|
{"p3", 0xB0, 0xFF, 1, 0},
|
|
{"pcon", 0x87, 0x00, 1, 0},
|
|
{"psw", 0xD0, 0x00, 1, 0},
|
|
{"r0", 0x00, 0x00, 1, 1},
|
|
{"r1", 0x01, 0x00, 1, 1},
|
|
{"r2", 0x02, 0x00, 1, 1},
|
|
{"r3", 0x03, 0x00, 1, 1},
|
|
{"r4", 0x04, 0x00, 1, 1},
|
|
{"r5", 0x05, 0x00, 1, 1},
|
|
{"r6", 0x06, 0x00, 1, 1},
|
|
{"r7", 0x07, 0x00, 1, 1},
|
|
{"sbuf", 0x99, 0x00, 1, 0},
|
|
{"scon", 0x98, 0x00, 1, 0},
|
|
{"sp", 0x81, 0x07, 1, 0},
|
|
{"tcon", 0x88, 0x00, 1, 0},
|
|
{"th0", 0x8C, 0x00, 1, 0},
|
|
{"th1", 0x8D, 0x00, 1, 0},
|
|
{"tl0", 0x8A, 0x00, 1, 0},
|
|
{"tl1", 0x8B, 0x00, 1, 0},
|
|
{"tmod", 0x89, 0x00, 1, 0}
|
|
};
|
|
#endif
|
|
|
|
#define e(frag) rz_strbuf_append(&op->esil, frag)
|
|
#define ef(frag, ...) rz_strbuf_appendf(&op->esil, frag, __VA_ARGS__)
|
|
|
|
#define flag_c "7,$c,c,:=,"
|
|
#define flag_b "8,$b,c,:=,"
|
|
#define flag_ac "3,$c,ac,:=,"
|
|
#define flag_ab "3,$b,ac,:=,"
|
|
#define flag_ov "6,$c,ov,:=,"
|
|
#define flag_ob "7,$b,6,$b,^,ov,:=,"
|
|
#define flag_p "0xff,a,&=,$p,!,p,:=,"
|
|
|
|
#define ev_a 0
|
|
#define ev_bit bitindex[buf[1] >> 3]
|
|
#define ev_c 0
|
|
#define ev_dir1 buf[1]
|
|
#define ev_dir2 buf[2]
|
|
#define ev_dp 0
|
|
#define ev_dpx 0
|
|
#define ev_imm1 buf[1]
|
|
#define ev_imm2 buf[2]
|
|
#define ev_imm16 op->val
|
|
#define ev_ri (1 & buf[0])
|
|
#define ev_rix (1 & buf[0])
|
|
#define ev_rn (7 & buf[0])
|
|
#define ev_sp2 0
|
|
#define ev_sp1 0
|
|
|
|
static void exr_a(RzAnalysisOp *op, ut8 dummy) {
|
|
e("a,");
|
|
}
|
|
|
|
static void exr_dir1(RzAnalysisOp *op, ut8 addr) {
|
|
if (addr < 0x80) {
|
|
ef("_idata,%d,+,[1],", addr);
|
|
} else {
|
|
ef("_sfr,%d,+,[1],", addr);
|
|
}
|
|
}
|
|
|
|
static void exr_bit(RzAnalysisOp *op, ut8 addr) {
|
|
exr_dir1(op, addr);
|
|
}
|
|
|
|
static void exr_dpx(RzAnalysisOp *op, ut8 dummy) {
|
|
e("_xdata,dptr,+,[1],");
|
|
}
|
|
|
|
static void exr_imm1(RzAnalysisOp *op, ut8 val) {
|
|
ef("%d,", val);
|
|
}
|
|
|
|
static void exr_imm2(RzAnalysisOp *op, ut8 val) {
|
|
ef("%d,", val);
|
|
}
|
|
|
|
static void exr_imm16(RzAnalysisOp *op, ut16 val) {
|
|
ef("%d,", val);
|
|
}
|
|
|
|
static void exr_ri(RzAnalysisOp *op, ut8 reg) {
|
|
ef("_idata,r%d,+,[1],", reg);
|
|
}
|
|
|
|
static void exr_rix(RzAnalysisOp *op, ut8 reg) {
|
|
ef("8,0xff,_pdata,&,<<,_xdata,+,r%d,+,[1],", reg);
|
|
}
|
|
|
|
static void exr_rn(RzAnalysisOp *op, ut8 reg) {
|
|
ef("r%d,", reg);
|
|
}
|
|
|
|
static void exr_sp1(RzAnalysisOp *op, ut8 dummy) {
|
|
e("_idata,sp,+,[1],");
|
|
e("1,sp,-=,");
|
|
}
|
|
|
|
static void exr_sp2(RzAnalysisOp *op, ut8 dummy) {
|
|
e("1,sp,-=,");
|
|
e("_idata,sp,+,[2],");
|
|
e("1,sp,-=,");
|
|
}
|
|
|
|
static void exw_a(RzAnalysisOp *op, ut8 dummy) {
|
|
e("a,=,");
|
|
}
|
|
|
|
static void exw_c(RzAnalysisOp *op, ut8 dummy) {
|
|
e("c,=,");
|
|
}
|
|
|
|
static void exw_dir1(RzAnalysisOp *op, ut8 addr) {
|
|
if (addr < 0x80) {
|
|
ef("_idata,%d,+,=[1],", addr);
|
|
} else {
|
|
ef("_sfr,%d,+,=[1],", addr);
|
|
}
|
|
}
|
|
|
|
static void exw_dir2(RzAnalysisOp *op, ut8 addr) {
|
|
exw_dir1(op, addr);
|
|
}
|
|
|
|
static void exw_bit(RzAnalysisOp *op, ut8 addr) {
|
|
exw_dir1(op, addr);
|
|
}
|
|
|
|
static void exw_dp(RzAnalysisOp *op, ut8 dummy) {
|
|
e("dptr,=,");
|
|
}
|
|
|
|
static void exw_dpx(RzAnalysisOp *op, ut8 dummy) {
|
|
e("_xdata,dptr,+,=[1],");
|
|
}
|
|
|
|
static void exw_ri(RzAnalysisOp *op, ut8 reg) {
|
|
ef("_idata,r%d,+,=[1],", reg);
|
|
}
|
|
|
|
static void exw_rix(RzAnalysisOp *op, ut8 reg) {
|
|
ef("8,0xff,_pdata,&,<<,_xdata,+,r%d,+,=[1],", reg);
|
|
}
|
|
|
|
static void exw_rn(RzAnalysisOp *op, ut8 reg) {
|
|
ef("r%d,=,", reg);
|
|
}
|
|
|
|
static void exw_sp1(RzAnalysisOp *op, ut8 dummy) {
|
|
e("1,sp,+=,");
|
|
e("_idata,sp,+,=[1],");
|
|
}
|
|
|
|
static void exw_sp2(RzAnalysisOp *op, ut8 dummy) {
|
|
e("1,sp,+=,");
|
|
e("_idata,sp,+,=[2],");
|
|
e("1,sp,+=,");
|
|
}
|
|
|
|
static void exi_a(RzAnalysisOp *op, ut8 dummy, const char *operation) {
|
|
ef("a,%s=,", operation);
|
|
}
|
|
|
|
static void exi_c(RzAnalysisOp *op, ut8 dummy, const char *operation) {
|
|
ef("c,%s=,", operation);
|
|
}
|
|
|
|
static void exi_dp(RzAnalysisOp *op, ut8 dummy, const char *operation) {
|
|
ef("dptr,%s=,", operation);
|
|
}
|
|
|
|
static void exi_dir1(RzAnalysisOp *op, ut8 addr, const char *operation) {
|
|
if (addr < 0x80) {
|
|
ef("_idata,%d,+,%s=[1],", addr, operation);
|
|
} else {
|
|
ef("_sfr,%d,+,%s=[1],", addr, operation);
|
|
}
|
|
}
|
|
|
|
static void exi_bit(RzAnalysisOp *op, ut8 addr, const char *operation) {
|
|
exi_dir1(op, addr, operation);
|
|
}
|
|
|
|
static void exi_ri(RzAnalysisOp *op, ut8 reg, const char *operation) {
|
|
ef("_idata,r%d,+,%s=[1],", reg, operation);
|
|
}
|
|
|
|
static void exi_rn(RzAnalysisOp *op, ut8 reg, const char *operation) {
|
|
ef("r%d,%s=,", reg, operation);
|
|
}
|
|
|
|
#define xr(subject) exr_##subject(op, ev_##subject)
|
|
#define xw(subject) exw_##subject(op, ev_##subject)
|
|
#define xi(subject, operation) exi_##subject(op, ev_##subject, operation)
|
|
|
|
#define bit_set ef("%d,1,<<,", buf[1] & 7)
|
|
#define bit_mask \
|
|
bit_set; \
|
|
e("255,^,")
|
|
#define bit_r \
|
|
ef("%d,", buf[1] & 7); \
|
|
xr(bit); \
|
|
e(">>,1,&,")
|
|
#define bit_c ef("%d,c,<<,", buf[1] & 7);
|
|
|
|
#define jmp ef("%" PFMT64d ",pc,=", op->jump)
|
|
#define cjmp \
|
|
e("?{,"); \
|
|
jmp; \
|
|
e(",}")
|
|
#define call \
|
|
ef("%" PFMT64d ",", op->fail); \
|
|
xw(sp2); \
|
|
jmp
|
|
|
|
#define alu_op(val, aluop, flags) \
|
|
xr(val); \
|
|
e("a," aluop "=," flags)
|
|
#define alu_op_c(val, aluop, flags) \
|
|
e("c,"); \
|
|
xr(val); \
|
|
e("+,a," aluop "=," flags)
|
|
#define alu_op_d(val, aluop) \
|
|
xr(val); \
|
|
xi(dir1, aluop)
|
|
|
|
#define template_alu4_c(base, aluop, flags) \
|
|
case base + 0x4: \
|
|
alu_op_c(imm1, aluop, flags); \
|
|
break; \
|
|
case base + 0x5: \
|
|
alu_op_c(dir1, aluop, flags); \
|
|
break; \
|
|
case base + 0x6: \
|
|
case base + 0x7: \
|
|
alu_op_c(ri, aluop, flags); \
|
|
break; \
|
|
case base + 0x8: \
|
|
case base + 0x9: \
|
|
case base + 0xA: \
|
|
case base + 0xB: \
|
|
case base + 0xC: \
|
|
case base + 0xD: \
|
|
case base + 0xE: \
|
|
case base + 0xF: \
|
|
alu_op_c(rn, aluop, flags); \
|
|
break;
|
|
|
|
#define template_alu2(base, aluop) \
|
|
case base + 0x2: \
|
|
alu_op_d(a, aluop); \
|
|
break; \
|
|
case base + 0x3: \
|
|
alu_op_d(imm2, aluop); \
|
|
break;
|
|
|
|
#define template_alu4(base, aluop, flags) \
|
|
case base + 0x4: \
|
|
alu_op(imm1, aluop, flags); \
|
|
break; \
|
|
case base + 0x5: \
|
|
alu_op(dir1, aluop, flags); \
|
|
break; \
|
|
case base + 0x6: \
|
|
case base + 0x7: \
|
|
alu_op(ri, aluop, flags); \
|
|
break; \
|
|
case base + 0x8: \
|
|
case base + 0x9: \
|
|
case base + 0xA: \
|
|
case base + 0xB: \
|
|
case base + 0xC: \
|
|
case base + 0xD: \
|
|
case base + 0xE: \
|
|
case base + 0xF: \
|
|
alu_op(rn, aluop, flags); \
|
|
break;
|
|
|
|
static void analop_esil(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *buf) {
|
|
rz_strbuf_init(&op->esil);
|
|
rz_strbuf_set(&op->esil, "");
|
|
|
|
switch (buf[0]) {
|
|
// Irregulars sorted by lower nibble
|
|
case 0x00: /* nop */
|
|
e(",");
|
|
break;
|
|
|
|
case 0x10: /* jbc bit, offset */
|
|
bit_r;
|
|
e("?{,");
|
|
bit_mask;
|
|
xi(bit, "&");
|
|
jmp;
|
|
e(",}");
|
|
break;
|
|
case 0x20: /* jb bit, offset */
|
|
bit_r;
|
|
cjmp;
|
|
break;
|
|
case 0x30: /* jnb bit, offset */
|
|
bit_r;
|
|
e("!,");
|
|
cjmp;
|
|
break;
|
|
case 0x40: /* jc offset */
|
|
e("c,1,&,");
|
|
cjmp;
|
|
break;
|
|
case 0x50: /* jnc offset */
|
|
e("c,1,&,!,");
|
|
cjmp;
|
|
break;
|
|
case 0x60: /* jz offset */
|
|
e("a,0,==,$z,");
|
|
cjmp;
|
|
break;
|
|
case 0x70: /* jnz offset */
|
|
e("a,0,==,$z,!,");
|
|
cjmp;
|
|
break;
|
|
|
|
case 0x11:
|
|
case 0x31:
|
|
case 0x51:
|
|
case 0x71:
|
|
case 0x91:
|
|
case 0xB1:
|
|
case 0xD1:
|
|
case 0xF1: /* acall addr11 */
|
|
case 0x12: /* lcall addr16 */
|
|
call;
|
|
break;
|
|
case 0x01:
|
|
case 0x21:
|
|
case 0x41:
|
|
case 0x61:
|
|
case 0x81:
|
|
case 0xA1:
|
|
case 0xC1:
|
|
case 0xE1: /* ajmp addr11 */
|
|
case 0x02: /* ljmp addr16 */
|
|
case 0x80: /* sjmp offset */
|
|
jmp;
|
|
break;
|
|
|
|
case 0x22: /* ret */
|
|
case 0x32: /* reti */
|
|
xr(sp2);
|
|
e("pc,=");
|
|
break;
|
|
|
|
case 0x03: /* rr a */
|
|
e("1,a,0x101,*,>>,a,=," flag_p);
|
|
break;
|
|
case 0x04: /* inc a */
|
|
xi(a, "++");
|
|
e(flag_p);
|
|
break;
|
|
case 0x05: /* inc direct */
|
|
xi(dir1, "++");
|
|
break;
|
|
case 0x06:
|
|
case 0x07: /* inc @Ri */
|
|
xi(ri, "++");
|
|
break;
|
|
case 0x08:
|
|
case 0x09:
|
|
case 0x0A:
|
|
case 0x0B:
|
|
case 0x0C:
|
|
case 0x0D:
|
|
case 0x0E:
|
|
case 0x0F: /* inc @Rn */
|
|
xi(rn, "++");
|
|
break;
|
|
case 0x13: /* rrc a */
|
|
e("7,c,<<,1,a,&,c,=,0x7f,1,a,>>,&,+,a,=," flag_p);
|
|
break;
|
|
case 0x14: /* dec a */
|
|
xi(a, "--");
|
|
e(flag_p);
|
|
break;
|
|
case 0x15: /* dec direct */
|
|
xi(dir1, "--");
|
|
e(flag_p);
|
|
break;
|
|
case 0x16:
|
|
case 0x17: /* dec @Ri */
|
|
xi(ri, "--");
|
|
break;
|
|
case 0x18:
|
|
case 0x19:
|
|
case 0x1A:
|
|
case 0x1B:
|
|
case 0x1C:
|
|
case 0x1D:
|
|
case 0x1E:
|
|
case 0x1F: /* dec @Rn */
|
|
xi(rn, "--");
|
|
break;
|
|
case 0x23: /* rl a */
|
|
e("7,a,0x101,*,>>,a,=," flag_p);
|
|
break;
|
|
template_alu4(0x20, "+", flag_c flag_ac flag_ov flag_p) /* 0x24..0x2f add a,.. */
|
|
case 0x33 : /* rlc a */
|
|
e("c,1,&,a,a,+=,7,$c,c,:=,a,+=," flag_p);
|
|
break;
|
|
template_alu4_c(0x30, "+", flag_c flag_ac flag_ov flag_p) /* 0x34..0x3f addc a,.. */
|
|
template_alu2(0x40, "|") /* 0x42..0x43 orl direct,.. */
|
|
template_alu4(0x40, "|", flag_p) /* 0x44..0x4f orl a,.. */
|
|
template_alu2(0x50, "&") /* 0x52..0x53 anl direct,.. */
|
|
template_alu4(0x50, "&", flag_p) /* 0x54..0x5f anl a,.. */
|
|
template_alu2(0x60, "^") /* 0x62..0x63 xrl direct,.. */
|
|
template_alu4(0x60, "^", flag_p) /* 0x64..0x6f xrl a,.. */
|
|
case 0x72 : /* orl C, bit */
|
|
bit_r;
|
|
xi(c, "|");
|
|
break;
|
|
case 0x73: /* jmp @a+dptr */
|
|
e("dptr,a,+,pc,=");
|
|
break;
|
|
case 0x74: /* mov a, imm */
|
|
xr(imm1);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0x75: /* mov direct, imm */
|
|
xr(imm2);
|
|
xw(dir1);
|
|
break;
|
|
case 0x76:
|
|
case 0x77: /* mov @Ri, imm */
|
|
xr(imm1);
|
|
xw(ri);
|
|
break;
|
|
case 0x78:
|
|
case 0x79:
|
|
case 0x7A:
|
|
case 0x7B:
|
|
case 0x7C:
|
|
case 0x7D:
|
|
case 0x7E:
|
|
case 0x7F: /* mov Rn, imm */
|
|
xr(imm1);
|
|
xw(rn);
|
|
break;
|
|
case 0x82: /* anl C, bit */
|
|
bit_r;
|
|
xi(c, "&");
|
|
break;
|
|
case 0x83: /* movc a, @a+pc */
|
|
e("a,pc,--,+,[1],a,=," flag_p);
|
|
break;
|
|
case 0x84: /* div ab */
|
|
// note: escape % if this becomes a format string
|
|
e("b,0,==,$z,ov,:=,b,a,%,b,a,/=,b,=,0,c,=," flag_p);
|
|
break;
|
|
case 0x85: /* mov direct, direct */
|
|
xr(dir1);
|
|
xw(dir2);
|
|
break;
|
|
case 0x86:
|
|
case 0x87: /* mov direct, @Ri */
|
|
xr(ri);
|
|
xw(dir1);
|
|
break;
|
|
case 0x88:
|
|
case 0x89:
|
|
case 0x8A:
|
|
case 0x8B:
|
|
case 0x8C:
|
|
case 0x8D:
|
|
case 0x8E:
|
|
case 0x8F: /* mov direct, Rn */
|
|
xr(rn);
|
|
xw(dir1);
|
|
break;
|
|
case 0x90: /* mov dptr, imm */
|
|
xr(imm16);
|
|
xw(dp);
|
|
break;
|
|
case 0x92: /* mov bit, C */
|
|
bit_c;
|
|
bit_mask;
|
|
xr(bit);
|
|
e("&,|,");
|
|
xw(bit);
|
|
break;
|
|
case 0x93: /* movc a, @a+dptr */
|
|
e("a,dptr,+,[1],a,=," flag_p);
|
|
break;
|
|
template_alu4_c(0x90, "-", flag_b flag_ab flag_ob flag_p) /* 0x94..0x9f subb a,.. */
|
|
case 0xA0 : /* orl C, /bit */
|
|
bit_r;
|
|
e("!,");
|
|
xi(c, "|");
|
|
break;
|
|
case 0xA2: /* mov C, bit */
|
|
bit_r;
|
|
xw(c);
|
|
break;
|
|
case 0xA3: /* inc dptr */
|
|
xi(dp, "++");
|
|
break;
|
|
case 0xA4: /* mul ab */
|
|
e("8,a,b,*,DUP,a,=,>>,DUP,b,=,0,==,$z,!,ov,:=,0,c,=," flag_p);
|
|
break;
|
|
case 0xA5: /* "reserved" */
|
|
e("0,trap");
|
|
break;
|
|
case 0xA6:
|
|
case 0xA7: /* mov @Ri, direct */
|
|
xr(dir1);
|
|
xw(ri);
|
|
break;
|
|
case 0xA8:
|
|
case 0xA9:
|
|
case 0xAA:
|
|
case 0xAB:
|
|
case 0xAC:
|
|
case 0xAD:
|
|
case 0xAE:
|
|
case 0xAF: /* mov Rn, direct */
|
|
xr(dir1);
|
|
xw(rn);
|
|
break;
|
|
case 0xB0: /* anl C, /bit */
|
|
bit_r;
|
|
e("!,");
|
|
xi(c, "&");
|
|
break;
|
|
case 0xB2: /* cpl bit */
|
|
bit_set;
|
|
xi(bit, "^");
|
|
break;
|
|
case 0xB3: /* cpl C */
|
|
e("1,");
|
|
xi(c, "^");
|
|
break;
|
|
case 0xB4: /* cjne a, imm, offset */
|
|
xr(imm1);
|
|
xr(a);
|
|
e("==,$z,!," flag_b);
|
|
cjmp;
|
|
break;
|
|
case 0xB5: /* cjne a, direct, offset */
|
|
xr(dir1);
|
|
xr(a);
|
|
e("==,$z,!," flag_b);
|
|
cjmp;
|
|
break;
|
|
case 0xB6:
|
|
case 0xB7: /* cjne @ri, imm, offset */
|
|
xr(imm1);
|
|
xr(ri);
|
|
e("==,$z,!," flag_b);
|
|
cjmp;
|
|
break;
|
|
case 0xB8:
|
|
case 0xB9:
|
|
case 0xBA:
|
|
case 0xBB:
|
|
case 0xBC:
|
|
case 0xBD:
|
|
case 0xBE:
|
|
case 0xBF: /* cjne Rn, imm, offset */
|
|
xr(imm1);
|
|
xr(rn);
|
|
e("==,$z,!," flag_b);
|
|
cjmp;
|
|
break;
|
|
case 0xC0: /* push direct */
|
|
xr(dir1);
|
|
xw(sp1);
|
|
break;
|
|
case 0xC2: /* clr bit */
|
|
bit_mask;
|
|
xi(bit, "&");
|
|
break;
|
|
case 0xC3: /* clr C */
|
|
e("0,");
|
|
xw(c);
|
|
break;
|
|
case 0xC4: /* swap a */
|
|
e("0xff,4,a,0x101,*,>>,&,a,=," flag_p);
|
|
break;
|
|
case 0xC5: /* xch a, direct */
|
|
xr(a);
|
|
e("0,+,");
|
|
xr(dir1);
|
|
xw(a);
|
|
xw(dir1);
|
|
e(flag_p);
|
|
break;
|
|
case 0xC6:
|
|
case 0xC7: /* xch a, @Ri */
|
|
xr(a);
|
|
e("0,+,");
|
|
xr(ri);
|
|
xw(a);
|
|
xw(ri);
|
|
e(flag_p);
|
|
break;
|
|
case 0xC8:
|
|
case 0xC9:
|
|
case 0xCA:
|
|
case 0xCB:
|
|
case 0xCC:
|
|
case 0xCD:
|
|
case 0xCE:
|
|
case 0xCF: /* xch a, Rn */
|
|
xr(a);
|
|
e("0,+,");
|
|
xr(rn);
|
|
xw(a);
|
|
xw(rn);
|
|
e(flag_p);
|
|
break;
|
|
case 0xD0: /* pop direct */
|
|
xr(sp1);
|
|
xw(dir1);
|
|
break;
|
|
case 0xD2: /* setb bit */
|
|
bit_set;
|
|
xi(bit, "|");
|
|
break;
|
|
case 0xD3: /* setb C */
|
|
e("1,");
|
|
xw(c);
|
|
break;
|
|
case 0xD4: /* da a */
|
|
// BCD adjust after add:
|
|
// if (lower nibble > 9) or (AC == 1) add 6
|
|
// if (higher nibble > 9) or (C == 1) add 0x60
|
|
// carry |= carry caused by this operation
|
|
e("a,0x0f,&,9,==,4,$b,ac,|,?{,6,a,+=,7,$c,c,|,c,:=,},a,0xf0,&,0x90,==,8,$b,c,|,?{,0x60,a,+=,7,$c,c,|,c,:=,}," flag_p);
|
|
break;
|
|
case 0xD5: /* djnz direct, offset */
|
|
xi(dir1, "--");
|
|
xr(dir1);
|
|
e("0,==,$z,!,");
|
|
cjmp;
|
|
break;
|
|
case 0xD6:
|
|
case 0xD7: /* xchd a, @Ri*/
|
|
xr(a);
|
|
e("0xf0,&,");
|
|
xr(ri);
|
|
e("0x0f,&,|,");
|
|
xr(ri);
|
|
e("0xf0,&,");
|
|
xr(a);
|
|
e("0x0f,&,|,");
|
|
xw(ri);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xD8:
|
|
case 0xD9:
|
|
case 0xDA:
|
|
case 0xDB:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
case 0xDE:
|
|
case 0xDF: /* djnz Rn, offset */
|
|
xi(rn, "--");
|
|
xr(rn);
|
|
e("0,==,$z,!,");
|
|
cjmp;
|
|
break;
|
|
case 0xE0: /* movx a, @dptr */
|
|
xr(dpx);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xE2:
|
|
case 0xE3: /* movx a, @Ri */
|
|
xr(rix);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xE4: /* clr a */
|
|
e("0,");
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xE5: /* mov a, direct */
|
|
xr(dir1);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xE6:
|
|
case 0xE7: /* mov a, @Ri */
|
|
xr(ri);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xE8:
|
|
case 0xE9:
|
|
case 0xEA:
|
|
case 0xEB:
|
|
case 0xEC:
|
|
case 0xED:
|
|
case 0xEE:
|
|
case 0xEF: /* mov a, Rn */
|
|
xr(rn);
|
|
xw(a);
|
|
e(flag_p);
|
|
break;
|
|
case 0xF0: /* movx @dptr, a */
|
|
xr(a);
|
|
xw(dpx);
|
|
break;
|
|
case 0xF2:
|
|
case 0xF3: /* movx @Ri, a */
|
|
xr(a);
|
|
xw(rix);
|
|
break;
|
|
case 0xF4: /* cpl a */
|
|
e("255,");
|
|
xi(a, "^");
|
|
e(flag_p);
|
|
break;
|
|
case 0xF5: /* mov direct, a */
|
|
xr(a);
|
|
xw(dir1);
|
|
break;
|
|
case 0xF6:
|
|
case 0xF7: /* mov @Ri, a */
|
|
xr(a);
|
|
xw(ri);
|
|
break;
|
|
case 0xF8:
|
|
case 0xF9:
|
|
case 0xFA:
|
|
case 0xFB:
|
|
case 0xFC:
|
|
case 0xFD:
|
|
case 0xFE:
|
|
case 0xFF: /* mov Rn, a */
|
|
xr(a);
|
|
xw(rn);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static RzAnalysisEsilCallbacks ocbs = { 0 };
|
|
|
|
#if 0
|
|
// custom reg read/write temporarily disabled - see r2 issue #9242
|
|
static int i8051_hook_reg_read(RzAnalysisEsil *, const char *, ut64 *, int *);
|
|
|
|
static int i8051_reg_compare(const void *name, const void *reg) {
|
|
return strcmp ((const char*)name, ((RI8051Reg*)reg)->name);
|
|
}
|
|
|
|
static RI8051Reg *i8051_reg_find(const char *name) {
|
|
return (RI8051Reg *) bsearch (
|
|
name, registers,
|
|
sizeof (registers) / sizeof (registers[0]),
|
|
sizeof (registers[0]),
|
|
i8051_reg_compare);
|
|
}
|
|
|
|
static int i8051_reg_get_offset(RzAnalysisEsil *esil, RI8051Reg *ri) {
|
|
ut8 offset = ri->offset;
|
|
if (ri->banked) {
|
|
ut64 psw = 0LL;
|
|
i8051_hook_reg_read (esil, "psw", &psw, NULL);
|
|
offset += psw & 0x18;
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
// dkreuter: It would be nice if we could attach hooks to RzRegItems directly.
|
|
// That way we could avoid doing a string lookup on register names
|
|
// as rz_reg_get already does this. Also, the analysis esil callbacks
|
|
// approach interferes with rz_reg_arena_swap.
|
|
|
|
static int i8051_hook_reg_read(RzAnalysisEsil *esil, const char *name, ut64 *res, int *size) {
|
|
int ret = 0;
|
|
ut64 val = 0LL;
|
|
RI8051Reg *ri;
|
|
RzAnalysisEsilCallbacks cbs = esil->cb;
|
|
|
|
if ((ri = i8051_reg_find (name))) {
|
|
ut8 offset = i8051_reg_get_offset(esil, ri);
|
|
ret = rz_analysis_esil_mem_read (esil, IRAM_BASE + offset, (ut8*)res, ri->num_bytes);
|
|
}
|
|
esil->cb = ocbs;
|
|
if (!ret && ocbs.hook_reg_read) {
|
|
ret = ocbs.hook_reg_read (esil, name, res, NULL);
|
|
}
|
|
if (!ret && ocbs.reg_read) {
|
|
ret = ocbs.reg_read (esil, name, &val, NULL);
|
|
}
|
|
esil->cb = cbs;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int i8051_hook_reg_write(RzAnalysisEsil *esil, const char *name, ut64 *val) {
|
|
int ret = 0;
|
|
RI8051Reg *ri;
|
|
RzAnalysisEsilCallbacks cbs = esil->cb;
|
|
if ((ri = i8051_reg_find (name))) {
|
|
ut8 offset = i8051_reg_get_offset(esil, ri);
|
|
ret = rz_analysis_esil_mem_write (esil, IRAM_BASE + offset, (ut8*)val, ri->num_bytes);
|
|
}
|
|
esil->cb = ocbs;
|
|
if (!ret && ocbs.hook_reg_write) {
|
|
ret = ocbs.hook_reg_write (esil, name, val);
|
|
}
|
|
esil->cb = cbs;
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int esil_i8051_init(RzAnalysisEsil *esil) {
|
|
if (esil->cb.user) {
|
|
return true;
|
|
}
|
|
ocbs = esil->cb;
|
|
/* these hooks break esil emulation */
|
|
/* pc is not read properly, mem mapped registers are not shown in ar, ... */
|
|
/* all 8051 regs are mem mapped, and reg access via mem is very common */
|
|
// disabled to make esil work, before digging deeper
|
|
// esil->cb.hook_reg_read = i8051_hook_reg_read;
|
|
// esil->cb.hook_reg_write = i8051_hook_reg_write;
|
|
i8051_is_init = true;
|
|
return true;
|
|
}
|
|
|
|
static int esil_i8051_fini(RzAnalysisEsil *esil) {
|
|
if (!i8051_is_init) {
|
|
return false;
|
|
}
|
|
RZ_FREE(ocbs.user);
|
|
i8051_is_init = false;
|
|
return true;
|
|
}
|
|
|
|
static bool set_reg_profile(RzAnalysis *analysis) {
|
|
const char *p =
|
|
"=PC pc\n"
|
|
"=SP sp\n"
|
|
"=A0 r0\n"
|
|
"=A1 r1\n"
|
|
"gpr r0 .8 0 0\n"
|
|
"gpr r1 .8 1 0\n"
|
|
"gpr r2 .8 2 0\n"
|
|
"gpr r3 .8 3 0\n"
|
|
"gpr r4 .8 4 0\n"
|
|
"gpr r5 .8 5 0\n"
|
|
"gpr r6 .8 6 0\n"
|
|
"gpr r7 .8 7 0\n"
|
|
"gpr a .8 8 0\n"
|
|
"gpr b .8 9 0\n"
|
|
"gpr dptr .16 10 0\n"
|
|
"gpr dpl .8 10 0\n"
|
|
"gpr dph .8 11 0\n"
|
|
"gpr psw .8 12 0\n"
|
|
"gpr p .1 .96 0\n"
|
|
"gpr ov .1 .98 0\n"
|
|
"gpr ac .1 .102 0\n"
|
|
"gpr c .1 .103 0\n"
|
|
"gpr sp .8 13 0\n"
|
|
"gpr pc .16 15 0\n"
|
|
// ---------------------------------------------------
|
|
// 8051 memory emulation control registers
|
|
// These registers map 8051 memory classes to r2's
|
|
// linear address space. Registers contain base addr
|
|
// in r2 memory space representing the memory class.
|
|
// Offsets are initialized based on asm.cpu, but can
|
|
// be updated with ar command.
|
|
//
|
|
// _code
|
|
// program memory (CODE)
|
|
// _idata
|
|
// internal data memory (IDATA, IRAM)
|
|
// _sfr
|
|
// special function registers (SFR)
|
|
// _xdata
|
|
// external data memory (XDATA, XRAM)
|
|
// _pdata
|
|
// page accessed by movx @ri op (PDATA, XREG)
|
|
// r2 addr = (_pdata & 0xff) << 8 + x_data
|
|
// if 0xffffffnn, addr = ([SFRnn] << 8) + _xdata (TODO)
|
|
"gpr _code .32 20 0\n"
|
|
"gpr _idata .32 24 0\n"
|
|
"gpr _sfr .32 28 0\n"
|
|
"gpr _xdata .32 32 0\n"
|
|
"gpr _pdata .32 36 0\n";
|
|
|
|
int retval = rz_reg_set_profile_string(analysis->reg, p);
|
|
if (retval) {
|
|
// reset emulation control registers based on cpu
|
|
set_cpu_model(analysis, true);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static ut32 map_direct_addr(RzAnalysis *analysis, ut8 addr) {
|
|
if (addr < 0x80) {
|
|
return addr + i8051_reg_read(analysis->reg, "_idata");
|
|
} else {
|
|
return addr + i8051_reg_read(analysis->reg, "_sfr");
|
|
}
|
|
}
|
|
|
|
static int i8051_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) {
|
|
set_cpu_model(analysis, false);
|
|
|
|
int i = 0;
|
|
while (_8051_ops[i].string && _8051_ops[i].op != (buf[0] & ~_8051_ops[i].mask)) {
|
|
i++;
|
|
}
|
|
|
|
ut8 arg1 = _8051_ops[i].arg1;
|
|
ut8 arg2 = _8051_ops[i].arg2;
|
|
|
|
op->cycles = _8051_ops[i].cycles;
|
|
op->failcycles = _8051_ops[i].cycles;
|
|
op->nopcode = 1;
|
|
op->size = _8051_ops[i].len;
|
|
op->type = _8051_ops[i].type;
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_CPU; // maybe also FAMILY_IO...
|
|
op->id = i;
|
|
|
|
switch (_8051_ops[i].instr) {
|
|
default:
|
|
op->cond = RZ_ANALYSIS_COND_AL;
|
|
break;
|
|
case OP_CJNE:
|
|
case OP_DJNZ:
|
|
case OP_JB:
|
|
case OP_JBC:
|
|
case OP_JNZ:
|
|
op->cond = RZ_ANALYSIS_COND_NE;
|
|
break;
|
|
case OP_JNB:
|
|
case OP_JZ:
|
|
op->cond = RZ_ANALYSIS_COND_EQ;
|
|
break;
|
|
case OP_JC:
|
|
op->cond = RZ_ANALYSIS_COND_HS;
|
|
break;
|
|
case OP_JNC:
|
|
op->cond = RZ_ANALYSIS_COND_LO;
|
|
}
|
|
|
|
switch (_8051_ops[i].instr) {
|
|
default:
|
|
op->eob = false;
|
|
break;
|
|
case OP_CJNE:
|
|
case OP_DJNZ:
|
|
case OP_JB:
|
|
case OP_JBC:
|
|
case OP_JC:
|
|
case OP_JMP:
|
|
case OP_JNB:
|
|
case OP_JNC:
|
|
case OP_JNZ:
|
|
case OP_JZ:
|
|
op->eob = true;
|
|
}
|
|
|
|
// TODO: op->datatype
|
|
|
|
switch (arg1) {
|
|
default:
|
|
break;
|
|
case A_DIRECT:
|
|
op->ptr = map_direct_addr(analysis, buf[1]);
|
|
break;
|
|
case A_BIT:
|
|
op->ptr = map_direct_addr(analysis, arg_bit(buf[1]));
|
|
break;
|
|
case A_IMMEDIATE:
|
|
op->val = buf[1];
|
|
break;
|
|
case A_IMM16:
|
|
op->val = buf[1] * 256 + buf[2];
|
|
op->ptr = op->val + i8051_reg_read(analysis->reg, "_xdata"); // best guess, it's a XRAM pointer
|
|
}
|
|
|
|
switch (arg2) {
|
|
default:
|
|
break;
|
|
case A_DIRECT:
|
|
if (arg1 == A_RI || arg1 == A_RN) {
|
|
op->ptr = map_direct_addr(analysis, buf[1]);
|
|
} else if (arg1 != A_DIRECT) {
|
|
op->ptr = map_direct_addr(analysis, buf[2]);
|
|
}
|
|
break;
|
|
case A_BIT:
|
|
op->ptr = arg_bit((arg1 == A_RI || arg1 == A_RN) ? buf[1] : buf[2]);
|
|
op->ptr = map_direct_addr(analysis, op->ptr);
|
|
break;
|
|
case A_IMMEDIATE:
|
|
op->val = (arg1 == A_RI || arg1 == A_RN) ? buf[1] : buf[2];
|
|
}
|
|
|
|
switch (_8051_ops[i].instr) {
|
|
default:
|
|
break;
|
|
case OP_PUSH:
|
|
op->stackop = RZ_ANALYSIS_STACK_INC;
|
|
op->stackptr = 1;
|
|
break;
|
|
case OP_POP:
|
|
op->stackop = RZ_ANALYSIS_STACK_INC;
|
|
op->stackptr = -1;
|
|
break;
|
|
case OP_RET:
|
|
op->stackop = RZ_ANALYSIS_STACK_INC;
|
|
op->stackptr = -2;
|
|
break;
|
|
case OP_CALL:
|
|
op->stackop = RZ_ANALYSIS_STACK_INC;
|
|
op->stackptr = 2;
|
|
if (arg1 == A_ADDR11) {
|
|
op->jump = arg_addr11(addr + op->size, buf);
|
|
op->fail = addr + op->size;
|
|
} else if (arg1 == A_ADDR16) {
|
|
op->jump = 0x100 * buf[1] + buf[2];
|
|
op->fail = addr + op->size;
|
|
}
|
|
break;
|
|
case OP_JMP:
|
|
if (arg1 == A_ADDR11) {
|
|
op->jump = arg_addr11(addr + op->size, buf);
|
|
op->fail = addr + op->size;
|
|
} else if (arg1 == A_ADDR16) {
|
|
op->jump = 0x100 * buf[1] + buf[2];
|
|
op->fail = addr + op->size;
|
|
} else if (arg1 == A_OFFSET) {
|
|
op->jump = arg_offset(addr + op->size, buf[1]);
|
|
op->fail = addr + op->size;
|
|
}
|
|
break;
|
|
case OP_CJNE:
|
|
case OP_DJNZ:
|
|
case OP_JC:
|
|
case OP_JNC:
|
|
case OP_JZ:
|
|
case OP_JNZ:
|
|
case OP_JB:
|
|
case OP_JBC:
|
|
case OP_JNB:
|
|
op->jump = arg_offset(addr + op->size, buf[op->size - 1]);
|
|
op->fail = addr + op->size;
|
|
}
|
|
|
|
if (op->ptr != -1 && op->refptr == 0) {
|
|
op->refptr = 1;
|
|
}
|
|
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut8 copy[3] = { 0, 0, 0 };
|
|
memcpy(copy, buf, len >= 3 ? 3 : len);
|
|
analop_esil(analysis, op, addr, copy);
|
|
}
|
|
|
|
int olen = 0;
|
|
op->mnemonic = rz_8051_disas(addr, buf, len, &olen);
|
|
op->size = olen;
|
|
|
|
if (mask & RZ_ANALYSIS_OP_MASK_HINT) {
|
|
// TODO: op->hint
|
|
}
|
|
|
|
return op->size;
|
|
}
|
|
|
|
RzAnalysisPlugin rz_analysis_plugin_8051 = {
|
|
.name = "8051",
|
|
.arch = "8051",
|
|
.esil = true,
|
|
.bits = 8 | 16,
|
|
.desc = "8051 CPU code analysis plugin",
|
|
.license = "LGPL3",
|
|
.op = &i8051_op,
|
|
.set_reg_profile = &set_reg_profile,
|
|
.esil_init = esil_i8051_init,
|
|
.esil_fini = esil_i8051_fini
|
|
};
|
|
|
|
#ifndef RZ_PLUGIN_INCORE
|
|
RZ_API RzLibStruct rizin_plugin = {
|
|
.type = RZ_LIB_TYPE_ANALYSIS,
|
|
.data = &rz_analysis_plugin_8051,
|
|
.version = RZ_VERSION
|
|
};
|
|
#endif
|