From f4b95c02265154908d08a2f2d5ae227f3a49dae8 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 12 Feb 2017 21:12:08 +0300 Subject: [PATCH] PPU analyser improved --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 147 ++++++++++++++++++++++++++++-- rpcs3/Emu/Cell/PPUDisAsm.cpp | 9 +- rpcs3/Emu/Cell/PPUInterpreter.cpp | 6 +- rpcs3/Emu/Cell/PPUOpcodes.h | 2 + rpcs3/Emu/Cell/PPUTranslator.cpp | 7 +- 5 files changed, 153 insertions(+), 18 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index e3c19f0d30..aeb6fd7ae9 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -60,7 +60,7 @@ void ppu_validate(const std::string& fname, const std::vector& fun while (addr > found && index + 1 < funcs.size()) { - LOG_ERROR(LOADER, "%s.yml : validation failed at 0x%x (0x%x, 0x%x)", fname, found, addr, size); + LOG_WARNING(LOADER, "%s.yml : unexpected function at 0x%x (0x%x, 0x%x)", fname, found, addr, size); index++; found = funcs[index].addr - reloc; } @@ -71,14 +71,12 @@ void ppu_validate(const std::string& fname, const std::vector& fun continue; } - if (size && size < funcs[index].size) + if (size && size != funcs[index].size) { - LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].size, addr, size); - } - - if (size > funcs[index].size) - { - LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].size, addr, size); + if (size + 4 != funcs[index].size || vm::read32(addr + size) != ppu_instructions::NOP()) + { + LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].size, addr, size); + } } index++; @@ -631,6 +629,34 @@ std::vector ppu_analyse(const std::vector>& se } } + if (ptr + 0x4 <= fend && + (ptr[0] & 0xffff0000) == LIS(r11, 0) && + (ptr[1] & 0xffff0000) == ADDI(r11, r11, 0) && + ptr[2] == MTCTR(r11) && + ptr[3] == BCTR()) + { + // Simple gate + const u32 target = (ptr[0] << 16) + ppu_opcode_t{ptr[1]}.simm16; + + if (target >= start && target < end) + { + auto& new_func = add_func(target, func.toc, func.addr); + + if (new_func.blocks.empty()) + { + func_queue.emplace_back(func); + continue; + } + + func.size = 0x10; + func.blocks.emplace(func.addr, func.size); + func.attr += new_func.attr & ppu_attr::no_return; + func.called_from.emplace(target); + func.gate_target = target; + continue; + } + } + if (ptr + 4 <= fend && ptr[0] == STD(r2, r1, 0x28) && (ptr[1] & 0xffff0000) == ADDIS(r2, r2, {}) && @@ -663,6 +689,7 @@ std::vector ppu_analyse(const std::vector>& se } if (ptr + 8 <= fend && + (ptr[0] == STD(r2, r1, 0x28) && (ptr[1] & 0xfc000000) == HACK(0) && ptr[2] == BLR() || (ptr[0] & 0xffff0000) == LI(r12, 0) && (ptr[1] & 0xffff0000) == ORIS(r12, r12, 0) && (ptr[2] & 0xffff0000) == LWZ(r12, r12, 0) && @@ -670,13 +697,48 @@ std::vector ppu_analyse(const std::vector>& se ptr[4] == LWZ(r0, r12, 0) && ptr[5] == LWZ(r2, r12, 4) && ptr[6] == MTCTR(r0) && - ptr[7] == BCTR()) + ptr[7] == BCTR())) { // The most used simple import stub func.size = 0x20; func.blocks.emplace(func.addr, func.size); func.attr += ppu_attr::known_addr; func.attr += ppu_attr::known_size; + + // Look for another imports to fill gaps (hack) + auto p2 = ptr + 8; + + while (p2 + 8 <= fend && + (p2[0] == STD(r2, r1, 0x28) && (p2[1] & 0xfc000000) == HACK(0) && p2[2] == BLR() || + (p2[0] & 0xffff0000) == LI(r12, 0) && + (p2[1] & 0xffff0000) == ORIS(r12, r12, 0) && + (p2[2] & 0xffff0000) == LWZ(r12, r12, 0) && + p2[3] == STD(r2, r1, 0x28) && + p2[4] == LWZ(r0, r12, 0) && + p2[5] == LWZ(r2, r12, 4) && + p2[6] == MTCTR(r0) && + p2[7] == BCTR())) + { + auto& next = add_func(p2.addr(), 0, func.addr); + next.size = 0x20; + next.blocks.emplace(next.addr, next.size); + next.attr += ppu_attr::known_addr; + next.attr += ppu_attr::known_size; + p2 += 8; + } + + continue; + } + + if (ptr + 3 <= fend && + ptr[0] == 0x7c0004ac && + ptr[1] == 0x00000000 && + ptr[2] == BLR()) + { + // Weird function (illegal instruction) + func.size = 0xc; + func.blocks.emplace(func.addr, func.size); + //func.attr += ppu_attr::no_return; continue; } @@ -884,6 +946,11 @@ std::vector ppu_analyse(const std::vector>& se block.second = _ptr.addr() - block.first; break; } + else if (op.opcode == ppu_instructions::TRAP()) + { + block.second = _ptr.addr() - block.first; + break; + } } } @@ -997,6 +1064,68 @@ std::vector ppu_analyse(const std::vector>& se } } } + + // Suspicious block start + u32 start = func.addr + func.size; + + if (next == end) + { + continue; + } + + // Analyse gaps between functions + for (vm::cptr _ptr = vm::cast(start); _ptr.addr() < next;) + { + const u32 addr = _ptr.addr(); + const ppu_opcode_t op{*_ptr++}; + const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + + if (type == ppu_itype::UNK) + { + break; + } + else if (addr == start && op.opcode == ppu_instructions::NOP()) + { + if (start == func.addr + func.size) + { + // Extend function with tail NOPs (hack) + func.size += 4; + } + + start += 4; + continue; + } + else if (type == ppu_itype::SC && op.opcode != ppu_instructions::SC(0)) + { + break; + } + else if (addr == start && op.opcode == ppu_instructions::BLR()) + { + start += 4; + continue; + } + else if (type == ppu_itype::B || type == ppu_itype::BC) + { + const u32 target = (op.aa ? 0 : addr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); + + if (target == addr) + { + break; + } + + _ptr.set(next); + } + else if (type == ppu_itype::BCLR || type == ppu_itype::BCCTR) + { + _ptr.set(next); + } + + if (_ptr.addr() >= next) + { + LOG_WARNING(PPU, "Function gap: [0x%x] 0x%x bytes at 0x%x", func.addr, next - start, start); + break; + } + } } // Convert map to vector (destructive) diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index 2d2f93e8d9..a52e76672e 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -899,13 +899,12 @@ void PPUDisAsm::HACK(ppu_opcode_t op) void PPUDisAsm::SC(ppu_opcode_t op) { - switch (op.lev) + if (op.opcode != ppu_instructions::SC(0)) { - case 0x0: Write("sc"); break; - case 0x1: Write("HyperCall LV1"); break; - case 0x3: Write("fast_stop()"); break; // hack - default: Write(fmt::format("Unknown sc: 0x%x", op.lev)); + return UNK(op); } + + Write("sc"); } void PPUDisAsm::B(ppu_opcode_t op) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index 5cebb2b9e8..512a3cb5ef 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -1944,12 +1944,12 @@ bool ppu_interpreter::HACK(ppu_thread& ppu, ppu_opcode_t op) bool ppu_interpreter::SC(ppu_thread& ppu, ppu_opcode_t op) { - switch (u32 lv = op.lev) + if (op.opcode != ppu_instructions::SC(0)) { - case 0x0: ppu_execute_syscall(ppu, ppu.gpr[11]); break; - default: fmt::throw_exception("SC lv%u", lv); + return UNK(ppu, op); } + ppu_execute_syscall(ppu, ppu.gpr[11]); return true; } diff --git a/rpcs3/Emu/Cell/PPUOpcodes.h b/rpcs3/Emu/Cell/PPUOpcodes.h index 45bf429c1a..458ecb7ccb 100644 --- a/rpcs3/Emu/Cell/PPUOpcodes.h +++ b/rpcs3/Emu/Cell/PPUOpcodes.h @@ -643,6 +643,8 @@ namespace ppu_instructions inline u32 EXTRDI(u32 x, u32 y, u32 n, u32 b) { return RLDICL(x, y, b + n, 64 - b, false); } inline u32 SRDI(u32 x, u32 y, u32 n) { return RLDICL(x, y, 64 - n, n, false); } inline u32 CLRLDI(u32 x, u32 y, u32 n) { return RLDICL(x, y, 0, n, false); } + + inline u32 TRAP() { return 0x7FE00008; } // tw 31,r0,r0 } using namespace implicts; diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 210272d652..1bb05d25fe 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -1758,7 +1758,12 @@ void PPUTranslator::HACK(ppu_opcode_t op) void PPUTranslator::SC(ppu_opcode_t op) { - Call(GetType(), fmt::format(op.lev == 0 ? "__syscall" : "__lv%ucall", +op.lev), m_thread, m_ir->CreateLoad(m_gpr[11])); + if (op.opcode != ppu_instructions::SC(0) && op.opcode != ppu_instructions::SC(1)) + { + return UNK(op); + } + + Call(GetType(), op.lev ? "__lv1call" : "__syscall", m_thread, m_ir->CreateLoad(m_gpr[11])); UndefineVolatileRegisters(); }