/* * Interactive disassembler (IDA). * Zilog Z8 module * */ #include "z8.hpp" #include int data_id; //-------------------------------------------------------------------------- static const char *const RegNames[] = { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "RR0", "RR1", "RR2", "RR3", "RR4", "RR5", "RR6", "RR7", "RR8", "RR9", "RR10", "RR11", "RR12", "RR13", "RR14", "RR15", "cs", "ds", "rp", }; //---------------------------------------------------------------------- typedef struct { int off; const char *name; //lint !e958 padding is required to align members const char *cmt; } entry_t; static const entry_t entries[] = { { 0, "irq0", "DAV0, IRQ0, Comparator" }, { 2, "irq1", "DAV1, IRQ1" }, { 4, "irq2", "DAV2, IRQ2, TIN, Comparator" }, { 6, "irq3", "IRQ3, Serial in" }, { 8, "irq4", "T0, Serial out" }, { 10, "irq5", "T1" }, }; //---------------------------------------------------------------------- static ea_t AdditionalSegment(size_t size, size_t offset, const char *name, const char *sclass, uchar stype) { segment_t s; s.start_ea = free_chunk(0, size, -0xF); s.end_ea = s.start_ea + size; s.sel = allocate_selector((s.start_ea-offset) >> 4); s.type = stype; add_segm_ex(&s, name, sclass, ADDSEG_NOSREG|ADDSEG_OR_DIE); return s.start_ea - offset; } //---------------------------------------------------------------------- // special handling for areas bool z8_iohandler_t::area_processing(ea_t start, ea_t end, const char *name, const char *aclass) { if ( start >= end ) { warning("Error in definition of segment %s %s", aclass, name); return false; } if ( strcmp(aclass, "CODE") == 0 ) { AdditionalSegment(end-start, start, name, aclass, SEG_CODE); } else if ( strcmp(aclass, "DATA") == 0 ) { uchar type = stristr(name, "FSR") != NULL ? SEG_IMEM : SEG_DATA; AdditionalSegment(end-start, start, name, aclass, type); } else { return false; } return true; } //------------------------------------------------------------------ const char *z8_t::find_ioport(uval_t port) { const ioport_t *p = ::find_ioport(ioh.ports, port); return p ? p->name.c_str() : NULL; } //---------------------------------------------------------------------- static ea_t specialSeg(sel_t sel, bool make_imem = true) { segment_t *s = get_segm_by_sel(sel); if ( s != NULL ) { if ( make_imem && s->type != SEG_IMEM ) { s->type = SEG_IMEM; s->update(); } return s->start_ea; } return BADADDR; } //---------------------------------------------------------------------- void z8_t::setup_data_segment_pointers(void) { sel_t sel; if ( atos(&sel, "INTMEM") || atos(&sel, "RAM") ) intmem = specialSeg(sel); else intmem = BADADDR; } //---------------------------------------------------------------------- bool z8_t::select_device(int resp_info) { char cfgfile[QMAXFILE]; ioh.get_cfg_filename(cfgfile, sizeof(cfgfile)); if ( !choose_ioport_device(&ioh.device, cfgfile) ) { ioh.device = NONEPROC; return false; } if ( !ioh.display_infotype_dialog(IORESP_ALL, &resp_info, cfgfile) ) return false; ioh.set_device_name(ioh.device.c_str(), resp_info & ~IORESP_PORT); setup_data_segment_pointers(); if ( (resp_info & IORESP_PORT) != 0 ) { if ( intmem == BADADDR ) { AdditionalSegment(0x1000, 0, "INTMEM", NULL, SEG_IMEM); setup_data_segment_pointers(); } for ( int i=0; i < ioh.ports.size(); i++ ) { const ioport_t &p = ioh.ports[i]; ea_t ea = p.address + intmem; ea_t oldea = get_name_ea(BADADDR, p.name.c_str()); if ( oldea != ea ) { if ( oldea != BADADDR ) set_name(oldea, NULL); del_items(ea, DELIT_EXPAND); set_name(ea, p.name.c_str(), SN_NODUMMY); } if ( !p.cmt.empty() ) set_cmt(ea, p.cmt.c_str(), true); } } return true; } //-------------------------------------------------------------------------- const char *z8_t::set_idp_options( const char *keyword, int /*value_type*/, const void * /*value*/, bool /*idb_loaded*/) { if ( keyword != NULL ) return IDPOPT_BADKEY; select_device(IORESP_PORT|IORESP_INT); return IDPOPT_OK; } //---------------------------------------------------------------------- void z8_t::load_from_idb() { ioh.restore_device(); } //---------------------------------------------------------------------- // 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(SET_MODULE_DATA(z8_t)); return 0; } //-------------------------------------------------------------------------- ssize_t idaapi z8_t::on_event(ssize_t msgid, va_list va) { int code = 0; switch ( msgid ) { case processor_t::ev_init: helper.create(PROCMOD_NODE_NAME); inf_set_be(true); // MSB first break; case processor_t::ev_term: ioh.ports.clear(); clr_module_data(data_id); break; case processor_t::ev_newfile: { segment_t *sptr = get_first_seg(); if ( sptr != NULL ) { if ( sptr->start_ea - get_segm_base(sptr) == 0 ) { inf_set_start_ea(sptr->start_ea + 0xC); inf_set_start_ip(0xC); if ( !inf_like_binary() ) { // set default entries for ( int i = 0; i < qnumber(entries); i++ ) { ea_t ea = sptr->start_ea + entries[i].off; if ( is_mapped(ea) ) { create_word(ea, 2); op_plain_offset(ea, 0, sptr->start_ea); ea_t ea1 = sptr->start_ea + get_word(ea); auto_make_proc(ea1); set_name(ea, entries[i].name, SN_NODUMMY); set_cmt(sptr->start_ea+get_word(ea), entries[i].cmt, 1); } } } } set_segm_class(sptr, "CODE"); } select_device(IORESP_ALL); if ( intmem == BADADDR ) { AdditionalSegment(0x1000, 0, "INTMEM", NULL, SEG_IMEM); setup_data_segment_pointers(); } } break; case processor_t::ev_oldfile: load_from_idb(); setup_data_segment_pointers(); break; case processor_t::ev_creating_segm: { // default DS is equal to CS segment_t *sptr = va_arg(va, segment_t *); sptr->defsr[rVds-ph.reg_first_sreg] = sptr->sel; } break; case processor_t::ev_out_header: { outctx_t *ctx = va_arg(va, outctx_t *); z8_header(*ctx); return 1; } case processor_t::ev_out_footer: { outctx_t *ctx = va_arg(va, outctx_t *); z8_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 *); z8_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 *); z8_segend(*ctx, seg); return 1; } case processor_t::ev_out_assumes: { outctx_t *ctx = va_arg(va, outctx_t *); z8_assumes(*ctx); return 1; } case processor_t::ev_ana_insn: { insn_t *out = va_arg(va, insn_t *); return z8_ana(out); } case processor_t::ev_emu_insn: { const insn_t *insn = va_arg(va, const insn_t *); return z8_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_out_data: { outctx_t *ctx = va_arg(va, outctx_t *); bool analyze_only = va_argi(va, bool); z8_data(*ctx, analyze_only); 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; } default: break; } return code; } //-------------------------------------------------------------------------- static const asm_t Z8asm = { AS_COLON, 0, "Zilog Z8 assembler", 0, NULL, ".org", ".end", ";", // comment string '\'', // string delimiter '\0', // char delimiter (no char consts) "\\\"'", // special symbols in char and string constants ".ascii", // ascii string directive ".byte", // byte directive ".word", // word directive NULL, // dword (4 bytes) NULL, // qword (8 bytes) NULL, // oword (16 bytes) NULL, // float (4 bytes) NULL, // double (8 bytes) NULL, // tbyte (10/12 bytes) NULL, // packed decimal real NULL, // arrays (#h,#d,#v,#s(...) ".block %s", // uninited arrays ".equ", // Equ NULL, // seg prefix "$", NULL, // func_header NULL, // func_footer NULL, // public NULL, // weak NULL, // extrn NULL, // comm NULL, // get_type_name NULL, // align '(', ')', // lbrace, rbrace NULL, // mod NULL, // and NULL, // or NULL, // xor NULL, // not NULL, // shl NULL, // shr NULL, // sizeof }; static const asm_t *const asms[] = { &Z8asm, NULL }; //-------------------------------------------------------------------------- #define FAMILY "Zilog Z8 series:" static const char *const shnames[] = { "Z8", NULL }; static const char *const lnames[] = { FAMILY"Zilog Z8 MCU", NULL }; //-------------------------------------------------------------------------- static const uchar retcode[] = { 0xAF }; // ret static const uchar iretcode[] = { 0xBF }; // iret static const bytes_t retcodes[] = { { sizeof(retcode), retcode }, { sizeof(iretcode), iretcode }, { 0, NULL } }; //----------------------------------------------------------------------- // Processor Definition //----------------------------------------------------------------------- processor_t LPH = { IDP_INTERFACE_VERSION, // version PLFM_Z8, // id // flag PRN_HEX | PR_RNAMESOK // can use register names for byte names | PR_SEGTRANS // segment translation is supported (map_code_ea) | PR_BINMEM // The module creates RAM/ROM segments for binary files // (the kernel shouldn't ask the user about their sizes and addresses) | PR_SEGS // has segment registers? | PR_SGROTHER, // the segment registers don't contain // the segment selectors, something else // flag2 PR2_IDP_OPTS, // the module has processor-specific configuration options 8, // 8 bits in a byte for code segments 8, // 8 bits in a byte for other segments shnames, // short processor names (null term) lnames, // long processor names (null term) asms, // array of enabled assemblers notify, // Various messages: RegNames, // Register names qnumber(RegNames), // Number of registers rVcs,rRp, 1, // size of a segment register rVcs,rVds, NULL, // No known code start sequences retcodes, 0, Z8_last, Instructions, // instruc 0, // int tbyte_size; -- doesn't exist { 0, 0, 0, 0 }, // char real_width[4]; // number of symbols after decimal point // 2byte float (0-does not exist) // normal float // normal double // long double Z8_ret, // Icode of return instruction. It is ok to give any of possible return instructions };