rizin/librz/arch/p_gnu/analysis/analysis_z80_gnu.c
2024-09-15 19:12:07 +08:00

440 lines
8.1 KiB
C

// SPDX-FileCopyrightText: 2012 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2013 condret
// 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 "z80/z80_tab.h"
static void z80_op_size(const ut8 *data, int len, int *size, int *size_prefix) {
int type = 0;
if (len < 1) {
return;
}
switch (data[0]) {
case 0xed:
if (len > 1) {
int idx = z80_ed_branch_index_res(data[1]);
type = ed[idx].type;
}
break;
case 0xcb:
type = Z80_OP16;
break;
case 0xdd:
if (len > 1) {
type = dd[z80_fddd_branch_index_res(data[1])].type;
}
break;
case 0xfd:
if (len > 1) {
type = fd[z80_fddd_branch_index_res(data[1])].type;
}
break;
default:
type = z80_op[data[0]].type;
break;
}
if (type & Z80_OP8) {
*size_prefix = 1;
} else if (type & Z80_OP16) {
*size_prefix = 2;
} else if (type & Z80_OP24) {
*size_prefix = 3;
}
if (type & Z80_ARG16) {
*size = *size_prefix + 2;
} else if (type & Z80_ARG8) {
*size = *size_prefix + 1;
} else {
*size = *size_prefix;
}
}
static int z80_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask) {
int ilen = 0;
z80_op_size(data, len, &ilen, &op->nopcode);
op->addr = addr;
op->size = ilen;
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
switch (data[0]) {
case 0x00:
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
break;
case 0x03:
case 0x04:
case 0x0c:
case 0x13:
case 0x14:
case 0x1c:
case 0x23:
case 0x24:
case 0x2c:
case 0x33:
case 0x34:
case 0x3c:
op->type = RZ_ANALYSIS_OP_TYPE_ADD; // INC
break;
case 0x09:
case 0x19:
case 0x29:
case 0x39:
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0xc6:
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
break;
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0xd6:
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
break;
case 0x22: // ld (**), hl
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->refptr = 2;
op->ptr = data[1] | data[2] << 8;
break;
case 0x32: // ld (**), a
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->refptr = 1;
op->ptr = data[1] | data[2] << 8;
break;
case 0x2a: // ld hl, (**)
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->refptr = 2;
op->ptr = data[1] | data[2] << 8;
break;
case 0x3a: // ld a, (**)
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->refptr = 1;
op->ptr = data[1] | data[2] << 8;
break;
case 0xc0:
case 0xc8:
case 0xd0:
case 0xd8:
case 0xe0:
case 0xe8:
case 0xf0:
case 0xf8:
op->type = RZ_ANALYSIS_OP_TYPE_CRET;
break;
case 0xc9:
op->type = RZ_ANALYSIS_OP_TYPE_RET;
op->eob = true;
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = -2;
break;
case 0xed:
switch (data[1]) {
case 0x43:
case 0x53:
case 0x63:
case 0x73:
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->refptr = 2;
op->ptr = data[2] | data[3] << 8;
break;
case 0x4b:
case 0x5b:
case 0x6b:
case 0x7b:
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->refptr = 2;
op->ptr = data[2] | data[3] << 8;
break;
case 0x45: // retn
case 0x4d: // reti
op->type = RZ_ANALYSIS_OP_TYPE_RET;
op->eob = true;
break;
}
break;
case 0xdd: // IX ops prefix
case 0xfd: // IY ops prefix
switch (data[1]) {
case 0x22: // ld (**), ix; ld (**), iy
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
op->refptr = 2;
op->ptr = data[2] | data[3] << 8;
break;
case 0x2a: // ld ix, (**); ld ix, (**)
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
op->refptr = 2;
op->ptr = data[2] | data[3] << 8;
break;
}
break;
case 0x05:
case 0x0b:
case 0x0d:
case 0x15:
case 0x1b:
case 0x1d:
case 0x25:
case 0x2b:
case 0x2d:
case 0x35:
case 0x3b:
case 0x3d:
// XXXX: DEC
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
break;
case 0xc5:
case 0xd5:
case 0xe5:
case 0xf5:
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
break;
case 0xc1:
case 0xd1:
case 0xe1:
case 0xf1:
op->type = RZ_ANALYSIS_OP_TYPE_POP;
break;
// ld from register to register
case 0x40:
case 0x49:
case 0x52:
case 0x5b:
case 0x64:
case 0x6d:
case 0x7f:
break;
case 0x76:
op->type = RZ_ANALYSIS_OP_TYPE_TRAP; // HALT
break;
case 0x10: // djnz
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
op->jump = addr + (st8)data[1] + ilen;
op->fail = addr + ilen;
break;
case 0x18: // jr xx
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
op->jump = addr + (st8)data[1] + ilen;
break;
// jr cond, xx
case 0x20:
case 0x28:
case 0x30:
case 0x38:
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
op->jump = addr + ((len > 1) ? (st8)data[1] : 0) + ilen;
op->fail = addr + ilen;
break;
// conditional jumps
case 0xc2:
case 0xca:
case 0xd2:
case 0xda:
case 0xe2:
case 0xea:
case 0xf2:
case 0xfa:
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
op->jump = (len > 2) ? data[1] | data[2] << 8 : 0;
op->fail = addr + ilen;
break;
case 0xc3: // jp xx
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
op->jump = (len > 2) ? data[1] | data[2] << 8 : 0;
break;
case 0xe9: // jp (HL)
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
break;
case 0xc7: // rst 0
op->jump = 0x00;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xcf: // rst 8
op->jump = 0x08;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xd7: // rst 16
op->jump = 0x10;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xdf: // rst 24
op->jump = 0x18;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xe7: // rst 32
op->jump = 0x20;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xef: // rst 40
op->jump = 0x28;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xf7: // rst 48
op->jump = 0x30;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case 0xff: // rst 56
op->jump = 0x38;
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
// conditional call
case 0xc4: // nz
case 0xd4: // nc
case 0xe4: // po
case 0xf4: // p
case 0xcc: // z
case 0xdc: // c
case 0xec: // pe
case 0xfc: // m
op->type = RZ_ANALYSIS_OP_TYPE_CCALL;
op->jump = (len > 2) ? data[1] | data[2] << 8 : 0;
op->fail = addr + ilen;
break;
// call
case 0xcd:
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = 2;
op->jump = data[1] | data[2] << 8;
break;
case 0xcb: // the same as for gameboy
switch (data[1] / 8) {
case 0:
case 2:
case 4:
case 6: // swap
op->type = RZ_ANALYSIS_OP_TYPE_ROL;
break;
case 1:
case 3:
case 5:
case 7:
op->type = RZ_ANALYSIS_OP_TYPE_ROR;
break;
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
op->type = RZ_ANALYSIS_OP_TYPE_AND;
break; // bit
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
break; // set
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
break; // res
}
break;
}
return ilen;
}
static char *get_reg_profile(RzAnalysis *analysis) {
const char *p =
"=PC mpc\n"
"=SP sp\n"
"=A0 af\n"
"=A1 bc\n"
"=A2 de\n"
"=A3 hl\n"
"gpr mpc .32 0 0\n"
"gpr pc .16 0 0\n"
"gpr m .16 2 0\n"
"gpr sp .16 4 0\n"
"gpr af .16 6 0\n"
"gpr f .8 6 0\n"
"gpr a .8 7 0\n"
"gpr Z .1 .55 0\n"
"gpr N .1 .54 0\n"
"gpr H .1 .53 0\n"
"gpr C .1 .52 0\n"
"gpr bc .16 8 0\n"
"gpr c .8 8 0\n"
"gpr b .8 9 0\n"
"gpr de .16 10 0\n"
"gpr e .8 10 0\n"
"gpr d .8 11 0\n"
"gpr hl .16 12 0\n"
"gpr l .8 12 0\n"
"gpr h .8 13 0\n"
"gpr mbcrom .16 14 0\n"
"gpr mbcram .16 16 0\n"
"gpr ime .1 18 0\n";
return rz_str_dup(p);
}
static int archinfo(RzAnalysis *a, RzAnalysisInfoType query) {
switch (query) {
case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
/* fall-thru */
case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
/* fall-thru */
case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN:
/* fall-thru */
case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN:
return 1;
case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS:
return true;
default:
return -1;
}
}
RzAnalysisPlugin rz_analysis_plugin_z80_gnu = {
.name = "z80",
.arch = "z80",
.license = "LGPL3",
.bits = 16,
.get_reg_profile = &get_reg_profile,
.desc = "Z80 CPU code analysis plugin",
.archinfo = archinfo,
.op = &z80_analysis_op,
};