bool IsRegisterUsed(u32 reg, u32 addr) { while (true) { u32 op = Memory::Read_Instruction(addr); u32 info = MIPSGetInfo(op); if ( ((info & IN_RS) || (info & IN_RS_SHIFT) || (info & IN_RS_ADDR)) && (MIPS_GET_RS(op) == reg) ) return true; if ((info & IN_RT) && (MIPS_GET_RT(op) == reg)) return true; if ((info & IS_CONDBRANCH)) return true; // could also follow both paths if ((info & IS_JUMP)) return true; // could also follow the path if ((info & OUT_RT) && (MIPS_GET_RT(op) == reg)) return false; //the reg got clobbed! yay! if ((info & OUT_RD) && (MIPS_GET_RD(op) == reg)) return false; //the reg got clobbed! yay! if ((info & OUT_RA) && (reg == MIPS_REG_RA)) return false; //the reg got clobbed! yay! addr+=4; } return true; }
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; }
std::vector<int> GetInputRegs(u32 op) { std::vector<int> vec; u32 info = MIPSGetInfo(op); if ((info & IS_VFPU) == 0) { if (info & IN_RS) vec.push_back(MIPS_GET_RS(op)); if (info & IN_RT) vec.push_back(MIPS_GET_RT(op)); } return vec; }
bool ReadsFromReg(u32 op, u32 reg) { u32 opinfo = MIPSGetInfo(op); if (opinfo & IN_RT) { if (MIPS_GET_RT(opinfo) == reg) return true; } if (opinfo & (IN_RS | IN_RS_ADDR | IN_RS_SHIFT)) { if (MIPS_GET_RS(opinfo) == reg) return true; } return false; //TODO: there are more cases! }
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; }
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; }
MipsOpcodeInfo GetOpcodeInfo(DebugInterface* cpu, u32 address) { MipsOpcodeInfo info; memset(&info, 0, sizeof(info)); if (!cpu->isValidAddress(address)) return info; info.cpu = cpu; info.opcodeAddress = address; info.encodedOpcode = cpu->read32(address); u32 op = info.encodedOpcode; const R5900::OPCODE& opcode = R5900::GetInstruction(op); // extract all the branch related information info.isBranch = (opcode.flags & IS_BRANCH) != 0; if (info.isBranch) { info.isLinkedBranch = (opcode.flags & IS_LINKED) != 0; info.isLikelyBranch = (opcode.flags & IS_LIKELY) != 0; u64 rs,rt; u32 value; switch (opcode.flags & BRANCHTYPE_MASK) { case BRANCHTYPE_JUMP: info.isConditional = false; info.branchTarget = (info.opcodeAddress & 0xF0000000) | ((op&0x03FFFFFF) << 2); break; case BRANCHTYPE_BRANCH: info.isConditional = true; info.branchTarget = info.opcodeAddress + 4 + ((s16)(op&0xFFFF)<<2); rs = info.cpu->getRegister(0,MIPS_GET_RS(op))._u64[0]; rt = info.cpu->getRegister(0,MIPS_GET_RT(op))._u64[0]; switch (opcode.flags & CONDTYPE_MASK) { case CONDTYPE_EQ: info.conditionMet = (rt == rs); if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) // always true info.isConditional = false; break; case CONDTYPE_NE: info.conditionMet = (rt != rs); if (MIPS_GET_RT(op) == MIPS_GET_RS(op)) // always false info.isConditional = false; break; case CONDTYPE_LEZ: info.conditionMet = (((s64)rs) <= 0); break; case CONDTYPE_GTZ: info.conditionMet = (((s64)rs) > 0); break; case CONDTYPE_LTZ: info.conditionMet = (((s64)rs) < 0); break; case CONDTYPE_GEZ: info.conditionMet = (((s64)rs) >= 0); break; } break; case BRANCHTYPE_REGISTER: info.isConditional = false; info.isBranchToRegister = true; info.branchRegisterNum = (int)MIPS_GET_RS(op); info.branchTarget = info.cpu->getRegister(0,info.branchRegisterNum)._u32[0]; break; case BRANCHTYPE_SYSCALL: info.isConditional = false; info.isSyscall = true; info.branchTarget = 0x80000000+0x180; break; case BRANCHTYPE_ERET: info.isConditional = false; // probably shouldn't be hard coded like this... if (cpuRegs.CP0.n.Status.b.ERL) { info.branchTarget = cpuRegs.CP0.n.ErrorEPC; } else { info.branchTarget = cpuRegs.CP0.n.EPC; } break; case BRANCHTYPE_BC1: info.isConditional = true; value = info.cpu->getRegister(EECAT_FCR,31)._u32[0] & 0x00800000; info.branchTarget = info.opcodeAddress + 4 + ((s16)(op&0xFFFF)<<2); switch (opcode.flags & CONDTYPE_MASK) { case CONDTYPE_EQ: info.conditionMet = value == 0; break; case CONDTYPE_NE: info.conditionMet = value != 0; break; } break; } } // extract the accessed memory address info.isDataAccess = (opcode.flags & IS_MEMORY) != 0; if (info.isDataAccess) { if (opcode.flags & IS_LEFT) info.lrType = LOADSTORE_LEFT; else if (opcode.flags & IS_RIGHT) info.lrType = LOADSTORE_RIGHT; u32 rs = info.cpu->getRegister(0, (int)MIPS_GET_RS(op)); s16 imm16 = op & 0xFFFF; info.dataAddress = rs + imm16; switch (opcode.flags & MEMTYPE_MASK) { case MEMTYPE_BYTE: info.dataSize = 1; break; case MEMTYPE_HALF: info.dataSize = 2; break; case MEMTYPE_WORD: info.dataSize = 4; if (info.lrType == LOADSTORE_LEFT) info.dataAddress -= 3; break; case MEMTYPE_DWORD: info.dataSize = 8; if (info.lrType == LOADSTORE_LEFT) info.dataAddress -= 7; break; case MEMTYPE_QWORD: info.dataSize = 16; break; } info.hasRelevantAddress = true; info.releventAddress = info.dataAddress; } // gather relevant address for alu operations if (opcode.flags & IS_ALU) { u64 rs,rt; rs = info.cpu->getRegister(0,MIPS_GET_RS(op))._u64[0]; rt = info.cpu->getRegister(0,MIPS_GET_RT(op))._u64[0]; switch (opcode.flags & ALUTYPE_MASK) { case ALUTYPE_ADDI: info.hasRelevantAddress = true; info.releventAddress = rs+((s16)(op & 0xFFFF)); break; case ALUTYPE_ADD: info.hasRelevantAddress = true; info.releventAddress = rs+rt; break; case ALUTYPE_SUB: info.hasRelevantAddress = true; info.releventAddress = rs-rt; break; case ALUTYPE_CONDMOVE: info.isConditional = true; switch (opcode.flags & CONDTYPE_MASK) { case CONDTYPE_EQ: info.conditionMet = (rt == 0); break; case CONDTYPE_NE: info.conditionMet = (rt != 0); break; } 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); }
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); }
void Analyze(u32 address) { //set everything to -1 (FF) memset(regAnal, 255, sizeof(AnalysisResults)*32); for (int i=0; i<32; i++) { regAnal[i].used=false; regAnal[i].readCount=0; regAnal[i].writeCount=0; regAnal[i].readAsAddrCount=0; } u32 addr = address; bool exitFlag = false; while (true) { u32 op = Memory::Read_Instruction(addr); u32 info = MIPSGetInfo(op); for (int reg=0; reg < 32; reg++) { int rs = MIPS_GET_RS(op); int rt = MIPS_GET_RT(op); int rd = MIPS_GET_RD(op); if ( ((info & IN_RS) && (rs == reg)) || ((info & IN_RS_SHIFT) && (rs == reg)) || ((info & IN_RT) && (rt == reg))) { if (regAnal[reg].firstRead == -1) regAnal[reg].firstRead = addr; regAnal[reg].lastRead = addr; regAnal[reg].readCount++; regAnal[reg].used=true; } if ( ((info & IN_RS_ADDR) && (rs == reg)) ) { if (regAnal[reg].firstReadAsAddr == -1) regAnal[reg].firstReadAsAddr = addr; regAnal[reg].lastReadAsAddr = addr; regAnal[reg].readAsAddrCount++; regAnal[reg].used=true; } if ( ((info & OUT_RT) && (rt == reg)) || ((info & OUT_RD) && (rd == reg)) || ((info & OUT_RA) && (reg == MIPS_REG_RA)) ) { if (regAnal[reg].firstWrite == -1) regAnal[reg].firstWrite = addr; regAnal[reg].lastWrite = addr; regAnal[reg].writeCount++; regAnal[reg].used=true; } } if (exitFlag) //delay slot done, let's quit! break; if ((info & IS_JUMP) || (info & IS_CONDBRANCH)) { exitFlag = true; // now do the delay slot } addr += 4; } int numUsedRegs=0; static int totalUsedRegs=0; static int numAnalyzings=0; for (int i=0; i<32; i++) { if (regAnal[i].used) numUsedRegs++; } totalUsedRegs+=numUsedRegs; numAnalyzings++; DEBUG_LOG(CPU,"[ %08x ] Used regs: %i Average: %f",address,numUsedRegs,(float)totalUsedRegs/(float)numAnalyzings); }