340 lines
8.4 KiB
C++
340 lines
8.4 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) Hex-Rays
|
|
* ALL RIGHTS RESERVED.
|
|
*
|
|
* Output
|
|
*
|
|
*/
|
|
#include "necv850.hpp"
|
|
#include "ins.hpp"
|
|
|
|
//--------------------------------------------------------------------------
|
|
// LIST12 table mapping to corresponding registers
|
|
static const int list12_table[] =
|
|
{
|
|
rR31, // 0
|
|
rR29, // 1
|
|
rR28, // 2
|
|
rR23, // 3
|
|
rR22, // 4
|
|
rR21, // 5
|
|
rR20, // 6
|
|
rR27, // 7
|
|
rR26, // 8
|
|
rR25, // 9
|
|
rR24, // 10
|
|
rEP // 11
|
|
};
|
|
|
|
// Using the indexes in this table as indexes in list12_table[]
|
|
// we can test for bits in List12 in order
|
|
static const int list12order_table[] =
|
|
{
|
|
6, // 0 r20
|
|
5, // 1 r21
|
|
4, // 2 r22
|
|
3, // 3 r23
|
|
10, // 4 r24
|
|
9, // 5 r25
|
|
8, // 6 r26
|
|
7, // 7 r27
|
|
2, // 8 r28
|
|
1, // 9 r29
|
|
11, // 10 r30
|
|
0, // 11 r31
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
int get_displ_outf(const insn_t &insn, const op_t &x, flags_t F)
|
|
{
|
|
qnotused(insn);
|
|
qnotused(F);
|
|
|
|
int outf = OOF_ADDR;
|
|
outf |= ( x.specflag1 & N850F_VAL32 ) ? OOFW_32 : OOFW_16;
|
|
if ( ( x.specflag1 & N850F_OUTSIGNED ) != 0 )
|
|
outf |= OOFS_IFSIGN | OOF_SIGNED;
|
|
|
|
return outf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
class out_nec850_t : public outctx_t
|
|
{
|
|
out_nec850_t(void) = delete; // not used
|
|
public:
|
|
void OutReg(const op_t &r);
|
|
void out_reg_list(uint32 L);
|
|
void out_reg_range(const op_t &op);
|
|
bool out_operand(const op_t &x);
|
|
void out_insn(void);
|
|
void out_cond(const op_t &r);
|
|
void out_fcond(const op_t &r);
|
|
};
|
|
CASSERT(sizeof(out_nec850_t) == sizeof(outctx_t));
|
|
|
|
DECLARE_OUT_FUNCS_WITHOUT_OUTMNEM(out_nec850_t)
|
|
|
|
//--------------------------------------------------------------------------
|
|
bool reg_in_list12(uint16 reg, uint32 L)
|
|
{
|
|
if ( rR20 <= reg && reg <= rR31 )
|
|
{
|
|
uint32 idx = list12order_table[reg - rR20]; //lint !e676 possibly indexing before the beginning of an allocation
|
|
return (L & (1 << idx)) != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void out_nec850_t::out_reg_list(uint32 L)
|
|
{
|
|
int last = qnumber(list12_table);
|
|
int in_order = 0, c = 0;
|
|
const char *last_rn = NULL;
|
|
|
|
out_symbol('{');
|
|
for ( int i=0; i < qnumber(list12order_table); i++ )
|
|
{
|
|
uint32 idx = list12order_table[i];
|
|
if ( (L & (1 << idx)) == 0 )
|
|
continue;
|
|
c++;
|
|
const char *rn = RegNames[list12_table[idx]];
|
|
if ( last + 1 == i )
|
|
in_order++;
|
|
else
|
|
{
|
|
if ( in_order > 1 )
|
|
{
|
|
out_symbol('-');
|
|
out_register(last_rn);
|
|
out_line(", ", COLOR_SYMBOL);
|
|
}
|
|
else if ( c > 1 )
|
|
{
|
|
out_line(", ", COLOR_SYMBOL);
|
|
}
|
|
out_register(rn);
|
|
in_order = 1;
|
|
}
|
|
last_rn = rn;
|
|
last = i;
|
|
}
|
|
if ( in_order > 1 )
|
|
{
|
|
out_symbol('-');
|
|
out_register(last_rn);
|
|
}
|
|
out_symbol('}');
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
void out_nec850_t::out_reg_range(const op_t &op)
|
|
{
|
|
out_register(RegNames[op.regrange_high]);
|
|
out_symbol('-');
|
|
out_register(RegNames[op.regrange_low]);
|
|
}
|
|
//--------------------------------------------------------------------------
|
|
void idaapi nec850_header(outctx_t &ctx)
|
|
{
|
|
ctx.gen_header(GH_PRINT_PROC_AND_ASM);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void nec850_t::nec850_footer(outctx_t &ctx) const
|
|
{
|
|
ctx.gen_empty_line();
|
|
ctx.out_line(ash.end, COLOR_ASMDIR);
|
|
ctx.flush_outbuf(DEFAULT_INDENT);
|
|
ctx.gen_cmt_line("-------------- end of module --------------");
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//lint -esym(1764, ctx) could be made const
|
|
//lint -esym(818, s) could be made const
|
|
void idaapi nec850_segstart(outctx_t &ctx, segment_t *s)
|
|
{
|
|
qstring sname;
|
|
qstring sclass;
|
|
|
|
get_visible_segm_name(&sname, s);
|
|
get_segm_class(&sclass, s);
|
|
|
|
const char *p_class;
|
|
if ( (s->perm == (SEGPERM_READ|SEGPERM_WRITE)) && s->type == SEG_BSS )
|
|
p_class = "bss";
|
|
else if ( s->perm == SEGPERM_READ )
|
|
p_class = "const";
|
|
else if ( s->perm == (SEGPERM_READ|SEGPERM_WRITE) )
|
|
p_class = "data";
|
|
else if ( s->perm == (SEGPERM_READ|SEGPERM_EXEC) )
|
|
p_class = "text";
|
|
else if ( s->type == SEG_XTRN )
|
|
p_class = "symtab";
|
|
else
|
|
p_class = sclass.c_str();
|
|
|
|
ctx.gen_printf(0, COLSTR(".section \"%s\", %s", SCOLOR_ASMDIR), sname.c_str(), p_class);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi nec850_segend(outctx_t &, segment_t *)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_nec850_t::OutReg(const op_t &r)
|
|
{
|
|
bool brackets = r.specflag1 & N850F_USEBRACKETS;
|
|
if ( brackets )
|
|
out_symbol('[');
|
|
out_register(ph.reg_names[r.reg]);
|
|
if ( brackets )
|
|
out_symbol(']');
|
|
}
|
|
|
|
static const char *cond_tbl[16] =
|
|
{
|
|
"v", // 0000: Overflow (OV=1)
|
|
"c/l", // 0001: Carry (CY=1)
|
|
"z", // 0010: Zero (Z=1)
|
|
"nh", // 0011: Not higher (Less than or equal) ((CY or Z) = 1)
|
|
"s/n", // 0100: Negative) S=1
|
|
"t", // 0101: Always (true)
|
|
"lt", // 0110: Less than signed (S xor OV) = 1
|
|
"le", // 0111: Less than or equal signed (((S xor OV) or Z) = 1)
|
|
"nv", // 1000: no overflow (OV=0)
|
|
"nc/nl",// 1001: no carry (CY=0)
|
|
"nz", // 1010: not zero (Z=0)
|
|
"h", // 0011: Higher (Greater than) ((CY or Z) = 0)
|
|
"ns/p", // 0100: Positive (S=0)
|
|
"sat", // 1101: Saturated (SAT=1)
|
|
"ge", // 1110: Greater than or equal signed (S xor OV) = 0
|
|
"gt", // 1111: Greater than signed (((S xor OV) or Z) = 0)
|
|
};
|
|
|
|
static const char *fcond_tbl[16] =
|
|
{
|
|
"f/t",
|
|
"un/or",
|
|
"eq/neq",
|
|
"ueq/ogl",
|
|
"olt/uge",
|
|
"ult/oge",
|
|
"ole/ugt",
|
|
"ule/ogt",
|
|
"sf/st",
|
|
"ngle/gle",
|
|
"seq/sne",
|
|
"ngl/gl",
|
|
"lt/nlt",
|
|
"nge/ge",
|
|
"le/nle",
|
|
"ngt/gt"
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_nec850_t::out_cond(const op_t &r)
|
|
{
|
|
int cc = r.value;
|
|
QASSERT(10327, r.type == o_cond && cc < qnumber(cond_tbl));
|
|
out_keyword(cond_tbl[cc]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_nec850_t::out_fcond(const op_t &r)
|
|
{
|
|
int cc = r.value;
|
|
QASSERT(10323, r.type == o_cond && cc < qnumber(fcond_tbl));
|
|
out_keyword(fcond_tbl[cc]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void out_nec850_t::out_insn(void)
|
|
{
|
|
out_mnemonic();
|
|
|
|
out_one_operand(0);
|
|
|
|
for ( int i=1; i < UA_MAXOP; i++ )
|
|
{
|
|
if ( insn.ops[i].type == o_void )
|
|
break;
|
|
out_symbol(',');
|
|
out_char(' ');
|
|
out_one_operand(i);
|
|
}
|
|
flush_outbuf();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Generate text representation of an instructon operand.
|
|
// This function shouldn't change the database, flags or anything else.
|
|
// All these actions should be performed only by u_emu() function.
|
|
// The output text is placed in the output buffer initialized with init_output_buffer()
|
|
// This function uses out_...() functions from ua.hpp to generate the operand text
|
|
// Returns: 1-ok, 0-operand is hidden.
|
|
bool out_nec850_t::out_operand(const op_t &x)
|
|
{
|
|
switch ( x.type )
|
|
{
|
|
case o_void:
|
|
return false;
|
|
case o_reglist:
|
|
out_reg_list(x.value);
|
|
break;
|
|
case o_regrange:
|
|
out_reg_range(x);
|
|
break;
|
|
case o_reg:
|
|
OutReg(x);
|
|
break;
|
|
case o_imm:
|
|
out_value(x, OOFW_IMM | ((x.specflag1 & N850F_OUTSIGNED) ? OOF_SIGNED : 0));
|
|
break;
|
|
case o_near:
|
|
case o_mem:
|
|
if ( !out_name_expr(x, x.addr, BADADDR) )
|
|
{
|
|
out_tagon(COLOR_ERROR);
|
|
out_value(x, OOF_ADDR | OOFW_IMM | OOFW_32);
|
|
out_tagoff(COLOR_ERROR);
|
|
remember_problem(PR_NONAME, insn.ea);
|
|
}
|
|
break;
|
|
case o_displ:
|
|
if ( x.addr != 0 || x.reg == rSP )
|
|
out_value(x, get_displ_outf(insn, x, F));
|
|
OutReg(x);
|
|
if ( x.reg != rGP && x.reg != rSP && x.addr == 0 )
|
|
{ // add name comment
|
|
xrefblk_t xb;
|
|
for ( bool ok=xb.first_from(insn.ea, XREF_DATA); ok; ok=xb.next_from() )
|
|
{
|
|
if ( has_cmt(F) )
|
|
continue;
|
|
if ( xb.type != dr_R && xb.type != dr_W )
|
|
continue;
|
|
|
|
qstring qbuf;
|
|
if ( get_name_expr(&qbuf, insn.ea, x.n, xb.to, BADADDR) > 0 )
|
|
set_cmt(insn.ea, qbuf.begin(), false);
|
|
}
|
|
}
|
|
break;
|
|
case o_cond:
|
|
if ( insn.auxpref & N850F_FP )
|
|
out_fcond(x);
|
|
else
|
|
out_cond(x);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|