// discpu.cpp #include "discpu.h" #include "discmt.h" // ===================================================== // class CPU // ===================================================== CPU *curCpu = &generic; // pointer to current CPU CPU *defCpu = &generic; // pointer to default CPU CPU *cpu_list = NULL; // pointer to CPU list uint8_t next_cpu_id = mCode; // next CPU ID to assign // ===================================================== // register this disassembler in the list of disassemblers void CPU::add_cpu() { // curCpu = this; _id = next_cpu_id++; // add new disassembler to the end of the linked list // so they will appear in registration order // (registration order will depend on link order of C++ constructors) if (!cpu_list) { // empty list cpu_list = this; } else { // find last item in list CPU *p = cpu_list; while (p->_next) { p = p->_next; } // add after last item p->_next = this; } } // ===================================================== void CPU::set_cur_cpu(CPU *cpu) { cpu->set_cur_cpu(); } // ===================================================== void CPU::set_cur_cpu() { curCpu = this; if (defCpu == &generic) { set_def_cpu(); } } // ===================================================== void CPU::set_def_cpu(CPU *cpu) { cpu->set_def_cpu(); } // ===================================================== void CPU::set_def_cpu() { defCpu = this; // copy settings into generic generic._dbopcd = _dbopcd; generic._dwopcd = _dwopcd; generic._dlopcd = _dlopcd; generic._drwopcd = _drwopcd; generic._endian = _endian; generic._curpc = _curpc; generic._hexchr = _hexchr; generic._addrwid = _addrwid; generic._usefcc = _usefcc; } // ===================================================== // find a disassembler in the list by name class CPU *CPU::get_cpu(const char *s) { #if 0 if (strcasecmp(s, curCpu->_name)) { return NULL; } else { return curCpu; } #else for (class CPU *cpu = cpu_list; cpu; cpu = cpu->_next) { if (strcasecmp(s, cpu->_name) == 0) { return cpu; } } return NULL; #endif } // ===================================================== // find a disassembler in the list by id class CPU *CPU::get_cpu(int id) { // if id is not for a disasssembler, use the generic disassembler if (id < mCode) { return &generic; } // find the CPU from the list // could probably cache the last used one before searching the list #if 0 if (id != curCpu->_id) { return NULL; } else { return curCpu; } #else for (class CPU *cpu = cpu_list; cpu; cpu = cpu->_next) { if (id == cpu->_id) { return cpu; } } return NULL; #endif } // ===================================================== // find a dissasembler in the list in order // start with cpu = NULL, returns NULL at end of list class CPU *CPU::next_cpu(class CPU *cpu) { #if 0 // currently only supporting curCpu if (cpu) { return NULL; } else { return curCpu; } #else if (cpu) { // return next CPU in list return cpu->_next; } else { // return start of list return cpu_list; } #endif } // ===================================================== void CPU::show_list() { const char *file = ""; printf("Supported CPU types:"); for (CPU *cpu = CPU::next_cpu(NULL); cpu; cpu = CPU::next_cpu(cpu)) { if (strcmp(file, cpu->_file)) { printf("\n"); // print new file name // printf("%s: \"%s\"", cpu->_file, cpu->_version); printf(" %s:", cpu->_version); file = cpu->_file; } // printf(" %-8s - %s\n", cpu->_name, cpu->_version); printf(" %s", cpu->_name); } printf("\n"); } // ===================================================== int CPU::ReadByte(addr_t addr) const { return rom.get_data(addr++); } // ===================================================== int CPU::ReadWord(addr_t addr) const { if (_endian) { return ReadWordBE(addr); } else { return ReadWordLE(addr); } } // ===================================================== int CPU::ReadRWord(addr_t addr) const { if (_endian) { return ReadWordLE(addr); } else { return ReadWordBE(addr); } } // ===================================================== int CPU::ReadWordBE(addr_t addr) const { return (rom.get_data(addr) << 8) | rom.get_data(addr+1); } // ===================================================== int CPU::ReadWordLE(addr_t addr) const { return rom.get_data(addr) | (rom.get_data(addr+1) << 8); } // ===================================================== int CPU::ReadLong(addr_t addr) const { if (_endian) { return ReadLongBE(addr); } else { return ReadLongLE(addr); } } // ===================================================== int CPU::ReadRLong(addr_t addr) const { if (_endian) { return ReadLongLE(addr); } else { return ReadLongBE(addr); } } // ===================================================== int CPU::ReadLongBE(addr_t addr) const { return (rom.get_data(addr) << 24) | (rom.get_data(addr+1) << 16) | (rom.get_data(addr+2) << 8) | rom.get_data(addr+3); } // ===================================================== int CPU::ReadLongLE(addr_t addr) const { return rom.get_data(addr) | (rom.get_data(addr+1) << 8) | (rom.get_data(addr+2) << 16) | (rom.get_data(addr+3) << 24); } // ===================================================== char *CPU::H2Str(uint8_t b, char *s) const { if (_hexchr == '$') { sprintf(s, "$%.2X", b); } else { if (b > 0x9F) { sprintf(s, "0%.2XH", b); } else { sprintf(s, "%.2XH", b); } } return s; } // ===================================================== char *CPU::H4Str(uint16_t w, char *s) const { if (_hexchr == '$') { sprintf(s, "$%.4X", w); } else { if (w > 0x9FFF) { sprintf(s, "0%.4XH", w); } else { sprintf(s, "%.4XH", w); } } return s; } // ===================================================== char *CPU::H6Str(uint32_t l, char *s) const { if (_hexchr == '$') { sprintf(s, "$%.6X", l); } else { if (l > 0x9FFFFF) { sprintf(s, "0%.6XH", l); } else { sprintf(s, "%.6XH", l); } } return s; } // ===================================================== char *CPU::H8Str(uint32_t l, char *s) const { if (_hexchr == '$') { sprintf(s, "$%.8X", l); } else { if (l > 0x9FFFFFFF) { sprintf(s, "0%.8XH", l); } else { sprintf(s, "%.8XH", l); } } return s; } // ===================================================== char *CPU::HxStr(uint32_t l, char *s) const { if (l > 0x00FFFFFF) { return H8Str(l, s); } else if (rom._base > 0x0000FFFF) { return H6Str(l, s); } else { return H4Str(l, s); } } // ===================================================== char *CPU::RefStr2(addr_t addr, char *s, int &lfref, addr_t &refaddr) const { s[0] = 0; if (rom._base <= addr && addr <= rom.get_end() && addr != 0) { lfref |= REFFLAG; make_label(addr, s); refaddr = addr; } // note that if the label wasn't actually flagged as a label, // make_label will leave the string empty, but refaddr will still be set if (!s[0]) { H2Str(addr, s); } return s; } // ===================================================== char *CPU::RefStr4(addr_t addr, char *s, int &lfref, addr_t &refaddr) const { s[0] = 0; if (rom._base <= addr && addr <= rom.get_end() && addr != 0) { lfref |= REFFLAG; make_label(addr, s); refaddr = addr; } // note that if the label wasn't actually flagged as a label, // make_label will leave the string empty, but refaddr will still be set if (!s[0]) { H4Str(addr, s); } return s; } // ===================================================== char *CPU::RefStr6(addr_t addr, char *s, int &lfref, addr_t &refaddr) const { s[0] = 0; if (rom._base <= addr && addr <= rom.get_end() && addr != 0) { lfref |= REFFLAG; make_label(addr, s); refaddr = addr; } // note that if the label wasn't actually flagged as a label, // make_label will leave the string empty, but refaddr will still be set if (!s[0]) { H6Str(addr, s); } return s; } // ===================================================== char *CPU::RefStr8(addr_t addr, char *s, int &lfref, addr_t &refaddr) const { // check for an odd_code situation (odd addresses of Thumb code) // This is only done in RefStr8 simply because that's // the only place where it is needed. bool odd = is_odd_code(addr); if (odd) { addr--; } s[0] = 0; if (rom._base <= addr && addr <= rom.get_end() && addr != 0) { lfref |= REFFLAG; make_label(addr, s); refaddr = addr; } // note that if the label wasn't actually flagged as a label, // make_label will leave the string empty, but refaddr will still be set if (!s[0]) { H8Str(addr, s); } // append a trailing "+1" for the odd_code situation if (odd) { strcat(s, "+1"); } return s; } // ===================================================== char *CPU::RefStr(addr_t addr, char *s, int &lfref, addr_t &refaddr) const { switch(_addrwid) { default: case ADDR_16: return RefStr4(addr, s, lfref, refaddr); case ADDR_24: return RefStr6(addr, s, lfref, refaddr); case ADDR_32: return RefStr8(addr, s, lfref, refaddr); } return s; } // ===================================================== void CPU::make_label(addr_t addr, char *s) const { s[0] = 0; char c = 0; // check if external symbol defined at this address const char *str; if ((str = sym.get_sym(addr))) { strcpy(s, str); return; } // get label type for this address switch(rom.get_attr(addr) & ATTR_LMASK) { default: // no label case ATTR_LNONE: return; case ATTR_LDATA: c = 'D'; break; case ATTR_LCODE: c = 'L'; break; case ATTR_LXXXX: c = 'X'; break; } // concatenate address to label type switch (defCpu->_addrwid) { default: case ADDR_16: sprintf(s, "%c%.4X", c, (unsigned) addr); break; case ADDR_24: case ADDR_32: sprintf(s, "%c%.6X", c, (unsigned) addr); break; } } // ===================================================== // returns address of previous instruction, or zero if invalid // (invalid means either before start of ROM or not from this CPU) addr_t CPU::find_prev_instr(addr_t addr) const { // have to go back at least one byte addr--; while (addr >= rom._base) { // look for previous instruction start if (!rom.test_attr(addr, ATTR_CONT)) { // ignore if wrong CPU if (rom.get_type(addr) != _id) { // it's not from this CPU return 0; } // found the previous instruction from this CPU! return addr; } // try next byte back addr--; } return 0; // was at beginning of rom area } // ===================================================== // returns address of previous label, or zero if none found addr_t CPU::find_prev_label(addr_t addr) const { while (addr >= rom._base) { if (rom.test_attr(addr, ATTR_LMASK)) { return addr; } addr--; } return 0; // previous label not found } // ===================================================== // class DisDefault // ===================================================== DisDefault generic; // ===================================================== DisDefault::DisDefault() { _file = __FILE__; _name = "(none)"; _version = ""; _subtype = 0; _next = NULL; _id = 0; _dbopcd = "DB"; _dwopcd = "DW"; _dlopcd = "DL"; _curpc = '$'; _endian = LITTLE_END; _hexchr = 'H'; _addrwid = ADDR_16; // should be using defCpu! _usefcc = false; } // ===================================================== void DisDefault::byte_dis_line(addr_t addr, char *opcode, char *parms) const { int len = rom.get_len(addr); char *p = parms; strcpy(opcode, _dbopcd); for (int i = 0; i < len; i++) { if (i) { p = stpcpy(p, ","); } H2Str(rom.get_data(addr++),p); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::word_dis_line(addr_t addr, char *opcode, char *parms, int &lfref, addr_t &refaddr) const { int len = rom.get_len(addr) / 2; bool w1 = rom.get_type(addr) == mWord1; char *p = parms; strcpy(opcode, _dwopcd); int w = ReadWord(addr); if (w1) { w++; // mWord1 reference address is w + 1 } addr += 2; RefStr4(w, p, lfref, refaddr); p += strlen(p); if (w1) { strcat(p, "-1"); p += 2; } for (int i = 0; i < len - 1; i++) { int w = ReadWord(addr); addr += 2; p = stpcpy(p, ","); H4Str(w, p); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::rword_dis_line(addr_t addr, char *opcode, char *parms, int &lfref, addr_t &refaddr) const { int len = rom.get_len(addr) / 2; char *p = parms; // use DW reverse opcode if specified if (_drwopcd) { strcpy(opcode, _drwopcd); } else { strcpy(opcode, _dwopcd); strcat(opcode, "*"); } int w = ReadRWord(addr); addr += 2; RefStr4(w, p, lfref, refaddr); p += strlen(p); for (int i = 0; i < len - 1; i++) { int w = ReadRWord(addr); addr += 2; p = stpcpy(p, ","); H4Str(w, p); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::long_dis_line(addr_t addr, char *opcode, char *parms, int &lfref, addr_t &refaddr) const { int len = rom.get_len(addr) / 4; char *p = parms; strcpy(opcode, _dlopcd); uint32_t w = ReadLong(addr); addr += 4; RefStr8(w, p, lfref, refaddr); p += strlen(p); for (int i = 0; i < len - 1; i++) { uint32_t l = ReadLong(addr); addr += 4; p = stpcpy(p, ","); H8Str(l, p); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::rlong_dis_line(addr_t addr, char *opcode, char *parms, int &lfref, addr_t &refaddr) const { int len = rom.get_len(addr) / 4; char *p = parms; strcpy(opcode, _dlopcd); strcat(opcode, "*"); uint32_t w = ReadRLong(addr); addr += 4; RefStr8(w, p, lfref, refaddr); p += strlen(p); for (int i = 0; i < len - 1; i++) { uint32_t l = ReadRLong(addr); addr += 4; p = stpcpy(p, ","); H8Str(l, p); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::decw_dis_line(addr_t addr, char *opcode, char *parms) const { int len = rom.get_len(addr) / 2; char *p = parms; strcpy(opcode, _dwopcd); for (int i = 0; i < len; i++) { int w = ReadWord(addr); addr += 2; if (i) { p = stpcpy(p, ","); } sprintf(p, "%d", w); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::decl_dis_line(addr_t addr, char *opcode, char *parms) const { int len = rom.get_len(addr) / 4; char *p = parms; strcpy(opcode, _dlopcd); for (int i = 0; i < len; i++) { uint32_t l = ReadLong(addr); addr += 4; if (i) { p = stpcpy(p, ","); } sprintf(p, "%d", l); p += strlen(p); // avoid buffer overflow the lazy way if (strlen(parms) > 200) { strcpy(p, "..."); break; } } } // ===================================================== void DisDefault::ofs_dis_line(addr_t addr, char *opcode, char *parms, int &lfref, addr_t &refaddr) const { // if not exactly 2 bytes, disassemble as bytes and exit int len = rom.get_len(addr); if (len != 2) { byte_dis_line(addr, opcode, parms); return; //printf(" *** addr=%.8X len=%d label not found ***\r\n", (int) addr, len); fflush(stdout); sleep(3); } // get previous label addr_t label = find_prev_label(addr); // no label found, disassemble as word and exit // note: this will also fail if the label is at address zero if (!addr) { word_dis_line(addr, opcode, parms, lfref, refaddr); return; //printf(" *** addr=%.8X label=%.8X label not found ***\r\n", (int) addr, (int) label); fflush(stdout); sleep(3); } // get word in default endian int w = ReadWord(addr); addr += 2; // sign-extend word w = (int16_t) w; // create disassembly line "DW -