/* * Interactive disassembler (IDA). * Copyright (c) 1990-98 by Ilfak Guilfanov. * ALL RIGHTS RESERVED. * E-mail: ig@estar.msk.su, ig@datarescue.com * FIDO: 2:50620/209 * */ #include "i51.hpp" #include #include int data_id; //-------------------------------------------------------------------------- static const char *const RegNames[] = { "A", "AB", "B", "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "WR0", "WR2", "WR4", "WR6", "WR8", "WR10", "WR12", "WR14", "WR16", "WR18", "WR20", "WR22", "WR24", "WR26", "WR28", "WR30", "DR0", "DR4", "DR8", "DR12", "DR16", "DR20", "DR24", "DR28", "DR32", "DR36", "DR40", "DR44", "DR48", "DR52", "DPX", "SPX", "DPTR","C", "PC", "EPTR", "PR0", "PR1", "cs","ds" }; //---------------------------------------------------------------------- static const char cfgname[] = "i51.cfg"; void i51_iohandler_t::get_cfg_filename(char *buf, size_t bufsize) { qstrncpy(buf, cfgname, bufsize); } void i51_iohandler_t::apply_io_port(ea_t ea, const char *name, const char *cmt) { if ( ea >= 0x80 && ea < 0x100 ) { // specail mapping alg for i51 FSR regs segment_t *s = get_segm_by_name("FSR"); if ( s != NULL ) { ea_t map = ea + s->start_ea - 0x80; if ( is_mapped(map) ) ea = map; } } set_name(ea, name, SN_NODUMMY); set_cmt(ea, cmt, true); } bool i51_iohandler_t::segment_created(ea_t start, ea_t end, const char *word, const char *) { if ( stristr(word, "FSR") != NULL || stristr(word, "RAM") != NULL ) { pm.AdditionalSegment(end-start, start, word); return true; } return false; } //------------------------------------------------------------------ const char *i51_t::set_idp_options( const char *keyword, int /*value_type*/, const void * /*value*/, bool /*idb_loaded*/) { if ( keyword != NULL ) return IDPOPT_BADKEY; iohandler_t::parse_area_line0_t cb(ioh); if ( choose_ioport_device2(&ioh.device, cfgname, &cb) ) ioh.set_device_name(ioh.device.c_str(), IORESP_PORT|IORESP_INT); return IDPOPT_OK; } //------------------------------------------------------------------ const ioport_bit_t *i51_t::find_bit(ea_t address, int bit) { return find_ioport_bit(ioh.ports, address, bit); } //---------------------------------------------------------------------- bool i51_t::IsPredefined(const char *name) { for ( int i=0; i < ioh.ports.size(); i++ ) { const ioport_t &p = ioh.ports[i]; if ( p.name == name ) return true; for ( int j=0; j < p.bits.size(); j++ ) if ( p.bits[j].name == name ) return true; } return false; } //---------------------------------------------------------------------- struct entry_t { char proc; char off; const char *name; //lint !e958 padding is required to align members const char *cmt; }; static const entry_t entries[] = { { prc_51, 0x03, "extint0", "External interrupt 0 (INT0 / EX0)" }, { prc_51, 0x0B, "timint0", "Timer interrupt 0 (TIM0)" }, { prc_51, 0x13, "extint1", "External interrupt 1 (INT1 / EX1)" }, { prc_51, 0x1B, "timint1", "Timer interrupt 1 (TIM1)" }, { prc_51, 0x23, "serint", "Serial port interrupt (SERIAL)" }, { prc_51, 0x2B, "timint2", "Timer interrupt 2 (TIM2) (52 or higher)" }, { prc_51, 0x33, "pcaint", "PCA (programmable counter array) interrupt\n(only 51f or higher)" }, { prc_930, 0x43, "usbhub", "USB Hub/SOF (isochronous end point) (only 930)" }, { prc_930, 0x4B, "usbfun", "USB Function (non-isochronous end point) (only 930)" }, { prc_930, 0x53, "usbglb", "USB Global Suspend/Resume and USB Reset (only 930)" }, { prc_251, 0x7B, "trapint", "TRAP (program interrupt) (only 251 or 930)" } }; //---------------------------------------------------------------------- // Get linear address of a special segment // sel - selector of the segment static ea_t specialSeg(segment_t *s) { if ( s->type != SEG_IMEM ) // is the segment type correct? - no { s->type = SEG_IMEM; // fix it s->update(); } return s->start_ea; } //---------------------------------------------------------------------- ea_t i51_t::AdditionalSegment(size_t size, size_t offset, const char *name) const { segment_t s; s.start_ea = (ptype > prc_51) ? (inf_get_max_ea() + 0xF) & ~0xF : free_chunk(0, size, 0xF); s.end_ea = s.start_ea + size; s.sel = allocate_selector((s.start_ea-offset) >> 4); s.type = SEG_IMEM; // internal memory add_segm_ex(&s, name, NULL, ADDSEG_NOSREG|ADDSEG_OR_DIE); return s.start_ea - offset; } //---------------------------------------------------------------------- void i51_t::setup_data_segment_pointers(void) { segment_t *s = get_segm_by_name("INTMEM"); if ( s == NULL ) s = get_segm_by_name("RAM"); if ( s != NULL ) intmem = specialSeg(s); s = get_segm_by_name("SFR"); if ( s == NULL ) s = get_segm_by_name("FSR"); if ( s != NULL ) sfrmem = specialSeg(s) - 0x80; } //-------------------------------------------------------------------------- void i51_t::load_from_idb() { ioh.restore_device(); // restore ptype ptype = processor_subtype_t(ph.get_proc_index()); setup_data_segment_pointers(); } //-------------------------------------------------------------------------- ssize_t idaapi idb_listener_t::on_event(ssize_t code, va_list) { switch ( code ) { case idb_event::segm_moved: // A segment is moved // Fix processor dependent address sensitive information { // ea_t from = va_arg(va, ea_t); // ea_t to = va_arg(va, ea_t); // asize_t size = va_arg(va, asize_t); // bool changed_netmap = va_argi(va, bool); // Add commands to adjust your internal variables here // Most of the time this callback will be empty // // If you keep information in a netnode's altval array, you can use // node.altshift(from, s->start_ea, s->end_ea - s->start_ea); // // If you have a variables pointing to somewhere in the disassembled program memory, // you can adjust it like this: // // if ( var >= from && var < from+size ) // var += to - from; } 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(SET_MODULE_DATA(i51_t)); return 0; } ssize_t idaapi i51_t::on_event(ssize_t msgid, va_list va) { int code = 0; switch ( msgid ) { case processor_t::ev_init: hook_event_listener(HT_IDB, &idb_listener, &LPH); helper.create(PROCMOD_NODE_NAME); inf_set_be(true); // Set a big endian mode of the IDA kernel break; case processor_t::ev_term: ioh.ports.clear(); unhook_event_listener(HT_IDB, &idb_listener); 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); inf_set_start_ip(0); for ( int i=0; i < qnumber(entries); i++ ) { if ( entries[i].proc > ptype ) continue; ea_t ea = inf_get_start_ea()+entries[i].off; if ( is_mapped(ea) && get_byte(ea) != 0xFF ) { add_entry(ea, ea, entries[i].name, 1); set_cmt(ea, entries[i].cmt, 1); } } } } segment_t *scode = get_first_seg(); set_segm_class(scode, "CODE"); if ( ptype > prc_51 ) { AdditionalSegment(0x10000-256-128, 256+128, "RAM"); if ( scode != NULL ) { ea_t align = (scode->end_ea + 0xFFF) & ~0xFFF; if ( getseg(align-7) == scode ) // the code segment size is { // multiple of 4K or near it uchar b0 = get_byte(align-8); // 251: // 0 : 1-source, 0-binary mode // 6,7: must be 1s // 82930: // 0 : 1-source, 0-binary mode // 7 : must be 1s // uchar b1 = get_byte(align-7); // 251 // 0: eprommap 0 - FE2000..FE4000 is mapped into 00E000..100000 // 1 - .............. is not mapped ............... // 1: must be 1 // 3: // 2: must be 1 // 4: intr 1 - upon interrupt PC,PSW are pushed into stack // 0 - upon interrupt only PC is pushed into stack // 5: must be 1 // 6: must be 1 // 7: must be 1 // 82930: // 3: must be 1 // 5: must be 1 // 6: must be 1 // 7: must be 1 // msg("b0=%x b1=%x\n", b0, b1); // if ( (b0 & 0x80) == 0x80 && (b1 & 0xEA) == 0xEA ) { // the init bits are correct char pname[IDAINFO_PROCNAME_SIZE]; inf_get_procname(pname, sizeof(pname)); char ntype = (b0 & 1) ? 's' : 'b'; char *ptr = tail(pname)-1; if ( ntype != *ptr && ask_yn(ASKBTN_YES, "HIDECANCEL\n" "The input file seems to be for the %s mode of the processor.\n" "Do you want to change the current processor type?", ntype == 's' ? "source" : "binary") > 0 ) { *ptr = ntype; first_time = true; set_processor_type(pname, SETPROC_USER); } } } } } // the default data segment will be INTMEM { segment_t *s = getseg(intmem); if ( s != NULL ) set_default_dataseg(s->sel); } iohandler_t::parse_area_line0_t cb(ioh); if ( choose_ioport_device2(&ioh.device, cfgname, &cb) ) ioh.set_device_name(ioh.device.c_str(), IORESP_ALL); if ( get_segm_by_name("RAM") == NULL ) AdditionalSegment(256, 0, "RAM"); if ( get_segm_by_name("FSR") == NULL ) AdditionalSegment(128, 128, "FSR"); setup_data_segment_pointers(); } break; case processor_t::ev_ending_undo: case processor_t::ev_oldfile: load_from_idb(); break; case processor_t::ev_creating_segm: // make the default DS point to INTMEM // (8051 specific issue) { segment_t *newseg = va_arg(va, segment_t *); segment_t *intseg = getseg(intmem); if ( intseg != NULL ) newseg->defsr[rVds-ph.reg_first_sreg] = intseg->sel; } break; case processor_t::ev_newprc: { processor_subtype_t prcnum = processor_subtype_t(va_arg(va, int)); // bool keep_cfg = va_argi(va, bool); if ( !first_time && prcnum != ptype ) { warning("Sorry, it is not possible to change" // (this is 8051 specific) " the processor mode on the fly." " Please reload the input file" " if you want to change the processor."); code = -1; break; } first_time = false; ptype = prcnum; } break; case processor_t::ev_newasm: // new assembler type ioh.restore_device(); break; case processor_t::ev_is_sane_insn: // is the instruction sane for the current file type? // arg: int no_crefs // 1: the instruction has no code refs to it. // ida just tries to convert unexplored bytes // to an instruction (but there is no other // reason to convert them into an instruction) // -1: the instruction is created because // of some coderef, user request or another // weighty reason. // The instruction is in 'cmd' // returns: 1-ok, <=0-no, the instruction isn't // likely to appear in the program { const insn_t *insn = va_arg(va, insn_t *); int reason = va_arg(va, int); return is_sane_insn(*insn, reason) == 1 ? 1 : -1; } case processor_t::ev_out_header: { outctx_t *ctx = va_arg(va, outctx_t *); i51_header(*ctx); return 1; } case processor_t::ev_out_footer: { outctx_t *ctx = va_arg(va, outctx_t *); i51_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 *); i51_segstart(*ctx, seg); 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_out_data: { outctx_t *ctx = va_arg(va, outctx_t *); bool analyze_only = va_argi(va, bool); i51_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; } //----------------------------------------------------------------------- // ASMI //----------------------------------------------------------------------- static const asm_t asmi = { AS_COLON | ASH_HEXF3 | AS_1TEXT | AS_NCHRE | ASO_OCTF1 | AS_RELSUP, UAS_PSAM | UAS_NOSEG | UAS_AUBIT | UAS_PBIT | UAS_NOENS, "ASMI", 0, NULL, // no headers ".equ $, ", ".end", ";", // comment string '"', // string delimiter '\'', // char delimiter "\\\"'", // special symbols in char and string constants ".text", // 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(...) ".byte 0xFF;(array %s)", // uninited arrays ".equ", // equ NULL, // seg prefix "$", // curip NULL, // func_header NULL, // func_footer NULL, // public NULL, // weak NULL, // extrn NULL, // comm NULL, // get_type_name NULL, // align '(', ')', // lbrace, rbrace "%", // mod "&", // and "|", // or "^", // xor "!", // not "<<", // shl ">>", // shr NULL, // sizeof }; //----------------------------------------------------------------------- // 8051 Macro Assembler - Version 4.02a // Copyright (C) 1985 by 2500 A.D. Software, Inc. //----------------------------------------------------------------------- static const asm_t adasm = { AS_COLON | ASH_HEXF0, UAS_PBIT | UAS_SECT, "8051 Macro Assembler by 2500 A.D. Software", 0, NULL, // no headers "org", "end", ";", // comment string '"', // string delimiter '\'', // char delimiter "\\\"'", // special symbols in char and string constants "db", // ascii string directive "db", // byte directive "dw", // word directive "long", // 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(...) "ds %s", // uninited arrays "reg", // equ NULL, // seg prefix "$", // curip 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 0, // flag2 NULL, // close comment COLSTR("<", SCOLOR_SYMBOL) "%s", // low8 COLSTR(">", SCOLOR_SYMBOL) "%s", // high8 NULL, // low16 NULL, // high16 }; //----------------------------------------------------------------------- // PseudoSam //----------------------------------------------------------------------- static const char *const ps_headers[] = { ".code", NULL }; static const asm_t pseudosam = { AS_COLON | ASH_HEXF1 | AS_N2CHR, UAS_PBIT | UAS_PSAM | UAS_SELSG, "PseudoSam by PseudoCode", 0, ps_headers, ".org", ".end", ";", // comment string '"', // string delimiter '\'', // char delimiter "\\\"'", // special symbols in char and string constants ".db", // ascii string directive ".db", // byte directive ".dw", // 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(...) ".rs %s", // uninited arrays ".equ", // equ NULL, // seg prefix "$", // curip 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 }; //----------------------------------------------------------------------- // Cross-16 assembler definiton //----------------------------------------------------------------------- static const char *const cross16_headers[] = { "cpu \"8051.tbl\"", NULL }; static const asm_t cross16 = { AS_COLON | ASH_HEXF0 | AS_NHIAS, UAS_PBIT | UAS_NOSEG | UAS_NOBIT | UAS_EQCLN, "Cross-16 by Universal Cross-Assemblers", 0, cross16_headers, "org", "end", ";", // comment string '"', // string delimiter '\0', // char delimiter (no char consts) "\\\"'", // special symbols in char and string constants "dfb", // ascii string directive "dfb", // byte directive "dwm", // 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(...) "dfs %s", // uninited arrays "equ", // Equ NULL, // seg prefix "$", // curip 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 }; //----------------------------------------------------------------------- // 8051 Cross-Assembler by MetaLink Corporation //----------------------------------------------------------------------- static const asm_t mcross = { AS_COLON | ASH_HEXF0 | AS_NHIAS, UAS_NOSEG | UAS_CDSEG | UAS_AUBIT | UAS_NODS | UAS_NOENS, "8051 Cross-Assembler by MetaLink Corporation", 0, NULL, "org", "end", ";", // comment string '\'', // string delimiter '\0', // char delimiter (no char consts) "\\\"'", // special symbols in char and string constants "db", // ascii string directive "db", // byte directive "dw", // 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(...) "ds %s", // uninited arrays "equ", // Equ NULL, // seg prefix "$", // curip 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 }; //----------------------------------------------------------------------- // TASM assembler definiton //----------------------------------------------------------------------- static const char *const tasm_headers[] = { ".msfirst", NULL }; static const asm_t tasm = { AS_COLON | AS_N2CHR | AS_1TEXT, UAS_PBIT | UAS_NOENS | UAS_EQCLN | UAS_NOSEG, "Table Driven Assembler (TASM) by Speech Technology Inc.", 0, tasm_headers, ".org", ".end", ";", // comment string '"', // string delimiter '\'', // char delimiter "\\\"'", // special symbols in char and string constants ".text", // ascii string directive ".db", // byte directive ".dw", // 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", NULL, // seg prefix "$", // curip NULL, // func_header NULL, // func_footer NULL, // public NULL, // weak NULL, // extrn NULL, // comm NULL, // get_type_name NULL, // align '(', ')', // lbrace, rbrace NULL, // mod "and", // and "or", // or NULL, // xor "not", // not NULL, // shl NULL, // shr NULL, // sizeof }; static const asm_t *const asms[] = { &asmi, &adasm, &pseudosam, &cross16, &mcross, &tasm, NULL }; //----------------------------------------------------------------------- // The short and long names of the supported processors #define FAMILY "Intel 51 series:" static const char *const shnames[] = { "8051", "80251b", "80251s", "80930b", "80930s", "8051mx", NULL }; static const char *const lnames[] = { FAMILY"Intel 8051", "Intel 80251 in binary mode", "Intel 80251 in source mode", "Intel 80930 in binary mode", "Intel 80930 in source mode", "Intel 8051MX", NULL }; //-------------------------------------------------------------------------- // Opcodes of "return" instructions. This information will be used in 2 ways: // - if an instruction has the "return" opcode, its autogenerated label // will be "locret" rather than "loc". // - IDA will use the first "return" opcode to create empty subroutines. static const uchar retcode_1[] = { 0x22 }; static const uchar retcode_2[] = { 0x32 }; static const bytes_t retcodes[] = { { sizeof(retcode_1), retcode_1 }, { sizeof(retcode_2), retcode_2 }, { 0, NULL } // NULL terminated array }; //----------------------------------------------------------------------- // Processor Definition //----------------------------------------------------------------------- processor_t LPH = { IDP_INTERFACE_VERSION, // version PLFM_8051, // id // flag 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) // 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, // array of short processor names // the short names are used to specify the processor // with the -p command line switch) lnames, // array of long processor names // the long names are used to build the processor type // selection menu asms, // array of target assemblers notify, // the kernel event notification callback RegNames, // Regsiter names qnumber(RegNames), // Number of registers rVcs,rVds, 0, // size of a segment register rVcs,rVds, NULL, // No known code start sequences retcodes, 0,I51_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 I51_ret, // Icode of return instruction. It is ok to give any of possible return instructions };