This commit is contained in:
olari
2021-06-05 21:10:25 +03:00
parent 807cffd9de
commit e0e0f2be99
923 changed files with 911857 additions and 15 deletions

View File

@@ -0,0 +1,296 @@
#include "kr1878.hpp"
#define FUNCS_COUNT 3
struct funcdesc_t
{
bool (kr1878_t:: *func)(const insn_t &insn, int);
int mask;
int shift;
};
struct opcode
{
ushort itype;
const char *recog; //lint !e958 padding is required to align members
funcdesc_t funcs[FUNCS_COUNT];
uint32 mask;
uint32 value;
};
//----------------------------------------------------------------------
inline uint32 ua_16bits(const insn_t &insn)
{
return get_wide_byte(insn.ea);
}
//----------------------------------------------------------------------
void kr1878_t::opreg(uint16 reg)
{
op->type = o_reg;
op->dtype = dt_word;
op->reg = reg;
}
//----------------------------------------------------------------------
void kr1878_t::make_o_mem(const insn_t &insn)
{
switch ( insn.itype )
{
case KR1878_jmp:
case KR1878_jsr:
case KR1878_jnz:
case KR1878_jz:
case KR1878_jns:
case KR1878_js:
case KR1878_jnc:
case KR1878_jc:
op->type = o_near;
op->dtype = dt_code;
return;
}
op->type = o_mem;
op->dtype = dt_byte;
}
//----------------------------------------------------------------------
bool kr1878_t::D_ddddd(const insn_t &, int value)
{
op->type = o_phrase;
op->dtype = dt_byte;
op->reg = (value >> 3) & 0x03;
op->value = value & 7;
return true;
}
//----------------------------------------------------------------------
bool kr1878_t::S_ddddd(const insn_t &insn, int value)
{
if ( D_ddddd(insn, value) )
{
op++;
return true;
}
return false;
}
//----------------------------------------------------------------------
bool kr1878_t::D_SR(const insn_t &, int value)
{
op->type = o_reg;
op->dtype = dt_word;
op->reg = uint16(SR0 + value);
return true;
}
//----------------------------------------------------------------------
bool kr1878_t::S_SR(const insn_t &insn, int value)
{
if ( D_SR(insn, value) )
{
op++;
return true;
}
return false;
}
//----------------------------------------------------------------------
bool kr1878_t::D_Imm(const insn_t &, int value)
{
op->type = o_imm;
op->dtype = dt_word;
op->value = value & 0xffff;
return true;
}
//----------------------------------------------------------------------
bool kr1878_t::D_pImm(const insn_t &insn, int value)
{
if ( value & 0x10 )
D_Imm(insn, (value & 0x0f) << 4);
else
D_Imm(insn, value & 0x0f);
return true;
}
//----------------------------------------------------------------------
bool kr1878_t::D_EA(const insn_t &insn, int value)
{
op->addr = value;
make_o_mem(insn);
return true;
}
//----------------------------------------------------------------------
// singleton to init table thread-aware
struct table_t
{
static int count() { return qnumber(table); }
static const opcode &get(int opcode)
{
static const table_t instance; //lint !e1788 only by its constructor/destructor
return instance.table[opcode]; //lint !e727 static local symbol 'instance' of type 'const struct table_t' not explicitly initialized
}
private:
opcode table[52] =
{
{ KR1878_mov, "000001sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_cmp, "000010sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_add, "000100sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_sub, "000011sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_and, "000101sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_or, "000110sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_xor, "000111sssssddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_ddddd, 0x3e0}} },
{ KR1878_movl, "010ccccccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_Imm, 0x1fe0}} },
{ KR1878_cmpl, "011ccccccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_Imm, 0x1fe0}} },
{ KR1878_addl, "001100cccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_Imm, 0x3e0}} },
{ KR1878_subl, "001011cccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_Imm, 0x3e0}} },
{ KR1878_bic, "001010pccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_pImm, 0x3e0}} },
{ KR1878_bis, "001110pccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_pImm, 0x3e0}} },
{ KR1878_btg, "001111pccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_pImm, 0x3e0}} },
{ KR1878_btt, "001101pccccddddd", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_pImm, 0x3e0}} },
{ KR1878_swap, "00000000001ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_neg, "00000000010ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_not, "00000000011ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_shl, "00000000100ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_shr, "00000000101ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_shra, "00000000110ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_rlc, "00000000111ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_rrc, "00000001000ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_adc, "00000001001ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_sbc, "00000001010ddddd", {{&kr1878_t::D_ddddd, 0x1f}} },
{ KR1878_ldr, "00100ccccccccnnn", {{&kr1878_t::S_SR, 0x07}, {&kr1878_t::D_Imm, 0x07f8}} },
{ KR1878_mtpr, "00000010nnnsssss", {{&kr1878_t::S_ddddd, 0x1f}, {&kr1878_t::D_SR, 0xe0 }} },
{ KR1878_mfpr, "00000011nnnddddd", {{&kr1878_t::S_SR, 0xe0}, {&kr1878_t::D_ddddd, 0x1f }} },
{ KR1878_push, "0000000000010nnn", {{&kr1878_t::D_SR, 0x07}} },
{ KR1878_pop, "0000000000011nnn", {{&kr1878_t::D_SR, 0x07}} },
{ KR1878_sst, "000000011000bbbb", {{&kr1878_t::D_Imm, 0x0f}} },
{ KR1878_cst, "000000011100bbbb", {{&kr1878_t::D_Imm, 0x0f}} },
{ KR1878_tof, "0000000000000100" },
{ KR1878_tdc, "0000000000000101" },
{ KR1878_jmp, "100000aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_jsr, "100100aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_jnz, "101100aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_jz, "101000aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_jns, "110000aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_js, "110100aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_jnc, "111000aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_jc, "111100aaaaaaaaaa", {{&kr1878_t::D_EA, 0x3ff}} },
{ KR1878_ijmp, "0000000000000011" },
{ KR1878_ijsr, "0000000000000111" },
{ KR1878_rts, "0000000000001100" },
{ KR1878_rtsc, "000000000000111c", {{&kr1878_t::D_Imm, 0x01}} },
{ KR1878_rti, "0000000000001101" },
{ KR1878_nop, "0000000000000000" },
{ KR1878_wait, "0000000000000001" },
{ KR1878_stop, "0000000000001000" },
{ KR1878_reset, "0000000000000010" },
{ KR1878_sksp, "0000000000000110" },
};
table_t()
{
make_masks();
}
~table_t() = default;
table_t(const table_t&) = delete;
table_t &operator=(const table_t&) = delete;
void make_masks(void)
{
for ( int i = 0; i < qnumber(table); i++ )
{
int bmax = strlen(table[i].recog);
for ( int b = 0; b < bmax; b++ )
{
table[i].value <<= 1;
table[i].mask <<= 1;
if ( table[i].recog[b] == '1' || table[i].recog[b] == '0' )
table[i].mask++;
if ( table[i].recog[b] == '1' )
table[i].value++;
}
for ( int j = 0; j < FUNCS_COUNT; j++ )
{
if ( table[i].funcs[j].func != NULL )
{
for ( int b = 0; b < 16; b++ )
{
if ( table[i].funcs[j].mask & (1 << b) )
break;
else
table[i].funcs[j].shift++;
}
}
}
}
}
};
//----------------------------------------------------------------------
void init_analyzer(void)
{
// TODO make_masks();
}
//----------------------------------------------------------------------
bool kr1878_t::use_table(const insn_t &insn, uint32 code, int entry, int start, int end) //lint !e1762 could be made const
{
const opcode &ptr = table_t::get(entry);
for ( int j = start; j <= end; j++ )
{
if ( ptr.funcs[j].func == NULL )
break;
int value = (code & ptr.funcs[j].mask) >> ptr.funcs[j].shift;
if ( !(this->*ptr.funcs[j].func)(insn, value) )
return false;
}
return true;
}
//----------------------------------------------------------------------
int kr1878_t::ana(insn_t *_insn)
{
insn_t &insn = *_insn;
uint code = ua_16bits(insn);
op = &insn.Op1;
int cnt = table_t::count();
for ( int i = 0; i < cnt; i++ )
{
const auto &te = table_t::get(i);
if ( (code & te.mask) == te.value )
{
insn.itype = te.itype;
insn.size = 1;
if ( !use_table(insn, code, i, 0, FUNCS_COUNT - 1) )
continue;
return insn.size;
}
}
return 0;
}
//--------------------------------------------------------------------------
void interr(const insn_t &insn, const char *module)
{
const char *name = NULL;
if ( insn.itype < KR1878_last )
name = Instructions[insn.itype].name;
warning("%a(%s): internal error in %s", insn.ea, name, module);
}

View File

@@ -0,0 +1,184 @@
#include "kr1878.hpp"
#include <frame.hpp>
#include <segregs.hpp>
//----------------------------------------------------------------------
ea_t calc_mem(const insn_t &insn, const op_t &x)
{
return to_ea(insn.cs, x.addr);
}
//------------------------------------------------------------------------
ea_t kr1878_t::calc_data_mem(const insn_t &insn, const op_t &x, ushort segreg) const
{
sel_t dpage = get_sreg(insn.ea, segreg);
if ( dpage == BADSEL )
return BADSEL;
return xmem + (((dpage & 0xFF) << 3) | (x.value));
}
//------------------------------------------------------------------------
inline bool is_stkreg(int r)
{
return r == DSP;
}
//------------------------------------------------------------------------
int idaapi is_sp_based(const insn_t &, const op_t &x)
{
return OP_SP_ADD | (x.phrase == DSP ? OP_SP_BASED : OP_FP_BASED);
}
//------------------------------------------------------------------------
static void process_immediate_number(const insn_t &insn, int n)
{
set_immd(insn.ea);
if ( is_defarg(get_flags(insn.ea),n) )
return;
switch ( insn.itype )
{
case KR1878_movl:
case KR1878_cmpl: // Compare
case KR1878_addl: // Addition
case KR1878_subl: // Subtract
case KR1878_bic:
case KR1878_bis:
case KR1878_btg:
case KR1878_btt:
case KR1878_ldr:
case KR1878_sst:
case KR1878_cst:
op_num(insn.ea, n);
break;
}
}
//----------------------------------------------------------------------
void kr1878_t::add_near_ref(const insn_t &insn, const op_t &x, ea_t ea)
{
cref_t ftype = fl_JN;
if ( has_insn_feature(insn.itype, CF_CALL) )
{
if ( !func_does_return(ea) )
flow = false;
ftype = fl_CN;
}
insn.add_cref(ea, x.offb, ftype);
}
//----------------------------------------------------------------------
void kr1878_t::handle_operand(const insn_t &insn, const op_t &x, bool isAlt, bool isload)
{
switch ( x.type )
{
case o_reg:
break;
default:
// interr("emu");
break;
case o_imm:
if ( !isload )
interr(insn, "emu2");
process_immediate_number(insn, x.n);
if ( op_adds_xrefs(get_flags(insn.ea), x.n) )
insn.add_off_drefs(x, dr_O, OOFS_IFSIGN);
break;
case o_mem:
if ( !isAlt )
{
ea_t ea = calc_mem(insn, x);
insn.add_dref(ea, x.offb, isload ? dr_R : dr_W);
insn.create_op_data(ea, x);
}
break;
case o_phrase:
if ( !isAlt )
{
if ( x.reg != SR3 || x.value < 6 )
{
ea_t ea = calc_data_mem(insn, x, as + x.reg);
insn.add_dref(ea, x.offb, isload ? dr_R : dr_W);
insn.create_op_data(ea, x);
}
}
break;
case o_near:
if ( !isAlt )
add_near_ref(insn, x, calc_mem(insn, x));
break;
}
}
//----------------------------------------------------------------------
int kr1878_t::emu(const insn_t &insn)
{
if ( segtype(insn.ea) == SEG_XTRN )
return 1;
uint32 Feature = insn.get_canon_feature(ph);
bool flag1 = is_forced_operand(insn.ea, 0);
bool flag2 = is_forced_operand(insn.ea, 1);
bool flag3 = is_forced_operand(insn.ea, 2);
flow = ((Feature & CF_STOP) == 0);
if ( Feature & CF_USE1 ) handle_operand(insn, insn.Op1, flag1, true);
if ( Feature & CF_USE2 ) handle_operand(insn, insn.Op2, flag2, true);
if ( Feature & CF_USE3 ) handle_operand(insn, insn.Op3, flag3, true);
if ( Feature & CF_CHG1 ) handle_operand(insn, insn.Op1, flag1, false);
if ( Feature & CF_CHG2 ) handle_operand(insn, insn.Op2, flag2, false);
if ( Feature & CF_CHG3 ) handle_operand(insn, insn.Op3, flag3, false);
// check for Segment changes
if ( insn.itype == KR1878_ldr
&& insn.Op1.type == o_reg
&& insn.Op1.reg < SR4 )
{
split_sreg_range(get_item_end(insn.ea), as + insn.Op1.reg, insn.Op2.value & 0xFF, SR_auto);
}
//
// Determine if the next instruction should be executed
//
if ( Feature & CF_STOP )
flow = false;
if ( flow )
add_cref(insn.ea, insn.ea+insn.size, fl_F);
return 1;
}
//----------------------------------------------------------------------
int may_be_func(const insn_t &) // can a function start here?
{
return 0;
}
//----------------------------------------------------------------------
int is_sane_insn(const insn_t &insn, int /*nocrefs*/)
{
// disallow jumps to nowhere
if ( insn.Op1.type == o_near && !is_mapped(calc_mem(insn, insn.Op1)) )
return 0;
return 1;
}
//----------------------------------------------------------------------
int idaapi is_align_insn(ea_t ea)
{
insn_t insn;
if ( decode_insn(&insn, ea) < 1 )
return 0;
switch ( insn.itype )
{
case KR1878_nop:
break;
default:
return 0;
}
return insn.size;
}

View File

@@ -0,0 +1,69 @@
#include "kr1878.hpp"
const instruc_t Instructions[] =
{
{ "", 0 }, // Unknown Operation
{ "mov", CF_USE2|CF_CHG1 },
{ "cmp", CF_USE1|CF_USE2 }, // Compare
{ "add", CF_USE1|CF_USE2|CF_CHG1 }, // Addition
{ "sub", CF_USE1|CF_USE2|CF_CHG1 }, // Subtract
{ "and", CF_USE1|CF_USE2|CF_CHG1 }, // Logical AND
{ "or", CF_USE1|CF_USE2|CF_CHG1 }, // Logical Inclusive OR
{ "xor", CF_USE1|CF_USE2|CF_CHG1 }, // Logical Exclusive OR
{ "movl", CF_USE2|CF_CHG1 },
{ "cmpl", CF_USE1|CF_USE2 }, // Compare
{ "addl", CF_USE1|CF_USE2|CF_CHG1 }, // Addition
{ "subl", CF_USE1|CF_USE2|CF_CHG1 }, // Subtract
{ "bic", CF_USE1|CF_USE2|CF_CHG1 },
{ "bis", CF_USE1|CF_USE2|CF_CHG1 },
{ "btg", CF_USE1|CF_USE2|CF_CHG1 },
{ "btt", CF_USE1|CF_USE2|CF_CHG1 },
{ "swap", CF_USE1|CF_CHG1 },
{ "neg", CF_USE1|CF_CHG1 },
{ "not", CF_USE1|CF_CHG1 },
{ "shl", CF_USE1|CF_CHG1 }, // Shift Left
{ "shr", CF_USE1|CF_CHG1 }, // Shift Right
{ "shra", CF_USE1|CF_CHG1 }, // Arithmetic Shift Right
{ "rlc", CF_USE1|CF_CHG1 }, // Rotate Left
{ "rrc", CF_USE1|CF_CHG1 }, // Rotate Right
{ "adc", CF_USE1|CF_CHG1 }, // Add with Carry
{ "sbc", CF_USE1|CF_CHG1 }, // Subtract with Carry
{ "ldr", CF_USE2|CF_CHG1 },
{ "mtpr", CF_USE2|CF_CHG1 },
{ "mfpr", CF_USE2|CF_CHG1 },
{ "push", CF_USE1 },
{ "pop", CF_CHG1 },
{ "sst", CF_USE1 },
{ "cst", CF_USE1 },
{ "tof", 0 },
{ "tdc", 0 },
{ "jmp", CF_USE1|CF_STOP|CF_JUMP }, // Jump
{ "jsr", CF_USE1|CF_CALL }, // Jump to Subroutine
{ "jnz", CF_USE1|CF_JUMP }, // Jump
{ "jz", CF_USE1|CF_JUMP }, // Jump
{ "jns", CF_USE1|CF_JUMP }, // Jump
{ "js", CF_USE1|CF_JUMP }, // Jump
{ "jnc", CF_USE1|CF_JUMP }, // Jump
{ "jc", CF_USE1|CF_JUMP }, // Jump
{ "ijmp", CF_STOP }, // Jump
{ "ijsr", CF_STOP }, // Jump to Subroutine
{ "rts", CF_STOP }, // Return from Subroutine
{ "rtsc", CF_USE1|CF_STOP }, // Return from Subroutine
{ "rti", CF_STOP }, // Return from Interrupt
{ "nop", 0 }, // No Operation
{ "wait", 0 },
{ "stop", 0 },
{ "reset", 0 },
{ "sksp", 0 },
};
CASSERT(qnumber(Instructions) == KR1878_last);

View File

@@ -0,0 +1,73 @@
#ifndef __INSTRS_HPP
#define __INSTRS_HPP
extern const instruc_t Instructions[];
enum nameNum
{
KR1878_null = 0, // Unknown Operation
KR1878_mov,
KR1878_cmp, // Compare
KR1878_add, // Addition
KR1878_sub, // Subtract
KR1878_and, // Logical AND
KR1878_or, // Logical Inclusive OR
KR1878_xor, // Logical Exclusive OR
KR1878_movl,
KR1878_cmpl, // Compare
KR1878_addl, // Addition
KR1878_subl, // Subtract
KR1878_bic,
KR1878_bis,
KR1878_btg,
KR1878_btt,
KR1878_swap,
KR1878_neg,
KR1878_not,
KR1878_shl, // Shift Left
KR1878_shr, // Shift Right
KR1878_shra, // Arithmetic Shift Right
KR1878_rlc, // Rotate Left
KR1878_rrc, // Rotate Right
KR1878_adc, // Add with Carry
KR1878_sbc, // Subtract with Carry
KR1878_ldr,
KR1878_mtpr,
KR1878_mfpr,
KR1878_push,
KR1878_pop,
KR1878_sst,
KR1878_cst,
KR1878_tof,
KR1878_tdc,
KR1878_jmp, // Jump
KR1878_jsr, // Jump to Subroutine
KR1878_jnz, // Jump
KR1878_jz, // Jump
KR1878_jns, // Jump
KR1878_js, // Jump
KR1878_jnc, // Jump
KR1878_jc, // Jump
KR1878_ijmp, // Jump
KR1878_ijsr, // Jump to Subroutine
KR1878_rts, // Return from Subroutine
KR1878_rtsc, // Return from Subroutine
KR1878_rti, // Return from Interrupt
KR1878_nop, // No Operation
KR1878_wait,
KR1878_stop,
KR1878_reset,
KR1878_sksp,
KR1878_last,
};
#endif

View File

@@ -0,0 +1,20 @@
; This file describes the standard addresses for Angstrem KR1878
.default kr1878
.kr1878
ST 0x00
PortA 0x01
PortB 0x02
TCtl 0x04
Twrk 0x05
PCtlA 0x19
PCtlB 0x1A
WDCtl 0x1D
EEPCtl 0x38
EEPLoA 0x39
EEPHiA 0x3A
EEPData 0x3F

View File

@@ -0,0 +1,122 @@
#ifndef _KR1878_HPP
#define _KR1878_HPP
#include "../idaidp.hpp"
#include "ins.hpp"
#include <diskio.hpp>
//------------------------------------------------------------------
#define amode specflag2 // addressing mode
#define amode_x 0x10 // X:
//------------------------------------------------------------------
#define UAS_GNU 0x0001 // GNU assembler
//------------------------------------------------------------------
enum RegNo
{
SR0,
SR1,
SR2,
SR3,
SR4,
SR5,
SR6,
SR7,
DSP,
ISP,
as,
bs,
cs,
ds,
vCS, vDS, // virtual registers for code and data segments
};
//------------------------------------------------------------------
struct addargs_t
{
ea_t ea;
int nargs;
op_t args[4][2];
};
//------------------------------------------------------------------
#define IDP_SIMPLIFY 0x0001 // simplify instructions
#define IDP_PSW_W 0x0002 // W-bit in PSW is set
extern ushort idpflags;
inline bool dosimple(void) { return (idpflags & IDP_SIMPLIFY) != 0; }
inline bool psw_w(void) { return (idpflags & IDP_PSW_W) != 0; }
ea_t calc_mem(const insn_t &insn, const op_t &x);
//------------------------------------------------------------------
void interr(const insn_t &insn, const char *module);
void idaapi kr1878_header(outctx_t &ctx);
void idaapi kr1878_segend(outctx_t &ctx, segment_t *seg);
int idaapi is_align_insn(ea_t ea);
int idaapi is_sp_based(const insn_t &insn, const op_t &x);
int is_jump_func(const func_t *pfn, ea_t *jump_target);
int is_sane_insn(const insn_t &insn, int nocrefs);
int may_be_func(const insn_t &insn); // can a function start here?
void init_analyzer(void);
//------------------------------------------------------------------
DECLARE_PROC_LISTENER(idb_listener_t, struct kr1878_t);
struct kr1878_t : public procmod_t
{
ioports_t ports;
qstring device;
netnode helper;
ea_t xmem = BADADDR;
op_t *op = nullptr; // current operand
bool flow = false;
idb_listener_t idb_listener = idb_listener_t(*this);
virtual ssize_t idaapi on_event(ssize_t msgid, va_list va) override;
const ioport_t *find_port(ea_t address);
void read_kr1878_cfg(void);
void set_device_name(const char *dev);
const char *set_idp_options(
const char *keyword,
int /*value_type*/,
const void * /*value*/,
bool /*idb_loaded*/);
ea_t calc_data_mem(const insn_t &insn, const op_t &x, ushort segreg) const;
void handle_operand(const insn_t &insn, const op_t &x, bool isAlt, bool isload);
void add_near_ref(const insn_t &insn, const op_t &x, ea_t ea);
int emu(const insn_t &insn);
void opreg(uint16 reg);
void make_o_mem(const insn_t &insn);
bool D_ddddd(const insn_t &, int value);
bool S_ddddd(const insn_t &insn, int value);
bool D_SR(const insn_t &, int value);
bool S_SR(const insn_t &insn, int value);
bool D_Imm(const insn_t &, int value);
bool D_pImm(const insn_t &insn, int value);
bool D_EA(const insn_t &insn, int value);
bool use_table(const insn_t &insn, uint32 code, int entry, int start, int end);
int ana(insn_t *_insn);
void kr1878_assumes(outctx_t &ctx);
void print_segment_register(outctx_t &ctx, int reg, sel_t value);
void kr1878_segstart(outctx_t &ctx, segment_t *Srange) const;
void kr1878_footer(outctx_t &ctx) const;
};
#endif // _KR1878_HPP

View File

@@ -0,0 +1,46 @@
PROC=kr1878
CONFIGS=kr1878.cfg
include ../module.mak
# MAKEDEP dependency list ------------------
$(F)ana$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)diskio.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)offset.hpp $(I)pro.h \
$(I)problems.hpp $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp ../idaidp.hpp ana.cpp ins.hpp \
kr1878.hpp
$(F)emu$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)diskio.hpp $(I)fpro.h $(I)frame.hpp \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)name.hpp $(I)netnode.hpp $(I)offset.hpp $(I)pro.h \
$(I)problems.hpp $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)ua.hpp $(I)xref.hpp ../idaidp.hpp \
emu.cpp ins.hpp kr1878.hpp
$(F)ins$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)diskio.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)offset.hpp $(I)pro.h \
$(I)problems.hpp $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp ../idaidp.hpp ins.cpp ins.hpp \
kr1878.hpp
$(F)out$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)diskio.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)offset.hpp $(I)pro.h \
$(I)problems.hpp $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)ua.hpp $(I)xref.hpp ../idaidp.hpp \
ins.hpp kr1878.hpp out.cpp
$(F)reg$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)diskio.hpp $(I)entry.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)name.hpp $(I)netnode.hpp $(I)offset.hpp $(I)pro.h \
$(I)problems.hpp $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)ua.hpp $(I)xref.hpp ../idaidp.hpp \
ins.hpp kr1878.hpp reg.cpp

