mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
7c40d33db3
Phase 1a (boot slice): 30 instructions at 0xBFC00000, compiles clean. Phase 1b (instruction coverage): 54 opcodes implemented in strict_translator, 44,629 walker-reachable instructions covered, 0 missing. Phase 1c (function discovery): 76 functions from 2 seeds, 2,988 instructions, 199 call edges, 27 indirect jumps. All validated through StrictTranslator. Phase 1d (indirect control flow): 27 sites classified — 21 A0/B0/C0 dispatch stubs, 4 computed calls, 2 computed tail calls. Ghidra cross-referenced with 0 disagreements. Phase 1e (relocation): ROM-to-RAM copy at FUN_bfc00420 proven with both Ghidra (static) and DuckStation (runtime). 69 address aliases, 68/69 function entries match byte-for-byte in RAM. Normalization rule documented. Includes PRINCIPLES.md and DEBUG.md adapted from recomp-template. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
76 lines
2.6 KiB
C++
76 lines
2.6 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include "ps1_exe_parser.h"
|
|
#include "mips_decoder.h"
|
|
|
|
namespace PSXRecomp {
|
|
|
|
// A basic block is a maximal straight-line sequence of instructions with:
|
|
// - A single entry point (the first instruction)
|
|
// - A single exit point (the last instruction, which is a branch/jump or end-of-code)
|
|
// - No branches/jumps except at the end
|
|
// MIPS delay slots: the instruction after a branch/jump is part of the same block.
|
|
struct BasicBlock {
|
|
uint32_t start_addr; // Address of first instruction
|
|
uint32_t end_addr; // Address of last instruction (inclusive)
|
|
uint32_t instr_count; // Total instructions including delay slot
|
|
bool has_delay_slot; // Ends with branch/jump (delay slot follows)
|
|
uint32_t delay_slot_addr; // Address of delay slot (0 if none)
|
|
|
|
// Control flow edges
|
|
std::vector<uint32_t> successors; // Addresses of possible next blocks
|
|
std::vector<uint32_t> predecessors; // Addresses of blocks that jump here
|
|
|
|
// Convenience helpers
|
|
bool contains(uint32_t addr) const {
|
|
return addr >= start_addr && addr <= end_addr;
|
|
}
|
|
};
|
|
|
|
class BasicBlockAnalyzer {
|
|
public:
|
|
explicit BasicBlockAnalyzer(const PS1Executable& exe);
|
|
|
|
// Find all basic blocks in the executable.
|
|
// Returns blocks sorted by start_addr.
|
|
std::vector<BasicBlock> analyze();
|
|
|
|
// After analyze(), find the block containing addr.
|
|
// Returns nullptr if not found.
|
|
const BasicBlock* find_block(uint32_t addr) const;
|
|
|
|
// Build predecessor lists for all blocks.
|
|
// Call this after analyze() to populate block.predecessors.
|
|
static void build_predecessors(std::vector<BasicBlock>& blocks);
|
|
|
|
private:
|
|
const PS1Executable& exe_;
|
|
|
|
// Decoded instruction cache (filled by analyze())
|
|
std::vector<DecodedInstruction> decoded_;
|
|
|
|
// Map from address → index in decoded_
|
|
std::unordered_map<uint32_t, size_t> addr_to_idx_;
|
|
|
|
// Resulting blocks (populated by analyze())
|
|
std::vector<BasicBlock> blocks_;
|
|
|
|
// Map from start_addr → block index (for find_block)
|
|
std::unordered_map<uint32_t, size_t> addr_to_block_;
|
|
|
|
// Collect all block leader addresses.
|
|
// A leader is:
|
|
// 1. The entry point of the executable
|
|
// 2. Any branch/jump target
|
|
// 3. Any instruction immediately after a branch/jump (+ delay slot)
|
|
std::vector<uint32_t> find_leaders();
|
|
|
|
// Build a single block starting at start_addr.
|
|
BasicBlock build_block(uint32_t start_addr);
|
|
};
|
|
|
|
} // namespace PSXRecomp
|