864 lines
23 KiB
C++
864 lines
23 KiB
C++
/************************************************************************/
|
|
/* Disassembler for Samsung SAM8 processors */
|
|
/************************************************************************/
|
|
|
|
#include "sam8.hpp"
|
|
|
|
|
|
/**
|
|
* Register operand
|
|
*/
|
|
static void reg_operand(
|
|
insn_t &insn,
|
|
int op,
|
|
bool indirect,
|
|
bool workingReg,
|
|
bool regPair,
|
|
uint16 regNum)
|
|
{
|
|
// do it
|
|
if ( !indirect )
|
|
{
|
|
insn.ops[op].type = o_reg;
|
|
insn.ops[op].reg = regNum;
|
|
insn.ops[op].fl_workingReg = workingReg;
|
|
insn.ops[op].fl_regPair = regPair;
|
|
}
|
|
else
|
|
{
|
|
insn.ops[op].type = o_phrase;
|
|
insn.ops[op].phrase = fIndReg;
|
|
insn.ops[op].v_phrase_reg = regNum;
|
|
insn.ops[op].fl_workingReg = workingReg;
|
|
insn.ops[op].fl_regPair = regPair;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Indexed register operand
|
|
*/
|
|
static void idx_reg_operand(insn_t &insn, int op, uint16 baseRegNum, uint16 idxRegNum)
|
|
{
|
|
insn.ops[op].type = o_phrase;
|
|
insn.ops[op].phrase = fIdxReg;
|
|
insn.ops[op].v_phrase_reg = baseRegNum;
|
|
insn.ops[op].v_phrase_idxreg = idxRegNum;
|
|
}
|
|
|
|
|
|
/**
|
|
* Indexed address operand in code memory
|
|
*/
|
|
static void idx_cdata_operand(
|
|
insn_t &insn,
|
|
int op,
|
|
int offset,
|
|
int baseAddr,
|
|
uint16 idxRegNum)
|
|
{
|
|
insn.ops[op].type = o_displ;
|
|
insn.ops[op].phrase = fIdxCAddr;
|
|
insn.ops[op].addr = baseAddr;
|
|
insn.ops[op].v_phrase_idxreg = idxRegNum;
|
|
insn.ops[op].dtype = dt_word;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Indexed address operand in external (data) memory
|
|
*/
|
|
static void idx_edata_operand(
|
|
insn_t &insn,
|
|
int op,
|
|
int offset,
|
|
int baseAddr,
|
|
uint16 idxRegNum)
|
|
{
|
|
insn.ops[op].type = o_displ;
|
|
insn.ops[op].phrase = fIdxEAddr;
|
|
insn.ops[op].addr = baseAddr;
|
|
insn.ops[op].v_phrase_idxreg = idxRegNum;
|
|
insn.ops[op].dtype = dt_word;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Register bit operand
|
|
*/
|
|
static void regbit_operand(insn_t &insn, int op, bool workingReg, uint16 regNum, int bit)
|
|
{
|
|
insn.ops[op].type = o_reg_bit;
|
|
insn.ops[op].reg = regNum;
|
|
insn.ops[op].fl_workingReg = workingReg;
|
|
insn.ops[op].v_bit = bit;
|
|
}
|
|
|
|
|
|
/**
|
|
* Immediate operand
|
|
*/
|
|
static void imm_operand(insn_t &insn, int op, int offset, uint32 value, char dtype)
|
|
{
|
|
insn.ops[op].type = o_imm;
|
|
insn.ops[op].value = value;
|
|
insn.ops[op].dtype = dtype;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Address operand in external (data) memory
|
|
*/
|
|
static void addr_edata_operand(insn_t &insn, int op, int offset, ea_t address)
|
|
{
|
|
insn.ops[op].type = o_emem;
|
|
insn.ops[op].addr = address;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Address operand in code memory
|
|
*/
|
|
static void addr_cdata_operand(insn_t &insn, int op, int offset, ea_t address)
|
|
{
|
|
insn.ops[op].type = o_cmem;
|
|
insn.ops[op].addr = address;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Code operand (e.g. JP destination)
|
|
*/
|
|
static void code_operand(insn_t &insn, int op, int offset, ea_t address)
|
|
{
|
|
insn.ops[op].type = o_near;
|
|
insn.ops[op].addr = address;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Indirect code operand in "zero" page
|
|
*/
|
|
static void ind_code_operand(insn_t &insn, int op, int offset, ea_t address)
|
|
{
|
|
insn.ops[op].type = o_cmem_ind;
|
|
insn.ops[op].addr = address;
|
|
insn.ops[op].offb = (uchar)offset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Finalise cmd data structure
|
|
*/
|
|
static int finalise_insn(insn_t &insn, uint16 opcode)
|
|
{
|
|
// final checks on operands
|
|
for ( int i=0; i < 3; i++ )
|
|
{
|
|
// check reg pair operands are even
|
|
if ( insn.ops[i].type == o_reg
|
|
&& insn.ops[i].fl_regPair
|
|
&& insn.ops[i].reg & 1 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// check idxreg is even
|
|
if ( insn.ops[i].type == o_displ
|
|
&& (insn.ops[i].phrase == fIdxCAddr
|
|
|| insn.ops[i].phrase == fIdxEAddr)
|
|
&& insn.ops[i].v_phrase_idxreg & 1 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// check workingReg is valid for register operands
|
|
if ( insn.ops[i].type == o_reg
|
|
&& insn.ops[i].fl_workingReg
|
|
&& insn.ops[i].reg > 15 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// check workingReg is valid for indreg operands
|
|
if ( insn.ops[i].type == o_phrase
|
|
&& insn.ops[i].phrase == fIndReg
|
|
&& insn.ops[i].fl_workingReg
|
|
&& insn.ops[i].v_phrase_reg > 15 )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// set opcode & set no condition code
|
|
insn.itype = opcode;
|
|
insn.c_condition = ccNone;
|
|
|
|
// return size
|
|
return insn.size;
|
|
}
|
|
|
|
|
|
/**
|
|
* Finalise cmd data structure, with condition code
|
|
*/
|
|
static int finalise_insn(insn_t &insn, uint16 opcode, uchar condition)
|
|
{
|
|
// do initial instruction setup
|
|
if ( !finalise_insn(insn, opcode) )
|
|
return 0;
|
|
|
|
// set return code
|
|
insn.c_condition = condition;
|
|
|
|
// return size
|
|
return insn.size;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the next LITTLE ENDIAN word.
|
|
* For some reason this processor uses LITTLE ENDIAN OCCASIONALLY!!!
|
|
*/
|
|
static inline ushort next_word_le(insn_t &insn)
|
|
{
|
|
return insn.get_next_byte() | (insn.get_next_byte() << 8);
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// analyze an basic instruction
|
|
static int ana_basic(insn_t &insn)
|
|
{
|
|
// get the command code byte
|
|
ushort code = insn.get_next_byte();
|
|
|
|
// decode the special case (annoying) instructions
|
|
switch ( code )
|
|
{
|
|
case 0x30:
|
|
reg_operand(insn, 0, true, false, true, insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_JP);
|
|
|
|
case 0x31:
|
|
{
|
|
// need to decode second byte to determine exact type
|
|
ushort tmp = insn.get_next_byte();
|
|
switch ( tmp & 0x03 )
|
|
{
|
|
case 0:
|
|
imm_operand(insn, 0, 1, tmp & 0xF0, dt_byte);
|
|
return finalise_insn(insn, SAM8_SRP);
|
|
|
|
case 1:
|
|
imm_operand(insn, 0, 1, tmp & 0xF8, dt_byte);
|
|
return finalise_insn(insn, SAM8_SRP1);
|
|
|
|
case 2:
|
|
imm_operand(insn, 0, 1, tmp & 0xF8, dt_byte);
|
|
return finalise_insn(insn, SAM8_SRP0);
|
|
|
|
case 3:
|
|
return 0; // invalid instruction
|
|
}
|
|
}
|
|
// fallthrough
|
|
|
|
case 0x82: case 0x92: case 0x83: case 0x93:
|
|
{
|
|
// work out correct code
|
|
ushort opcode = 0;
|
|
switch ( code )
|
|
{
|
|
case 0x82: opcode = SAM8_PUSHUD; break;
|
|
case 0x92: opcode = SAM8_POPUD; break;
|
|
case 0x83: opcode = SAM8_PUSHUI; break;
|
|
case 0x93: opcode = SAM8_POPUI; break;
|
|
}
|
|
|
|
// setup operands
|
|
if ( opcode == SAM8_POPUD || opcode == SAM8_POPUI )
|
|
{
|
|
reg_operand(insn, 1, true, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
}
|
|
else
|
|
{
|
|
reg_operand(insn, 0, true, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 1, false, false, false, insn.get_next_byte());
|
|
}
|
|
return finalise_insn(insn, opcode);
|
|
}
|
|
|
|
case 0xC2: case 0xD2:
|
|
{
|
|
// work out correct code
|
|
ushort opcode = 0;
|
|
switch ( code )
|
|
{
|
|
case 0xC2: opcode = SAM8_CPIJE; break;
|
|
case 0xD2: opcode = SAM8_CPIJNE; break;
|
|
}
|
|
|
|
// decode it
|
|
ushort tmp = insn.get_next_byte();
|
|
reg_operand(insn, 0, false, true, false, bottom_nibble(tmp));
|
|
reg_operand(insn, 1, true, true, false, top_nibble(tmp));
|
|
code_operand(insn, 2, 2, insn.ea + 3 + (char) insn.get_next_byte());
|
|
return finalise_insn(insn, opcode);
|
|
}
|
|
|
|
case 0xE2: case 0xF2: case 0xC3: case 0xD3: case 0xE3: case 0xF3:
|
|
{
|
|
// need the next byte to tell whether data or code memory
|
|
ushort opcode = 0;
|
|
ushort tmp = insn.get_next_byte();
|
|
ushort operandT = top_nibble(tmp);
|
|
ushort operandB = bottom_nibble(tmp);
|
|
if ( operandB & 1 )
|
|
{
|
|
switch ( code )
|
|
{
|
|
case 0xE2: opcode = SAM8_LDED; break;
|
|
case 0xF2: opcode = SAM8_LDEPD; break;
|
|
case 0xC3: opcode = SAM8_LDE; break;
|
|
case 0xD3: opcode = SAM8_LDE; break;
|
|
case 0xE3: opcode = SAM8_LDEI; break;
|
|
case 0xF3: opcode = SAM8_LDEPI; break;
|
|
}
|
|
operandB--;
|
|
}
|
|
else
|
|
{
|
|
switch ( code )
|
|
{
|
|
case 0xE2: opcode = SAM8_LDCD; break;
|
|
case 0xF2: opcode = SAM8_LDCPD; break;
|
|
case 0xC3: opcode = SAM8_LDC; break;
|
|
case 0xD3: opcode = SAM8_LDC; break;
|
|
case 0xE3: opcode = SAM8_LDCI; break;
|
|
case 0xF3: opcode = SAM8_LDCPI; break;
|
|
}
|
|
}
|
|
|
|
// decode it
|
|
if ( code & 0x10 )
|
|
{
|
|
reg_operand(insn, 0, true, true, true, operandB);
|
|
reg_operand(insn, 1, false, true, false, operandT);
|
|
}
|
|
else
|
|
{
|
|
reg_operand(insn, 0, false, true, false, operandT);
|
|
reg_operand(insn, 1, true, true, true, operandB);
|
|
}
|
|
return finalise_insn(insn, opcode);
|
|
}
|
|
|
|
case 0xD4:
|
|
{
|
|
// get indirect address & check it is valid
|
|
ushort tmp = insn.get_next_byte();
|
|
if ( tmp & 1 )
|
|
return 0;
|
|
|
|
// generate operation
|
|
ind_code_operand(insn, 0, 1, tmp);
|
|
return finalise_insn(insn, SAM8_CALL);
|
|
}
|
|
|
|
case 0xF4:
|
|
reg_operand(insn, 0, true, false, true, insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_CALL);
|
|
|
|
case 0xF6:
|
|
code_operand(insn, 0, 1, insn.get_next_word());
|
|
return finalise_insn(insn, SAM8_CALL);
|
|
|
|
case 0xE4:
|
|
reg_operand(insn, 1, false, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0xE5:
|
|
reg_operand(insn, 1, true, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0xF5:
|
|
reg_operand(insn, 1, false, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 0, true, false, false, insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0xD5:
|
|
return 0; // invalid instruction
|
|
|
|
case 0x87: case 0x97:
|
|
{
|
|
// get next byte
|
|
ushort tmp = insn.get_next_byte();
|
|
|
|
// setup operands
|
|
switch ( code )
|
|
{
|
|
case 0x87:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(tmp));
|
|
idx_reg_operand(insn, 1, insn.get_next_byte(), bottom_nibble(tmp));
|
|
break;
|
|
|
|
case 0x97:
|
|
idx_reg_operand(insn, 0, insn.get_next_byte(), bottom_nibble(tmp));
|
|
reg_operand(insn, 1, false, true, false, top_nibble(tmp));
|
|
break;
|
|
}
|
|
|
|
// finalise the instruction
|
|
return finalise_insn(insn, SAM8_LD);
|
|
}
|
|
|
|
case 0xd6:
|
|
reg_operand(insn, 0, true, false, false, insn.get_next_byte());
|
|
imm_operand(insn, 1, 2, insn.get_next_byte(), dt_byte);
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0xe6:
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
imm_operand(insn, 1, 2, insn.get_next_byte(), dt_byte);
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0xc7:
|
|
{
|
|
ushort tmp = insn.get_next_byte();
|
|
reg_operand(insn, 0, false, true, false, top_nibble(tmp));
|
|
reg_operand(insn, 1, true, true, false, bottom_nibble(tmp));
|
|
return finalise_insn(insn, SAM8_LD);
|
|
}
|
|
|
|
case 0xd7:
|
|
{
|
|
ushort tmp = insn.get_next_byte();
|
|
reg_operand(insn, 0, true, true, false, top_nibble(tmp));
|
|
reg_operand(insn, 1, false, true, false, bottom_nibble(tmp));
|
|
return finalise_insn(insn, SAM8_LD);
|
|
}
|
|
|
|
case 0xa7: case 0xb7:
|
|
{
|
|
// extract data
|
|
ushort tmp = insn.get_next_byte();
|
|
|
|
// decode opcode + setup operands
|
|
ushort opcode;
|
|
switch ( bottom_nibble(tmp) )
|
|
{
|
|
case 0:
|
|
opcode = SAM8_LDC;
|
|
switch ( code )
|
|
{
|
|
case 0xa7:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(tmp));
|
|
addr_cdata_operand(insn, 1, 2, next_word_le(insn));
|
|
break;
|
|
|
|
case 0xb7:
|
|
addr_cdata_operand(insn, 0, 2, next_word_le(insn));
|
|
reg_operand(insn, 1, false, true, false, top_nibble(tmp));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
opcode = SAM8_LDE;
|
|
switch ( code )
|
|
{
|
|
case 0xa7:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(tmp));
|
|
addr_edata_operand(insn, 1, 2, next_word_le(insn));
|
|
break;
|
|
|
|
case 0xb7:
|
|
addr_edata_operand(insn, 0, 2, next_word_le(insn));
|
|
reg_operand(insn, 1, false, true, false, top_nibble(tmp));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// extract operand nibbles
|
|
ushort operandT = top_nibble(tmp);
|
|
ushort operandB = bottom_nibble(tmp);
|
|
|
|
// decode the correct opcode
|
|
if ( operandB & 1 )
|
|
{
|
|
opcode = SAM8_LDE;
|
|
operandB--;
|
|
}
|
|
else
|
|
{
|
|
opcode = SAM8_LDC;
|
|
}
|
|
|
|
// generate operands
|
|
switch ( code )
|
|
{
|
|
case 0xA7:
|
|
reg_operand(insn, 0, false, true, false, operandT);
|
|
if ( opcode == SAM8_LDC )
|
|
idx_cdata_operand(insn, 1, 2, next_word_le(insn), operandB);
|
|
else
|
|
idx_edata_operand(insn, 1, 2, next_word_le(insn), operandB);
|
|
break;
|
|
|
|
case 0xB7:
|
|
if ( opcode == SAM8_LDC )
|
|
idx_cdata_operand(insn, 0, 2, next_word_le(insn), operandB);
|
|
else
|
|
idx_edata_operand(insn, 0, 2, next_word_le(insn), operandB);
|
|
reg_operand(insn, 1, false, true, false, operandT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// finalise instruction
|
|
return finalise_insn(insn, opcode);
|
|
}
|
|
|
|
case 0xE7: case 0xF7:
|
|
{
|
|
// extract data
|
|
ushort tmp = insn.get_next_byte();
|
|
ushort operandT = top_nibble(tmp);
|
|
ushort operandB = bottom_nibble(tmp);
|
|
|
|
// decode the correct opcode
|
|
ushort opcode;
|
|
if ( operandB & 1 )
|
|
{
|
|
opcode = SAM8_LDE;
|
|
operandB--;
|
|
}
|
|
else
|
|
{
|
|
opcode = SAM8_LDC;
|
|
}
|
|
|
|
// generate operands
|
|
switch ( code )
|
|
{
|
|
case 0xE7:
|
|
reg_operand(insn, 0, false, true, false, operandT);
|
|
if ( opcode == SAM8_LDC )
|
|
idx_cdata_operand(insn, 1, 2, (int) (char) insn.get_next_byte(), operandB);
|
|
else
|
|
idx_edata_operand(insn, 1, 2, (int) (char) insn.get_next_byte(), operandB);
|
|
break;
|
|
|
|
case 0xF7:
|
|
if ( opcode == SAM8_LDC )
|
|
idx_cdata_operand(insn, 0, 2, (int) (char) insn.get_next_byte(), operandB);
|
|
else
|
|
idx_edata_operand(insn, 0, 2, (int) (char) insn.get_next_byte(), operandB);
|
|
reg_operand(insn, 1, false, true, false, operandT);
|
|
break;
|
|
}
|
|
|
|
// finalise the instruction
|
|
return finalise_insn(insn, opcode);
|
|
}
|
|
|
|
case 0x84: case 0x85: case 0x86:
|
|
case 0x94: case 0x95: case 0x96:
|
|
{
|
|
// decode correct opcode
|
|
ushort opcode = 0;
|
|
switch ( top_nibble(code) )
|
|
{
|
|
case 8: opcode = SAM8_MULT; break;
|
|
case 9: opcode = SAM8_DIV; break;
|
|
}
|
|
|
|
// Now, generate instruction
|
|
ushort src = insn.get_next_byte();
|
|
ushort dst = insn.get_next_byte();
|
|
reg_operand(insn, 0, false, false, true, dst);
|
|
switch ( bottom_nibble(code) )
|
|
{
|
|
case 4: reg_operand(insn, 1, false, false, false, src); break;
|
|
case 5: reg_operand(insn, 1, true, false, false, src); break;
|
|
case 6: imm_operand(insn, 1, 1, src, dt_byte); break;
|
|
}
|
|
return finalise_insn(insn, opcode);
|
|
}
|
|
|
|
case 0xC4: case 0xC5:
|
|
{
|
|
// get data
|
|
ushort src = insn.get_next_byte();
|
|
ushort dst = insn.get_next_byte();
|
|
|
|
// generate instruction
|
|
reg_operand(insn, 0, false, false, true, dst);
|
|
|
|
// decode addrmode for opcode 2
|
|
switch ( code )
|
|
{
|
|
case 0xC4: reg_operand(insn, 1, false, false, true, src); break;
|
|
case 0xC5: reg_operand(insn, 1, true, false, false, src); break;
|
|
}
|
|
return finalise_insn(insn, SAM8_LDW);
|
|
}
|
|
|
|
case 0xC6:
|
|
reg_operand(insn, 0, false, false, true, insn.get_next_byte());
|
|
imm_operand(insn, 1, 2, insn.get_next_word(), dt_word);
|
|
return finalise_insn(insn, SAM8_LDW);
|
|
|
|
case 0x17:
|
|
{
|
|
// get data
|
|
ushort operandA = insn.get_next_byte();
|
|
ushort src = insn.get_next_byte();
|
|
|
|
// ensure operandA bit0 is 0
|
|
if ( operandA & 1 )
|
|
return 0;
|
|
|
|
// generate instruction
|
|
reg_operand(insn, 0, false, true, false, top_nibble(operandA));
|
|
regbit_operand(insn, 1, false, src, bottom_nibble(operandA) >> 1);
|
|
return finalise_insn(insn, SAM8_BCP);
|
|
}
|
|
|
|
case 0x37:
|
|
{
|
|
// get data
|
|
ushort operandA = insn.get_next_byte();
|
|
ushort dst = insn.get_next_byte();
|
|
|
|
// generate operands
|
|
code_operand(insn, 0, 2, insn.ea + 3 + (char) dst);
|
|
regbit_operand(insn, 1, true,
|
|
top_nibble(operandA), bottom_nibble(operandA) >> 1);
|
|
|
|
// generate operand
|
|
switch ( operandA & 1 )
|
|
{
|
|
case 0: return finalise_insn(insn, SAM8_BTJRF);
|
|
case 1: return finalise_insn(insn, SAM8_BTJRT);
|
|
}
|
|
}
|
|
// fallthrough
|
|
|
|
case 0x57:
|
|
{
|
|
// get data
|
|
ushort operandA = insn.get_next_byte();
|
|
|
|
// ensure operandA bit0 is 0
|
|
if ( operandA & 1 )
|
|
return 0;
|
|
|
|
// generate instruction
|
|
regbit_operand(insn, 0, true,
|
|
top_nibble(operandA), bottom_nibble(operandA) >> 1);
|
|
return finalise_insn(insn, SAM8_BITC);
|
|
}
|
|
|
|
case 0x77:
|
|
{
|
|
// get data
|
|
ushort operandA = insn.get_next_byte();
|
|
|
|
// generate instruction
|
|
regbit_operand(insn, 0, true,
|
|
top_nibble(operandA), bottom_nibble(operandA) >> 1);
|
|
switch ( operandA & 1 )
|
|
{
|
|
case 0: return finalise_insn(insn, SAM8_BITR);
|
|
case 1: return finalise_insn(insn, SAM8_BITS);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Decode bit instructions
|
|
if ( (bottom_nibble(code) == 7) && (top_nibble(code) < 8) )
|
|
{
|
|
static const uint16 codeTable[] =
|
|
{
|
|
SAM8_BOR, SAM8_null,
|
|
SAM8_BXOR, SAM8_null,
|
|
SAM8_LDB, SAM8_null,
|
|
SAM8_BAND, SAM8_null
|
|
};
|
|
|
|
// extract data
|
|
ushort operandA = insn.get_next_byte();
|
|
ushort operandB = insn.get_next_byte();
|
|
|
|
// generate instruction
|
|
switch ( operandA & 1 )
|
|
{
|
|
case 0:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(operandA));
|
|
regbit_operand(insn, 1, false, operandB, bottom_nibble(operandA) >> 1);
|
|
break;
|
|
|
|
case 1:
|
|
regbit_operand(insn, 0, false, operandB, bottom_nibble(operandA) >> 1);
|
|
reg_operand(insn, 1, false, true, false, top_nibble(operandA));
|
|
break;
|
|
}
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
}
|
|
|
|
|
|
// Do the instructions with stuff encoded in them
|
|
switch ( bottom_nibble(code) )
|
|
{
|
|
case 0x08:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(code));
|
|
reg_operand(insn, 1, false, false, false, insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0x09:
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 1, false, true, false, top_nibble(code));
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0x0A:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(code));
|
|
code_operand(insn, 1, 1, insn.ea + 2 + (char) insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_DJNZ);
|
|
|
|
case 0x0B:
|
|
code_operand(insn, 0, 1, insn.ea + 2 + (char) insn.get_next_byte());
|
|
return finalise_insn(insn, SAM8_JR, top_nibble(code));
|
|
|
|
case 0x0C:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(code));
|
|
imm_operand(insn, 1, 1, insn.get_next_byte(), dt_byte);
|
|
return finalise_insn(insn, SAM8_LD);
|
|
|
|
case 0x0D:
|
|
code_operand(insn, 0, 1, insn.get_next_word()); // UNSURE ****
|
|
return finalise_insn(insn, SAM8_JP, top_nibble(code));
|
|
|
|
case 0x0E:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(code));
|
|
return finalise_insn(insn, SAM8_INC);
|
|
|
|
case 0x0F:
|
|
{
|
|
static const uint16 codeTable[] =
|
|
{
|
|
SAM8_NEXT, SAM8_ENTER,
|
|
SAM8_EXIT, SAM8_WFI,
|
|
SAM8_SB0, SAM8_SB1,
|
|
SAM8_IDLE, SAM8_STOP,
|
|
SAM8_DI, SAM8_EI,
|
|
SAM8_RET, SAM8_IRET,
|
|
SAM8_RCF, SAM8_SCF,
|
|
SAM8_CCF, SAM8_NOP
|
|
};
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
}
|
|
}
|
|
|
|
// Do R/RR/IR-only mode instructions
|
|
if ( bottom_nibble(code) < 2 )
|
|
{
|
|
static const uint16 codeTable[] =
|
|
{
|
|
SAM8_DEC, SAM8_RLC,
|
|
SAM8_INC, SAM8_null,
|
|
SAM8_DA, SAM8_POP,
|
|
SAM8_COM, SAM8_PUSH,
|
|
SAM8_DECW, SAM8_RL,
|
|
SAM8_INCW, SAM8_CLR,
|
|
SAM8_RRC, SAM8_SRA,
|
|
SAM8_RR, SAM8_SWAP
|
|
};
|
|
// do the operand
|
|
if ( code & 1 )
|
|
{
|
|
reg_operand(insn, 0, true, false, false, insn.get_next_byte());
|
|
}
|
|
else
|
|
{
|
|
if ( (top_nibble(code) == 8) || (top_nibble(code) == 0xA) )
|
|
reg_operand(insn, 0, false, false, true, insn.get_next_byte());
|
|
else
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
}
|
|
|
|
// finalise it
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
}
|
|
|
|
// Decode arithmetic-style instructions
|
|
if ( (bottom_nibble(code) > 1) && (bottom_nibble(code) < 7) )
|
|
{
|
|
static const uint16 codeTable[] =
|
|
{
|
|
SAM8_ADD, SAM8_ADC,
|
|
SAM8_SUB, SAM8_SBC,
|
|
SAM8_OR, SAM8_AND,
|
|
SAM8_TCM, SAM8_TM,
|
|
SAM8_null, SAM8_null,
|
|
SAM8_CP, SAM8_XOR,
|
|
SAM8_null, SAM8_null,
|
|
SAM8_null, SAM8_null
|
|
};
|
|
ushort operandA = insn.get_next_byte();
|
|
switch ( bottom_nibble(code) )
|
|
{
|
|
case 2:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(operandA));
|
|
reg_operand(insn, 1, false, true, false, bottom_nibble(operandA));
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
|
|
case 3:
|
|
reg_operand(insn, 0, false, true, false, top_nibble(operandA));
|
|
reg_operand(insn, 1, true, true, false, bottom_nibble(operandA));
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
|
|
case 4:
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 1, false, false, false, operandA);
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
|
|
case 5:
|
|
reg_operand(insn, 0, false, false, false, insn.get_next_byte());
|
|
reg_operand(insn, 1, true, false, false, operandA);
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
|
|
case 6:
|
|
reg_operand(insn, 0, false, false, false, operandA);
|
|
imm_operand(insn, 1, 1, insn.get_next_byte(), dt_byte);
|
|
return finalise_insn(insn, codeTable[top_nibble(code)]);
|
|
}
|
|
}
|
|
|
|
// If we get here, we've got an invalid instruction
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// analyze an instruction
|
|
int idaapi ana(insn_t *_insn)
|
|
{
|
|
insn_t &insn = *_insn;
|
|
|
|
// analyze it!
|
|
return ana_basic(insn);
|
|
}
|