View File

@@ -0,0 +1,243 @@
#include <ctype.h>
#include "kr1878.hpp"
#include <segregs.hpp>
//----------------------------------------------------------------------
class out_kr1878_t : public outctx_t
{
out_kr1878_t(void) = delete; // not used
kr1878_t &pm() { return *static_cast<kr1878_t *>(procmod); }
public:
void outreg(int r) { out_register(ph.reg_names[r]); }
bool out_port_address(ea_t addr);
void out_bad_address(ea_t addr);
void out_address(ea_t ea, const op_t &x);
void out_ip_rel(int displ);
bool out_operand(const op_t &x);
void out_insn(void);
};
CASSERT(sizeof(out_kr1878_t) == sizeof(outctx_t));
DECLARE_OUT_FUNCS_WITHOUT_OUTMNEM(out_kr1878_t)
//----------------------------------------------------------------------
bool out_kr1878_t::out_port_address(ea_t addr)
{
const ioport_t *port = pm().find_port(addr);
if ( port != NULL && !port->name.empty() )
{
out_line(port->name.c_str(), COLOR_IMPNAME);
return true;
}
return false;
}
//----------------------------------------------------------------------
void out_kr1878_t::out_bad_address(ea_t addr)
{
if ( !out_port_address(addr) )
{
out_tagon(COLOR_ERROR);
out_btoa(addr, 16);
out_tagoff(COLOR_ERROR);
remember_problem(PR_NONAME, insn.ea);
}
}
//----------------------------------------------------------------------
void out_kr1878_t::out_address(ea_t ea, const op_t &x)
{
segment_t *s = getseg(ea);
ea_t value = s != NULL ? ea - get_segm_base(s) : ea;
if ( !out_name_expr(x, ea, value) )
{
out_tagon(COLOR_ERROR);
out_printf("%a", ea);
out_tagoff(COLOR_ERROR);
remember_problem(PR_NONAME, insn.ea);
}
}
//----------------------------------------------------------------------
void out_kr1878_t::out_ip_rel(int displ)
{
out_printf(COLSTR("%s+", SCOLOR_SYMBOL) COLSTR("%d", SCOLOR_NUMBER),
ash.a_curip, displ);
}
//----------------------------------------------------------------------
bool out_kr1878_t::out_operand(const op_t & x)
{
ea_t ea;
if ( x.type == o_imm )
out_symbol('#');
char buf[MAXSTR];
switch ( x.type )
{
case o_void:
return 0;
case o_imm:
out_value(x, OOFS_IFSIGN|OOFW_IMM);
break;
case o_reg:
outreg(x.reg);
break;
case o_mem:
// no break;
case o_near:
{
ea = calc_mem(insn, x);
if ( ea == insn.ea+insn.size )
out_ip_rel(insn.size);
else if ( !out_name_expr(x, ea, x.addr) )
out_bad_address(x.addr);
}
break;
case o_phrase:
qsnprintf(buf, sizeof(buf), "%%%c%" FMT_EA "x", 'a' + x.reg, x.value);
ea = pm().calc_data_mem(insn, x, as + x.reg);
if ( ea != BADADDR && (x.reg != SR3 || x.value < 6) )
{
out_line(buf, COLOR_AUTOCMT);
out_symbol(' ');
out_address(ea, x);
}
else
{
out_line(buf, COLOR_REG);
}
break;
default:
interr(insn, "out");
break;
}
return 1;
}
//----------------------------------------------------------------------
void out_kr1878_t::out_insn(void)
{
out_mnemonic();
bool comma = out_one_operand(0);
if ( insn.Op2.type != o_void )
{
if ( comma )
out_symbol(',');
out_one_operand(1);
}
if ( insn.Op3.type != o_void )
{
out_symbol(',');
out_one_operand(2);
}
out_immchar_cmts();
flush_outbuf();
}
//--------------------------------------------------------------------------
void kr1878_t::print_segment_register(outctx_t &ctx, int reg, sel_t value)
{
if ( reg == ph.reg_data_sreg )
return;
if ( value != BADADDR )
{
char buf[MAX_NUMBUF];
btoa(buf, sizeof(buf), value);
ctx.gen_cmt_line("assume %s = %s", ph.reg_names[reg], buf);
}
else
{
ctx.gen_cmt_line("drop %s", ph.reg_names[reg]);
}
}
//--------------------------------------------------------------------------
// function to produce assume directives
void kr1878_t::kr1878_assumes(outctx_t &ctx)
{
ea_t ea = ctx.insn_ea;
segment_t *seg = getseg(ea);
if ( seg == NULL || (inf_get_outflags() & OFLG_GEN_ASSUME) == 0 )
return;
bool seg_started = (ea == seg->start_ea);
for ( int i = ph.reg_first_sreg; i <= ph.reg_last_sreg; ++i )
{
if ( i == ph.reg_code_sreg )
continue;
sreg_range_t sra;
if ( !get_sreg_range(&sra, ea, i) )
continue;
sel_t now = get_sreg(ea, i);
if ( seg_started || sra.start_ea == ea )
{
sreg_range_t prev_sra;
bool prev_exists = get_sreg_range(&prev_sra, ea - 1, i);
if ( seg_started || (prev_exists && get_sreg(prev_sra.start_ea, i) != now) )
print_segment_register(ctx, i, now);
}
}
}
//--------------------------------------------------------------------------
//lint -esym(1764, ctx) could be made const
//lint -esym(818, Srange) could be made const
void kr1878_t::kr1878_segstart(outctx_t &ctx, segment_t *Srange) const
{
if ( is_spec_segm(Srange->type) )
return;
qstring sclas;
get_segm_class(&sclas, Srange);
if ( sclas == "CODE" )
ctx.gen_printf(DEFAULT_INDENT, COLSTR(".text", SCOLOR_ASMDIR));
else if ( sclas == "DATA" )
ctx.gen_printf(DEFAULT_INDENT, COLSTR(".data", SCOLOR_ASMDIR));
if ( Srange->orgbase != 0 )
{
char buf[MAX_NUMBUF];
btoa(buf, sizeof(buf), Srange->orgbase);
ctx.gen_printf(DEFAULT_INDENT, COLSTR("%s %s", SCOLOR_ASMDIR), ash.origin, buf);
}
}
//--------------------------------------------------------------------------
void idaapi kr1878_segend(outctx_t &, segment_t *)
{
}
//--------------------------------------------------------------------------
void idaapi kr1878_header(outctx_t &ctx)
{
ctx.gen_header(GH_PRINT_ALL);
}
//--------------------------------------------------------------------------
void kr1878_t::kr1878_footer(outctx_t &ctx) const
{
qstring nbuf = get_colored_name(inf_get_start_ea());
const char *name = nbuf.c_str();
const char *end = ash.end;
if ( end == NULL )
ctx.gen_printf(DEFAULT_INDENT, COLSTR("%s end %s",SCOLOR_AUTOCMT), ash.cmnt, name);
else
ctx.gen_printf(DEFAULT_INDENT,
COLSTR("%s",SCOLOR_ASMDIR) " " COLSTR("%s %s",SCOLOR_AUTOCMT),
ash.end, ash.cmnt, name);
}

