mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
25474410d0
Add Pass 2.6 to FunctionAnalyzer: detect JAL targets whose callee starts with `sw $ra, imm($a0)` (SaveState-style context-save helpers) and force the post-delay-slot PC into the dispatch table. Recovers continuation entries that aren't reached by JAL prologue or jr $ra discovery. Extend find_function_start to recognize `lui rt; lw rt, off(rt)` 8 bytes before jr $ra as a prologue marker. Rewrite analyze_function block-build from std::set boundaries to a sorted std::vector + binary_search. main_psx CLI: --seeds, --out-dir, --strict, --inspect; out_dir support. Add psxrecomp-game executable target alongside PSXRecomp. code_generator: reg_name() now emits cpu->gpr[N] consistently (matching what the rest of the codegen already emitted — the old reg_names[] table returning cpu->v0/cpu->ra was unreachable because CPUState has no named fields, only gpr[32]). LWL/LWR/SWL/SWR moved from cpu->lwl(...) member form to psx_lwl(cpu, ...) free-function form for game-EXE codegen reuse. Remove a stray fmt::print left in build_basic_blocks that spammed stdout on every regen — per the no-stdio-debug rule (memory v4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
68 lines
2.1 KiB
C++
68 lines
2.1 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <string>
|
|
#include "ps1_exe_parser.h"
|
|
|
|
namespace PSXRecomp {
|
|
|
|
struct Function {
|
|
uint32_t start_addr;
|
|
uint32_t end_addr;
|
|
uint32_t size;
|
|
bool has_prologue;
|
|
bool has_epilogue;
|
|
int32_t stack_frame_size; // Size of stack frame (if has prologue)
|
|
std::string name; // Optional name (for now, just "func_<addr>")
|
|
bool is_data_section = false; // True if this "function" is actually a data section
|
|
};
|
|
|
|
struct FunctionAnalysisResult {
|
|
std::vector<Function> functions;
|
|
int total_instructions;
|
|
int jr_ra_count;
|
|
int prologue_count;
|
|
int call_discovered_count = 0; // Functions found via JAL call-target following
|
|
int state_continuation_count = 0; // Split entries after calls to SaveState-style helpers
|
|
};
|
|
|
|
class FunctionAnalyzer {
|
|
public:
|
|
explicit FunctionAnalyzer(const PS1Executable& exe);
|
|
|
|
// Scan entire executable for function boundaries
|
|
FunctionAnalysisResult analyze();
|
|
|
|
// Add a forced entry point address that is treated as a function start
|
|
// even if it has no standard ADDIU $sp prologue. The function will be
|
|
// included in the analysis result with has_prologue = false.
|
|
void add_forced_entry(uint32_t addr);
|
|
|
|
// Check if instruction is jr $ra (return)
|
|
static bool is_jr_ra(uint32_t instr);
|
|
|
|
// Check if instruction is function prologue (addiu $sp, $sp, -N)
|
|
static bool is_prologue(uint32_t instr, int32_t& stack_size);
|
|
|
|
// Check if instruction is epilogue (addiu $sp, $sp, +N)
|
|
static bool is_epilogue(uint32_t instr, int32_t& stack_size);
|
|
|
|
// Check if instruction is a branch or jump (has a delay slot)
|
|
static bool is_branch_or_jump(uint32_t instr);
|
|
|
|
private:
|
|
const PS1Executable& exe_;
|
|
|
|
// Forced entry points (from add_forced_entry())
|
|
std::vector<uint32_t> forced_entry_points_;
|
|
|
|
// Find function start by scanning backward from jr $ra
|
|
uint32_t find_function_start(uint32_t return_addr);
|
|
|
|
// Detect if a region is likely a data section masquerading as code
|
|
bool is_likely_data_section(uint32_t start_addr, uint32_t end_addr) const;
|
|
};
|
|
|
|
} // namespace PSXRecomp
|