Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ add_executable(p3p_pc_bootstrap
target_include_directories(p3p_pc_bootstrap PRIVATE
core/include
recomp/PSPRecomp/include
profiles/p3p/config
)

target_link_libraries(p3p_pc_bootstrap PRIVATE
Expand Down Expand Up @@ -339,6 +340,8 @@ else()
target_link_libraries(p3p3ds_builder PRIVATE p3p3ds_builder_core p3p3ds_core)
endif()
set_target_properties(p3p3ds_builder PROPERTIES OUTPUT_NAME "P3P3DS-Builder")
configure_file(profiles/p3p/config/aot_layout.hpp.in generated/aot_layout.hpp @ONLY)
target_include_directories(p3p3ds_builder PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated")
add_executable(test_psp_eboot tests/test_psp_eboot.cpp)
target_link_libraries(test_psp_eboot PRIVATE p3p3ds_builder_core p3p3ds_core)
file(GLOB P3P_LOCAL_ISOS "${CMAKE_CURRENT_SOURCE_DIR}/*.iso")
Expand Down
6 changes: 6 additions & 0 deletions docs/CURRENT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

This is the current source of truth, newest entries first. Evidence concerns ULUS-10512; each section says whether it was measured on the PC runner, in Azahar or on a real New 3DS.

## Builder AOT layout and runner mod paths use the P3P profile (2026-11-04)

- [VERIFIED] `profiles/p3p/config/aot_layout.cmake` supplies the load base, span and count to CMake and to the Builder through its configured header. The PC and New 3DS runners use the mod alias in `profiles/p3p/config/mod_paths.hpp`, from `p3p/p3p-patches/ULUS10512.ini:6-9`.
- [VERIFIED] Windows Release build, `ctest --test-dir build -j6 --output-on-failure` (27/27), `python tools/verify_aot_layout.py build/generated/auto 237 16384`, stable bootstrap and devkitARM `cmake --build build/3ds --target p3p3ds_objects -j4` passed.
- [VERIFIED] Two PC runs with `--run-until-blocker --max-dispatches 20000000 --ms0 .tmp/issue27/ms0 --mods .tmp/issue27/mods --dump-events <path>` and the pre-change run all reached the dispatch limit at `0x08A87150`. Their event dumps (`.tmp/issue27/baseline.json`, `run1.json`, `run2.json` in the same directory) share SHA-256 `1a5f566002148933522c869ed3febb9eae3d652764501d20e8437b3b1898e605`. No new execution blocker was observed. [UNVERIFIED] Mod loading on physical New 3DS hardware.

## Known issues tracked on GitHub; issue -> pull request workflow (2026-11-04)

No runtime change; the frontier is the same as in the entry below (logo fix 4d198db in master, not released).
Expand Down
2 changes: 1 addition & 1 deletion docs/VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Current execution and coverage measurements: [CURRENT_STATE.md](CURRENT_STATE.md
| Codegen lowering covers 99.91% of .text (912,275 / 913,059) | `[VERIFIED]` | Audit of `codegen_main.cpp` lowering rules in `verify_p3p_decoder` | 912,275 instructions directly lowerable; 62 decoded instructions (10 `vfpu1`, 52 `vfpu4`) hit unlowered generic VFPU stub. |
| Intro Skip Hook address is `0x002594A4` (`li $v0, 6`) | `[VERIFIED]` | `p3p/p3p-patches/ULUS10512.ini:4` | Forces title screen state. |
| Mod Loader path prefix is `ms0:/PSP/GAME/P3P/%s` at `0x00395EE4` | `[WRONG]` | `p3p/p3p-patches/ULUS10512.ini:6-9` | The four words `0x3A30736D 0x5053502F 0x5033502F 0x0073252F` (little-endian) spell `ms0:/PSP/P3P/%s`, as used by the runners and `CURRENT_STATE.md`. |
| Mod Loader path prefix is `ms0:/PSP/P3P/%s` at `0x00395EE4` | `[VERIFIED]` | `p3p/p3p-patches/ULUS10512.ini:6-9`; `platform/pc/main.cpp`, `platform/3ds/main.cpp` | Memory string replacement for loose file redirection. The prefix is hardcoded in both runners instead of `profiles/p3p/config/` (CLAUDE.md section 8). |
| Mod Loader path prefix is `ms0:/PSP/P3P/%s` at `0x00395EE4` | `[VERIFIED]` | `p3p/p3p-patches/ULUS10512.ini:6-9`; `profiles/p3p/config/mod_paths.hpp` | Memory string replacement for loose file redirection. Both runners mount the alias at the profile's prefix. |
| Mod Loader search sequence is `bind/` -> `mod.cpk` -> `mod1.cpk` -> `mod2.cpk` -> `mod3.cpk` | `[VERIFIED]` | `p3p/p3p-patches/ULUS10512.ini:10-24` | Verified from community CWCheat table. |
| P3P event scripts are compiled `.bf` bytecode, not native MIPS | `[VERIFIED]` | `tools/Atlus-Script-Tools/Source/AtlusScriptCompiler/`, `p3p/Persona-3-Portable-Mod-Menu/ModMenu.flow` | Game engine uses an internal bytecode interpreter. |
| P3P uses CriWare CPK archives (`data.cpk`) | `[WRONG]` for the name: ULUS-10512 uses `USRDIR/umd0.cpk`, `umd1.cpk`, `MEMST.CPK` (ISO9660 listing); CPK format itself `[VERIFIED]` | `tools/CriFsV2Lib`, `tools/CriPakTools`, UMD file structure | Main asset container. |
Expand Down
5 changes: 3 additions & 2 deletions platform/3ds/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// written to sdmc:/p3p3ds/report.txt every few seconds and on exit.
//
// Game data: sdmc:/p3p3ds/*.iso (the user's ULUS-10512 image) mounted as
// disc0:, sdmc:/p3p3ds/ms0 as ms0:, sdmc:/p3p3ds/mods as ms0:/PSP/P3P.
// disc0:, sdmc:/p3p3ds/ms0 as ms0:, sdmc:/p3p3ds/mods as the profile's mod directory.
// The decrypted executable is read from romfs:/eboot.elf (embedded at build
// time from the local build; it must match the generated AOT code).
//
Expand All @@ -30,6 +30,7 @@
#include "p3p3ds/profile.hpp"
#include "p3p3ds/vfs.hpp"
#include "bootstrap_expectations.hpp"
#include "mod_paths.hpp"
#include "gpu_renderer.hpp"
#include "p3p3ds/ge/async_renderer.hpp"
#include "psprecomp/elf32.hpp"
Expand Down Expand Up @@ -825,7 +826,7 @@ int main() {
kernel.io().mount("ms0:", std::make_shared<p3p3ds::vfs::HostFileSystem>(ms0), true);
kernel.savedata().root = ms0 + "/PSP/SAVEDATA";
// Read-only and unchanged while the game runs: lookups cached (vfs.hpp).
kernel.io().alias("ms0:/PSP/P3P", std::make_shared<p3p3ds::vfs::HostFileSystem>(std::string(kBase) + "/mods", true));
kernel.io().alias(p3p3ds::profile::kModPrefix, std::make_shared<p3p3ds::vfs::HostFileSystem>(std::string(kBase) + "/mods", true));
kernel.threads().init_root_thread("root", entry, sp, module->gp);
std::set<std::uint32_t> stubs;
std::vector<std::tuple<std::string, std::uint32_t, std::uint32_t>> imports;
Expand Down
2 changes: 1 addition & 1 deletion platform/pc/builder/fun_facts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ inline constexpr const wchar_t *kFunFacts[] = {
L"Persona 3 Portable was re-released for modern consoles and PC in January 2023.",
L"Persona 3 Reload (2024) remade the game from scratch, but without the female protagonist.",
L"P3P3DS does not emulate the PSP processor on your 3DS: the game's 3.6 MB of MIPS code is translated to C++ and compiled to about 44 MB of ARM code - right now, on your PC.",
L"About 750,000 PSP instructions are being translated in this build, grouped into 237 C++ files of 16 KiB of game code each.",
L"About 750,000 PSP instructions are being translated in this build and split across C++ files for parallel compilation.",
L"On the 3DS, the PICA200 graphics chip draws what the PSP's Graphics Engine drew. The 480x272 picture is scaled to the 400x240 top screen.",
L"Your ISO stays on the SD card: P3P3DS reads the game's CPK archives from it while you play.",
L"Mods made for the community \"Mod Support\" patch (mod.cpk, bind/) go to sdmc:/p3p3ds/mods/.",
Expand Down
11 changes: 6 additions & 5 deletions platform/pc/builder/pipeline.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// See pipeline.hpp.
#include "pipeline.hpp"
#include "aot_layout.hpp"

#include "psp_eboot.hpp"
#include "p3p3ds/vfs.hpp"
Expand Down Expand Up @@ -34,9 +35,6 @@ namespace fs = std::filesystem;
namespace p3p3ds::builder {
namespace {

constexpr unsigned kUnitCount = 237; // profiles/p3p/config/aot_layout.cmake
constexpr const char *kLoadBase = "0x08804000";
constexpr const char *kUnitSpan = "16384";
// Measured on the largest unit and the whole program (docs/3DS_PLATFORM.md
// section 8.5): GCSE and instruction scheduling cost 27 % of the compile
// time for 0.8 % of code size.
Expand Down Expand Up @@ -316,14 +314,17 @@ void Pipeline::generate() {
std::error_code ec;
fs::remove_all(gen_, ec);
fs::create_directories(gen_);
const Args cmd = {tool(s_.sdk / "bin", "psp_recomp"), elf_, "--auto", gen_, kLoadBase, kUnitSpan, s_.sdk / "patches.txt",
const Args cmd = {tool(s_.sdk / "bin", "psp_recomp"), elf_, "--auto", gen_, profile::kAotLoadBase, profile::kAotUnitSpan, s_.sdk / "patches.txt",
"--no-transfer-records"};
const fs::path log = s_.work / "generate.log";
if (run_tool(cmd, log) != 0) throw std::runtime_error("Code generation failed:\n" + read_text(log));
unsigned units = 0;
for (const auto &e : fs::directory_iterator(gen_))
if (e.path().filename().string().rfind("generated_unit_", 0) == 0) ++units;
if (units != kUnitCount) throw std::runtime_error("Code generation produced " + std::to_string(units) + " units, expected 237.");
if (units != profile::kAotUnitCount) {
throw std::runtime_error("Code generation produced " + std::to_string(units) +
" units, expected " + std::to_string(profile::kAotUnitCount) + ".");
}
write_text(stamp, want);
}

Expand Down
9 changes: 5 additions & 4 deletions platform/pc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "host_input.hpp"
#include "p3p3ds/profile.hpp"
#include "sampler.hpp"
#include "mod_paths.hpp"

#include <algorithm>
#include <chrono>
Expand Down Expand Up @@ -113,8 +114,8 @@ int main(int argc,char **argv) {
} else std::cout<<"UMD image: none (disc0: unmounted)\n";
kernel.io().trace_reads=!io_trace_path.empty();
{
// ms0: memory stick root; the community Mod Support patch reads
// ms0:/PSP/P3P/{bind/,mod.cpk,mod1-3.cpk}, mapped onto --mods.
// Mount the memory stick root and optionally map the profile's
// Mod Support directory onto --mods.
std::filesystem::create_directories(ms0_path);
kernel.io().mount("ms0:",std::make_shared<p3p3ds::vfs::HostFileSystem>(ms0_path),true);
// Saves: ms0:/PSP/SAVEDATA. The list dialogs have no window here, so
Expand All @@ -130,8 +131,8 @@ int main(int argc,char **argv) {
return best>=0 ? best : (d.kind==p3p3ds::hle::SavedataDialog::Kind::Save ? 0 : -1);
};
if(!mods_path.empty()) {
kernel.io().alias("ms0:/PSP/P3P",std::make_shared<p3p3ds::vfs::HostFileSystem>(mods_path,true)); // read-only: cached lookups
std::cout<<"Mods: ms0:/PSP/P3P -> "<<mods_path.string()<<"\n";
kernel.io().alias(p3p3ds::profile::kModPrefix,std::make_shared<p3p3ds::vfs::HostFileSystem>(mods_path,true)); // read-only: cached lookups
std::cout<<"Mods: "<<p3p3ds::profile::kModPrefix<<" -> "<<mods_path.string()<<"\n";
}
}
kernel.threads().init_root_thread("root",entry,sp,module->gp);
Expand Down
10 changes: 10 additions & 0 deletions profiles/p3p/config/aot_layout.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

namespace p3p3ds::profile {

// Values and verification evidence come from aot_layout.cmake.
inline constexpr unsigned kAotUnitCount = @P3P_AOT_UNIT_COUNT@;
inline constexpr char kAotLoadBase[] = "@P3P_AOT_LOAD_BASE@";
inline constexpr char kAotUnitSpan[] = "@P3P_AOT_UNIT_SPAN@";

} // namespace p3p3ds::profile
8 changes: 8 additions & 0 deletions profiles/p3p/config/mod_paths.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

namespace p3p3ds::profile {

// [VERIFIED] p3p/p3p-patches/ULUS10512.ini:6-9 (Mod Support).
inline constexpr char kModPrefix[] = "ms0:/PSP/P3P";

} // namespace p3p3ds::profile