mirror of
https://github.com/rizinorg/rizin.git
synced 2026-10-12 02:21:57 +00:00
Renesas RL78 disassembler plugin (#3649)
This commit is contained in:
@@ -29,6 +29,7 @@ analysis_plugins_list = [
|
||||
'ppc_cs',
|
||||
'propeller',
|
||||
'pyc',
|
||||
'rl78',
|
||||
'rsp',
|
||||
'snes',
|
||||
'sparc_cs',
|
||||
@@ -139,6 +140,7 @@ rz_analysis_sources = [
|
||||
'p/analysis_ppc_cs.c',
|
||||
'p/analysis_propeller.c',
|
||||
'p/analysis_pyc.c',
|
||||
'p/analysis_rl78.c',
|
||||
'p/analysis_rsp.c',
|
||||
'p/analysis_snes.c',
|
||||
'p/analysis_sparc_cs.c',
|
||||
@@ -230,7 +232,11 @@ rz_analysis_sources = [
|
||||
'../asm/arch/luac/v54/analysis_54.c',
|
||||
'../asm/arch/luac/v54/opcode_54.c',
|
||||
'../asm/arch/luac/v53/analysis_53.c',
|
||||
'../asm/arch/luac/v53/opcode_53.c'
|
||||
'../asm/arch/luac/v53/opcode_53.c',
|
||||
'../asm/arch/rl78/rl78_instr.c',
|
||||
'../asm/arch/rl78/rl78_maps.c',
|
||||
'../asm/arch/rl78/rl78_operand.c',
|
||||
'../asm/arch/rl78/rl78.c'
|
||||
]
|
||||
|
||||
if capstone_dep.version() == 'next' or capstone_dep.version().split('.')[0].to_int() > 4
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_asm.h>
|
||||
#include <rz_analysis.h>
|
||||
#include "../../asm/arch/rl78/rl78_instr.h"
|
||||
#include "../../asm/arch/rl78/rl78.h"
|
||||
|
||||
static int rl78_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len, RzAnalysisOpMask mask);
|
||||
static void populate_jump_fields(const RL78Instr *instr, size_t instr_size, ut64 addr, RzAnalysisOp *op);
|
||||
static char *get_reg_profile(RzAnalysis *analysis);
|
||||
|
||||
static int rl78_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len, RzAnalysisOpMask mask) {
|
||||
op->addr = addr;
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ILL;
|
||||
|
||||
RL78Instr instr = { 0 };
|
||||
size_t bytes_read = 0;
|
||||
if (!rl78_dis(&instr, &bytes_read, buf, len)) {
|
||||
op->size = bytes_read;
|
||||
return bytes_read;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
|
||||
}
|
||||
|
||||
op->size = bytes_read;
|
||||
op->addr = addr;
|
||||
switch (instr.operation) {
|
||||
case RL78_OPERATION_ADD:
|
||||
case RL78_OPERATION_ADDC:
|
||||
case RL78_OPERATION_ADDW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
case RL78_OPERATION_AND:
|
||||
case RL78_OPERATION_AND1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
||||
break;
|
||||
case RL78_OPERATION_BC:
|
||||
case RL78_OPERATION_BF:
|
||||
case RL78_OPERATION_BH:
|
||||
case RL78_OPERATION_BNC:
|
||||
case RL78_OPERATION_BNH:
|
||||
case RL78_OPERATION_BNZ:
|
||||
case RL78_OPERATION_BT:
|
||||
case RL78_OPERATION_BTCLR:
|
||||
case RL78_OPERATION_BZ:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
populate_jump_fields(&instr, bytes_read, addr, op);
|
||||
break;
|
||||
case RL78_OPERATION_BR:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
populate_jump_fields(&instr, bytes_read, addr, op);
|
||||
break;
|
||||
// conditional skip instructions
|
||||
case RL78_OPERATION_SKC:
|
||||
case RL78_OPERATION_SKH:
|
||||
case RL78_OPERATION_SKNC:
|
||||
case RL78_OPERATION_SKNH:
|
||||
case RL78_OPERATION_SKNZ:
|
||||
case RL78_OPERATION_SKZ:
|
||||
// TODO set op->jump to instruction after next (i.e. skip)
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
break;
|
||||
case RL78_OPERATION_BRK:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
|
||||
break;
|
||||
case RL78_OPERATION_CALL:
|
||||
case RL78_OPERATION_CALLT:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
populate_jump_fields(&instr, bytes_read, addr, op);
|
||||
break;
|
||||
case RL78_OPERATION_CLRB:
|
||||
case RL78_OPERATION_CLRW:
|
||||
case RL78_OPERATION_CLR1:
|
||||
// TODO (byte/word/bitclear)
|
||||
break;
|
||||
case RL78_OPERATION_CMP:
|
||||
case RL78_OPERATION_CMPS:
|
||||
case RL78_OPERATION_CMPW:
|
||||
case RL78_OPERATION_CMP0:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
break;
|
||||
case RL78_OPERATION_DEC:
|
||||
case RL78_OPERATION_DECW:
|
||||
case RL78_OPERATION_SUB:
|
||||
case RL78_OPERATION_SUBC:
|
||||
case RL78_OPERATION_SUBW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
||||
break;
|
||||
case RL78_OPERATION_EI:
|
||||
case RL78_OPERATION_DI:
|
||||
// TODO (interrupt enable/disable)
|
||||
break;
|
||||
case RL78_OPERATION_DIVHU:
|
||||
case RL78_OPERATION_DIVWU:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
|
||||
break;
|
||||
case RL78_OPERATION_HALT:
|
||||
// TODO (halt)
|
||||
break;
|
||||
case RL78_OPERATION_INC:
|
||||
case RL78_OPERATION_INCW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
case RL78_OPERATION_MULH:
|
||||
case RL78_OPERATION_MULHU:
|
||||
case RL78_OPERATION_MULU:
|
||||
// multiply-and-accumulate
|
||||
case RL78_OPERATION_MACH:
|
||||
case RL78_OPERATION_MACHU:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
|
||||
break;
|
||||
case RL78_OPERATION_MOV:
|
||||
case RL78_OPERATION_MOVS:
|
||||
case RL78_OPERATION_MOVW:
|
||||
case RL78_OPERATION_MOV1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
case RL78_OPERATION_NOP:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
break;
|
||||
case RL78_OPERATION_NOT1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOT;
|
||||
break;
|
||||
case RL78_OPERATION_ONEB:
|
||||
case RL78_OPERATION_ONEW:
|
||||
// TODO (byte/word set to 1)
|
||||
break;
|
||||
case RL78_OPERATION_OR:
|
||||
case RL78_OPERATION_OR1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_OR;
|
||||
break;
|
||||
case RL78_OPERATION_POP:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_POP;
|
||||
break;
|
||||
case RL78_OPERATION_PUSH:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
|
||||
break;
|
||||
case RL78_OPERATION_RET:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
break;
|
||||
case RL78_OPERATION_RETB:
|
||||
case RL78_OPERATION_RETI:
|
||||
// TODO (return from software/hardware vectored interrupt)
|
||||
break;
|
||||
case RL78_OPERATION_ROL:
|
||||
case RL78_OPERATION_ROLC:
|
||||
case RL78_OPERATION_ROLWC:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ROL;
|
||||
break;
|
||||
case RL78_OPERATION_ROR:
|
||||
case RL78_OPERATION_RORC:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ROR;
|
||||
break;
|
||||
case RL78_OPERATION_SAR:
|
||||
case RL78_OPERATION_SARW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
|
||||
break;
|
||||
case RL78_OPERATION_SEL:
|
||||
case RL78_OPERATION_SET1:
|
||||
case RL78_OPERATION_SHL:
|
||||
case RL78_OPERATION_SHLW:
|
||||
case RL78_OPERATION_SHR:
|
||||
case RL78_OPERATION_SHRW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
|
||||
break;
|
||||
case RL78_OPERATION_STOP:
|
||||
// TODO (stop mode set)
|
||||
break;
|
||||
case RL78_OPERATION_XCH:
|
||||
case RL78_OPERATION_XCHW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_XCHG;
|
||||
break;
|
||||
case RL78_OPERATION_XOR:
|
||||
case RL78_OPERATION_XOR1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
|
||||
return op->size;
|
||||
}
|
||||
|
||||
static void populate_jump_fields(const RL78Instr *instr, size_t instr_size, ut64 addr, RzAnalysisOp *op) {
|
||||
const RL78Operand *target = &instr->op0;
|
||||
if (instr->operation == RL78_OPERATION_BT ||
|
||||
instr->operation == RL78_OPERATION_BF ||
|
||||
instr->operation == RL78_OPERATION_BTCLR) {
|
||||
target = &instr->op1;
|
||||
}
|
||||
|
||||
switch (target->type) {
|
||||
case RL78_OP_TYPE_ABSOLUTE_ADDR_20:
|
||||
case RL78_OP_TYPE_ABSOLUTE_ADDR_16:
|
||||
op->jump = target->v0;
|
||||
break;
|
||||
case RL78_OP_TYPE_RELATIVE_ADDR_16:
|
||||
case RL78_OP_TYPE_RELATIVE_ADDR_8:
|
||||
op->jump = addr + target->v0;
|
||||
case RL78_OP_TYPE_SYMBOL:
|
||||
// TODO indirect call to symbol (CALL AX)
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
break;
|
||||
}
|
||||
|
||||
if (op->type == RZ_ANALYSIS_OP_TYPE_CJMP) {
|
||||
op->fail = addr + instr_size;
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_reg_profile(RzAnalysis *analysis) {
|
||||
const char *p =
|
||||
"=PC pc\n"
|
||||
"=SP sp\n"
|
||||
"=ZF z\n"
|
||||
"=CF cy\n"
|
||||
"=SN %s\n" // x8 on linux or android, x16 for the rest
|
||||
// ABI: https://www.renesas.com/eu/en/document/mat/cc-rl-compiler-users-manual
|
||||
"=A0 ax\n"
|
||||
"=A1 bc\n"
|
||||
"=A2 de\n"
|
||||
// general-purpose registers
|
||||
"gpr hl .16 0 0\n"
|
||||
"gpr de .16 0 0\n"
|
||||
"gpr bc .16 0 0\n"
|
||||
"gpr ax .16 0 0\n"
|
||||
"gpr h .8 0 0\n"
|
||||
"gpr l .8 0 0\n"
|
||||
"gpr d .8 0 0\n"
|
||||
"gpr e .8 0 0\n"
|
||||
"gpr b .8 0 0\n"
|
||||
"gpr c .8 0 0\n"
|
||||
"gpr a .8 0 0\n"
|
||||
"gpr x .8 0 0\n"
|
||||
|
||||
// flags
|
||||
"flg psw .8 0 0 ie_z_rbs1_ac_rbs0_isp1_isp0_cy\n"
|
||||
"flg ie .1 0 0 interrupt_enable\n"
|
||||
"flg z .1 0 0 zero\n"
|
||||
"flg rbs1 .1 0 0 register_bank_select_bit_1\n"
|
||||
"flg ac .1 0 0 auxiliary_carry\n" // set if carry or borrow at bit 3
|
||||
"flg rbs0 .1 0 0 register_bank_select_bit_0\n"
|
||||
"flg isp1 .1 0 0 in_service_priority_flags_bit_1\n"
|
||||
"flg isp0 .1 0 0 in_service_priority_flags_bit_0\n"
|
||||
"flg cy .1 0 0 carry\n";
|
||||
|
||||
return strdup(p);
|
||||
}
|
||||
|
||||
RzAnalysisPlugin rz_analysis_plugin_rl78 = {
|
||||
.name = "rl78",
|
||||
.desc = "Renesas RL78 analysis plugin",
|
||||
.license = "LGPL3",
|
||||
.arch = "rl78",
|
||||
.bits = 16,
|
||||
.op = &rl78_op,
|
||||
.get_reg_profile = &get_reg_profile
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_ANALYSIS,
|
||||
.data = &rz_analysis_plugin_rl78,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,271 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "rl78.h"
|
||||
#include "rl78_maps.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define CHECK_BOUNDS(p, len) \
|
||||
if (*p >= len) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
static bool parse_operand(RL78Operand RZ_INOUT *operand, size_t RZ_INOUT *next_byte_p,
|
||||
const ut8 RZ_BORROW *buf, size_t buf_len);
|
||||
|
||||
static inline bool optype_es_applies(RL78OperandType type);
|
||||
|
||||
// used to decide whether an operand could resolve to a symbol
|
||||
static inline bool final_value_known(RL78OperandType type);
|
||||
static inline RL78Label addr_to_symbol(int addr);
|
||||
|
||||
/**
|
||||
* \brief Disassemble a byte sequence into an rl78_instr
|
||||
* \param instr Resulting instruction
|
||||
* \param byte_read Number of bytes disassembled
|
||||
* \param buf Byte buffer
|
||||
* \param buf_len Length of the buffer
|
||||
* \return false if byte sequence does not represent a valid instruction
|
||||
*/
|
||||
bool rl78_dis(RL78Instr RZ_OUT *instr, size_t RZ_OUT *bytes_read,
|
||||
const ut8 *buf, size_t buf_len) {
|
||||
if (buf_len == 0) {
|
||||
*bytes_read = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t next_byte_p = 0;
|
||||
int byte = buf[next_byte_p++];
|
||||
|
||||
bool extension_addressing = false;
|
||||
if (byte == 0x11) {
|
||||
extension_addressing = true;
|
||||
if (next_byte_p >= buf_len) {
|
||||
*bytes_read = next_byte_p;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte = buf[next_byte_p++];
|
||||
}
|
||||
|
||||
if (next_byte_p + 1 < buf_len) {
|
||||
// MULHU, MULH...
|
||||
if (byte == 0xCE && buf[next_byte_p] == 0xFB) {
|
||||
switch (buf[next_byte_p + 1]) {
|
||||
case 0x01: instr->operation = RL78_OPERATION_MULHU; break;
|
||||
case 0x02: instr->operation = RL78_OPERATION_MULH; break;
|
||||
case 0x03: instr->operation = RL78_OPERATION_DIVHU; break;
|
||||
case 0x0B: instr->operation = RL78_OPERATION_DIVWU; break;
|
||||
case 0x05: instr->operation = RL78_OPERATION_MACHU; break;
|
||||
case 0x06: instr->operation = RL78_OPERATION_MACH; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// EI/DI aliases for set1 psw.7/clr1 psw.7
|
||||
if (byte == 0x71 && buf[next_byte_p + 1] == 0xFA) {
|
||||
switch (buf[next_byte_p]) {
|
||||
case 0x7A: instr->operation = RL78_OPERATION_EI; break;
|
||||
case 0x7B: instr->operation = RL78_OPERATION_DI; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (instr->operation != RL78_OPERATION_NONE) {
|
||||
*bytes_read = next_byte_p + 2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// regular processing (get correct map, extract instruction and parse operands)
|
||||
int map = 0;
|
||||
switch (byte) {
|
||||
case 0x31:
|
||||
// 4th map
|
||||
map = 3;
|
||||
break;
|
||||
case 0x61:
|
||||
// 2nd map
|
||||
map = 1;
|
||||
break;
|
||||
case 0x71:
|
||||
// 3rd map
|
||||
map = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// get next byte if other map was chosen
|
||||
if (map > 0) {
|
||||
if (next_byte_p >= buf_len) {
|
||||
*bytes_read = next_byte_p;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte = buf[next_byte_p++];
|
||||
}
|
||||
|
||||
*instr = rl78_instr_maps[(map * 256) + byte];
|
||||
|
||||
// an empty slot was indexed
|
||||
if (instr->operation == RL78_OPERATION_NONE) {
|
||||
*bytes_read = next_byte_p;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_operand(&instr->op0, &next_byte_p, buf, buf_len) ||
|
||||
!parse_operand(&instr->op1, &next_byte_p, buf, buf_len)) {
|
||||
*bytes_read = next_byte_p;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extension_addressing) {
|
||||
if (optype_es_applies(instr->op0.type)) {
|
||||
instr->op0.flags |= RL78_OP_FLAG_ES;
|
||||
} else if (optype_es_applies(instr->op1.type)) {
|
||||
instr->op1.flags |= RL78_OP_FLAG_ES;
|
||||
}
|
||||
}
|
||||
|
||||
*bytes_read = next_byte_p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_operand(RL78Operand RZ_INOUT *operand, size_t RZ_INOUT *next_byte_p,
|
||||
const ut8 RZ_BORROW *buf, size_t buf_len) {
|
||||
switch (operand->type) {
|
||||
// byte-sized operands
|
||||
case RL78_OP_TYPE_IMMEDIATE_8:
|
||||
case RL78_OP_TYPE_RELATIVE_ADDR_8:
|
||||
case RL78_OP_TYPE_BASED_ADDR_8:
|
||||
// already has value
|
||||
if (operand->v1 != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
if (operand->type == RL78_OP_TYPE_BASED_ADDR_8) {
|
||||
// write to v1 since v0 already has base register
|
||||
operand->v1 = buf[*next_byte_p];
|
||||
} else {
|
||||
operand->v0 = buf[*next_byte_p];
|
||||
}
|
||||
(*next_byte_p)++;
|
||||
|
||||
break;
|
||||
|
||||
case RL78_OP_TYPE_SFR:
|
||||
if (operand->v0 != 0) {
|
||||
// already has value
|
||||
return true;
|
||||
}
|
||||
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
operand->v0 = 0xFFF00 + buf[*next_byte_p];
|
||||
(*next_byte_p)++;
|
||||
|
||||
break;
|
||||
|
||||
case RL78_OP_TYPE_SADDR:
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
operand->v0 = 0xFFE20 + buf[*next_byte_p];
|
||||
(*next_byte_p)++;
|
||||
|
||||
break;
|
||||
|
||||
// word-sized operands
|
||||
case RL78_OP_TYPE_IMMEDIATE_16:
|
||||
case RL78_OP_TYPE_ABSOLUTE_ADDR_16:
|
||||
case RL78_OP_TYPE_RELATIVE_ADDR_16:
|
||||
case RL78_OP_TYPE_BASED_ADDR_16:
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
int word = buf[*next_byte_p];
|
||||
(*next_byte_p)++;
|
||||
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
word |= (int)buf[*next_byte_p] << 8;
|
||||
(*next_byte_p)++;
|
||||
|
||||
if (operand->type == RL78_OP_TYPE_BASED_ADDR_16) {
|
||||
// write to v1 since v0 already has base register
|
||||
operand->v1 = word;
|
||||
} else {
|
||||
operand->v0 = word;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// 20 bit
|
||||
case RL78_OP_TYPE_ABSOLUTE_ADDR_20:
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
int val = buf[*next_byte_p];
|
||||
(*next_byte_p)++;
|
||||
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
val |= (int)buf[*next_byte_p] << 8;
|
||||
(*next_byte_p)++;
|
||||
|
||||
CHECK_BOUNDS(next_byte_p, buf_len);
|
||||
|
||||
val |= (int)(buf[*next_byte_p] & 0xf) << 16;
|
||||
(*next_byte_p)++;
|
||||
|
||||
operand->v0 = val;
|
||||
|
||||
break;
|
||||
|
||||
default: // no need to get additional data (NONE, SYMBOL, ...)
|
||||
break;
|
||||
}
|
||||
|
||||
if (final_value_known(operand->type)) {
|
||||
// try resolving address to symbol for known fixed addresses
|
||||
RL78Label symbol = addr_to_symbol(operand->v0);
|
||||
if (rl78_symbol_valid(symbol)) {
|
||||
operand->v0 = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool optype_es_applies(RL78OperandType type) {
|
||||
return type == RL78_OP_TYPE_INDIRECT_ADDR ||
|
||||
type == RL78_OP_TYPE_BASED_ADDR_8 ||
|
||||
type == RL78_OP_TYPE_BASED_ADDR_16 ||
|
||||
type == RL78_OP_TYPE_ABSOLUTE_ADDR_16 ||
|
||||
type == RL78_OP_TYPE_BASED_INDEX_ADDR;
|
||||
}
|
||||
|
||||
static inline bool final_value_known(RL78OperandType type) {
|
||||
return type == RL78_OP_TYPE_ABSOLUTE_ADDR_16 ||
|
||||
type == RL78_OP_TYPE_ABSOLUTE_ADDR_20 ||
|
||||
type == RL78_OP_TYPE_SFR ||
|
||||
type == RL78_OP_TYPE_SADDR;
|
||||
}
|
||||
|
||||
static inline RL78Label addr_to_symbol(int addr) {
|
||||
switch (addr) {
|
||||
// TODO add general-purpose registers
|
||||
case 0xFFFF8: return RL78_SFR_SPL;
|
||||
case 0xFFFF9: return RL78_SFR_SPH;
|
||||
case 0xFFFFA:
|
||||
return RL78_SFR_PSW;
|
||||
// case 0xFB: (reserved)
|
||||
case 0xFFFFC: return RL78_SFR_CS;
|
||||
case 0xFFFFD: return RL78_SFR_ES;
|
||||
case 0xFFFFE: return RL78_SFR_PMC;
|
||||
case 0xFFFFF: return RL78_SFR_MEM;
|
||||
default:
|
||||
// invalid
|
||||
return _RL78_SYMBOL_COUNT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RL78_H
|
||||
#define RL78_H
|
||||
|
||||
#include "rl78_instr.h"
|
||||
|
||||
#include <rz_util.h>
|
||||
#include <rz_types.h>
|
||||
|
||||
bool rl78_dis(RL78Instr RZ_OUT *instr, size_t RZ_OUT *bytes_read,
|
||||
const ut8 *buf, size_t buf_len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "rl78_instr.h"
|
||||
|
||||
#include <rz_util.h>
|
||||
#include <rz_types.h>
|
||||
|
||||
static const char *RL78_STRINGS_OPERATIONS[] = {
|
||||
[RL78_OPERATION_ADD] = "add",
|
||||
[RL78_OPERATION_ADDC] = "addc",
|
||||
[RL78_OPERATION_ADDW] = "addw",
|
||||
[RL78_OPERATION_AND] = "and",
|
||||
[RL78_OPERATION_AND1] = "and1",
|
||||
[RL78_OPERATION_BC] = "bc",
|
||||
[RL78_OPERATION_BF] = "bf",
|
||||
[RL78_OPERATION_BH] = "bh",
|
||||
[RL78_OPERATION_BNC] = "bnc",
|
||||
[RL78_OPERATION_BNH] = "bnh",
|
||||
[RL78_OPERATION_BNZ] = "bnz",
|
||||
[RL78_OPERATION_BR] = "br",
|
||||
[RL78_OPERATION_BRK] = "brk",
|
||||
[RL78_OPERATION_BT] = "bt",
|
||||
[RL78_OPERATION_BTCLR] = "btclr",
|
||||
[RL78_OPERATION_BZ] = "bz",
|
||||
[RL78_OPERATION_CALL] = "call",
|
||||
[RL78_OPERATION_CALLT] = "callt",
|
||||
[RL78_OPERATION_CLRB] = "clrb",
|
||||
[RL78_OPERATION_CLRW] = "clrw",
|
||||
[RL78_OPERATION_CLR1] = "clr1",
|
||||
[RL78_OPERATION_CMP] = "cmp",
|
||||
[RL78_OPERATION_CMPS] = "cmps",
|
||||
[RL78_OPERATION_CMPW] = "cmpw",
|
||||
[RL78_OPERATION_CMP0] = "cmp0",
|
||||
[RL78_OPERATION_DEC] = "dec",
|
||||
[RL78_OPERATION_DECW] = "decw",
|
||||
[RL78_OPERATION_DI] = "di",
|
||||
[RL78_OPERATION_DIVHU] = "divhu",
|
||||
[RL78_OPERATION_DIVWU] = "divwu",
|
||||
[RL78_OPERATION_EI] = "ei",
|
||||
[RL78_OPERATION_HALT] = "halt",
|
||||
[RL78_OPERATION_INC] = "inc",
|
||||
[RL78_OPERATION_INCW] = "incw",
|
||||
[RL78_OPERATION_MACH] = "mach",
|
||||
[RL78_OPERATION_MACHU] = "machu",
|
||||
[RL78_OPERATION_MOV] = "mov",
|
||||
[RL78_OPERATION_MOVS] = "movs",
|
||||
[RL78_OPERATION_MOVW] = "movw",
|
||||
[RL78_OPERATION_MOV1] = "mov1",
|
||||
[RL78_OPERATION_MULH] = "mulh",
|
||||
[RL78_OPERATION_MULHU] = "mulhu",
|
||||
[RL78_OPERATION_MULU] = "mulu",
|
||||
[RL78_OPERATION_NOP] = "nop",
|
||||
[RL78_OPERATION_NOT1] = "not1",
|
||||
[RL78_OPERATION_ONEB] = "oneb",
|
||||
[RL78_OPERATION_ONEW] = "onew",
|
||||
[RL78_OPERATION_OR] = "or",
|
||||
[RL78_OPERATION_OR1] = "or1",
|
||||
[RL78_OPERATION_POP] = "pop",
|
||||
[RL78_OPERATION_PUSH] = "push",
|
||||
[RL78_OPERATION_RET] = "ret",
|
||||
[RL78_OPERATION_RETB] = "retb",
|
||||
[RL78_OPERATION_RETI] = "reti",
|
||||
[RL78_OPERATION_ROL] = "rol",
|
||||
[RL78_OPERATION_ROLC] = "rolc",
|
||||
[RL78_OPERATION_ROLWC] = "rolwc",
|
||||
[RL78_OPERATION_ROR] = "ror",
|
||||
[RL78_OPERATION_RORC] = "rorc",
|
||||
[RL78_OPERATION_SAR] = "sar",
|
||||
[RL78_OPERATION_SARW] = "sarw",
|
||||
[RL78_OPERATION_SEL] = "sel",
|
||||
[RL78_OPERATION_SET1] = "set1",
|
||||
[RL78_OPERATION_SHL] = "shl",
|
||||
[RL78_OPERATION_SHLW] = "shlw",
|
||||
[RL78_OPERATION_SHR] = "shr",
|
||||
[RL78_OPERATION_SHRW] = "shrw",
|
||||
[RL78_OPERATION_SKC] = "skc",
|
||||
[RL78_OPERATION_SKH] = "skh",
|
||||
[RL78_OPERATION_SKNC] = "sknc",
|
||||
[RL78_OPERATION_SKNH] = "sknh",
|
||||
[RL78_OPERATION_SKNZ] = "sknz",
|
||||
[RL78_OPERATION_SKZ] = "skz",
|
||||
[RL78_OPERATION_STOP] = "stop",
|
||||
[RL78_OPERATION_SUB] = "sub",
|
||||
[RL78_OPERATION_SUBC] = "subc",
|
||||
[RL78_OPERATION_SUBW] = "subw",
|
||||
[RL78_OPERATION_XCH] = "xch",
|
||||
[RL78_OPERATION_XCHW] = "xchw",
|
||||
[RL78_OPERATION_XOR] = "xor",
|
||||
[RL78_OPERATION_XOR1] = "xor1",
|
||||
};
|
||||
|
||||
bool rl78_instr_to_string(RzStrBuf RZ_OUT *dst, const RL78Instr RZ_BORROW *instr) {
|
||||
rz_return_val_if_fail(instr->operation > 0 &&
|
||||
instr->operation < _RL78_OPERATION_COUNT,
|
||||
false);
|
||||
|
||||
// 16 characters suffice for each operand
|
||||
RzStrBuf buf_op0, buf_op1;
|
||||
bool has_op0 = instr->op0.type != RL78_OP_TYPE_NONE;
|
||||
bool has_op1 = instr->op1.type != RL78_OP_TYPE_NONE;
|
||||
|
||||
rz_return_val_if_fail(!has_op0 || rl78_operand_to_string(&buf_op0, &instr->op0),
|
||||
false);
|
||||
|
||||
rz_return_val_if_fail(!has_op1 || rl78_operand_to_string(&buf_op1, &instr->op1),
|
||||
false);
|
||||
|
||||
if (has_op0 && has_op1) {
|
||||
rz_strbuf_appendf(dst, "%s %s, %s",
|
||||
RL78_STRINGS_OPERATIONS[instr->operation],
|
||||
buf_op0.buf, buf_op1.buf);
|
||||
} else if (has_op0) {
|
||||
rz_strbuf_appendf(dst, "%s %s",
|
||||
RL78_STRINGS_OPERATIONS[instr->operation],
|
||||
buf_op0.buf);
|
||||
} else {
|
||||
rz_strbuf_appendf(dst, "%s",
|
||||
RL78_STRINGS_OPERATIONS[instr->operation]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RL78_INSTR_H
|
||||
#define RL78_INSTR_H
|
||||
|
||||
#include "rl78_operand.h"
|
||||
|
||||
#include <rz_util.h>
|
||||
#include <rz_types.h>
|
||||
|
||||
typedef enum RL78Operation {
|
||||
RL78_OPERATION_NONE,
|
||||
|
||||
RL78_OPERATION_ADD,
|
||||
RL78_OPERATION_ADDC,
|
||||
RL78_OPERATION_ADDW,
|
||||
RL78_OPERATION_AND,
|
||||
RL78_OPERATION_AND1,
|
||||
RL78_OPERATION_BC,
|
||||
RL78_OPERATION_BF,
|
||||
RL78_OPERATION_BH,
|
||||
RL78_OPERATION_BNC,
|
||||
RL78_OPERATION_BNH,
|
||||
RL78_OPERATION_BNZ,
|
||||
RL78_OPERATION_BR,
|
||||
RL78_OPERATION_BRK,
|
||||
RL78_OPERATION_BT,
|
||||
RL78_OPERATION_BTCLR,
|
||||
RL78_OPERATION_BZ,
|
||||
RL78_OPERATION_CALL,
|
||||
RL78_OPERATION_CALLT,
|
||||
RL78_OPERATION_CLRB,
|
||||
RL78_OPERATION_CLRW,
|
||||
RL78_OPERATION_CLR1,
|
||||
RL78_OPERATION_CMP,
|
||||
RL78_OPERATION_CMPS,
|
||||
RL78_OPERATION_CMPW,
|
||||
RL78_OPERATION_CMP0,
|
||||
RL78_OPERATION_DEC,
|
||||
RL78_OPERATION_DECW,
|
||||
RL78_OPERATION_DI,
|
||||
RL78_OPERATION_DIVHU,
|
||||
RL78_OPERATION_DIVWU,
|
||||
RL78_OPERATION_EI,
|
||||
RL78_OPERATION_HALT,
|
||||
RL78_OPERATION_INC,
|
||||
RL78_OPERATION_INCW,
|
||||
RL78_OPERATION_MACH,
|
||||
RL78_OPERATION_MACHU,
|
||||
RL78_OPERATION_MOV,
|
||||
RL78_OPERATION_MOVS,
|
||||
RL78_OPERATION_MOVW,
|
||||
RL78_OPERATION_MOV1,
|
||||
RL78_OPERATION_MULH,
|
||||
RL78_OPERATION_MULHU,
|
||||
RL78_OPERATION_MULU,
|
||||
RL78_OPERATION_NOP,
|
||||
RL78_OPERATION_NOT1,
|
||||
RL78_OPERATION_ONEB,
|
||||
RL78_OPERATION_ONEW,
|
||||
RL78_OPERATION_OR,
|
||||
RL78_OPERATION_OR1,
|
||||
RL78_OPERATION_POP,
|
||||
RL78_OPERATION_PUSH,
|
||||
RL78_OPERATION_RET,
|
||||
RL78_OPERATION_RETB,
|
||||
RL78_OPERATION_RETI,
|
||||
RL78_OPERATION_ROL,
|
||||
RL78_OPERATION_ROLC,
|
||||
RL78_OPERATION_ROLWC,
|
||||
RL78_OPERATION_ROR,
|
||||
RL78_OPERATION_RORC,
|
||||
RL78_OPERATION_SAR,
|
||||
RL78_OPERATION_SARW,
|
||||
RL78_OPERATION_SEL,
|
||||
RL78_OPERATION_SET1,
|
||||
RL78_OPERATION_SHL,
|
||||
RL78_OPERATION_SHLW,
|
||||
RL78_OPERATION_SHR,
|
||||
RL78_OPERATION_SHRW,
|
||||
RL78_OPERATION_SKC,
|
||||
RL78_OPERATION_SKH,
|
||||
RL78_OPERATION_SKNC,
|
||||
RL78_OPERATION_SKNH,
|
||||
RL78_OPERATION_SKNZ,
|
||||
RL78_OPERATION_SKZ,
|
||||
RL78_OPERATION_STOP,
|
||||
RL78_OPERATION_SUB,
|
||||
RL78_OPERATION_SUBC,
|
||||
RL78_OPERATION_SUBW,
|
||||
RL78_OPERATION_XCH,
|
||||
RL78_OPERATION_XCHW,
|
||||
RL78_OPERATION_XOR,
|
||||
RL78_OPERATION_XOR1,
|
||||
|
||||
_RL78_OPERATION_COUNT
|
||||
} RL78Operation;
|
||||
|
||||
typedef struct RL78Instr {
|
||||
RL78Operand op0;
|
||||
RL78Operand op1;
|
||||
|
||||
RL78Operation operation;
|
||||
} RL78Instr;
|
||||
|
||||
/**
|
||||
* \brief Convert an RL78 instruction to a string
|
||||
* \param dst Caller-supplied character buffer to print into
|
||||
* \param n Size of dst
|
||||
* \param operand RL78 instruction to be printed
|
||||
* \return false On failure
|
||||
*/
|
||||
bool rl78_instr_to_string(RzStrBuf RZ_OUT *dst, const RL78Instr RZ_BORROW *instr);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RL78_MAPS_H
|
||||
#define RL78_MAPS_H
|
||||
|
||||
#include "rl78_instr.h"
|
||||
|
||||
extern RL78Instr rl78_instr_maps[4 * 256];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "rl78_operand.h"
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_core.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
static const char *RL78_STRINGS_SYMBOLS[] = {
|
||||
[RL78_GPR_X] = "x",
|
||||
[RL78_GPR_A] = "a",
|
||||
[RL78_GPR_C] = "c",
|
||||
[RL78_GPR_B] = "b",
|
||||
[RL78_GPR_E] = "e",
|
||||
[RL78_GPR_D] = "d",
|
||||
[RL78_GPR_L] = "l",
|
||||
[RL78_GPR_H] = "h",
|
||||
[RL78_GPR_AX] = "ax",
|
||||
[RL78_GPR_BC] = "bc",
|
||||
[RL78_GPR_DE] = "de",
|
||||
[RL78_GPR_HL] = "hl",
|
||||
[RL78_SFR_MEM] = "mem",
|
||||
[RL78_SFR_PMC] = "pmc",
|
||||
[RL78_SFR_ES] = "es",
|
||||
[RL78_SFR_CS] = "cs",
|
||||
[RL78_SFR_PSW] = "psw",
|
||||
[RL78_SFR_SPH] = "sph",
|
||||
[RL78_SFR_SPL] = "spl",
|
||||
[RL78_CR_PC] = "pc",
|
||||
[RL78_CR_PSW] = "psw",
|
||||
[RL78_CR_SP] = "sp",
|
||||
[RL78_RB_RB0] = "rb0",
|
||||
[RL78_RB_RB1] = "rb1",
|
||||
[RL78_RB_RB2] = "rb2",
|
||||
[RL78_RB_RB3] = "rb3",
|
||||
[RL78_PSW_CY] = "cy",
|
||||
[RL78_PSW_AC] = "ac",
|
||||
[RL78_PSW_Z] = "z",
|
||||
};
|
||||
|
||||
bool rl78_operand_to_string(RzStrBuf RZ_OUT *dst, const RL78Operand RZ_BORROW *operand) {
|
||||
if (operand->type <= RL78_OP_TYPE_NONE ||
|
||||
operand->type >= _RL78_OP_TYPE_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RzStrBuf strbuf;
|
||||
switch (operand->type) {
|
||||
case RL78_OP_TYPE_IMMEDIATE_8:
|
||||
case RL78_OP_TYPE_IMMEDIATE_16:
|
||||
rz_strf(strbuf.buf, "#0x%" PFMT32x, operand->v0);
|
||||
break;
|
||||
case RL78_OP_TYPE_SYMBOL:
|
||||
rz_return_val_if_fail(rl78_symbol_valid(operand->v0), false);
|
||||
|
||||
rz_strf(strbuf.buf, "%s", RL78_STRINGS_SYMBOLS[operand->v0]);
|
||||
break;
|
||||
case RL78_OP_TYPE_SFR:
|
||||
case RL78_OP_TYPE_SADDR:
|
||||
if (rl78_symbol_valid(operand->v0)) {
|
||||
rz_strf(strbuf.buf, "%s", RL78_STRINGS_SYMBOLS[operand->v0]);
|
||||
} else {
|
||||
rz_strf(strbuf.buf, "0x%" PFMT32x, operand->v0);
|
||||
}
|
||||
break;
|
||||
case RL78_OP_TYPE_ABSOLUTE_ADDR_16:
|
||||
if (rl78_symbol_valid(operand->v0)) {
|
||||
rz_strf(strbuf.buf, "%s", RL78_STRINGS_SYMBOLS[operand->v0]);
|
||||
} else {
|
||||
rz_strf(strbuf.buf, "!0x%" PFMT32x, operand->v0);
|
||||
}
|
||||
break;
|
||||
case RL78_OP_TYPE_DECIMAL:
|
||||
rz_strf(strbuf.buf, "%d", operand->v0);
|
||||
break;
|
||||
case RL78_OP_TYPE_ABSOLUTE_ADDR_20:
|
||||
if (rl78_symbol_valid(operand->v0)) {
|
||||
rz_strf(strbuf.buf, "%s", RL78_STRINGS_SYMBOLS[operand->v0]);
|
||||
} else {
|
||||
rz_strf(strbuf.buf, "!!0x%" PFMT32x, operand->v0);
|
||||
}
|
||||
break;
|
||||
case RL78_OP_TYPE_RELATIVE_ADDR_8:
|
||||
rz_strf(strbuf.buf, "$0x%" PFMT32x, operand->v0);
|
||||
break;
|
||||
case RL78_OP_TYPE_RELATIVE_ADDR_16:
|
||||
rz_strf(strbuf.buf, "$!0x%" PFMT32x, operand->v0);
|
||||
break;
|
||||
case RL78_OP_TYPE_INDIRECT_ADDR:
|
||||
if (rl78_symbol_valid(operand->v0)) {
|
||||
rz_strf(strbuf.buf, "[%s]", RL78_STRINGS_SYMBOLS[operand->v0]);
|
||||
} else {
|
||||
rz_strf(strbuf.buf, "[0x%" PFMT32x "]", operand->v0);
|
||||
}
|
||||
|
||||
break;
|
||||
case RL78_OP_TYPE_BASED_ADDR_8:
|
||||
rz_return_val_if_fail(rl78_symbol_valid(operand->v0), false);
|
||||
|
||||
rz_strf(strbuf.buf, "[%s+0x%" PFMT32x "]",
|
||||
RL78_STRINGS_SYMBOLS[operand->v0], operand->v1);
|
||||
break;
|
||||
case RL78_OP_TYPE_BASED_ADDR_16:
|
||||
rz_return_val_if_fail(rl78_symbol_valid(operand->v0), false);
|
||||
|
||||
rz_strf(strbuf.buf, "0x%" PFMT32x "[%s]",
|
||||
operand->v1, RL78_STRINGS_SYMBOLS[operand->v0]);
|
||||
break;
|
||||
case RL78_OP_TYPE_BASED_INDEX_ADDR:
|
||||
rz_return_val_if_fail(rl78_symbol_valid(operand->v0) &&
|
||||
rl78_symbol_valid(operand->v1),
|
||||
false);
|
||||
|
||||
rz_strf(strbuf.buf, "[%s+%s]",
|
||||
RL78_STRINGS_SYMBOLS[operand->v0],
|
||||
RL78_STRINGS_SYMBOLS[operand->v1]);
|
||||
break;
|
||||
default:
|
||||
rz_warn_if_reached();
|
||||
}
|
||||
|
||||
// prefix (extension addressing) and suffix (bit index)
|
||||
const char *prefix = operand->flags & RL78_OP_FLAG_ES ? "es:" : "";
|
||||
if (operand->flags & RL78_OP_FLAG_BA) {
|
||||
rz_strf(dst->buf, "%s%s.%d", prefix, strbuf.buf, operand->v1);
|
||||
} else {
|
||||
rz_strf(dst->buf, "%s%s", prefix, strbuf.buf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rl78_symbol_valid(int symbol) {
|
||||
return symbol >= 0 && symbol < _RL78_SYMBOL_COUNT;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RL78_OP_H
|
||||
#define RL78_OP_H
|
||||
|
||||
#include <rz_util.h>
|
||||
#include <rz_types.h>
|
||||
|
||||
typedef enum RL78Label {
|
||||
// 8-bit general-purpose registers
|
||||
RL78_GPR_X,
|
||||
RL78_GPR_A,
|
||||
RL78_GPR_C,
|
||||
RL78_GPR_B,
|
||||
RL78_GPR_E,
|
||||
RL78_GPR_D,
|
||||
RL78_GPR_L,
|
||||
RL78_GPR_H,
|
||||
|
||||
// 16-bit general-purpose registers
|
||||
RL78_GPR_AX,
|
||||
RL78_GPR_BC,
|
||||
RL78_GPR_DE,
|
||||
RL78_GPR_HL,
|
||||
|
||||
// special function registers
|
||||
RL78_SFR_MEM,
|
||||
RL78_SFR_PMC, // processor mode control
|
||||
RL78_SFR_ES, // higher part of address for data access
|
||||
RL78_SFR_CS, // higher part of address for branching
|
||||
RL78_SFR_PSW, // program status word
|
||||
RL78_SFR_SPH,
|
||||
RL78_SFR_SPL,
|
||||
|
||||
// control registers
|
||||
RL78_CR_PC, // program counter
|
||||
RL78_CR_PSW, // program status word
|
||||
RL78_CR_SP, // stack pointer
|
||||
|
||||
// register banks
|
||||
RL78_RB_RB0,
|
||||
RL78_RB_RB1,
|
||||
RL78_RB_RB2,
|
||||
RL78_RB_RB3,
|
||||
|
||||
// program status word bits and flags
|
||||
RL78_PSW_CY, // carry
|
||||
RL78_PSW_AC, // auxiliary carry
|
||||
RL78_PSW_Z, // zero
|
||||
|
||||
_RL78_SYMBOL_COUNT
|
||||
} RL78Label;
|
||||
|
||||
typedef enum RL78OperandType {
|
||||
RL78_OP_TYPE_NONE, // used for instructions with less than 2 operands
|
||||
RL78_OP_TYPE_IMMEDIATE_8, // #byte
|
||||
RL78_OP_TYPE_IMMEDIATE_16, // #word
|
||||
|
||||
// operands of type SFR and SADDR will be parsed into
|
||||
// RL78_OP_TYPE_SYMBOL if they point to a labeled address
|
||||
RL78_OP_TYPE_SFR, // special function register
|
||||
RL78_OP_TYPE_SADDR, // short addressing
|
||||
RL78_OP_TYPE_SYMBOL, // A, X, BC
|
||||
RL78_OP_TYPE_DECIMAL, // only used for shifts
|
||||
|
||||
RL78_OP_TYPE_ABSOLUTE_ADDR_16, // !...
|
||||
RL78_OP_TYPE_ABSOLUTE_ADDR_20, // !!...
|
||||
RL78_OP_TYPE_RELATIVE_ADDR_8, // $...
|
||||
RL78_OP_TYPE_RELATIVE_ADDR_16, // $!...
|
||||
RL78_OP_TYPE_INDIRECT_ADDR, // [HL]
|
||||
RL78_OP_TYPE_BASED_ADDR_8, // [HL+byte]
|
||||
RL78_OP_TYPE_BASED_ADDR_16, // word[HL]
|
||||
RL78_OP_TYPE_BASED_INDEX_ADDR, // [HL+C]
|
||||
|
||||
_RL78_OP_TYPE_COUNT
|
||||
} RL78OperandType;
|
||||
|
||||
typedef enum RL78OperandFlags {
|
||||
RL78_OP_FLAG_BA = 1 << 0, // bit addressing (bit index stored in v1)
|
||||
RL78_OP_FLAG_ES = 1 << 1 // extension addressing
|
||||
} RL78OperandFlags;
|
||||
|
||||
typedef struct RL78Operand {
|
||||
int v0; // contains label enum if applicable or immediate data
|
||||
int v1; // contains additional data like the offset for based addressing
|
||||
int flags;
|
||||
RL78OperandType type;
|
||||
} RL78Operand;
|
||||
|
||||
/**
|
||||
* \brief Convert an RL78 operand to a string
|
||||
* \param dst A caller-supplied character buffer to print into
|
||||
* \param n Size of dst
|
||||
* \param operand RL78 operand to be printed
|
||||
* \return false If operand->type is out of range or equal to RL78_OP_TYPE_NONE
|
||||
*/
|
||||
bool rl78_operand_to_string(RzStrBuf RZ_OUT *dst, const RL78Operand RZ_BORROW *operand);
|
||||
|
||||
/**
|
||||
* \brief Check whether a symbol is valid, i.e. is in enum bounds
|
||||
* \param symbol A symbol
|
||||
* \return false If symbol is out of range (i.e. < 0 or >= _RL78_SYMBOL_COUNT)
|
||||
*/
|
||||
bool rl78_symbol_valid(int symbol);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
add=byte data addition
|
||||
addc=addition of byte data with carry
|
||||
addw=word data addition
|
||||
addw=addition of stack pointer
|
||||
and=logical product of byte data
|
||||
and1=1 bit data logical product
|
||||
bc=conditional branch with carry flag (cy = 1)
|
||||
bf=conditional branch by bit test (byte data bit = 0)
|
||||
bh=conditional branch by numeric value comparison ((z | cy) = 0)
|
||||
bnc=conditional branch with carry flag (cy = 0)
|
||||
bnh=conditional branch by numeric value comparison ((z | cy) = 1)
|
||||
bnz=conditional branch with zero flag (z = 0)
|
||||
br=unconditional branch
|
||||
brk=software vectored interrupt
|
||||
bt=conditional branch by bit test (byte data bit = 1)
|
||||
btclr=conditional branch and clear by bit test (byte data bit =1)
|
||||
bz=conditional branch with zero flag (z = 1)
|
||||
call=subroutine call
|
||||
callt=subroutine call (refer to the call table)
|
||||
clrb=byte data clear
|
||||
clrw=word data clear
|
||||
clr1=1 bit data clear
|
||||
cmp=byte data comparison
|
||||
cmps=byte data comparison
|
||||
cmpw=word data comparison
|
||||
cmp0=byte data zero comparison
|
||||
dec=byte data decrement
|
||||
decw=word data decrement
|
||||
di=interrupt disabled
|
||||
divhu=unsigned division of data
|
||||
divwu=unsigned division of data
|
||||
ei=interrupt enabled
|
||||
halt=halt mode set
|
||||
inc=byte data increment
|
||||
incw=word data increment
|
||||
mach=signed multiplication and accumulation of data
|
||||
machu=unsigned multiplication and accumulation of data
|
||||
mov=byte data transfer
|
||||
movs=byte data transfer and psw change
|
||||
movw=word data transfer
|
||||
movw=rp, sp word data transfer with stack pointer
|
||||
movw=sp, src word data transfer with stack pointer
|
||||
mov1=1 bit data transfer
|
||||
mulh=signed multiplication of data
|
||||
mulhu=unsigned multiplication of data
|
||||
mulu=unsigned multiplication of data
|
||||
nop=no operation
|
||||
not1=1 bit data logical negation
|
||||
oneb=byte data 01h set
|
||||
onew=word data 0001h set
|
||||
or=logical sum of byte data
|
||||
or1=1 bit data logical sum
|
||||
pop=pop
|
||||
push=push
|
||||
ret=return from subroutine
|
||||
retb=return from software vectored interrupt
|
||||
reti=return from hardware vectored interrupt
|
||||
rol=byte data rotation to the left
|
||||
rolc=byte data rotation to the left with carry
|
||||
rolwc=word data rotation to the left with carry
|
||||
ror=byte data rotation to the right
|
||||
rorc=byte data rotation to the right with carry
|
||||
sar=arithmetic shift to the right
|
||||
sarw=arithmetic shift to the right
|
||||
sel=rbn register bank selection
|
||||
set1=1 bit data set
|
||||
shl=logical shift to the left
|
||||
shlw=logical shift to the left
|
||||
shr=logical shift to the right
|
||||
shrw=logical shift to the right
|
||||
skc=skip with carry flag (cy = 1)
|
||||
skh=skip with numeric value comparison ((z | cy) = 0)
|
||||
sknc=skip with carry flag (cy = 0)
|
||||
sknh=skip with numeric value comparison ((z | cy) = 1)
|
||||
sknz=skip with zero flag (z = 0)
|
||||
skz=skip with zero flag (z = 1)
|
||||
@@ -35,6 +35,7 @@ asm_plugins_list = [
|
||||
'ppc_cs',
|
||||
'propeller',
|
||||
'pyc',
|
||||
'rl78',
|
||||
'rsp',
|
||||
'sh',
|
||||
'snes',
|
||||
@@ -122,6 +123,7 @@ rz_asm_sources = [
|
||||
'p/asm_ppc_cs.c',
|
||||
'p/asm_propeller.c',
|
||||
'p/asm_pyc.c',
|
||||
'p/asm_rl78.c',
|
||||
'p/asm_rsp.c',
|
||||
'p/asm_sh.c',
|
||||
'p/asm_snes.c',
|
||||
@@ -214,6 +216,10 @@ rz_asm_sources = [
|
||||
'arch/pyc/opcode_arg_fmt.c',
|
||||
'arch/pyc/opcode.c',
|
||||
'arch/pyc/pyc_dis.c',
|
||||
'arch/rl78/rl78_instr.c',
|
||||
'arch/rl78/rl78_maps.c',
|
||||
'arch/rl78/rl78_operand.c',
|
||||
'arch/rl78/rl78.c',
|
||||
'arch/rsp/rsp_idec.c',
|
||||
'arch/sh/disassembler.c',
|
||||
'arch/sh/assembler.c',
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2023 Bastian Engel <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_asm.h>
|
||||
|
||||
#include "../arch/rl78/rl78.h"
|
||||
|
||||
static int assemble(RzAsm *a, RzAsmOp *op, const char *buf) {
|
||||
return 0x69;
|
||||
}
|
||||
|
||||
static int disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int len) {
|
||||
RL78Instr instr = { 0 };
|
||||
size_t bytes_read = 0;
|
||||
if (!rl78_dis(&instr, &bytes_read, buf, len)) {
|
||||
rz_strbuf_set(&op->buf_asm, "(invalid)");
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
RzStrBuf *instr_strbuf = rz_strbuf_new("");
|
||||
if (rl78_instr_to_string(instr_strbuf, &instr)) {
|
||||
rz_strbuf_copy(&op->buf_asm, instr_strbuf);
|
||||
} else {
|
||||
rz_strbuf_set(&op->buf_asm, "(invalid)");
|
||||
}
|
||||
|
||||
rz_strbuf_free(instr_strbuf);
|
||||
|
||||
op->size = bytes_read;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
RzAsmPlugin rz_asm_plugin_rl78 = {
|
||||
.name = "rl78",
|
||||
.arch = "rl78",
|
||||
.desc = "Renesas RL78 disassembler",
|
||||
.author = "Bastian Engel",
|
||||
.license = "LGPL3",
|
||||
.bits = 32,
|
||||
.endian = RZ_SYS_ENDIAN_LITTLE | RZ_SYS_ENDIAN_BIG,
|
||||
.assemble = &assemble,
|
||||
.disassemble = &disassemble
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_ASM,
|
||||
.data = &rz_asm_plugin_rl78,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
@@ -2406,6 +2406,7 @@ extern RzAnalysisPlugin rz_analysis_plugin_ppc_cs;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_propeller;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_riscv;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_riscv_cs;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_rl78;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_rsp;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_sh;
|
||||
extern RzAnalysisPlugin rz_analysis_plugin_snes;
|
||||
|
||||
@@ -258,6 +258,7 @@ extern RzAsmPlugin rz_asm_plugin_ppc_cs;
|
||||
extern RzAsmPlugin rz_asm_plugin_propeller;
|
||||
extern RzAsmPlugin rz_asm_plugin_riscv;
|
||||
extern RzAsmPlugin rz_asm_plugin_riscv_cs;
|
||||
extern RzAsmPlugin rz_asm_plugin_rl78;
|
||||
extern RzAsmPlugin rz_asm_plugin_rsp;
|
||||
extern RzAsmPlugin rz_asm_plugin_sh;
|
||||
extern RzAsmPlugin rz_asm_plugin_snes;
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
NAME=RL78 caesar_nostrip info
|
||||
FILE=bins/rl78/caesar_nostrip
|
||||
ARGS=-a rl78
|
||||
CMDS=<<EOF
|
||||
pd 4 @ entry0
|
||||
iI
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
;-- entry0:
|
||||
;-- _start:
|
||||
0x0000077c movw spl, #0xfedc
|
||||
0x00000780 mov es, #0x0
|
||||
0x00000782 movw bc, #0x472
|
||||
0x00000785 clrw ax
|
||||
arch N/A
|
||||
cpu N/A
|
||||
baddr 0x00000000
|
||||
binsz 0x00008360
|
||||
bintype elf
|
||||
bits 32
|
||||
class ELF32
|
||||
compiler Linker: LLD 10.0.0 clang version LLVM for Renesas RL78 10.0.0.202306
|
||||
dbg_file N/A
|
||||
endian LE
|
||||
hdr.csum N/A
|
||||
guid N/A
|
||||
intrp N/A
|
||||
laddr 0x00000000
|
||||
lang c
|
||||
machine Renesas RL78 family
|
||||
minopsz 1
|
||||
os linux
|
||||
cc N/A
|
||||
rpath NONE
|
||||
subsys linux
|
||||
stripped false
|
||||
crypto false
|
||||
havecode true
|
||||
va true
|
||||
sanitiz false
|
||||
static true
|
||||
linenum true
|
||||
lsyms true
|
||||
canary false
|
||||
PIE false
|
||||
RELROCS true
|
||||
NX true
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=RL78 caesar_nostrip function bodies
|
||||
FILE=bins/rl78/caesar_nostrip
|
||||
ARGS=-a rl78
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
pdf @ sym._decrypt
|
||||
pdf @ sym._make_uppercase
|
||||
pdf @ sym._hash
|
||||
pdf @ sym._main
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
;-- section..text:
|
||||
;-- segment.LOAD4:
|
||||
;-- _decrypt:
|
||||
/ int decrypt(char *decrypted, const char *encrypted, size_t n)
|
||||
| ; arg char *decrypted @ ...
|
||||
| ; arg const char *encrypted @ ...
|
||||
| ; arg size_t n @ ...
|
||||
| 0x00000552 subw sp, #0x12 ; caesar.c:15 ; int decrypt(char *decrypted, const char *encrypted, size_t n)
|
||||
| 0x00000554 movw [sp+0x0], ax
|
||||
| 0x00000556 movw ax, bc
|
||||
| 0x00000557 movw [sp+0x2], ax
|
||||
| 0x00000559 movw ax, de
|
||||
| 0x0000055a movw [sp+0x4], ax
|
||||
| 0x0000055c clrw ax
|
||||
| 0x0000055d movw [sp+0x6], ax ; caesar.c:16
|
||||
\ @-> 0x0000055f br $0x0
|
||||
;-- _make_uppercase:
|
||||
/ void make_uppercase(char *buf, size_t n)
|
||||
| ; arg char *buf @ ...
|
||||
| ; arg size_t n @ ...
|
||||
| 0x000005a3 subw sp, #0x18 ; caesar.c:24 ; void make_uppercase(char *buf, size_t n)
|
||||
| 0x000005a5 movw [sp+0x0], ax
|
||||
| 0x000005a7 movw ax, bc
|
||||
| 0x000005a8 movw [sp+0x2], ax
|
||||
| 0x000005aa clrw ax
|
||||
| 0x000005ab movw [sp+0x4], ax ; caesar.c:25
|
||||
\ @-> 0x000005ad br $0x0
|
||||
; CALL XREFS from sym._main @ +0x8f, +0x9d
|
||||
;-- _hash:
|
||||
/ int hash(const char *buf, size_t n)
|
||||
| ; arg const char *buf @ ...
|
||||
| ; arg size_t n @ ...
|
||||
| ; var int sum @ ...
|
||||
| 0x00000634 subw sp, #0x1e ; caesar.c:33 ; int hash(const char *buf, size_t n)
|
||||
| 0x00000636 movw [sp+0x4], ax
|
||||
| 0x00000638 movw ax, bc
|
||||
| 0x00000639 movw [sp+0x6], ax
|
||||
| 0x0000063b clrw ax
|
||||
| 0x0000063c movw [sp+0xc], ax ; caesar.c:34
|
||||
| 0x0000063e movw [sp+0x8], ax
|
||||
| 0x00000640 movw ax, [sp+0xc] ; caesar.c:35
|
||||
| 0x00000642 movw [sp+0xa], ax
|
||||
\ @-> 0x00000644 br $0x0
|
||||
; CALL XREF from entry0 @ 0x7f8
|
||||
;-- _main:
|
||||
/ int main(int argc, char **argv)
|
||||
| ; arg int argc @ ...
|
||||
| ; arg char **argv @ ...
|
||||
| ; var const struct operations ops @ ...
|
||||
| ; var size_t len @ ...
|
||||
| ; var char *decrypted @ ...
|
||||
| ; var char *uppercase @ ...
|
||||
| ; var const char *pw @ ...
|
||||
| ; var size_t pwlen @ ...
|
||||
| 0x000006b3 subw sp, #0x26 ; caesar.c:43 ; int main(int argc, char **argv)
|
||||
| 0x000006b5 movw de, #0x0
|
||||
| 0x000006b8 movw [sp+0x14], ax
|
||||
| 0x000006ba movw ax, de
|
||||
| 0x000006bb movw [sp+0x0], ax
|
||||
| 0x000006bd movw ax, [sp+0x14]
|
||||
| 0x000006bf movw [sp+0x2], ax
|
||||
| 0x000006c1 movw ax, bc
|
||||
| 0x000006c2 movw [sp+0x4], ax
|
||||
| 0x000006c4 movw ax, !0x3004 ; caesar.c:44
|
||||
| 0x000006c7 movw [sp+0x8], ax
|
||||
| 0x000006c9 movw ax, !0x3002
|
||||
| 0x000006cc movw [sp+0x6], ax
|
||||
| 0x000006ce movw ax, !0x5700 ; caesar.c:49
|
||||
| 0x000006d1 call !0x84e ; sym._strlen ; size_t strlen(const char *s)
|
||||
| 0x000006d4 movw [sp+0xa], ax
|
||||
| 0x000006d6 movw ax, [sp+0xa] ; caesar.c:50
|
||||
| 0x000006d8 cmpw ax, #0x41
|
||||
| ,=< 0x000006db bc $0x8
|
||||
| @=-> 0x000006dd br $0x0
|
||||
..
|
||||
| |`-> 0x000006e3 dec c
|
||||
| | 0x000006e4 nop
|
||||
| | 0x000006e5 movw ax, [sp+0xa] ; caesar.c:54
|
||||
| | 0x000006e7 call !0xb18 ; sym._malloc ; void *malloc(size_t size)
|
||||
| | 0x000006ea movw [sp+0xc], ax
|
||||
| | 0x000006ec movw ax, [sp+0x6] ; caesar.c:55
|
||||
| | 0x000006ee movw [sp+0x16], ax
|
||||
| | 0x000006f0 movw ax, [sp+0xc]
|
||||
| | 0x000006f2 movw bc, !0x5700
|
||||
| | 0x000006f5 movw [sp+0x18], ax
|
||||
| | 0x000006f7 movw ax, [sp+0xa]
|
||||
| | 0x000006f9 movw de, ax
|
||||
| | 0x000006fa movw ax, [sp+0x18]
|
||||
| | 0x000006fc xchw ax, hl
|
||||
| | 0x000006fd movw ax, [sp+0x16]
|
||||
| | 0x000006ff xchw ax, hl
|
||||
| | 0x00000700 call hl
|
||||
| | 0x00000702 cmpw ax, #0x0
|
||||
| |,=< 0x00000705 bnz $0xc
|
||||
| @==-> 0x00000707 br $0x0
|
||||
..
|
||||
| ,=`-> 0x00000711 br $0x64
|
||||
..
|
||||
| || @ ; CODE XREF from dbg.main @ 0x711
|
||||
| || @ ; CODE XREF from sym._main @ +0xb3
|
||||
\ ``-@-> 0x00000775 br $0x0
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=RL78 caesar_nostrip function list
|
||||
FILE=bins/rl78/caesar_nostrip
|
||||
ARGS=-a rl78
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afl
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x0000077c 60 1024 -> 464 entry0
|
||||
0x0000093a 8 81 -> 83 sym.___do_fini
|
||||
0x000008de 2 11 sym.___do_init
|
||||
0x000013d7 7 90 -> 91 sym._malloc_extend_top
|
||||
0x00000552 2 15 dbg.decrypt
|
||||
0x00000b1f 1 7 sym._free
|
||||
0x00000634 2 18 dbg.hash
|
||||
0x000006b3 6 196 -> 86 dbg.main
|
||||
0x000005a3 2 12 dbg.make_uppercase
|
||||
0x00000b18 1 7 sym._malloc
|
||||
0x000015fb 1 7 sym._strdup
|
||||
0x0000084e 2 11 sym._strlen
|
||||
0x00000e5e 1 20 sym.__malloc_r
|
||||
0x0000083e 1 8 sym._memcpy
|
||||
0x000009ab 3 26 sym._exit
|
||||
0x000009c5 1 24 sym.___call_exitprocs
|
||||
0x00000b16 8 345 -> 80 sym.__exit
|
||||
0x00000816 7 39 sym._rl78_run_fini_array
|
||||
0x0000080b 1 11 sym._rl78_run_init_array
|
||||
0x00000800 1 11 sym._rl78_run_preinit_array
|
||||
0x000015e1 3 26 -> 28 sym._sbrk
|
||||
0x00000b26 1 11 sym.__free_r
|
||||
0x00000d9e 3 49 sym.__malloc_trim_r
|
||||
0x000015bd 1 1 sym.___malloc_lock
|
||||
0x000015be 1 1 sym.___malloc_unlock
|
||||
0x000015bf 4 34 sym.__sbrk_r
|
||||
0x00001602 3 44 sym.__strdup_r
|
||||
0x00000896 1 8 loc.__COM_slrem
|
||||
0x0000162e 1 13 loc.__rl78_init
|
||||
0x0000086e 5 25 loc.__COM_lshl
|
||||
EOF
|
||||
RUN
|
||||
+1331
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user