bool getDataAccessInfo(MipsOpcodeInfo& info) { int size = 0; u32 op = info.encodedOpcode; int off = 0; switch (MIPS_GET_OP(op)) { case 0x20: // lb case 0x24: // lbu case 0x28: // sb size = 1; break; case 0x21: // lh case 0x25: // lhu case 0x29: // sh size = 2; break; case 0x23: // lw case 0x26: // lwr case 0x2B: // sw case 0x2E: // swr size = 4; break; case 0x22: // lwl case 0x2A: // swl size = 4; off = -3; break; case 0x37: // ld case 0x1B: // ldr case 0x3F: // sd case 0x2D: // sdr size = 8; break; case 0x1A: // ldl case 0x2C: // sdl size = 8; off = -7; break; } if (size == 0) return false; info.isDataAccess = true; info.dataSize = size; u32 rs = info.cpu->getRegister(0, (int)MIPS_GET_RS(op)); s16 imm16 = op & 0xFFFF; info.dataAddress = rs + imm16 + off; info.hasRelevantAddress = true; info.releventAddress = info.dataAddress; return true; }
MipsOpcodeInfo GetOpcodeInfo(DebugInterface* cpu, u32 address) { MipsOpcodeInfo info; memset(&info, 0, sizeof(info)); if (cpu->isValidAddress(address) == false) { return info; } info.cpu = cpu; info.opcodeAddress = address; info.encodedOpcode = cpu->read32(address); u32 op = info.encodedOpcode; if (getBranchInfo(info) == true) return info; if (getDataAccessInfo(info) == true) return info; // gather relevant address for alu operations // that's usually the value of the dest register switch (MIPS_GET_OP(op)) { case 0: // special switch (MIPS_GET_FUNC(op)) { case 0x20: // add case 0x21: // addu info.hasRelevantAddress = true; info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]+cpu->getRegister(0,MIPS_GET_RT(op))._u32[0]; break; case 0x22: // sub case 0x23: // subu info.hasRelevantAddress = true; info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]-cpu->getRegister(0,MIPS_GET_RT(op))._u32[0]; break; } break; case 0x08: // addi case 0x09: // adiu info.hasRelevantAddress = true; info.releventAddress = cpu->getRegister(0,MIPS_GET_RS(op))._u32[0]+((s16)(op & 0xFFFF)); break; } // TODO: rest /* // movn, movz if (opInfo & IS_CONDMOVE) { info.isConditional = true; u32 rt = cpu->GetRegValue(0, (int)MIPS_GET_RT(op)); switch (opInfo & CONDTYPE_MASK) { case CONDTYPE_EQ: info.conditionMet = (rt == 0); break; case CONDTYPE_NE: info.conditionMet = (rt != 0); break; } }*/ return info; }
void DisassemblyFunction::load() { generateBranchLines(); // gather all branch targets std::set<u32> branchTargets; for (size_t i = 0; i < lines.size(); i++) { switch (lines[i].type) { case LINE_DOWN: branchTargets.insert(lines[i].second); break; case LINE_UP: branchTargets.insert(lines[i].first); break; default: break; } } DebugInterface* cpu = DisassemblyManager::getCpu(); u32 funcPos = address; u32 funcEnd = address+size; u32 nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); u32 opcodeSequenceStart = funcPos; while (funcPos < funcEnd) { if (funcPos == nextData) { if (opcodeSequenceStart != funcPos) addOpcodeSequence(opcodeSequenceStart,funcPos); DisassemblyData* data = new DisassemblyData(funcPos,symbolMap.GetDataSize(funcPos),symbolMap.GetDataType(funcPos)); entries[funcPos] = data; lineAddresses.push_back(funcPos); funcPos += data->getTotalSize(); nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); opcodeSequenceStart = funcPos; continue; } // force align if (funcPos % 4) { u32 nextPos = (funcPos+3) & ~3; DisassemblyComment* comment = new DisassemblyComment(funcPos,nextPos-funcPos,".align","4"); entries[funcPos] = comment; lineAddresses.push_back(funcPos); funcPos = nextPos; opcodeSequenceStart = funcPos; continue; } MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos); u32 opAddress = funcPos; funcPos += 4; // skip branches and their delay slots if (opInfo.isBranch) { funcPos += 4; continue; } // lui if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData) { MIPSOpcode next = Memory::Read_Instruction(funcPos); MIPSInfo nextInfo = MIPSGetInfo(next); u32 immediate = ((opInfo.encodedOpcode & 0xFFFF) << 16) + (s16)(next.encoding & 0xFFFF); int rt = MIPS_GET_RT(opInfo.encodedOpcode); int nextRs = MIPS_GET_RS(next.encoding); int nextRt = MIPS_GET_RT(next.encoding); // both rs and rt of the second op have to match rt of the first, // otherwise there may be hidden consequences if the macro is displayed. // also, don't create a macro if something branches into the middle of it if (nextRs == rt && nextRt == rt && branchTargets.find(funcPos) == branchTargets.end()) { DisassemblyMacro* macro = NULL; switch (MIPS_GET_OP(next.encoding)) { case 0x09: // addiu macro = new DisassemblyMacro(opAddress); macro->setMacroLi(immediate,rt); funcPos += 4; break; case 0x20: // lb case 0x21: // lh case 0x23: // lw case 0x24: // lbu case 0x25: // lhu case 0x28: // sb case 0x29: // sh case 0x2B: // sw macro = new DisassemblyMacro(opAddress); int dataSize; switch (nextInfo & MEMTYPE_MASK) { case MEMTYPE_BYTE: dataSize = 1; break; case MEMTYPE_HWORD: dataSize = 2; break; case MEMTYPE_WORD: case MEMTYPE_FLOAT: dataSize = 4; break; case MEMTYPE_VQUAD: dataSize = 16; break; default: return; } macro->setMacroMemory(MIPSGetName(next),immediate,rt,dataSize); funcPos += 4; break; } if (macro != NULL) { if (opcodeSequenceStart != opAddress) addOpcodeSequence(opcodeSequenceStart,opAddress); entries[opAddress] = macro; for (int i = 0; i < macro->getNumLines(); i++) { lineAddresses.push_back(macro->getLineAddress(i)); } opcodeSequenceStart = funcPos; continue; } } } // just a normal opcode } if (opcodeSequenceStart != funcPos) addOpcodeSequence(opcodeSequenceStart,funcPos); }
bool getBranchInfo(MipsOpcodeInfo& info) { BranchType type = NONE; bool link = false; bool likely = false; bool toRegister = false; bool met = false; u32 op = info.encodedOpcode; u32 opNum = MIPS_GET_OP(op); u32 rsNum = MIPS_GET_RS(op); u32 rtNum = MIPS_GET_RT(op); u32 rs = info.cpu->getRegister(0,rsNum); u32 rt = info.cpu->getRegister(0,rtNum); switch (MIPS_GET_OP(op)) { case 0x00: // special switch (MIPS_GET_FUNC(op)) { case 0x08: // jr type = JUMP; toRegister = true; break; case 0x09: // jalr type = JUMP; toRegister = true; link = true; break; } break; case 0x01: // regimm switch (MIPS_GET_RT(op)) { case 0x00: // bltz case 0x02: // bltzl case 0x10: // bltzal case 0x12: // bltzall type = BRANCH; met = (((s32)rs) < 0); likely = (rt & 2) != 0; link = rt >= 0x10; break; case 0x01: // bgez case 0x03: // bgezl case 0x11: // bgezal case 0x13: // bgezall type = BRANCH; met = (((s32)rs) >= 0); likely = (rt & 2) != 0; link = rt >= 0x10; break; } break; case 0x02: // j type = JUMP; break; case 0x03: // jal type = JUMP; link = true; break; case 0x04: // beq case 0x14: // beql type = BRANCH; met = (rt == rs); if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) // always true info.isConditional = false; likely = opNum >= 0x10; break; case 0x05: // bne case 0x15: // bnel type = BRANCH; met = (rt != rs); if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) // always false info.isConditional = false; likely = opNum >= 0x10; break; case 0x06: // blez case 0x16: // blezl type = BRANCH; met = (((s32)rs) <= 0); likely = opNum >= 0x10; break; case 0x07: // bgtz case 0x17: // bgtzl type = BRANCH; met = (((s32)rs) > 0); likely = opNum >= 0x10; break; } if (type == NONE) return false; info.isBranch = true; info.isLinkedBranch = link; info.isLikelyBranch = true; info.isBranchToRegister = toRegister; info.isConditional = type == BRANCH; info.conditionMet = met; switch (type) { case JUMP: if (toRegister) { info.branchRegisterNum = (int)MIPS_GET_RS(op); info.branchTarget = info.cpu->getRegister(0,info.branchRegisterNum)._u32[0]; } else { info.branchTarget = (info.opcodeAddress & 0xF0000000) | ((op&0x03FFFFFF) << 2); } break; case BRANCH: info.branchTarget = info.opcodeAddress + 4 + ((signed short)(op&0xFFFF)<<2); break; } return true; }
void DisassemblyFunction::load() { generateBranchLines(); // gather all branch targets std::set<u32> branchTargets; for (size_t i = 0; i < lines.size(); i++) { switch (lines[i].type) { case LINE_DOWN: branchTargets.insert(lines[i].second); break; case LINE_UP: branchTargets.insert(lines[i].first); break; } } u32 funcPos = address; u32 funcEnd = address+size; u32 nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); u32 opcodeSequenceStart = funcPos; while (funcPos < funcEnd) { if (funcPos == nextData) { if (opcodeSequenceStart != funcPos) addOpcodeSequence(opcodeSequenceStart,funcPos); DisassemblyData* data = new DisassemblyData(cpu,funcPos,symbolMap.GetDataSize(funcPos),symbolMap.GetDataType(funcPos)); entries[funcPos] = data; lineAddresses.push_back(funcPos); funcPos += data->getTotalSize(); nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); opcodeSequenceStart = funcPos; continue; } // force align if (funcPos % 4) { u32 nextPos = (funcPos+3) & ~3; DisassemblyComment* comment = new DisassemblyComment(cpu,funcPos,nextPos-funcPos,".align","4"); entries[funcPos] = comment; lineAddresses.push_back(funcPos); funcPos = nextPos; opcodeSequenceStart = funcPos; continue; } MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos); u32 opAddress = funcPos; funcPos += 4; // skip branches and their delay slots if (opInfo.isBranch) { funcPos += 4; continue; } // lui if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData) { u32 next = cpu->read32(funcPos); u32 immediate = ((opInfo.encodedOpcode & 0xFFFF) << 16) + (s16)(next & 0xFFFF); u32 immediateOr = ((opInfo.encodedOpcode & 0xFFFF) << 16) | (u16)(next & 0xFFFF); int rt = MIPS_GET_RT(opInfo.encodedOpcode); int nextRs = MIPS_GET_RS(next); int nextRt = MIPS_GET_RT(next); // both rs and rt of the second op have to match rt of the first, // otherwise there may be hidden consequences if the macro is displayed. // also, don't create a macro if something branches into the middle of it if (nextRs == rt && nextRt == rt && branchTargets.find(funcPos) == branchTargets.end()) { DisassemblyMacro* macro = NULL; switch (MIPS_GET_OP(next)) { case 0x09: // addiu macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroLi(immediate,rt); funcPos += 4; break; case 0x0D: // ori macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroLi(immediateOr,rt); funcPos += 4; break; case 0x20: // lb macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("lb",immediate,rt,1); funcPos += 4; break; case 0x21: // lh macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("lh",immediate,rt,2); funcPos += 4; break; case 0x23: // lw macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("lw",immediate,rt,4); funcPos += 4; break; case 0x24: // lbu macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("lbu",immediate,rt,1); funcPos += 4; break; case 0x25: // lhu macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("lhu",immediate,rt,2); funcPos += 4; break; case 0x28: // sb macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("sb",immediate,rt,1); funcPos += 4; break; case 0x29: // sh macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("sh",immediate,rt,2); funcPos += 4; case 0x2B: // sw macro = new DisassemblyMacro(cpu,opAddress); macro->setMacroMemory("sw",immediate,rt,4); funcPos += 4; break; } if (macro != NULL) { if (opcodeSequenceStart != opAddress) addOpcodeSequence(opcodeSequenceStart,opAddress); entries[opAddress] = macro; for (int i = 0; i < macro->getNumLines(); i++) { lineAddresses.push_back(macro->getLineAddress(i)); } opcodeSequenceStart = funcPos; continue; } } } // just a normal opcode } if (opcodeSequenceStart != funcPos) addOpcodeSequence(opcodeSequenceStart,funcPos); }