View File

@@ -0,0 +1,499 @@
#include <ctype.h>
#include "kr1878.hpp"
#include <diskio.hpp>
#include <entry.hpp>
#include <segregs.hpp>
//--------------------------------------------------------------------------
static const char *const register_names[] =
{
// data arithmetic logic unit
"SR0", "SR1", "SR2", "SR3",
"SR4", "SR5", "SR6", "SR7",
"DSP", "ISP",
"a", "b", "c", "d",
"cs","ds", // virtual registers for code and data segments
};
//--------------------------------------------------------------------------
static const uchar retcode_0[] = { 0x0c, 0x00 };
static const uchar retcode_1[] = { 0x0d, 0x00 };
static const bytes_t retcodes[] =
{
{ sizeof(retcode_0), retcode_0 },
{ sizeof(retcode_1), retcode_1 },
{ 0, NULL }
};
//--------------------------------------------------------------------------
struct interrupt_t
{
int offset;
const char *name; //lint !e958 padding is required to align members
};
static const interrupt_t ints[] =
{
{ 0x0000, "HRESET" }, // Hardware RESET
{ 0x0001, "WDOG" },
{ 0x0002, "STOVF" },
{ 0x0003, "TIMER" },
{ 0x0006, "PORTA" },
{ 0x0007, "PORTB" },
{ 0x000F, "EEPWr" },
};
//-----------------------------------------------------------------------
// Angstrem KR1878VE1 Assembler
//-----------------------------------------------------------------------
static const asm_t motasm =
{
ASH_HEXF4 // $34
|ASD_DECF0 // 34
|ASB_BINF2 // %01010
|ASO_OCTF1 // 0123
|AS_COLON
|AS_N2CHR
|AS_NCMAS
|AS_ONEDUP,
0,
"Angstrem KR1878VE1 Assembler",
0,
NULL, // header lines
"org", // org
"end", // end
";", // comment string
'"', // string delimiter
'\'', // char delimiter
"\"'", // special symbols in char and string constants
"dc", // ascii string directive
"dcb", // byte directive
"dc", // word directive
NULL, // double words
NULL, // qwords
NULL, // oword (16 bytes)
NULL, // float (4 bytes)
NULL, // double (8 bytes)
NULL, // tbyte (10/12 bytes)
NULL, // packed decimal real
"bs#s(c,) #d, #v", // arrays (#h,#d,#v,#s(...)
"ds %s", // uninited arrays
"equ", // equ
NULL, // 'seg' prefix (example: push seg seg001)
"*", // current IP (instruction pointer)
NULL, // func_header
NULL, // func_footer
"global", // "public" name keyword
NULL, // "weak" name keyword
"xref", // "extrn" name keyword
// .extern directive requires an explicit object size
NULL, // "comm" (communal variable)
NULL, // get_type_name
NULL, // "align" keyword
'(', ')', // lbrace, rbrace
"%", // mod
"&", // and
"|", // or
"^", // xor
"~", // not
"<<", // shl
">>", // shr
NULL, // sizeof
};
//-----------------------------------------------------------------------
// GNU ASM
//-----------------------------------------------------------------------
static const asm_t gas =
{
AS_ASCIIC
|ASH_HEXF4 // $34
|ASD_DECF0 // 34
|ASB_BINF3 // 0b01010
|ASO_OCTF1 // 0123
|AS_COLON
|AS_N2CHR
|AS_NCMAS
|AS_ONEDUP,
UAS_GNU,
"GNU-like hypothetical assembler",
0,
NULL, // header lines
".org", // org
NULL, // end
";", // comment string
'"', // string delimiter
'\'', // char delimiter
"\"'", // special symbols in char and string constants
".string", // ascii string directive
".byte", // byte directive
".short", // word directive
".long", // double words
NULL, // qwords
NULL, // oword (16 bytes)
NULL, // float (4 bytes)
NULL, // double (8 bytes)
NULL, // tbyte (10/12 bytes)
NULL, // packed decimal real
".ds.#s(b,w,l,d) #d, #v", // arrays (#h,#d,#v,#s(...)
".space %s", // uninited arrays
"=", // equ
NULL, // 'seg' prefix (example: push seg seg001)
".", // current IP (instruction pointer)
NULL, // func_header
NULL, // func_footer
".global", // "public" name keyword
NULL, // "weak" name keyword
".extern", // "extrn" name keyword
// .extern directive requires an explicit object size
".comm", // "comm" (communal variable)
NULL, // get_type_name
".align", // "align" keyword
'(', ')', // lbrace, rbrace
"mod", // mod
"and", // and
"or", // or
"xor", // xor
"not", // not
"shl", // shl
"shr", // shr
NULL, // sizeof
0, // flag2
NULL, // cmnt2
NULL, // low8
NULL, // high8
NULL, // low16
NULL, // high16
"#include \"%s\"", // a_include_fmt
};
static const asm_t *const asms[] = { &motasm, &gas, NULL };
//----------------------------------------------------------------------
static ea_t AdditionalSegment(int size, int offset, const char *name)
{
segment_t s;
s.start_ea = free_chunk(0x100000, size, 0xF);
s.end_ea = s.start_ea + size;
s.sel = allocate_selector((s.start_ea-offset) >> 4);
s.type = SEG_DATA;
add_segm_ex(&s, name, "DATA", ADDSEG_NOSREG|ADDSEG_OR_DIE);
return s.start_ea - offset;
}
inline ea_t get_start(const segment_t *s)
{
return s ? s->start_ea : BADADDR;
}
//--------------------------------------------------------------------------
const ioport_t *kr1878_t::find_port(ea_t address)
{
return find_ioport(ports, address);
}
void kr1878_t::read_kr1878_cfg(void)
{
read_ioports(&ports, &device, "kr1878.cfg");
for ( size_t i=0; i < ports.size(); i++ )
{
const ioport_t &p = ports[i];
ea_t ea = xmem + p.address;
const char *name = p.name.c_str();
ea_t nameea = get_name_ea(BADADDR, name);
if ( nameea != ea )
{
set_name(nameea, "");
if ( !set_name(ea, name, SN_NOCHECK|SN_NOWARN|SN_NODUMMY) )
set_cmt(ea, name, 0);
}
}
}
void kr1878_t::set_device_name(const char *dev)
{
if ( dev )
{
device = dev;
read_kr1878_cfg();
}
}
const char *kr1878_t::set_idp_options(
const char *keyword,
int /*value_type*/,
const void * /*value*/,
bool /*idb_loaded*/)
{
if ( keyword != NULL )
return IDPOPT_BADKEY;
if ( choose_ioport_device(&device, "kr1878.cfg") )
read_kr1878_cfg();
return IDPOPT_OK;
}
//-----------------------------------------------------------------------
// We always return "yes" because of the messy problem that
// there are additional operands with wrong operand number (always 1)
static bool idaapi can_have_type(const op_t &)
{
return true;
}
//--------------------------------------------------------------------------
ssize_t idaapi idb_listener_t::on_event(ssize_t code, va_list)
{
switch ( code )
{
case idb_event::savebase:
case idb_event::closebase:
pm.helper.supset(0, pm.device.c_str());
break;
}
return 0;
}
//----------------------------------------------------------------------
// This old-style callback only returns the processor module object.
static ssize_t idaapi notify(void *, int msgid, va_list)
{
if ( msgid == processor_t::ev_get_procmod )
return size_t(new kr1878_t);
return 0;
}
//--------------------------------------------------------------------------
ssize_t idaapi kr1878_t::on_event(ssize_t msgid, va_list va)
{
int code = 0;
switch ( msgid )
{
case processor_t::ev_init:
// __emit__(0xCC); // debugger trap
hook_event_listener(HT_IDB, &idb_listener, &LPH);
helper.create("$ kr1878");
init_analyzer();
inf_set_gen_tryblks(true);
break;
case processor_t::ev_term:
ports.clear();
unhook_event_listener(HT_IDB, &idb_listener);
break;
case processor_t::ev_newfile: // new file loaded
{
for ( int i=0; i < qnumber(ints); i++ )
{
ea_t ea = inf_get_min_ea() + ints[i].offset;
if ( !is_loaded(ea) )
continue;
add_entry(ea, ea, ints[i].name, true);
}
segment_t *s0 = get_first_seg();
if ( s0 != NULL )
{
segment_t *s1 = get_next_seg(s0->start_ea);
set_segm_name(s0, "CODE");
for ( int i = as; i <= vDS; i++ )
{
set_default_sreg_value(s0, i, BADSEL);
set_default_sreg_value(s1, i, BADSEL);
}
}
xmem = AdditionalSegment(0x100, 0, "MEM");
}
read_kr1878_cfg();
break;
case processor_t::ev_ending_undo:
case processor_t::ev_oldfile: // old file loaded
xmem = get_start(get_segm_by_name("MEM"));
if ( helper.supstr(&device, 0) > 0 )
set_device_name(device.c_str());
break;
case processor_t::ev_is_sane_insn:
{
const insn_t *insn = va_arg(va, insn_t *);
int nocrefs = va_arg(va, int);
return is_sane_insn(*insn, nocrefs) == 1 ? 1 : -1;
}
case processor_t::ev_may_be_func:
// can a function start here?
// arg: instruction
// returns: probability 0..100
{
const insn_t *insn = va_arg(va, insn_t *);
return may_be_func(*insn);
}
case processor_t::ev_out_header:
{
outctx_t *ctx = va_arg(va, outctx_t *);
kr1878_header(*ctx);
return 1;
}
case processor_t::ev_out_footer:
{
outctx_t *ctx = va_arg(va, outctx_t *);
kr1878_footer(*ctx);
return 1;
}
case processor_t::ev_out_segstart:
{
outctx_t *ctx = va_arg(va, outctx_t *);
segment_t *seg = va_arg(va, segment_t *);
kr1878_segstart(*ctx, seg);
return 1;
}
case processor_t::ev_out_segend:
{
outctx_t *ctx = va_arg(va, outctx_t *);
segment_t *seg = va_arg(va, segment_t *);
kr1878_segend(*ctx, seg);
return 1;
}
case processor_t::ev_out_assumes:
{
outctx_t *ctx = va_arg(va, outctx_t *);
kr1878_assumes(*ctx);
return 1;
}
case processor_t::ev_ana_insn:
{
insn_t *out = va_arg(va, insn_t *);
return ana(out);
}
case processor_t::ev_emu_insn:
{
const insn_t *insn = va_arg(va, const insn_t *);
return emu(*insn) ? 1 : -1;
}
case processor_t::ev_out_insn:
{
outctx_t *ctx = va_arg(va, outctx_t *);
out_insn(*ctx);
return 1;
}
case processor_t::ev_out_operand:
{
outctx_t *ctx = va_arg(va, outctx_t *);
const op_t *_op = va_arg(va, const op_t *);
return out_opnd(*ctx, *_op) ? 1 : -1;
}
case processor_t::ev_can_have_type:
{
const op_t *_op = va_arg(va, const op_t *);
return can_have_type(*_op) ? 1 : -1;
}
case processor_t::ev_is_sp_based:
{
int *mode = va_arg(va, int *);
const insn_t *insn = va_arg(va, const insn_t *);
const op_t *_op = va_arg(va, const op_t *);
*mode = is_sp_based(*insn, *_op);
return 1;
}
case processor_t::ev_set_idp_options:
{
const char *keyword = va_arg(va, const char *);
int value_type = va_arg(va, int);
const char *value = va_arg(va, const char *);
const char **errmsg = va_arg(va, const char **);
bool idb_loaded = va_argi(va, bool);
const char *ret = set_idp_options(keyword, value_type, value, idb_loaded);
if ( ret == IDPOPT_OK )
return 1;
if ( errmsg != NULL )
*errmsg = ret;
return -1;
}
case processor_t::ev_is_align_insn:
{
ea_t ea = va_arg(va, ea_t);
return is_align_insn(ea);
}
default:
break;
}
return code;
}
//-----------------------------------------------------------------------
#define FAMILY "Angstrem KR1878:"
static const char *const shnames[] = { "kr1878", NULL };
static const char *const lnames[] =
{
FAMILY"Angstrem KR1878",
NULL
};
//-----------------------------------------------------------------------
// Processor Definition
//-----------------------------------------------------------------------
processor_t LPH =
{
IDP_INTERFACE_VERSION, // version
PLFM_KR1878, // id
// flag
PRN_HEX // hex numbers
| PR_ALIGN // data items must be aligned
| PR_BINMEM // segmentation is done by the processor mode
| PR_SEGS, // has segment registers
// flag2
PR2_IDP_OPTS, // the module has processor-specific configuration options
16, // 16 bits in a byte for code segments
8, // 8 bits in a byte for other segments
shnames,
lnames,
asms,
notify,
register_names, // Register names
qnumber(register_names), // Number of registers
as, // first
vDS, // last
1, // size of a segment register
vCS, vDS,
NULL, // No known code start sequences
retcodes,
KR1878_null,
KR1878_last,
Instructions, // instruc
0, // int tbyte_size; -- doesn't exist
{ 0, 7, 15, 0 }, // char real_width[4];
// number of symbols after decimal point
// 2byte float (0-does not exist)
// normal float
// normal double
// long double
KR1878_rts, // Icode of return instruction. It is ok to give any of possible return instructions
};