Files
Matthew Stanley 7c40d33db3 Phase 1 complete: recompiler proven correct on SCPH1001.BIN
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>
2026-04-11 21:26:04 -07:00

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