10 Commits

Author SHA1 Message Date
Ranieri 93e221feaa Feature/runtime ecosystem refactor (#107)
* feat: remove memory and pad from stub section

* feat: add support for resume entry targets in CodeGenerator (this allow jumps in address outside function)
feat: refactor entry point discovery one more try to reduce big generated file

* feat: optmizations for release build

* feat: remove unused  file

* feat: added some test cases for code gen

* feat: added log macro and remove win specific code

* feat: refactor runtime folder structure
feat: added reset sound driver RPC state and compatibility layout
feat: rename and added new test
feat: update RPC calls to use defined constants
feat: added more PSMC(16, 32)
feat: change cd read to try find the asset ignoring case sensitive
fix: fix some render problems
feat: add logs on pad
feat: added more RPC handles

* feat: added game override for code veronica

* feat: apply vita patch

* feat: flags to disable build

* feat: fix merges
feat: break a lot of tests

* feat: better throw error on empty cd path
feat: remove recompiler unusde function
feat: apply missing patch

* feat: gamedp is now part of lib
feat: missing file

* feat: small cleanup

* feat: missing vita changes

* feat: fix more merge

* feat: fix tests

* feat: last missing feature

* feat: added missing import

* feat: rename test local functions

* feat: init syscall on ps2 list

* feat: added DMA helpers

* feat: faster builds
feat: more implement for darkcloud

* feat: back missing file

* feat: added missing includes

* feat: remove test

* feat: missing include

* feat: read register funtion

* feat: build  fix

* feat: force  exit on detach thread
2026-04-04 23:09:56 -03:00
roby65 553a9027d8 sceGsSetDefDBuff and sceGsSwapDBuff implementation based on the DC version (#106) 2026-03-24 21:34:17 -03:00
Ranieri cad1ca0bb5 Feature/added execution guess gs psmt (#104)
* fix: CRITICAL fix on code gen on generating BEQ translation, I added a small yeld because goto could spin forever and monopolize guest execution

* feat: add scratchpad alias base and improve scratchpad address handling

* feat: added debug logging on GifArbiter for submit and drain operations

* feat: add interrupt and thread management syscall implementations
fix: change some IDs calls to match ps2sdk

* feat: added vif1 logs

* feat: added logs on gs gpu
feat: added performLocalToLocalTransfer to GS emulation path for TRXDIR = 2. (emulates the PS2 GS “copy this rectangle from one place in VRAM to another”)

* feat: added PSMT8 and refactor PSMT4

* feat: some identation on vu1
feat: added some logs on vu1

* feat: added GuestExecutionScope to temporarily stop owning guest execution, then restore it exactly as it was.
feat: added vsync wizardry
feat: added some regression test

* fix: fix gs logger

* feat: remove extra logs I think they will help no one
feat: move join all threads to prevent the app to get stuck on close, but now it random crash on closing
feat: one more small test on psmt4 to try fix ghosting on re code veronica

* feat: added a small case for exporter from ghidra for metal slug 3

* feat: added ugly code to pass on test
2026-03-18 19:14:40 -03:00
Ranieri 7ca6a866c9 Feature/ghidra export and syscall fixes (#100)
* docs: deprecate the local analyzer workflow in favor of Ghidra
feat: improve the Ghidra exporter for stripped games and internal entry points
fix: correct FindAddress behavior in the runtime
fix: emit missing delay-slot code in recompiler  edge cases
feat: add SetSyscall support from @Whoneon
feat: add dispatchSyscallOverride support from @Whoneon
fix: fix  Unmatched '{' due to missing newlines from issue #96
feat: delete python ghidra script I never updated it anyway
feat: added a lot more of regression test
feat: added a lot of logs to help debug on runtime
fix: fix wrong syscall ID on runtime
2026-03-09 00:41:51 -03:00
Antonio Guastella 9e4fd794c1 kernel: implement syscall 0x83 FindAddress + unit tests (#93) 2026-03-06 12:55:34 -03:00
Antonio Guastella 493522a769 recompiler: filter auto-generated map symbols to reduce oversized outputs (#95) 2026-03-05 11:14:41 -03:00
Antonio Guastella da10073cb9 ghidra: normalize Windows executable path in exported TOML input (#92) 2026-03-03 16:19:31 -03:00
Antonio Guastella ccd17e5244 Issue 13: Minimal libpad (SIO2/PAD) stubs, input mapping and tests (#69)
* pad: implement basic input mapping and stubs

Add a minimal libpad implementation backed by raylib input
Provide override hooks for deterministic tests
Add pad input tests and link runtime into test target

* FIX: moved pad override test hooks to ps2_call_list.h

* pad: move test hooks to X macro list + removing __cplusplus ifdef

* removed one last ifdef remaining

* refresh PR merge status

---------

Co-authored-by: Ranieri <ran-junior@hotmail.com>
2026-03-03 16:02:04 -03:00
Vova Vovchok 765cbcca0f Integrate ps2xStudio and improve elf_analyzer callback detection (#83)
* Integrate ps2xStudio and improve elf_analyzer callback detection

* Update CMakeLists.txt for ImGui libraries

* CMakeLists Fixes Again

Updated dependencies and improved comments for clarity.

* Again CMakeLists. Now it definitely works

* again CMakeLists

* Update CMakeLists.txt ps2xStudio & ps2xTest

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update StudioState.hpp

* Update CMakeLists.txt
2026-03-01 16:41:33 -03:00
roby65 b3be2650b0 Fixed recompiler error when the cpp filename is too long (#88)
* Fix error when filename is too long

* Fix typo

* Added tests
2026-03-01 16:39:42 -03:00
150 changed files with 32677 additions and 13695 deletions
+51 -10
View File
@@ -1,14 +1,40 @@
cmake_minimum_required(VERSION 3.21)
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{VITASDK})
set(PS2X_VITA_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake")
if(EXISTS "${PS2X_VITA_TOOLCHAIN_FILE}")
set(CMAKE_TOOLCHAIN_FILE "${PS2X_VITA_TOOLCHAIN_FILE}" CACHE PATH
"Toolchain file used for cross-compiling" FORCE)
message(STATUS "Using VitaSDK toolchain from VITASDK: ${CMAKE_TOOLCHAIN_FILE}")
endif()
endif()
project("PS2 Retro X")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(PS2X_RUNTIME OFF)
option(PS2X_BUILD_RECOMP "Build ps2xRecomp" ON)
option(PS2X_BUILD_RUNTIME "Build ps2xRuntime" ON)
option(PS2X_BUILD_ANALYZER "Build ps2xAnalyzer" ON)
option(PS2X_BUILD_TEST "Build ps2xTest" ON)
option(PS2X_BUILD_STUDIO "Build ps2xStudio" ON)
# ARM64 support using sse2neon
set(PS2X_IS_ARM_TARGET OFF)
set(PS2X_IS_AARCH64_TARGET OFF)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64")
message(STATUS "ARM64 detected, fetching sse2neon")
set(PS2X_IS_ARM_TARGET ON)
set(PS2X_IS_AARCH64_TARGET ON)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm|^ARM")
set(PS2X_IS_ARM_TARGET ON)
endif()
if((CMAKE_C_COMPILER MATCHES "arm-vita-eabi") OR
(CMAKE_CXX_COMPILER MATCHES "arm-vita-eabi"))
set(PS2X_IS_ARM_TARGET ON)
endif()
if(PS2X_IS_ARM_TARGET)
message(STATUS "ARM target detected, fetching sse2neon")
include(FetchContent)
FetchContent_Declare(
@@ -22,13 +48,13 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64")
include_directories(${sse2neon_SOURCE_DIR})
add_compile_definitions(USE_SSE2NEON)
if(APPLE)
if(PS2X_IS_AARCH64_TARGET AND APPLE)
# macOS ARM64 already uses optimal defaults
message(STATUS "macOS ARM64 - using default compiler flags")
elseif(MSVC)
elseif(PS2X_IS_AARCH64_TARGET AND MSVC)
# Windows ARM64 - MSVC already uses optimal defaults
message(STATUS "Windows ARM64 (MSVC) - using default compiler flags")
else()
elseif(PS2X_IS_AARCH64_TARGET)
# Linux ARM64 - add NEON flags
message(STATUS "Non-Apple ARM64 - adding NEON compiler flags")
add_compile_options(-march=armv8-a+fp+simd)
@@ -39,13 +65,28 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64")
if(COMPILER_SUPPORTS_CRYPTO_CRC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+fp+simd+crypto+crc")
message(STATUS "Crypto and CRC extensions enabled")
else()
add_compile_options(-march=armv8-a+fp+simd)
endif()
endif()
endif()
add_subdirectory("ps2xRecomp")
if(PS2X_BUILD_RECOMP)
add_subdirectory("ps2xRecomp")
endif()
add_subdirectory("ps2xRuntime")
if(PS2X_BUILD_RUNTIME)
add_subdirectory("ps2xRuntime")
endif()
add_subdirectory("ps2xAnalyzer")
add_subdirectory("ps2xTest")
if(PS2X_BUILD_ANALYZER)
add_subdirectory("ps2xAnalyzer")
endif()
if(PS2X_BUILD_TEST)
add_subdirectory("ps2xTest")
endif()
if(PS2X_BUILD_STUDIO)
add_subdirectory("ps2xStudio")
endif()
+18 -10
View File
@@ -61,27 +61,35 @@ cmake --build out/build --config Debug
### Usage
1. Analyze ELF and generate config:
Preferred workflow for retail or stripped games:
```bash
./ps2_analyzer your_game.elf config.toml
```
*For better results on retail games, see the [Ghidra Workflow](ps2xAnalyzer/Readme.md#3-ghidra-integration-recommended-for-complex-games).*
2. Recompile using generated TOML:
1. Open the ELF in Ghidra.
2. Run `ps2xRecomp/tools/ghidra/ExportPS2Functions.java`.
3. Use the exported TOML and CSV map.
4. Recompile with the exported TOML:
```bash
./ps2_recomp config.toml
```
3. Build generated output and link with `ps2xRuntime`.
Fallback workflow for quick local experiments or ELFs with debug symbol :
```bash
./ps2_analyzer your_game.elf config.toml
```
Use this only when you do not have a Ghidra project yet. The native analyzer is faster to start, but it is less accurate on stripped retail games and more likely to miss internal callable entry points.
See the [Ghidra Workflow](ps2xAnalyzer/Readme.md#3-ghidra-integration-for-retail-and-stripped-games-preferred) for the recommended path.
Then build generated output and link with `ps2xRuntime`.
### Configuration
Main fields in `config.toml`:
* `general.input`: source ELF path.
* `general.ghidra_output`: optional function map CSV.
* `general.ghidra_output`: recommended function map CSV exported from Ghidra.
* `general.output`: generated C++ output folder.
* `general.single_file_output`: one combined cpp or one file per function.
* `general.patch_syscalls`: apply configured patches to `SYSCALL` instructions (`false` recommended).
@@ -96,7 +104,7 @@ Address binding for stripped ELFs:
* Use `handler@0xADDRESS` inside `general.stubs` to map a stripped function start directly to a runtime handler.
* Example: `sceCdRead@0x00123456` binds function start `0x00123456` to `ps2_stubs::sceCdRead(...)`.
* Generic temporary handlers are available: `ret0@0xADDR`, `ret1@0xADDR`, `reta0@0xADDR`.
* Before manual binding, try plain recompilation first: if ELF relocation symbols are present for calls, runtime handler routing can be inferred automatically.
* Before manual binding, prefer recompilation from a Ghidra-exported TOML/CSV first. The extra boundaries and synthetic entry points are usually more important than manual early triage.
* The address must be the function start in that exact ELF build.
* Addresses are not portable across different games/regions/builds.
* The handler name must exist in runtime call lists (`PS2_SYSCALL_LIST` or `PS2_STUB_LIST`).
+7
View File
@@ -32,3 +32,10 @@ install(TARGETS ps2_analyzer ps2_analyzer_lib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
include("${CMAKE_SOURCE_DIR}/ps2xRuntime/cmake/ReleaseMode.cmake")
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
EnableFastReleaseMode(ps2_analyzer_lib)
EnableFastReleaseMode(ps2_analyzer)
endif()
+18 -11
View File
@@ -9,18 +9,21 @@ The analyzer supports three distinct paths for discovering code within a PS2 bin
### 1. DWARF Debug Information
If the ELF was compiled with debug symbols (`-g`), the analyzer uses `libdwarf` to extract perfect function names and exact start/end addresses. This is common in homebrew or early development builds.
### 2. Native Heuristic Scanner (Retail/Stripped)
### 2. Native Heuristic Scanner (Test only)
For commercial games where symbols are stripped, the analyzer uses a "JAL Scanner":
* It scans executable sections for `JAL` (Jump and Link) instructions.
* It infers function start points based on jump targets.
* It generates names like `sub_XXXXXXXX`.
### 3. Ghidra Integration (For Complex Games)
For the highest accuracy in stripped games, you can use Ghidra's superior analysis engine:
1. Use the provided script: `ps2xRecomp/tools/ghidra/ExportPS2Functions.py` or `.java`.
Use this path only as a quick fallback when you do not yet have a Ghidra project. It is not the preferred workflow for retail games.
### 3. Ghidra Integration (For Retail and Stripped Games, Preferred)
This is the recommended workflow for almost every commercial game:
1. Use the provided script: `ps2xRecomp/tools/ghidra/ExportPS2Functions.java`.
2. Run it in Ghidra to export a CSV map of all functions.
3. Add the CSV path to your TOML: `ghidra_output = "path/to/map.csv"`.
4. The recompiler will prioritize Ghidra's boundaries over its own heuristics.
3. Let the script generate the TOML, and keep the CSV path in `ghidra_output = "path/to/map.csv"`.
4. Run the recompiler with that exported TOML.
5. The recompiler will prioritize Ghidra's boundaries over its own heuristics.
## Key Features
@@ -41,12 +44,16 @@ ps2_analyzer <input_elf> <output_toml>
* `output_toml`: Path where the generated TOML configuration will be saved.
## Example Workflow
1. Run the analyzer on your game:
`ps2_analyzer game.elf config.toml`
2. (Optional) Open `game.elf` in Ghidra, run the export script, and update `config.toml` with the CSV path.
3. Run the recompiler:
1. Open `game.elf` in Ghidra.
2. Run `ps2xRecomp/tools/ghidra/ExportPS2Functions.java`.
3. Use the exported TOML and CSV.
4. Run the recompiler:
`ps2recomp config.toml`
Fallback:
1. Run `ps2_analyzer game.elf config.toml`.
2. Use that TOML only for quick bring-up or symbol-rich builds.
## Generated Configuration
The tool creates a TOML file with the following sections:
* `[general]`: Paths to ELF and Ghidra maps.
@@ -60,4 +67,4 @@ The tool creates a TOML file with the following sections:
* Self-modifying code is flagged but requires manual review.
* Indirect jumps (jump tables) are detected but complex ones might need manual TOML entries.
For more details on the recompilation process, see the [Main README](../README.md).
For more details on the recompilation process, see the [Main README](../README.md).
@@ -33,6 +33,8 @@ namespace ps2recomp
bool analyze();
bool generateToml(const std::string &outputPath);
bool importGhidraMap(const std::string &csvPath);
const std::vector<Function>& getFunctions() const;
bool isLibrarySymbolNameForHeuristics(const std::string &name) const;
static bool isReliableSymbolNameForHeuristics(const std::string &name);
static bool isSystemSymbolNameForHeuristics(const std::string &name);
+101 -2
View File
@@ -24,6 +24,7 @@ namespace ps2recomp
static bool hasPs2ApiPrefix(const std::string &name);
static bool hasReliableSymbolName(const std::string &name);
static bool isDoNotSkipOrStub(const std::string &name);
static bool isKnownLocalHelperName(const std::string &name);
static bool matchesKernelRuntimeName(const std::string &name);
static uint32_t decodeAbsoluteJumpTarget(uint32_t instructionAddress, uint32_t targetField);
static bool tryReadWord(const ElfParser *parser, uint32_t address, uint32_t &outWord);
@@ -313,7 +314,7 @@ namespace ps2recomp
"malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign",
// Memory manipulation
"memcpy", "memset", "memmove", "memcmp", "memcpy2", "memchr", "bcopy", "bzero",
"memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero",
// String manipulation
"strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr",
@@ -2020,7 +2021,6 @@ namespace ps2recomp
const std::vector<std::string> libraryPrefixes = {
"sce", "Sce", "SCE", // Sony prefixes
"sif", "Sif", "SIF", // SIF functions
"pad", "Pad", "PAD", // Pad functions
"gs", "Gs", "GS", // Graphics Synthesizer
"dma", "Dma", "DMA", // DMA functions
"iop", "Iop", "IOP", // IOP functions
@@ -2068,6 +2068,15 @@ namespace ps2recomp
return kDoNotSkipOrStub.contains(name);
}
static bool isKnownLocalHelperName(const std::string &name)
{
static const std::unordered_set<std::string> kKnownLocalHelpers = {
"memcpy2",
"_memcpy2"};
return kKnownLocalHelpers.contains(name);
}
static bool hasReliableSymbolName(const std::string &name)
{
if (name.empty())
@@ -2182,12 +2191,18 @@ namespace ps2recomp
if (!hasReliableSymbolName(name))
return false;
if (isKnownLocalHelperName(name))
return false;
std::string normalizedName = name;
if (normalizedName[0] == '_' && normalizedName.size() > 1)
{
normalizedName = normalizedName.substr(1);
}
if (isKnownLocalHelperName(normalizedName))
return false;
if (matchesKernelRuntimeName(normalizedName))
return true;
@@ -2542,4 +2557,88 @@ namespace ps2recomp
return false;
}
}
bool ElfAnalyzer::importGhidraMap(const std::string &csvPath)
{
std::ifstream file(csvPath);
if (!file)
{
std::cerr << "Failed to open Ghidra CSV file: " << csvPath << std::endl;
return false;
}
std::string line;
int lineNum = 0;
int importedCount = 0;
while (std::getline(file, line))
{
lineNum++;
// Skip header line
if (lineNum == 1 && line.find("Name") != std::string::npos)
{
continue;
}
// Parse CSV line: Name,Start,End,Size
std::stringstream ss(line);
std::string name, startStr, endStr, sizeStr;
if (!std::getline(ss, name, ',') ||
!std::getline(ss, startStr, ',') ||
!std::getline(ss, endStr, ','))
{
continue; // Skip malformed lines
}
// Parse hex addresses (e.g., "0x00123456")
uint32_t startAddr = 0;
uint32_t endAddr = 0;
try
{
startAddr = std::stoul(startStr, nullptr, 16);
endAddr = std::stoul(endStr, nullptr, 16);
}
catch (...)
{
continue; // Skip lines with invalid addresses
}
// Update existing function or create new one
bool found = false;
for (auto &func : m_functions)
{
if (func.start == startAddr)
{
// Update with Ghidra's more accurate boundaries
func.name = name;
func.end = endAddr;
found = true;
importedCount++;
break;
}
}
// If not found, create new function from Ghidra data
if (!found)
{
Function newFunc;
newFunc.name = name;
newFunc.start = startAddr;
newFunc.end = endAddr;
m_functions.push_back(newFunc);
importedCount++;
}
}
std::cout << "Imported " << importedCount << " functions from Ghidra CSV: " << csvPath << std::endl;
return true;
}
const std::vector<Function>& ElfAnalyzer::getFunctions() const
{
return m_functions;
}
}
+7
View File
@@ -136,3 +136,10 @@ install(TARGETS ps2_recomp ps2_recomp_lib
install(DIRECTORY include/
DESTINATION include
)
include("${CMAKE_SOURCE_DIR}/ps2xRuntime/cmake/ReleaseMode.cmake")
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
EnableFastReleaseMode(ps2_recomp_lib)
EnableFastReleaseMode(ps2_recomp)
endif()
@@ -37,6 +37,8 @@ namespace ps2recomp
struct AnalysisResult {
std::unordered_set<uint32_t> entryPoints;
std::unordered_set<uint32_t> externalEntryPoints;
std::unordered_set<uint32_t> resumeEntryPoints;
std::unordered_map<uint32_t, std::vector<uint32_t>> jumpTableTargets;
};
@@ -49,15 +51,18 @@ namespace ps2recomp
void setBootstrapInfo(const BootstrapInfo &info);
void setRelocationCallNames(const std::unordered_map<uint32_t, std::string> &callNames);
void setConfiguredJumpTables(const std::vector<JumpTable> &jumpTables);
void setResumeEntryTargets(const std::unordered_map<uint32_t, std::vector<uint32_t>> &resumeTargetsByOwner);
AnalysisResult collectInternalBranchTargets(const Function &function,
const std::vector<Instruction> &instructions);
const std::vector<Instruction> &instructions,
const std::vector<Function> *allFunctions = nullptr);
public:
std::unordered_map<uint32_t, Symbol> m_symbols;
std::unordered_map<uint32_t, std::string> m_renamedFunctions;
std::unordered_map<uint32_t, std::string> m_relocationCallNames;
std::unordered_map<uint32_t, std::vector<uint32_t>> m_configJumpTableTargetsByAddress;
std::unordered_map<uint32_t, std::vector<uint32_t>> m_resumeEntryTargetsByOwner;
const std::vector<Section>& m_sections;
BootstrapInfo m_bootstrapInfo;
+4 -1
View File
@@ -5,6 +5,7 @@
#include <string>
#include <vector>
#include <memory>
#include <unordered_set>
namespace ps2recomp
{
@@ -45,6 +46,8 @@ namespace ps2recomp
std::vector<Symbol> m_symbols;
std::vector<Relocation> m_relocations;
std::vector<Function> m_extraFunctions;
bool m_hasLoadedGhidraMap = false;
std::unordered_set<uint32_t> m_ghidraMapStarts;
void loadSections();
void loadSymbols();
@@ -56,4 +59,4 @@ namespace ps2recomp
} // namespace ps2recomp
#endif // PS2RECOMP_ELF_PARSER_H
#endif // PS2RECOMP_ELF_PARSER_H
@@ -40,6 +40,8 @@ namespace ps2recomp
std::vector<Function> &functions,
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions);
static std::string ClampFilenameLength(const std::string& baseName, const std::string& extension, std::size_t maxLength);
private:
ConfigManager m_configManager;
std::unique_ptr<ElfParser> m_elfParser;
@@ -60,6 +62,7 @@ namespace ps2recomp
std::unordered_map<uint32_t, std::string> m_stubHandlerBindingsByStart;
std::map<uint32_t, std::string> m_generatedStubs;
std::unordered_map<uint32_t, std::string> m_functionRenames;
std::unordered_map<uint32_t, std::vector<uint32_t>> m_resumeEntryTargetsByOwner;
CodeGenerator::BootstrapInfo m_bootstrapInfo;
bool decodeFunction(Function &function);
@@ -70,6 +73,7 @@ namespace ps2recomp
bool generateStubHeader();
bool writeToFile(const std::string &path, const std::string &content);
std::filesystem::path getOutputPath(const Function &function) const;
static std::string clampFilenameLength(const std::string& baseName, const std::string& extension, std::size_t maxLength);
std::string sanitizeFunctionName(const std::string &name) const;
};
+264 -58
View File
@@ -37,6 +37,16 @@ namespace ps2recomp
return ((address + 4) & 0xF0000000u) | (target << 2);
}
static Instruction makeSyntheticDelaySlot(uint32_t address)
{
Instruction inst{};
inst.address = address;
inst.raw = 0;
inst.opcode = OPCODE_SPECIAL;
inst.function = SPECIAL_SLL;
return inst;
}
static std::string formatFloatLiteral(float value)
{
if (!std::isfinite(value))
@@ -143,6 +153,17 @@ namespace ps2recomp
}
}
void CodeGenerator::setResumeEntryTargets(const std::unordered_map<uint32_t, std::vector<uint32_t>> &resumeTargetsByOwner)
{
m_resumeEntryTargetsByOwner = resumeTargetsByOwner;
for (auto &[owner, targets] : m_resumeEntryTargetsByOwner)
{
(void)owner;
std::sort(targets.begin(), targets.end());
targets.erase(std::unique(targets.begin(), targets.end()), targets.end());
}
}
std::string CodeGenerator::getFunctionName(uint32_t address) const
{
auto it = m_renamedFunctions.find(address);
@@ -217,6 +238,24 @@ namespace ps2recomp
const uint32_t branchPc = branchInst.address;
const uint32_t delayPc = branchInst.address + 4u;
const uint32_t fallthroughPc = branchInst.address + 8u;
auto emitInternalTarget = [&](uint32_t target, uint32_t sourcePc, std::string_view indent)
{
ss << fmt::format("{}ctx->pc = 0x{:X}u;\n", indent, target);
const bool isCallLikeEdge =
(branchInst.opcode == OPCODE_JAL) ||
(branchInst.opcode == OPCODE_SPECIAL && branchInst.function == SPECIAL_JALR);
if (target <= sourcePc && !isCallLikeEdge)
{
ss << fmt::format("{}if (runtime->shouldPreemptGuestExecution()) {{\n", indent);
ss << fmt::format("{} return;\n", indent);
ss << fmt::format("{}}}\n", indent);
ss << fmt::format("{}goto label_{:x};\n", indent, target);
}
else
{
ss << fmt::format("{}goto label_{:x};\n", indent, target);
}
};
std::vector<uint32_t> sortedInternalTargets;
if (branchInst.opcode == OPCODE_SPECIAL &&
@@ -283,8 +322,7 @@ namespace ps2recomp
if (internalTargets.contains(target))
{
ss << fmt::format(" ctx->pc = 0x{:X}u;\n", target);
ss << fmt::format(" goto label_{:x};\n", target);
emitInternalTarget(target, branchPc, " ");
}
else
{
@@ -569,8 +607,7 @@ namespace ps2recomp
if (internalTargets.contains(target))
{
ss << fmt::format(" ctx->pc = 0x{:X}u;\n", target);
ss << fmt::format(" goto label_{:x};\n", target);
emitInternalTarget(target, branchPc, " ");
}
else
{
@@ -596,8 +633,7 @@ namespace ps2recomp
ss << " if (" << branchTakenVar << ") {\n";
if (internalTargets.contains(target))
{
ss << fmt::format(" ctx->pc = 0x{:X}u;\n", target);
ss << fmt::format(" goto label_{:x};\n", target);
emitInternalTarget(target, branchPc, " ");
}
else
{
@@ -632,7 +668,7 @@ namespace ps2recomp
CodeGenerator::~CodeGenerator() = default;
CodeGenerator::AnalysisResult CodeGenerator::collectInternalBranchTargets(
const Function &function, const std::vector<Instruction> &instructions)
const Function &function, const std::vector<Instruction> &instructions, const std::vector<Function> *allFunctions)
{
AnalysisResult result;
std::unordered_set<uint32_t> instructionAddresses;
@@ -640,6 +676,97 @@ namespace ps2recomp
bool hasIndirectRegisterJump = false;
std::vector<const Instruction*> indirectJumps;
auto isExecutableAddress = [&](uint32_t address) -> bool
{
for (const auto &section : m_sections)
{
if (!section.isCode)
{
continue;
}
if (address >= section.address && address < (section.address + section.size))
{
return true;
}
}
return false;
};
auto findContainingExternalFunction = [&](uint32_t address) -> const Function *
{
if (!allFunctions || !isExecutableAddress(address))
{
return nullptr;
}
const Function *best = nullptr;
for (const auto &candidateFn : *allFunctions)
{
if (!candidateFn.isRecompiled || candidateFn.isStub || candidateFn.isSkipped)
{
continue;
}
if (candidateFn.name.rfind("entry_", 0) == 0)
{
continue;
}
if (address < candidateFn.start || address >= candidateFn.end)
{
continue;
}
if (!best || candidateFn.start > best->start)
{
best = &candidateFn;
}
}
return best;
};
auto queueExternalEntryTarget = [&](uint32_t target)
{
const Function *containingFn = findContainingExternalFunction(target);
if (!containingFn)
{
return;
}
if (containingFn->start == function.start)
{
return;
}
if (target == containingFn->start)
{
return;
}
result.externalEntryPoints.insert(target);
};
auto queueResumeEntryTarget = [&](uint32_t resumeAddr)
{
if (resumeAddr >= function.start && resumeAddr < function.end &&
instructionAddresses.contains(resumeAddr))
{
result.entryPoints.insert(resumeAddr);
result.resumeEntryPoints.insert(resumeAddr);
}
};
auto queueLoopResumeEntryTarget = [&](uint32_t target, uint32_t sourcePc)
{
if (target > sourcePc || target == function.start)
{
return;
}
queueResumeEntryTarget(target);
};
for (const auto &inst : instructions)
{
instructionAddresses.insert(inst.address);
@@ -665,6 +792,11 @@ namespace ps2recomp
instructionAddresses.contains(target))
{
result.entryPoints.insert(target);
queueLoopResumeEntryTarget(target, inst.address);
}
else
{
queueExternalEntryTarget(target);
}
}
else if (isStaticJump)
@@ -674,15 +806,20 @@ namespace ps2recomp
instructionAddresses.contains(target))
{
result.entryPoints.insert(target);
queueLoopResumeEntryTarget(target, inst.address);
if (inst.opcode == OPCODE_JAL)
{
uint32_t returnAddr = inst.address + 8;
if (returnAddr >= function.start && returnAddr < function.end &&
instructionAddresses.contains(returnAddr))
{
result.entryPoints.insert(returnAddr);
}
queueResumeEntryTarget(inst.address + 8u);
}
}
else
{
queueExternalEntryTarget(target);
if (inst.opcode == OPCODE_JAL)
{
queueResumeEntryTarget(inst.address + 8u);
}
}
}
@@ -692,6 +829,11 @@ namespace ps2recomp
{
bool needsJrFallback = false;
for (const Instruction* jrInst : indirectJumps) {
if (jrInst->function == SPECIAL_JALR)
{
queueResumeEntryTarget(jrInst->address + 8u);
}
bool foundTable = false;
uint32_t jrReg = jrInst->rs;
@@ -770,6 +912,10 @@ namespace ps2recomp
{
jrTargets.push_back(target);
}
else
{
queueExternalEntryTarget(target);
}
}
if (!jrTargets.empty())
@@ -829,6 +975,10 @@ namespace ps2recomp
uniqueTargets.insert(target);
}
}
else
{
queueExternalEntryTarget(target);
}
} else {
validJumpTable = false;
break;
@@ -889,6 +1039,20 @@ namespace ps2recomp
}
AnalysisResult analysisResult = collectInternalBranchTargets(function, instructions);
std::vector<uint32_t> resumeTargets(analysisResult.resumeEntryPoints.begin(),
analysisResult.resumeEntryPoints.end());
auto resumeIt = m_resumeEntryTargetsByOwner.find(function.start);
if (resumeIt != m_resumeEntryTargetsByOwner.end())
{
resumeTargets.insert(resumeTargets.end(), resumeIt->second.begin(), resumeIt->second.end());
}
std::sort(resumeTargets.begin(), resumeTargets.end());
resumeTargets.erase(std::unique(resumeTargets.begin(), resumeTargets.end()), resumeTargets.end());
for (uint32_t target : resumeTargets)
{
analysisResult.entryPoints.insert(target);
}
const std::unordered_set<uint32_t>& internalTargets = analysisResult.entryPoints;
ss << "// Function: " << function.name << "\n";
ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n";
@@ -906,6 +1070,16 @@ namespace ps2recomp
ss << " PS_LOG_ENTRY(\"" << sanitizedName << "\");\n";
ss << "#endif\n";
ss << "\n";
if (!resumeTargets.empty())
{
ss << " switch (ctx->pc) {\n";
for (uint32_t target : resumeTargets)
{
ss << " case 0x" << std::hex << target << "u: goto label_" << target << ";\n" << std::dec;
}
ss << " default: break;\n";
ss << " }\n\n";
}
ss << " ctx->pc = 0x" << std::hex << function.start << "u;\n"
<< std::dec;
ss << "\n";
@@ -927,18 +1101,35 @@ namespace ps2recomp
try
{
if (inst.hasDelaySlot && i + 1 < instructions.size())
if (inst.hasDelaySlot)
{
const Instruction &delaySlot = instructions[i + 1];
const bool hasDecodedDelaySlot =
i + 1 < instructions.size() &&
instructions[i + 1].address == inst.address + 4u;
if (internalTargets.contains(delaySlot.address))
Instruction syntheticDelaySlot{};
const Instruction *delaySlot = nullptr;
if (hasDecodedDelaySlot)
{
ss << "label_" << std::hex << delaySlot.address << std::dec << ":\n";
delaySlot = &instructions[i + 1];
}
else
{
syntheticDelaySlot = makeSyntheticDelaySlot(inst.address + 4u);
delaySlot = &syntheticDelaySlot;
}
ss << handleBranchDelaySlots(inst, delaySlot, function, analysisResult);
if (hasDecodedDelaySlot && internalTargets.contains(delaySlot->address))
{
ss << "label_" << std::hex << delaySlot->address << std::dec << ":\n";
}
++i; // Skip delay slot instruction (handled inside branch logic)
ss << handleBranchDelaySlots(inst, *delaySlot, function, analysisResult);
if (hasDecodedDelaySlot)
{
++i; // Skip delay slot instruction (handled inside branch logic)
}
}
else
{
@@ -2949,18 +3140,18 @@ namespace ps2recomp
std::string CodeGenerator::translateVU_VRNEXT(const Instruction &inst)
{
return fmt::format(
"{{ "
" uint32_t r_vals[4]; "
" _mm_storeu_si128((__m128i*)r_vals, _mm_castps_si128(ctx->vu0_r)); "
" "
" // Simple LFSR-based random number generation (PS2-like behavior) "
" uint32_t feedback = r_vals[0] ^ (r_vals[0] << 13) ^ (r_vals[1] >> 19) ^ (r_vals[2] << 7); "
" r_vals[0] = r_vals[1]; "
" r_vals[1] = r_vals[2]; "
" r_vals[2] = r_vals[3]; "
" r_vals[3] = feedback; "
" "
" ctx->vu0_r = _mm_castsi128_ps(_mm_loadu_si128((__m128i*)r_vals)); \n"
"{{\n"
" uint32_t r_vals[4];\n"
" _mm_storeu_si128((__m128i*)r_vals, _mm_castps_si128(ctx->vu0_r));\n"
"\n"
" // Simple LFSR-based random number generation (PS2-like behavior)\n"
" uint32_t feedback = r_vals[0] ^ (r_vals[0] << 13) ^ (r_vals[1] >> 19) ^ (r_vals[2] << 7);\n"
" r_vals[0] = r_vals[1];\n"
" r_vals[1] = r_vals[2];\n"
" r_vals[2] = r_vals[3];\n"
" r_vals[3] = feedback;\n"
"\n"
" ctx->vu0_r = _mm_castsi128_ps(_mm_loadu_si128((__m128i*)r_vals));\n"
"}}");
}
@@ -3608,19 +3799,19 @@ namespace ps2recomp
uint8_t fsf = inst.vectorInfo.fsf;
return fmt::format(
"{{ "
" float src = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); "
" uint32_t seed; std::memcpy(&seed, &src, sizeof(seed)); "
" "
" // PS2 uses a specific LFSR initialization pattern "
" if (seed == 0) seed = 1; " // Prevent zero seed
" "
" uint32_t r0 = seed; "
" uint32_t r1 = seed * 0x41C64E6D + 0x3039; " // PS2-like LCG constants
" uint32_t r2 = r1 * 0x41C64E6D + 0x3039; "
" uint32_t r3 = r2 * 0x41C64E6D + 0x3039; "
" "
" ctx->vu0_r = _mm_castsi128_ps(_mm_set_epi32(r3, r2, r1, r0)); \n "
"{{\n"
" float src = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{})));\n"
" uint32_t seed; std::memcpy(&seed, &src, sizeof(seed));\n"
"\n"
" // PS2 uses a specific LFSR initialization pattern\n"
" if (seed == 0) seed = 1;\n"
"\n"
" uint32_t r0 = seed;\n"
" uint32_t r1 = seed * 0x41C64E6D + 0x3039;\n"
" uint32_t r2 = r1 * 0x41C64E6D + 0x3039;\n"
" uint32_t r3 = r2 * 0x41C64E6D + 0x3039;\n"
"\n"
" ctx->vu0_r = _mm_castsi128_ps(_mm_set_epi32(r3, r2, r1, r0));\n"
"}}",
fs_reg, fs_reg, fsf);
}
@@ -3631,20 +3822,20 @@ namespace ps2recomp
uint8_t fsf = inst.vectorInfo.fsf;
return fmt::format(
"{{ "
" float src = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{}))); "
" uint32_t src_bits; std::memcpy(&src_bits, &src, sizeof(src_bits)); "
" __m128i r_current = _mm_castps_si128(ctx->vu0_r); "
" __m128i fs_data = _mm_set1_epi32((int)src_bits); "
" "
" // XOR the current random value with the data from the VU vector register "
" __m128i xored = _mm_xor_si128(r_current, fs_data); "
" "
" // Apply a simple mixing function similar to PS2's LFSR "
" __m128i mixed = _mm_xor_si128(xored, _mm_slli_epi32(xored, 7)); "
" mixed = _mm_xor_si128(mixed, _mm_srli_epi32(mixed, 9)); "
" "
" ctx->vu0_r = _mm_castsi128_ps(mixed);"
"{{\n"
" float src = _mm_cvtss_f32(_mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], _MM_SHUFFLE(0,0,0,{})));\n"
" uint32_t src_bits; std::memcpy(&src_bits, &src, sizeof(src_bits));\n"
" __m128i r_current = _mm_castps_si128(ctx->vu0_r);\n"
" __m128i fs_data = _mm_set1_epi32((int)src_bits);\n"
"\n"
" // XOR the current random value with the data from the VU vector register\n"
" __m128i xored = _mm_xor_si128(r_current, fs_data);\n"
"\n"
" // Apply a simple mixing function similar to PS2's LFSR\n"
" __m128i mixed = _mm_xor_si128(xored, _mm_slli_epi32(xored, 7));\n"
" mixed = _mm_xor_si128(mixed, _mm_srli_epi32(mixed, 9));\n"
"\n"
" ctx->vu0_r = _mm_castsi128_ps(mixed);\n"
"}}",
fs_reg, fs_reg, fsf);
}
@@ -3745,6 +3936,21 @@ namespace ps2recomp
emitRegistration(first, second);
}
ss << "\n // Register resumable entry points\n";
for (const auto &[ownerStart, targets] : m_resumeEntryTargetsByOwner)
{
const std::string ownerName = getFunctionName(ownerStart);
if (ownerName.empty())
{
continue;
}
for (uint32_t target : targets)
{
emitRegistration(target, ownerName);
}
}
ss << "\n // Register stub functions\n";
for (const auto &[first, second] : stubFunctions)
{
+48 -2
View File
@@ -26,7 +26,10 @@ namespace
{
bool IsAutoGeneratedName(const std::string &name)
{
return name.rfind("sub_", 0) == 0;
return name.rfind("sub_", 0) == 0 ||
name.rfind("FUN_", 0) == 0 ||
name.rfind("LAB_", 0) == 0 ||
name.rfind("DAT_", 0) == 0;
}
void AppendLoadSegmentsAsSections(const ELFIO::elfio &elf, std::vector<ps2recomp::Section> &sections)
@@ -566,6 +569,13 @@ namespace ps2recomp
continue;
}
if (m_hasLoadedGhidraMap &&
IsAutoGeneratedName(symbol.name) &&
!m_ghidraMapStarts.contains(symbol.address))
{
continue;
}
const Section *functionSection = FindFunctionSectionByAddress(m_sections, symbol.address);
if (!functionSection)
{
@@ -947,6 +957,9 @@ namespace ps2recomp
return false;
}
m_hasLoadedGhidraMap = false;
m_ghidraMapStarts.clear();
std::ifstream file(mapPath);
if (!file.is_open())
{
@@ -963,6 +976,7 @@ namespace ps2recomp
int count = 0;
int skippedNonExecutable = 0;
int skippedInvalidRange = 0;
std::unordered_set<uint32_t> mapStarts;
while (std::getline(file, line))
{
if (line.empty())
@@ -1007,6 +1021,7 @@ namespace ps2recomp
func.isSkipped = false;
m_extraFunctions.push_back(std::move(func));
mapStarts.insert(start);
count++;
}
catch (...)
@@ -1017,6 +1032,8 @@ namespace ps2recomp
if (count > 0)
{
m_hasLoadedGhidraMap = true;
m_ghidraMapStarts = mapStarts;
std::cout << "Loaded " << count << " functions from Ghidra map" << std::endl;
if (skippedNonExecutable > 0)
{
@@ -1029,9 +1046,36 @@ namespace ps2recomp
<< " Ghidra function(s) with invalid ranges after section clamping." << std::endl;
}
m_extraFunctions.erase(
std::remove_if(m_extraFunctions.begin(), m_extraFunctions.end(),
[&](const Function &func)
{
return IsAutoGeneratedName(func.name) && !mapStarts.contains(func.start);
}),
m_extraFunctions.end());
std::sort(m_extraFunctions.begin(), m_extraFunctions.end(),
[](const Function &a, const Function &b)
{ return a.start < b.start; });
{
if (a.start != b.start)
{
return a.start < b.start;
}
const bool aAuto = IsAutoGeneratedName(a.name);
const bool bAuto = IsAutoGeneratedName(b.name);
if (aAuto != bAuto)
{
return !aAuto;
}
if (a.end != b.end)
{
return a.end > b.end;
}
return a.name < b.name;
});
m_extraFunctions.erase(
std::unique(m_extraFunctions.begin(), m_extraFunctions.end(),
@@ -1267,6 +1311,8 @@ namespace ps2recomp
void ElfParser::loadDebugFunctions()
{
m_extraFunctions.clear();
m_hasLoadedGhidraMap = false;
m_ghidraMapStarts.clear();
if (HasDwarfSections(*m_elf))
{
+209 -73
View File
@@ -241,10 +241,16 @@ namespace ps2recomp
size_t passCount = 0;
};
bool isEntryFunctionName(const std::string &name)
{
return name.rfind("entry_", 0) == 0;
}
EntryDiscoveryStats discoverAdditionalEntryPointsImpl(
std::vector<Function> &functions,
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions,
const std::vector<Section> &sections,
CodeGenerator *codeGenerator,
const std::function<bool(Function &)> &decodeExternalFunction)
{
std::unordered_set<uint32_t> existingStarts;
@@ -269,22 +275,6 @@ namespace ps2recomp
return false;
};
auto getStaticEntryTarget = [](const Instruction &inst) -> std::optional<uint32_t>
{
if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL)
{
return decodeAbsoluteJumpTarget(inst.address, inst.target);
}
if (inst.opcode == OPCODE_SPECIAL &&
(inst.function == SPECIAL_JR || inst.function == SPECIAL_JALR))
{
return std::nullopt;
}
return std::nullopt;
};
auto isSimpleReturnThunkStart = [](const Instruction &inst) -> bool
{
return inst.opcode == OPCODE_SPECIAL &&
@@ -346,6 +336,30 @@ namespace ps2recomp
std::vector<Function> newEntries;
std::unordered_set<uint32_t> pendingStarts;
auto queuePendingEntry = [&](uint32_t target)
{
if (!isExecutableAddress(target))
{
return;
}
if (existingStarts.contains(target) || pendingStarts.contains(target))
{
return;
}
PendingEntry pending{};
pending.target = target;
if (const Function *containingFunction = findContainingFunction(target))
{
pending.containingStart = containingFunction->start;
pending.containingEnd = containingFunction->end;
}
pendingEntries.push_back(pending);
pendingStarts.insert(target);
};
for (const auto &function : functions)
{
if (!function.isRecompiled || function.isStub || function.isSkipped)
@@ -353,6 +367,11 @@ namespace ps2recomp
continue;
}
if (isEntryFunctionName(function.name))
{
continue;
}
auto decodedIt = decodedFunctions.find(function.start);
if (decodedIt == decodedFunctions.end())
{
@@ -360,44 +379,26 @@ namespace ps2recomp
}
const auto &instructions = decodedIt->second;
for (const auto &inst : instructions)
CodeGenerator::AnalysisResult analysisResult{};
if (codeGenerator)
{
auto targetOpt = getStaticEntryTarget(inst);
if (!targetOpt.has_value())
{
continue;
}
analysisResult = codeGenerator->collectInternalBranchTargets(
function, instructions, &functions);
}
else
{
analysisResult.resumeEntryPoints.clear();
analysisResult.externalEntryPoints.clear();
}
uint32_t target = targetOpt.value();
for (uint32_t target : analysisResult.externalEntryPoints)
{
queuePendingEntry(target);
}
if ((target & 0x3) != 0 || !isExecutableAddress(target))
{
continue;
}
if (existingStarts.contains(target) || pendingStarts.contains(target))
{
continue;
}
const Function *containingFunction = findContainingFunction(target);
if (containingFunction && containingFunction->start == function.start)
{
// Internal branches within the same function are handled as labels/gotos and should not produce separate entry wrappers.
continue;
}
PendingEntry pending{};
pending.target = target;
if (containingFunction)
{
pending.containingStart = containingFunction->start;
pending.containingEnd = containingFunction->end;
}
pendingEntries.push_back(pending);
pendingStarts.insert(target);
for (uint32_t target : analysisResult.resumeEntryPoints)
{
queuePendingEntry(target);
}
}
@@ -528,12 +529,6 @@ namespace ps2recomp
return stats;
}
bool isEntryFunctionName(const std::string &name)
{
return name.rfind("entry_", 0) == 0;
}
size_t resliceEntryFunctionsImpl(
std::vector<Function> &functions,
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions)
@@ -1268,25 +1263,124 @@ namespace ps2recomp
void PS2Recompiler::discoverAdditionalEntryPoints()
{
const EntryDiscoveryStats stats = discoverAdditionalEntryPointsImpl(
m_functions,
m_decodedFunctions,
m_sections,
[&](Function &entryFunction)
{ return decodeFunction(entryFunction); });
if (stats.discoveredCount > 0)
m_resumeEntryTargetsByOwner.clear();
if (!m_codeGenerator)
{
std::cout << "Discovered " << stats.discoveredCount
<< " additional entry point(s) inside existing functions across "
<< stats.passCount << " pass(es)." << std::endl;
return;
}
const size_t reslicedCount = resliceEntryFunctionsImpl(m_functions, m_decodedFunctions);
if (reslicedCount > 0)
auto findContainingFunction = [&](uint32_t address) -> const Function *
{
std::cout << "Resliced " << reslicedCount
<< " entry function(s) after discovery." << std::endl;
const Function *best = nullptr;
for (const auto &function : m_functions)
{
if (!function.isRecompiled || function.isStub || function.isSkipped)
{
continue;
}
if (isEntryFunctionName(function.name))
{
continue;
}
if (address < function.start || address >= function.end)
{
continue;
}
auto decodedIt = m_decodedFunctions.find(function.start);
if (decodedIt == m_decodedFunctions.end())
{
continue;
}
const auto &decoded = decodedIt->second;
const bool hasAddress = std::any_of(decoded.begin(), decoded.end(),
[&](const Instruction &candidate)
{ return candidate.address == address; });
if (!hasAddress)
{
continue;
}
if (!best || function.start > best->start)
{
best = &function;
}
}
return best;
};
for (const auto &function : m_functions)
{
if (!function.isRecompiled || function.isStub || function.isSkipped)
{
continue;
}
if (isEntryFunctionName(function.name))
{
continue;
}
auto decodedIt = m_decodedFunctions.find(function.start);
if (decodedIt == m_decodedFunctions.end())
{
continue;
}
const auto &instructions = decodedIt->second;
CodeGenerator::AnalysisResult analysisResult =
m_codeGenerator->collectInternalBranchTargets(function, instructions, &m_functions);
auto &ownerTargets = m_resumeEntryTargetsByOwner[function.start];
ownerTargets.insert(ownerTargets.end(),
analysisResult.resumeEntryPoints.begin(),
analysisResult.resumeEntryPoints.end());
for (uint32_t target : analysisResult.externalEntryPoints)
{
const Function *owner = findContainingFunction(target);
if (!owner)
{
continue;
}
if (owner->start == target)
{
continue;
}
auto &targets = m_resumeEntryTargetsByOwner[owner->start];
targets.push_back(target);
}
}
size_t totalTargets = 0u;
for (auto it = m_resumeEntryTargetsByOwner.begin(); it != m_resumeEntryTargetsByOwner.end();)
{
auto &targets = it->second;
std::sort(targets.begin(), targets.end());
targets.erase(std::unique(targets.begin(), targets.end()), targets.end());
if (targets.empty())
{
it = m_resumeEntryTargetsByOwner.erase(it);
continue;
}
totalTargets += targets.size();
++it;
}
m_codeGenerator->setResumeEntryTargets(m_resumeEntryTargetsByOwner);
if (totalTargets > 0u)
{
std::cout << "Collected " << totalTargets
<< " resumable entry point(s) across "
<< m_resumeEntryTargetsByOwner.size()
<< " owner function(s)." << std::endl;
}
}
@@ -1451,11 +1545,46 @@ namespace ps2recomp
}
std::filesystem::path outputPath = m_config.outputPath;
outputPath /= safeName + ".cpp";
outputPath /= clampFilenameLength(safeName, ".cpp", 100);
return outputPath;
}
std::string PS2Recompiler::clampFilenameLength(const std::string& baseName, const std::string& extension, std::size_t maxLength)
{
if (maxLength == 0)
{
std::cerr << "clampFilenameLength::maxLength must be greater than 0" << std::endl;
//Better go over the limit than create files with an empty path
return baseName + extension;
}
if (baseName.size() + extension.size() <= maxLength)
return baseName + extension;
std::string namePart = baseName;
std::string preservedSuffix;
auto suffixPos = namePart.rfind("_0x");
if (suffixPos != std::string::npos)
{
preservedSuffix = namePart.substr(suffixPos);
namePart = namePart.substr(0, suffixPos);
}
std::size_t available = maxLength - extension.size() - preservedSuffix.size();
if (available == 0)
{
return preservedSuffix + extension;
}
if (namePart.size() > available)
namePart = namePart.substr(0, available);
return namePart + preservedSuffix + extension;
}
std::string PS2Recompiler::sanitizeFunctionName(const std::string &name) const
{
std::string sanitized = sanitizeIdentifierBody(name);
@@ -1482,10 +1611,12 @@ namespace ps2recomp
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions,
const std::vector<Section> &sections)
{
CodeGenerator codeGenerator({}, sections);
const EntryDiscoveryStats stats = discoverAdditionalEntryPointsImpl(
functions,
decodedFunctions,
sections,
&codeGenerator,
[](Function &)
{ return false; });
return stats.discoveredCount;
@@ -1510,4 +1641,9 @@ namespace ps2recomp
}
return StubTarget::Unknown;
}
std::string PS2Recompiler::ClampFilenameLength(const std::string& baseName, const std::string& extension, std::size_t maxLength)
{
return clampFilenameLength(baseName, extension, maxLength);
}
}
+242 -27
View File
@@ -2,10 +2,21 @@
// @category PS2Recomp
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionIterator;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import ghidra.program.model.symbol.SymbolType;
import java.io.File;
import java.io.PrintWriter;
@@ -41,8 +52,13 @@ public class ExportPS2Functions extends GhidraScript {
"cmd_sem_init"
));
private static final Set<String> KNOWN_LOCAL_HELPER_NAMES = new HashSet<>(Arrays.asList(
"memcpy2",
"_memcpy2"
));
private static final Set<String> PS2_API_PREFIXES = new HashSet<>(Arrays.asList(
"sce", "sif", "pad", "gs", "dma", "iop", "vif", "spu", "mc", "libc"
"sce", "sif", "gs", "dma", "iop", "vif", "spu", "mc", "libc"
));
private static final Set<String> KNOWN_STDLIB_NAMES = new HashSet<>(Arrays.asList(
@@ -50,7 +66,7 @@ public class ExportPS2Functions extends GhidraScript {
"puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf",
"sprint", "sbprintf",
"malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign",
"memcpy", "memset", "memmove", "memcmp", "memcpy2", "memchr", "bcopy", "bzero",
"memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero",
"strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr",
"strchr", "strrchr", "strdup", "strtok", "strtok_r", "strerror",
"fopen", "fclose", "fread", "fwrite", "fseek", "ftell", "rewind", "fflush",
@@ -91,6 +107,7 @@ public class ExportPS2Functions extends GhidraScript {
long start;
long endExclusive;
long size;
boolean syntheticEntry = false;
}
private enum ClassificationKind {
@@ -120,6 +137,23 @@ public class ExportPS2Functions extends GhidraScript {
return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}
// Ghidra on Windows can return executable paths like "/D:/path/to/elf".
// TOML expects "D:/path/to/elf" for this field.
private static String normalizeWindowsDrivePath(String value) {
if (value == null || value.length() < 4) {
return value;
}
if (value.charAt(0) == '/' &&
Character.isLetter(value.charAt(1)) &&
value.charAt(2) == ':' &&
(value.charAt(3) == '/' || value.charAt(3) == '\\')) {
return value.substring(1);
}
return value;
}
private static String normalizeOptionalLeadingUnderscore(String value) {
if (value == null || value.isEmpty()) {
return "";
@@ -208,7 +242,14 @@ public class ExportPS2Functions extends GhidraScript {
return false;
}
if (KNOWN_LOCAL_HELPER_NAMES.contains(name)) {
return false;
}
String normalized = normalizeOptionalLeadingUnderscore(name);
if (KNOWN_LOCAL_HELPER_NAMES.contains(normalized)) {
return false;
}
if (KERNEL_RUNTIME_NAME_PATTERN.matcher(normalized).matches()) {
return true;
}
@@ -314,6 +355,178 @@ public class ExportPS2Functions extends GhidraScript {
return selectors;
}
private boolean isExecutableAddress(Address address) {
if (address == null) {
return false;
}
MemoryBlock block = currentProgram.getMemory().getBlock(address);
return block != null && block.isExecute();
}
private boolean hasCallableLabelReference(Address address) {
if (address == null) {
return false;
}
ReferenceIterator refs = currentProgram.getReferenceManager().getReferencesTo(address);
while (refs.hasNext()) {
Reference ref = refs.next();
if (ref == null) {
continue;
}
RefType type = ref.getReferenceType();
if (type != null && type.isCall()) {
return true;
}
Address from = ref.getFromAddress();
if (from == null) {
continue;
}
MemoryBlock fromBlock = currentProgram.getMemory().getBlock(from);
if (fromBlock == null || !fromBlock.isExecute()) {
continue; // lets ignore DATA/non-code refs
}
}
return false;
}
private static String makeAnonymousEntryName(long start) {
return String.format("entry_%08x", start & 0xFFFFFFFFL);
}
private List<FunctionRecord> collectExecutableLabelRecords(List<FunctionRecord> functionRecords) {
List<FunctionRecord> labelRecords = new ArrayList<>();
Set<Long> existingStarts = new HashSet<>();
for (FunctionRecord record : functionRecords) {
existingStarts.add(record.start);
}
SymbolIterator symbols = currentProgram.getSymbolTable().getSymbolIterator(true);
while (symbols.hasNext() && !monitor.isCancelled()) {
Symbol symbol = symbols.next();
if (symbol == null || !symbol.isPrimary()) {
continue;
}
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
continue;
}
Address address = symbol.getAddress();
if (!isExecutableAddress(address)) {
continue;
}
long start = address.getOffset();
if (existingStarts.contains(start)) {
continue;
}
Instruction instruction = currentProgram.getListing().getInstructionAt(address);
if (instruction == null) {
continue;
}
if (!hasCallableLabelReference(address)) {
continue;
}
FunctionRecord record = new FunctionRecord();
record.name = symbol.getName();
record.start = start;
record.syntheticEntry = true;
labelRecords.add(record);
existingStarts.add(start);
}
AddressSet executableAddresses = new AddressSet();
for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
if (block != null && block.isExecute()) {
executableAddresses.addRange(block.getStart(), block.getEnd());
}
}
InstructionIterator instructions = currentProgram.getListing().getInstructions(executableAddresses, true);
while (instructions.hasNext() && !monitor.isCancelled()) {
Instruction instruction = instructions.next();
if (instruction == null) {
continue;
}
Address address = instruction.getAddress();
long start = address.getOffset();
if (existingStarts.contains(start)) {
continue;
}
if (!hasCallableLabelReference(address)) {
continue;
}
FunctionRecord record = new FunctionRecord();
record.name = makeAnonymousEntryName(start);
record.start = start;
record.syntheticEntry = true;
labelRecords.add(record);
existingStarts.add(start);
}
if (labelRecords.isEmpty()) {
return labelRecords;
}
List<Long> boundaries = new ArrayList<>();
for (FunctionRecord record : functionRecords) {
boundaries.add(record.start);
}
for (FunctionRecord record : labelRecords) {
boundaries.add(record.start);
}
Collections.sort(boundaries);
functionRecords.sort(Comparator.comparingLong(r -> r.start));
for (FunctionRecord record : labelRecords) {
long endExclusive = 0L;
for (FunctionRecord functionRecord : functionRecords) {
if (record.start > functionRecord.start && record.start < functionRecord.endExclusive) {
endExclusive = functionRecord.endExclusive;
break;
}
}
if (endExclusive == 0L) {
Address startAddress = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(record.start);
MemoryBlock block = currentProgram.getMemory().getBlock(startAddress);
if (block != null) {
endExclusive = block.getEnd().getOffset() + 1L;
}
}
for (Long boundary : boundaries) {
if (boundary > record.start && (endExclusive == 0L || boundary < endExclusive)) {
endExclusive = boundary;
break;
}
}
if (endExclusive <= record.start) {
endExclusive = record.start + 4L;
}
record.endExclusive = endExclusive;
record.size = record.endExclusive - record.start;
}
labelRecords.sort(Comparator.comparingLong(r -> r.start));
return labelRecords;
}
@Override
public void run() throws Exception {
File tomlFile = askFile("Choose output TOML config file", "Save");
@@ -323,11 +536,9 @@ public class ExportPS2Functions extends GhidraScript {
boolean exportCsv = askYesNo("Export CSV", "Also export compatibility CSV function map?");
File csvFile = null;
if (exportCsv) {
csvFile = askFile("Choose output CSV file", "Save");
if (csvFile == null) {
exportCsv = false;
}
csvFile = askFile("Choose output CSV file", "Save");
if (csvFile == null) {
return;
}
FunctionManager fm = currentProgram.getFunctionManager();
@@ -363,31 +574,35 @@ public class ExportPS2Functions extends GhidraScript {
}
}
List<String> stubSelectors = collectFunctionSelectors(stubNames, functionRecords, true);
List<String> skipSelectors = collectFunctionSelectors(skipNames, functionRecords, true);
final int functionCount = functionRecords.size();
List<FunctionRecord> labelRecords = collectExecutableLabelRecords(functionRecords);
List<FunctionRecord> exportRecords = new ArrayList<>(functionRecords);
exportRecords.addAll(labelRecords);
exportRecords.sort(Comparator.comparingLong(r -> r.start));
if (exportCsv && csvFile != null) {
try (PrintWriter writer = new PrintWriter(csvFile)) {
writer.println("Name,Start,End,Size");
functionRecords.sort(Comparator.comparingLong(r -> r.start));
for (FunctionRecord record : functionRecords) {
writer.printf("%s,0x%08X,0x%08X,%d%n",
record.name,
record.start,
record.endExclusive,
record.size
);
}
List<String> stubSelectors = collectFunctionSelectors(stubNames, exportRecords, true);
List<String> skipSelectors = collectFunctionSelectors(skipNames, exportRecords, true);
try (PrintWriter writer = new PrintWriter(csvFile)) {
writer.println("Name,Start,End,Size");
for (FunctionRecord record : exportRecords) {
writer.printf("%s,0x%08X,0x%08X,%d%n",
record.name,
record.start,
record.endExclusive,
record.size
);
}
}
}
String programPath = currentProgram.getExecutablePath();
if (programPath == null) {
programPath = "";
}
programPath = normalizeWindowsDrivePath(programPath);
File outputDir = tomlFile.getParentFile() == null ? new File("output") : new File(tomlFile.getParentFile(), "output");
String ghidraCsvPath = (exportCsv && csvFile != null) ? csvFile.getAbsolutePath() : "";
String ghidraCsvPath = csvFile.getAbsolutePath();
try (PrintWriter writer = new PrintWriter(tomlFile)) {
writer.println("# Auto-generated by ExportPS2Functions.java");
@@ -419,7 +634,9 @@ public class ExportPS2Functions extends GhidraScript {
writer.println();
writer.println("[ghidra_export]");
writer.println("function_count = " + functionRecords.size());
writer.println("function_count = " + functionCount);
writer.println("code_label_count = " + labelRecords.size());
writer.println("csv_record_count = " + exportRecords.size());
writer.println("stub_count = " + stubSelectors.size());
writer.println("skip_count = " + skipSelectors.size());
writer.println("uncategorized_count = " + uncategorizedCount);
@@ -427,9 +644,7 @@ public class ExportPS2Functions extends GhidraScript {
writer.println("runtime_call_source = \"regex_only\"");
}
if (exportCsv && csvFile != null) {
println(String.format("Exported %d functions to %s", functionRecords.size(), csvFile.getAbsolutePath()));
}
println(String.format("Exported %d functions and %d executable labels to %s", functionCount, labelRecords.size(), csvFile.getAbsolutePath()));
println("Using regex-only runtime/library classification (no ps2_call_list.h).");
println(String.format("Exported TOML config to %s", tomlFile.getAbsolutePath()));
@@ -1,43 +0,0 @@
# Exports function addresses and names to CSV for PS2Recomp
# @category PS2Recomp
import csv
import os
from ghidra.program.model.symbol import SourceType
def run():
f = askFile("Choose output CSV file", "Save")
if f is None:
return
with open(f.getAbsolutePath(), 'w') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['Name', 'Start', 'End', 'Size'])
fm = currentProgram.getFunctionManager()
functions = fm.getFunctions(True) # True iterates forward wtf kkkkk
count = 0
for func in functions:
name = func.getName()
start = func.getEntryPoint().getOffset()
body = func.getBody()
max_addr = body.getMaxAddress().getOffset()
size = body.getNumAddresses()
writer.writerow([
name,
"0x{:08X}".format(start),
"0x{:08X}".format(max_addr + 1), # End address is exclusive
size
])
count += 1
print("Exported {} functions to {}".format(count, f.getAbsolutePath()))
if __name__ == "__main__":
run()
+207 -15
View File
@@ -5,18 +5,161 @@ project(PS2Runtime VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_GAMES OFF CACHE BOOL "" FORCE)
option(PS2X_ENABLE_RUNNER_UNITY_BUILD "Build ps2EntryRunner with CMake unity build" ON)
set(PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE 8 CACHE STRING "Unity build batch size for ps2EntryRunner")
option(PS2X_ENABLE_SCCACHE "Use sccache as compiler launcher when available" ON)
FetchContent_Declare(
raylib
GIT_REPOSITORY "https://github.com/raysan5/raylib.git"
GIT_TAG "5.5" # we will migrate to 4.2.0 later, trust me it will be better
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(raylib)
if(PS2X_ENABLE_SCCACHE)
find_program(PS2X_SCCACHE_PROGRAM sccache)
if(PS2X_SCCACHE_PROGRAM)
if(CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER_LAUNCHER "${PS2X_SCCACHE_PROGRAM}")
endif()
set(CMAKE_CXX_COMPILER_LAUNCHER "${PS2X_SCCACHE_PROGRAM}")
message(STATUS "Using sccache: ${PS2X_SCCACHE_PROGRAM}")
else()
message(STATUS "sccache not found; continuing without compiler launcher")
endif()
endif()
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ReleaseMode.cmake")
set(PS2X_IS_VITA OFF)
if((CMAKE_C_COMPILER MATCHES "arm-vita-eabi") OR
(CMAKE_CXX_COMPILER MATCHES "arm-vita-eabi"))
set(PS2X_IS_VITA ON)
endif()
option(PS2X_VITA_CREATE_PACKAGE "Create Vita eboot/vpk targets" ON)
set(PS2X_VITA_APP_NAME "PS2 Retro X" CACHE STRING "Display name for the Vita bubble")
set(PS2X_VITA_TITLEID "RANJ00001" CACHE STRING "9-character Vita title id")
set(PS2X_VITA_VERSION "01.00" CACHE STRING "Vita app version")
set(PS2X_DEFAULT_BOOT_ELF "" CACHE STRING "Guest ELF path passed directly to main() when argv is unavailable")
if(PS2X_IS_VITA)
if(NOT DEFINED ENV{VITASDK})
message(FATAL_ERROR "VITASDK is not defined. Configure inside the VitaSDK environment or pass the Vita toolchain file.")
endif()
set(PS2X_VITASDK "$ENV{VITASDK}")
if(NOT EXISTS "${PS2X_VITASDK}/share/vita.cmake")
message(FATAL_ERROR "Could not find vita.cmake under ${PS2X_VITASDK}/share.")
endif()
include("${PS2X_VITASDK}/share/vita.cmake" REQUIRED)
else()
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_GAMES OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
raylib
GIT_REPOSITORY "https://github.com/raysan5/raylib.git"
GIT_TAG "5.5" # we will migrate to 4.2.0 later, trust me it will be better
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(raylib)
endif()
add_library(ps2_host_backend INTERFACE)
set(PS2X_DEFAULT_BOOT_ELF_DEFINE "")
if(PS2X_DEFAULT_BOOT_ELF)
set(PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF}")
string(REPLACE "\\" "\\\\" PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF_DEFINE}")
string(REPLACE "\"" "\\\"" PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF_DEFINE}")
endif()
if(PS2X_IS_VITA)
set(PS2X_VITA_PREFIX "${PS2X_VITASDK}/arm-vita-eabi")
set(PS2X_VITA_EXTRA_INCLUDE_DIRS "" CACHE STRING "Extra include directories for Vita host dependencies")
set(PS2X_VITA_LIBRARY_DIR "${PS2X_VITA_PREFIX}/lib")
set(PS2X_VITA_REQUIRED_LIBRARY_NAMES
raylib
SDL2
OpenSLES
taihen_stub
SceAppMgr_stub
SceCtrl_stub
SceGxm_stub
SceCommonDialog_stub
SceLibKernel_stub
SceAudio_stub
SceTouch_stub
SceHid_stub
SceMotion_stub
SceSysmodule_stub
SceIofilemgr_stub
SceNetCtl_stub
SceNet_stub
SceDisplay_stub
SceAppUtil_stub
SceAudioIn_stub
ScePower_stub
SceProcessmgr_stub
SceIme_stub
libIMGEGL_stub_weak
libgpu_es4_ext_stub_weak
libGLESv2_stub_weak
)
set(PS2X_VITA_INCLUDE_DIRS
"${PS2X_VITA_PREFIX}/include"
)
foreach(PS2X_VITA_OPTIONAL_INCLUDE_DIR IN ITEMS
"${PS2X_VITA_PREFIX}/include/raylib"
"${PS2X_VITA_PREFIX}/include/SDL2")
if(EXISTS "${PS2X_VITA_OPTIONAL_INCLUDE_DIR}")
list(APPEND PS2X_VITA_INCLUDE_DIRS "${PS2X_VITA_OPTIONAL_INCLUDE_DIR}")
endif()
endforeach()
if(PS2X_VITA_EXTRA_INCLUDE_DIRS)
list(APPEND PS2X_VITA_INCLUDE_DIRS ${PS2X_VITA_EXTRA_INCLUDE_DIRS})
endif()
if(NOT EXISTS "${PS2X_VITA_PREFIX}/include/SDL2/SDL.h")
message(FATAL_ERROR
"Missing SDL2 headers under ${PS2X_VITA_PREFIX}/include/SDL2. "
"Quenom/raylib-5.5-vita requires SDL2 with PVR support installed into VitaSDK.")
endif()
if(NOT EXISTS "${PS2X_VITA_LIBRARY_DIR}")
message(FATAL_ERROR "Missing Vita library directory: ${PS2X_VITA_LIBRARY_DIR}")
endif()
function(ps2x_find_vita_library out_var library_name)
string(MAKE_C_IDENTIFIER "${library_name}" library_id)
find_library(${out_var}
NAMES "${library_name}" "${library_name}.a" "lib${library_name}.a"
PATHS "${PS2X_VITA_LIBRARY_DIR}"
NO_DEFAULT_PATH
)
if(NOT ${out_var})
message(FATAL_ERROR
"Could not find Vita library '${library_name}' under ${PS2X_VITA_LIBRARY_DIR}. "
"Install Quenom/raylib-5.5-vita and its SDL2/PVR dependencies into VitaSDK first.")
endif()
set(${out_var} "${${out_var}}" PARENT_SCOPE)
endfunction()
set(PS2X_VITA_RESOLVED_LIBRARIES "")
foreach(PS2X_VITA_LIBRARY_NAME IN LISTS PS2X_VITA_REQUIRED_LIBRARY_NAMES)
ps2x_find_vita_library(PS2X_VITA_LIBRARY_PATH "${PS2X_VITA_LIBRARY_NAME}")
list(APPEND PS2X_VITA_RESOLVED_LIBRARIES "${PS2X_VITA_LIBRARY_PATH}")
endforeach()
target_compile_definitions(ps2_host_backend INTERFACE PLATFORM_VITA)
target_include_directories(ps2_host_backend INTERFACE ${PS2X_VITA_INCLUDE_DIRS})
target_link_libraries(ps2_host_backend INTERFACE
${PS2X_VITA_RESOLVED_LIBRARIES}
m
c
pthread
)
else()
target_link_libraries(ps2_host_backend INTERFACE raylib)
endif()
add_library(ps2_runtime STATIC
src/lib/game_overrides.cpp
@@ -30,10 +173,17 @@ add_library(ps2_runtime STATIC
src/lib/ps2_memory.cpp
src/lib/ps2_pad.cpp
src/lib/ps2_runtime.cpp
src/lib/ps2_stubs.cpp
src/lib/ps2_syscalls.cpp
src/lib/ps2_vif1_interpreter.cpp
src/lib/ps2_vu1.cpp
src/lib/games_database.cpp
)
file(GLOB_RECURSE KERNEL_SRC_FILES CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel/*.cpp"
)
target_sources(ps2_runtime PRIVATE
${KERNEL_SRC_FILES}
)
file(GLOB RUNNER_SRC_FILES CONFIGURE_DEPENDS
@@ -53,21 +203,63 @@ add_executable(ps2EntryRunner
${RUNNER_SRC_FILES}
)
if(PS2X_ENABLE_RUNNER_UNITY_BUILD)
set_target_properties(ps2EntryRunner PROPERTIES
UNITY_BUILD ON
UNITY_BUILD_BATCH_SIZE "${PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE}"
)
endif()
if(PS2X_DEFAULT_BOOT_ELF_DEFINE)
target_compile_definitions(ps2EntryRunner PRIVATE
PS2X_DEFAULT_BOOT_ELF="${PS2X_DEFAULT_BOOT_ELF_DEFINE}"
)
endif()
if(MSVC)
target_compile_options(ps2EntryRunner PRIVATE /FS)
endif()
target_include_directories(ps2_runtime PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel
)
target_link_libraries(ps2_runtime PRIVATE raylib)
target_link_libraries(ps2_runtime PUBLIC ps2_host_backend)
target_link_libraries(ps2EntryRunner
PRIVATE
ps2_runtime
raylib
)
if(PS2X_IS_VITA)
set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1")
if(PS2X_VITA_CREATE_PACKAGE)
vita_create_self(eboot.bin ps2EntryRunner UNSAFE)
vita_create_vpk(ps2EntryRunner.vpk ${PS2X_VITA_TITLEID} eboot.bin
VERSION ${PS2X_VITA_VERSION}
NAME ${PS2X_VITA_APP_NAME}
)
endif()
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
EnableFastReleaseMode(ps2_runtime)
EnableFastReleaseMode(ps2EntryRunner)
if(MSVC AND WIN32)
target_link_options(ps2EntryRunner PRIVATE
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/SUBSYSTEM:WINDOWS>
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/ENTRY:mainCRTStartup>
)
elseif(MINGW AND WIN32)
target_link_options(ps2EntryRunner PRIVATE
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:-mwindows>
)
endif()
endif()
# Work around WinAPI vs raylib symbol clash for CloseWindow on x64
if(MSVC)
target_link_options(ps2EntryRunner PRIVATE "/FORCE:MULTIPLE")
+14 -1
View File
@@ -10,6 +10,19 @@ The runtime library provides the execution environment for recompiled code, incl
Take your decompiled code and place the cpp files on ps2xRuntime/src/runner and header files on ps2xRuntime/include and compile/be happy.
## Vita Build Notes
The Vita runtime uses `Quenom/raylib-5.5-vita` for vita build. I recommend build runtime only.
Expected environment:
* `VITASDK` points to your VitaSDK root.
* `Quenom/raylib-5.5-vita` has already been built and installed into `$VITASDK/arm-vita-eabi`.
* SDL2 with the PVR backend required by that raylib fork is also installed into the same VitaSDK prefix.
* `PS2X_DEFAULT_BOOT_ELF` is mandatory, you need to define where your game is like "ux0:data/RANJ00001/game/SLUS_201.84".
The CMake for `ps2xRuntime` consumes those preinstalled headers and libraries from VitaSDK. It does not fetch or install the Vita raylib fork for you.
## Adding Custom Function Implementations
You can add custom implementations for PS2 system calls or game functions by:
@@ -41,4 +54,4 @@ You can patch specific instructions in the recompiled code to fix game issues or
* Graphics and sound output require external implementations
* Some PS2-specific hardware features may not be fully supported
* Performance may vary based on the complexity of the game
* Performance may vary based on the complexity of the game
+42
View File
@@ -0,0 +1,42 @@
include(CheckIPOSupported)
check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT IPO_ERROR)
function(EnableFastReleaseMode TargetName)
message("> Enabling optimization for: ${TargetName}")
if(MSVC)
target_compile_options(${TargetName} PRIVATE
$<$<CONFIG:Release>:
/O2 # speed
/Ob2 # inline aggressively
/Oi # intrinsics
/GL # whole program opt
/Gy # function-level linking
/Gw # global data in COMDAT
/GF # string pooling
/Zc:inline # remove unreferenced inline
/fp:fast # fast math (graphics friendly)
/DNDEBUG
/arch:AVX2 # Advanced Vector Extensions 2
/GS- # Disable Buffer Security Check (faster)
/Qspectre- # Disable Spectre mitigations (faster)
>
)
if(TARGET ${TargetName})
target_link_options(${TargetName} PRIVATE
$<$<CONFIG:Release>:
/LTCG # link-time code generation
/OPT:REF # remove unreferenced
/OPT:ICF # fold identical COMDATs
>
)
endif()
endif()
if(IPO_SUPPORTED)
set_property(TARGET ${TargetName} PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
else()
message(WARNING "Interprocedural optimization not supported: ${ipo_error}")
endif()
endfunction()
+169 -134
View File
@@ -1,127 +1,136 @@
#pragma once
#include <cstdint>
// I know ugly, but will work for now.
#define PS2_SYSCALL_LIST(X) \
X(FlushCache) \
X(iFlushCache) \
X(ResetEE) \
X(SetMemoryMode) \
\
X(CreateThread) \
X(DeleteThread) \
X(StartThread) \
X(ExitThread) \
X(ExitDeleteThread) \
X(TerminateThread) \
X(SuspendThread) \
X(ResumeThread) \
X(GetThreadId) \
X(ReferThreadStatus) \
X(iReferThreadStatus) \
X(SleepThread) \
X(WakeupThread) \
X(iWakeupThread) \
X(CancelWakeupThread) \
X(iCancelWakeupThread) \
X(ChangeThreadPriority) \
X(iChangeThreadPriority) \
X(RotateThreadReadyQueue) \
X(iRotateThreadReadyQueue)\
X(ReleaseWaitThread) \
X(iReleaseWaitThread) \
\
X(CreateSema) \
X(DeleteSema) \
X(SignalSema) \
X(iSignalSema) \
X(WaitSema) \
X(PollSema) \
X(iPollSema) \
X(ReferSemaStatus) \
X(iReferSemaStatus) \
\
X(CreateEventFlag) \
X(DeleteEventFlag) \
X(SetEventFlag) \
X(iSetEventFlag) \
X(ClearEventFlag) \
X(iClearEventFlag) \
X(WaitEventFlag) \
X(PollEventFlag) \
X(iPollEventFlag) \
X(ReferEventFlagStatus) \
X(iReferEventFlagStatus) \
\
X(SetAlarm) \
X(iSetAlarm) \
X(CancelAlarm) \
X(iCancelAlarm) \
\
X(AddIntcHandler) \
X(AddIntcHandler2) \
X(RemoveIntcHandler) \
X(AddDmacHandler) \
X(AddDmacHandler2) \
X(RemoveDmacHandler) \
X(EnableIntc) \
X(iEnableIntc) \
X(DisableIntc) \
X(iDisableIntc) \
X(EnableDmac) \
X(iEnableDmac) \
X(DisableDmac) \
X(iDisableDmac) \
\
X(SifStopModule) \
X(SifLoadModule) \
X(SifInitRpc) \
X(SifBindRpc) \
X(SifCallRpc) \
X(SifRegisterRpc) \
X(SifCheckStatRpc) \
X(SifSetRpcQueue) \
X(SifRemoveRpcQueue) \
X(SifRemoveRpc) \
X(sceSifCallRpc) \
X(sceSifSendCmd) \
X(sceRpcGetPacket) \
\
X(fioOpen) \
X(fioClose) \
X(fioRead) \
X(fioWrite) \
X(fioLseek) \
X(fioMkdir) \
X(fioChdir) \
X(fioRmdir) \
X(fioGetstat) \
X(fioRemove) \
\
X(SetGsCrt) \
X(GsSetCrt) \
X(GsGetIMR) \
X(iGsGetIMR) \
X(GsPutIMR) \
X(iGsPutIMR) \
X(SetVSyncFlag) \
X(GsSetVideoMode) \
\
X(GetOsdConfigParam) \
X(SetOsdConfigParam) \
X(GetRomName) \
X(SifLoadElfPart) \
X(sceSifLoadElf) \
X(sceSifLoadElfPart) \
X(sceSifLoadModule) \
X(sceSifLoadModuleBuffer) \
\
X(SetupThread) \
X(EndOfHeap) \
X(GetMemorySize) \
X(Deci2Call) \
X(QueryBootMode) \
X(GetThreadTLS) \
#define PS2_SYSCALL_LIST(X) \
X(FlushCache) \
X(iFlushCache) \
X(ResetEE) \
X(SetMemoryMode) \
\
X(InitThread) \
X(CreateThread) \
X(DeleteThread) \
X(StartThread) \
X(ExitThread) \
X(ExitDeleteThread) \
X(TerminateThread) \
X(SuspendThread) \
X(ResumeThread) \
X(GetThreadId) \
X(ReferThreadStatus) \
X(iReferThreadStatus) \
X(SleepThread) \
X(WakeupThread) \
X(iWakeupThread) \
X(CancelWakeupThread) \
X(iCancelWakeupThread) \
X(ChangeThreadPriority) \
X(iChangeThreadPriority) \
X(RotateThreadReadyQueue) \
X(iRotateThreadReadyQueue) \
X(ReleaseWaitThread) \
X(iReleaseWaitThread) \
\
X(CreateSema) \
X(DeleteSema) \
X(SignalSema) \
X(iSignalSema) \
X(WaitSema) \
X(PollSema) \
X(iPollSema) \
X(ReferSemaStatus) \
X(iReferSemaStatus) \
\
X(CreateEventFlag) \
X(DeleteEventFlag) \
X(SetEventFlag) \
X(iSetEventFlag) \
X(ClearEventFlag) \
X(iClearEventFlag) \
X(WaitEventFlag) \
X(PollEventFlag) \
X(iPollEventFlag) \
X(ReferEventFlagStatus) \
X(iReferEventFlagStatus) \
\
X(InitAlarm) \
X(SetAlarm) \
X(iSetAlarm) \
X(CancelAlarm) \
X(iCancelAlarm) \
X(ReleaseAlarm) \
X(iReleaseAlarm) \
\
X(AddIntcHandler) \
X(AddIntcHandler2) \
X(RemoveIntcHandler) \
X(AddDmacHandler) \
X(AddDmacHandler2) \
X(RemoveDmacHandler) \
X(EnableIntc) \
X(iEnableIntc) \
X(DisableIntc) \
X(iDisableIntc) \
X(EnableDmac) \
X(iEnableDmac) \
X(DisableDmac) \
X(iDisableDmac) \
\
X(SifStopModule) \
X(SifLoadModule) \
X(SifInitRpc) \
X(SifBindRpc) \
X(SifCallRpc) \
X(SifRegisterRpc) \
X(SifCheckStatRpc) \
X(SifSetRpcQueue) \
X(SifRemoveRpcQueue) \
X(SifRemoveRpc) \
X(sceSifCallRpc) \
X(sceSifSendCmd) \
X(sceRpcGetPacket) \
\
X(fioOpen) \
X(fioClose) \
X(fioRead) \
X(fioWrite) \
X(fioLseek) \
X(fioMkdir) \
X(fioChdir) \
X(fioRmdir) \
X(fioGetstat) \
X(fioRemove) \
\
X(SetGsCrt) \
X(GsSetCrt) \
X(GsGetIMR) \
X(iGsGetIMR) \
X(GsPutIMR) \
X(iGsPutIMR) \
X(SetVSyncFlag) \
X(SetSyscall) \
X(GsSetVideoMode) \
\
X(GetOsdConfigParam) \
X(SetOsdConfigParam) \
X(EnableCache) \
X(DisableCache) \
X(GetRomName) \
X(SifLoadElfPart) \
X(sceSifLoadElf) \
X(sceSifLoadElfPart) \
X(sceSifLoadModule) \
X(sceSifLoadModuleBuffer) \
\
X(SetupThread) \
X(EndOfHeap) \
X(GetMemorySize) \
X(Deci2Call) \
X(QueryBootMode) \
X(GetThreadTLS) \
X(RegisterExitHandler)
// Stubs
@@ -200,11 +209,11 @@
X(write) \
/* PS2 native */ \
X(DmaAddr) \
X(Pad_init) \
X(Pad_set) \
X(builtin_set_imask) \
X(sceCdRI) \
X(sceCdRM) \
X(sceDevVif0Reset) \
X(sceDevVu0Reset) \
X(sceFsDbChk) \
X(sceFsIntrSigSema) \
X(sceFsSemExit) \
@@ -221,7 +230,6 @@
X(sceSifLoadModule) \
X(sceSifSendCmd) \
X(sceVu0ecossin) \
X(iopGetArea) \
X(mcCallMessageTypeSe) \
X(mcCheckReadStartConfigFile) \
X(mcCheckReadStartSaveFile) \
@@ -265,8 +273,6 @@
X(mceGetInfoApdx) \
X(mceIntrReadFixAlign) \
X(mceStorePwd) \
X(pdGetPeripheral) \
X(pdInitPeripheral) \
X(sceCdApplyNCmd) \
X(sceCdBreak) \
X(sceCdCallback) \
@@ -338,6 +344,18 @@
X(sceDmaWatch) \
X(sceFsInit) \
X(sceFsReset) \
X(sceGifPkAddGsAD) \
X(sceGifPkAddGsData) \
X(sceGifPkCloseGifTag) \
X(sceGifPkCnt) \
X(sceGifPkEnd) \
X(sceGifPkInit) \
X(sceGifPkOpenGifTag) \
X(sceGifPkRef) \
X(sceGifPkRefLoadImage) \
X(sceGifPkReset) \
X(sceGifPkReserve) \
X(sceGifPkTerminate) \
X(sceGsExecLoadImage) \
X(sceGsExecStoreImage) \
X(sceGsGetGParam) \
@@ -347,16 +365,31 @@
X(sceGsResetPath) \
X(sceGsSetDefClear) \
X(sceGsSetDefDBuffDc) \
X(sceGsSetDefDBuff) \
X(sceGsSetDefDispEnv) \
X(sceGsSetDefDrawEnv) \
X(sceGsSetDefDrawEnv2) \
X(sceGsSetDefLoadImage) \
X(sceGsSetDefStoreImage) \
X(sceGsSwapDBuffDc) \
X(sceGsSwapDBuff) \
X(sceGsSyncPath) \
X(sceGsSyncV) \
X(sceGsSyncVCallback) \
X(sceGszbufaddr) \
X(sceVif1PkAddGsAD) \
X(sceVif1PkAlign) \
X(sceVif1PkCall) \
X(sceVif1PkCloseDirectCode) \
X(sceVif1PkCloseGifTag) \
X(sceVif1PkCnt) \
X(sceVif1PkEnd) \
X(sceVif1PkInit) \
X(sceVif1PkOpenDirectCode) \
X(sceVif1PkOpenGifTag) \
X(sceVif1PkReset) \
X(sceVif1PkReserve) \
X(sceVif1PkTerminate) \
X(sceeFontInit) \
X(sceeFontLoadFont) \
X(sceeFontPrintfAt) \
@@ -377,6 +410,7 @@
X(sceMcChdir) \
X(sceMcClose) \
X(sceMcDelete) \
X(sceMcEnd) \
X(sceMcFlush) \
X(sceMcFormat) \
X(sceMcGetDir) \
@@ -472,6 +506,7 @@
X(sceSetPtm) \
X(sceSifAddCmdHandler) \
X(sceSifAllocIopHeap) \
X(sceSifAllocSysMemory) \
X(sceSifBindRpc) \
X(sceSifCheckStatRpc) \
X(sceSifDmaStat) \
@@ -479,6 +514,7 @@
X(sceSifExitCmd) \
X(sceSifExitRpc) \
X(sceSifFreeIopHeap) \
X(sceSifFreeSysMemory) \
X(sceSifGetDataTable) \
X(sceSifGetIopAddr) \
X(sceSifGetNextRequest) \
@@ -504,6 +540,8 @@
X(sceSifSetCmdBuffer) \
X(sceSifSetDChain) \
X(sceSifSetDma) \
X(isceSifSetDChain) \
X(isceSifSetDma) \
X(sceSifSetIopAddr) \
X(sceSifSetReg) \
X(sceSifSetRpcQueue) \
@@ -634,13 +672,10 @@
X(sceVu0UnitMatrix) \
X(sceVu0ViewScreenMatrix) \
X(sceWrite) \
X(sdDrvInit) \
X(sdSndStopAll) \
X(sdSysFinish) \
X(syFree) \
X(syHwInit) \
X(syHwInit2) \
X(syMallocInit) \
X(syRtcInit) \
X(InitThread) \
/* Game/middleware */
// Test hooks: override pad input for scePadRead.
#define PS2_TEST_HOOK_LIST(X) \
X(setPadOverrideState, (uint16_t buttons, uint8_t lx, uint8_t ly, \
uint8_t rx, uint8_t ry)) \
X(clearPadOverrideState, (void))
-59
View File
@@ -1,59 +0,0 @@
#ifndef PS2_GS_PSMT4_H
#define PS2_GS_PSMT4_H
#include <cstdint>
namespace GSPSMT4
{
static const uint8_t blockTable4[8][4] = {
{ 0, 2, 8, 10 },
{ 1, 3, 9, 11 },
{ 4, 6, 12, 14 },
{ 5, 7, 13, 15 },
{ 16, 18, 24, 26 },
{ 17, 19, 25, 27 },
{ 20, 22, 28, 30 },
{ 21, 23, 29, 31 },
};
static const uint16_t columnTable4[16][32] = {
{ 0, 8, 32, 40, 64, 72, 96, 104, 2, 10, 34, 42, 66, 74, 98, 106, 4, 12, 36, 44, 68, 76, 100, 108, 6, 14, 38, 46, 70, 78, 102, 110 },
{ 16, 24, 48, 56, 80, 88, 112, 120, 18, 26, 50, 58, 82, 90, 114, 122, 20, 28, 52, 60, 84, 92, 116, 124, 22, 30, 54, 62, 86, 94, 118, 126 },
{ 65, 73, 97, 105, 1, 9, 33, 41, 67, 75, 99, 107, 3, 11, 35, 43, 69, 77, 101, 109, 5, 13, 37, 45, 71, 79, 103, 111, 7, 15, 39, 47 },
{ 81, 89, 113, 121, 17, 25, 49, 57, 83, 91, 115, 123, 19, 27, 51, 59, 85, 93, 117, 125, 21, 29, 53, 61, 87, 95, 119, 127, 23, 31, 55, 63 },
{ 192, 200, 224, 232, 128, 136, 160, 168, 194, 202, 226, 234, 130, 138, 162, 170, 196, 204, 228, 236, 132, 140, 164, 172, 198, 206, 230, 238, 134, 142, 166, 174 },
{ 208, 216, 240, 248, 144, 152, 176, 184, 210, 218, 242, 250, 146, 154, 178, 186, 212, 220, 244, 252, 148, 156, 180, 188, 214, 222, 246, 254, 150, 158, 182, 190 },
{ 129, 137, 161, 169, 193, 201, 225, 233, 131, 139, 163, 171, 195, 203, 227, 235, 133, 141, 165, 173, 197, 205, 229, 237, 135, 143, 167, 175, 199, 207, 231, 239 },
{ 145, 153, 177, 185, 209, 217, 241, 249, 147, 155, 179, 187, 211, 219, 243, 251, 149, 157, 181, 189, 213, 221, 245, 253, 151, 159, 183, 191, 215, 223, 247, 255 },
{ 256, 264, 288, 296, 320, 328, 352, 360, 258, 266, 290, 298, 322, 330, 354, 362, 260, 268, 292, 300, 324, 332, 356, 364, 262, 270, 294, 302, 326, 334, 358, 366 },
{ 272, 280, 304, 312, 336, 344, 368, 376, 274, 282, 306, 314, 338, 346, 370, 378, 276, 284, 308, 316, 340, 348, 372, 380, 278, 286, 310, 318, 342, 350, 374, 382 },
{ 321, 329, 353, 361, 257, 265, 289, 297, 323, 331, 355, 363, 259, 267, 291, 299, 325, 333, 357, 365, 261, 269, 293, 301, 327, 335, 359, 367, 263, 271, 295, 303 },
{ 337, 345, 369, 377, 273, 281, 305, 313, 339, 347, 371, 379, 275, 283, 307, 315, 341, 349, 373, 381, 277, 285, 309, 317, 343, 351, 375, 383, 279, 287, 311, 319 },
{ 448, 456, 480, 488, 384, 392, 416, 424, 450, 458, 482, 490, 386, 394, 418, 426, 452, 460, 484, 492, 388, 396, 420, 428, 454, 462, 486, 494, 390, 398, 422, 430 },
{ 464, 472, 496, 504, 400, 408, 432, 440, 466, 474, 498, 506, 402, 410, 434, 442, 468, 476, 500, 508, 404, 412, 436, 444, 470, 478, 502, 510, 406, 414, 438, 446 },
{ 385, 393, 417, 425, 449, 457, 481, 489, 387, 395, 419, 427, 451, 459, 483, 491, 389, 397, 421, 429, 453, 461, 485, 493, 391, 399, 423, 431, 455, 463, 487, 495 },
{ 401, 409, 433, 441, 465, 473, 497, 505, 403, 411, 435, 443, 467, 475, 499, 507, 405, 413, 437, 445, 469, 477, 501, 509, 407, 415, 439, 447, 471, 479, 503, 511 },
};
inline uint32_t blockIdPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return block + ((y >> 2) & ~0x1Fu) * (width >> 7) + ((x >> 2) & ~0x1Fu)
+ blockTable4[(y >> 4) & 7][(x >> 5) & 3];
}
inline uint32_t addrPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
uint32_t page = (block >> 5) + (y >> 7) * (width >> 1) + (x >> 7);
uint32_t blk = block & 0x1Fu;
uint32_t yy = y & 0x7Fu;
uint32_t xx = x & 0x7Fu;
uint32_t blockId = blk + blockTable4[(yy >> 4) & 7][(xx >> 5) & 3];
uint32_t column = columnTable4[yy & 15u][xx & 31u];
uint32_t offset = (blockId << 9) + column;
return (page << 14) + offset;
}
}
#endif
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "raylib.h"
-25
View File
@@ -1,25 +0,0 @@
#ifndef PS2_IOP_H
#define PS2_IOP_H
#include <cstdint>
constexpr uint32_t IOP_SID_LIBSD = 0x80000701u;
class ps2_iop
{
public:
ps2_iop();
~ps2_iop() = default;
void init(uint8_t *rdram);
void reset();
bool handleRPC(uint32_t sid, uint32_t rpcNum,
uint32_t sendBufAddr, uint32_t sendSize,
uint32_t recvBufAddr, uint32_t recvSize);
private:
uint8_t *m_rdram = nullptr;
};
#endif
+25 -11
View File
@@ -4,27 +4,31 @@
#include <fstream>
#include <iostream>
#include <filesystem>
#if defined(_WIN32)
#define NOMINMAX
#include <windows.h>
#if defined(AGRESSIVE_LOGS)
#define PS2_AGRESSIVE_LOGS_ENABLED 1
#else
#define PS2_AGRESSIVE_LOGS_ENABLED 0
#endif
#ifdef _DEBUG
#if defined(_DEBUG)
#define RUNTIME_LOG(x) do { std::cout << x; } while (0)
#else
#define RUNTIME_LOG(x) do {} while(0)
#endif
#ifdef neverDone
namespace ps2_log
{
inline constexpr bool agressive_logs_enabled = PS2_AGRESSIVE_LOGS_ENABLED != 0;
inline std::string log_path()
{
static std::string path;
if (path.empty())
{
#if defined(_WIN32)
char buf[MAX_PATH];
if (GetModuleFileNameA(nullptr, buf, sizeof(buf)))
path = (std::filesystem::path(buf).parent_path() / "ps2_log.txt").string();
#endif
if (path.empty())
path = (std::filesystem::current_path() / "ps2_log.txt").string();
path = (std::filesystem::current_path() / "ps2_log.txt").string();
}
return path;
}
@@ -64,14 +68,24 @@ inline void print_saved_location()
ps2_log::log_entry(name); \
struct _ps2_log_guard_ { const char *_n; _ps2_log_guard_(const char *n) : _n(n) {} \
~_ps2_log_guard_() { ps2_log::log_exit(_n); } } _ps2_log_guard_(name)
#define PS2_IF_AGRESSIVE_LOGS(code) \
do \
{ \
if constexpr (ps2_log::agressive_logs_enabled) \
{ \
code; \
} \
} while (0)
#else
namespace ps2_log
{
inline constexpr bool agressive_logs_enabled = false;
inline void print_saved_location() {}
}
#define PS_LOG_ENTRY(name) ((void)0)
#define PS2_IF_AGRESSIVE_LOGS(code) ((void)0)
#endif
+129 -7
View File
@@ -16,18 +16,20 @@
#include <smmintrin.h> // For SSE4.1 instructions
#endif
#include <atomic>
#include <array>
#include <mutex>
#include <filesystem>
#include <iostream>
#include <iomanip>
#include "ps2_gif_arbiter.h"
#include "ps2_memory.h"
#include "ps2_gs_gpu.h"
#include "ps2_iop.h"
#include "ps2_vu1.h"
#include "ps2_audio.h"
#include "ps2_pad.h"
#include "ps2_log.h"
#include "runtime/ps2_gif_arbiter.h"
#include "runtime/ps2_memory.h"
#include "runtime/ps2_gs_gpu.h"
#include "runtime/ps2_iop.h"
#include "runtime/ps2_vu1.h"
#include "runtime/ps2_audio.h"
#include "runtime/ps2_pad.h"
enum PS2Exception
{
@@ -356,6 +358,78 @@ inline void ps2TraceGuestRangeWrite(uint8_t *rdram,
std::cout << std::endl;
}
struct PS2SoundDriverCompatLayout
{
uint32_t primarySeCheckAddr = 0;
uint32_t primaryMidiCheckAddr = 0;
uint32_t fallbackSeCheckAddr = 0;
uint32_t fallbackMidiCheckAddr = 0;
uint32_t busyFlagAddr = 0;
std::array<uint32_t, 4> completionCallbacks{};
std::array<uint32_t, 2> clearBusyCallbacks{};
[[nodiscard]] bool hasChecksumTables() const
{
return primarySeCheckAddr != 0u || primaryMidiCheckAddr != 0u ||
fallbackSeCheckAddr != 0u || fallbackMidiCheckAddr != 0u;
}
[[nodiscard]] bool matchesCompletionCallback(uint32_t addr) const
{
for (const uint32_t candidate : completionCallbacks)
{
if (candidate != 0u && candidate == addr)
{
return true;
}
}
return false;
}
[[nodiscard]] bool matchesClearBusyCallback(uint32_t addr) const
{
for (const uint32_t candidate : clearBusyCallbacks)
{
if (candidate != 0u && candidate == addr)
{
return true;
}
}
return false;
}
};
struct PS2DtxCompatLayout
{
uint32_t rpcSid = 0;
uint32_t urpcObjBase = 0;
uint32_t urpcObjLimit = 0;
uint32_t urpcObjStride = 0x20u;
uint32_t urpcFnTableBase = 0;
uint32_t urpcObjTableBase = 0;
uint32_t dispatcherFuncAddr = 0;
[[nodiscard]] bool isConfigured() const
{
return rpcSid != 0u;
}
[[nodiscard]] bool hasUrpcObjectRange() const
{
return urpcObjBase != 0u && urpcObjLimit > urpcObjBase && urpcObjStride != 0u;
}
[[nodiscard]] bool hasUrpcTables() const
{
return urpcFnTableBase != 0u && urpcObjTableBase != 0u;
}
[[nodiscard]] bool isUrpcRpc(uint32_t sid, uint32_t rpcNum) const
{
return isConfigured() && sid == rpcSid && rpcNum >= 0x400u && rpcNum < 0x500u;
}
};
class PS2Runtime
{
public:
@@ -373,11 +447,39 @@ public:
~PS2Runtime();
bool initialize(const char *title = "PS2 Game");
bool syncCoreSubsystems();
bool loadELF(const std::string &elfPath);
void run();
using RecompiledFunction = void (*)(uint8_t *, R5900Context *, PS2Runtime *);
class GuestExecutionScope
{
public:
explicit GuestExecutionScope(PS2Runtime *runtime) noexcept;
~GuestExecutionScope();
GuestExecutionScope(const GuestExecutionScope &) = delete;
GuestExecutionScope &operator=(const GuestExecutionScope &) = delete;
private:
PS2Runtime *m_runtime = nullptr;
};
class GuestExecutionReleaseScope
{
public:
explicit GuestExecutionReleaseScope(PS2Runtime *runtime) noexcept;
~GuestExecutionReleaseScope();
GuestExecutionReleaseScope(const GuestExecutionReleaseScope &) = delete;
GuestExecutionReleaseScope &operator=(const GuestExecutionReleaseScope &) = delete;
private:
PS2Runtime *m_runtime = nullptr;
uint32_t m_depth = 0u;
};
void registerFunction(uint32_t address, RecompiledFunction func);
RecompiledFunction lookupFunction(uint32_t address);
bool hasFunction(uint32_t address) const;
@@ -409,9 +511,15 @@ public:
void guestFree(uint32_t guestAddr);
uint32_t guestHeapBase() const;
uint32_t guestHeapEnd() const;
uint32_t reserveAsyncCallbackStack(uint32_t size, uint32_t alignment = 16u);
void dispatchLoop(uint8_t *rdram, R5900Context *ctx);
bool shouldPreemptGuestExecution();
void requestStop();
bool isStopRequested() const;
uint32_t guestExecutionWaiterCountForTesting() const
{
return m_guestExecutionWaiters.load(std::memory_order_acquire);
}
uint8_t Load8(uint8_t *rdram, R5900Context *ctx, uint32_t vaddr);
uint16_t Load16(uint8_t *rdram, R5900Context *ctx, uint32_t vaddr);
@@ -496,9 +604,16 @@ private:
uint32_t allocateGuestBlockLocked(uint32_t size, uint32_t alignment);
void freeGuestBlockLocked(uint32_t guestAddr);
void coalesceGuestHeapLocked();
void enterGuestExecution();
void leaveGuestExecution();
uint32_t releaseGuestExecution();
void reacquireGuestExecution(uint32_t depth);
void HandleIntegerOverflow(R5900Context *ctx);
friend class GuestExecutionScope;
friend class GuestExecutionReleaseScope;
private:
PS2Memory m_memory;
GifArbiter m_gifArbiter;
@@ -508,13 +623,18 @@ private:
PSPadBackend m_padBackend;
VU1Interpreter m_vu1;
R5900Context m_cpuContext;
mutable std::recursive_mutex m_guestExecutionMutex;
mutable std::atomic<uint32_t> m_guestExecutionWaiters{0u};
mutable std::mutex m_guestHeapMutex;
mutable std::mutex m_asyncCallbackStackMutex;
std::vector<GuestHeapBlock> m_guestHeapBlocks;
uint32_t m_guestHeapBase = 0x00100000u;
uint32_t m_guestHeapEnd = 0x00100000u;
uint32_t m_guestHeapLimit = PS2_RAM_SIZE;
uint32_t m_guestHeapSuggestedBase = 0x00100000u;
bool m_guestHeapConfigured = false;
uint32_t m_asyncCallbackStackFloor = 0x01F00000u;
uint32_t m_asyncCallbackStackTop = PS2_RAM_SIZE;
std::unordered_map<uint32_t, RecompiledFunction> m_functionTable;
std::atomic<bool> m_stopRequested{false};
@@ -534,6 +654,8 @@ private:
};
std::vector<LoadedModule> m_loadedModules;
uint8_t *m_boundRdram = nullptr;
uint8_t *m_boundGSVram = nullptr;
};
#endif // PS2_RUNTIME_H
+35 -15
View File
@@ -1,24 +1,44 @@
#ifndef PS2_STUBS_H
#define PS2_STUBS_H
#pragma once
struct R5900Context;
class PS2Runtime;
#include "ps2_runtime.h"
#include "ps2_call_list.h"
#include <cstdint>
#include "ps2_call_list.h"
#include "runtime/ps2_memory.h"
#include "Stubs/Unimplemented.h"
struct PS2MpegCompatLayout
{
uint32_t mpegObjectAddr = 0;
uint32_t videoStateAddr = 0;
uint32_t movieStateAddr = 0;
uint32_t syntheticFramesBeforeEnd = 1u;
uint32_t playingVideoStateValue = 0u;
uint32_t playingMovieStateValue = 2u;
uint32_t finishedVideoStateValue = 3u;
uint32_t finishedMovieStateValue = 3u;
[[nodiscard]] bool matchesMpegObject(uint32_t addr) const
{
return mpegObjectAddr != 0u && ((addr & PS2_RAM_MASK) == (mpegObjectAddr & PS2_RAM_MASK));
}
[[nodiscard]] bool hasFinishTargets() const
{
return videoStateAddr != 0u || movieStateAddr != 0u;
}
};
namespace ps2_stubs
{
#define PS2_DECLARE_STUB(name) void name(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
#define PS2_DECLARE_STUB(name) void name(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
PS2_STUB_LIST(PS2_DECLARE_STUB)
#undef PS2_DECLARE_STUB
#undef PS2_DECLARE_STUB
void resetGsSyncVCallbackState();
void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime);
void resetSifState();
void syMalloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void setMpegCompatLayout(const PS2MpegCompatLayout &layout);
void clearMpegCompatLayout();
}
#endif // PS2_STUBS_H
+20 -1
View File
@@ -13,7 +13,7 @@ std::string translatePs2Path(const char *ps2Path);
extern std::atomic<int> g_activeThreads;
static std::mutex g_sys_fd_mutex;
inline std::mutex g_sys_fd_mutex;
namespace ps2_syscalls
{
@@ -21,10 +21,29 @@ namespace ps2_syscalls
PS2_SYSCALL_LIST(PS2_DECLARE_SYSCALL)
#undef PS2_DECLARE_SYSCALL
void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause);
void initializeGuestKernelState(uint8_t *rdram);
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId);
void notifyRuntimeStop();
void joinAllGuestHostThreads();
void detachAllGuestHostThreads();
void resetSoundDriverRpcState();
void setSoundDriverCompatLayout(const PS2SoundDriverCompatLayout &layout);
void clearSoundDriverCompatLayout();
void setDtxCompatLayout(const PS2DtxCompatLayout &layout);
void clearDtxCompatLayout();
void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime);
uint64_t GetCurrentVSyncTick();
uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime);
void WaitVSyncTick(uint8_t *rdram, PS2Runtime *runtime);
}
#endif // PS2_SYSCALLS_H
@@ -37,6 +37,7 @@ private:
bool m_audioReady = false;
uint32_t m_mostRecentSampleKey = 0;
std::vector<DecodedSample> m_loadOrderSamples;
std::vector<uint32_t> m_loadOrderSampleKeys;
std::unordered_map<uint32_t, DecodedSample> m_sampleBank;
std::mutex m_mutex;
@@ -39,6 +39,11 @@ static inline uint32_t fbStride(uint32_t fbw, uint8_t psm)
return pixelsPerRow * (bitsPerPixel(psm) / 8u);
}
static inline uint32_t framePageBaseToBlock(uint32_t fbp)
{
return fbp << 5u;
}
static inline int clampInt(int v, int lo, int hi)
{
if (v < lo) return lo;
@@ -148,6 +148,20 @@ struct GSXYOffsetReg
uint16_t ofy;
};
struct GSTexaReg
{
uint8_t ta0;
bool aem;
uint8_t ta1;
};
struct GSTexClutReg
{
uint8_t cbw;
uint8_t cou;
uint16_t cov;
};
struct GSContext
{
GSFrameReg frame;
@@ -216,6 +230,20 @@ public:
const uint8_t *lockDisplaySnapshot(uint32_t &outSize);
void unlockDisplaySnapshot();
uint32_t getLastDisplayBaseBytes() const;
const GSFrameReg &getContextFrame(int index) const
{
return m_ctx[(index != 0) ? 1 : 0].frame;
}
bool getPreferredDisplaySource(GSFrameReg &outSource, uint32_t &outDestFbp) const;
void latchHostPresentationFrame();
bool copyLatchedHostPresentationFrame(std::vector<uint8_t> &outPixels,
uint32_t &outWidth,
uint32_t &outHeight,
uint32_t *outDisplayFbp = nullptr,
uint32_t *outSourceFbp = nullptr,
bool *outUsedPreferred = nullptr) const;
bool clearFramebufferContext(uint32_t contextIndex, uint32_t rgba);
bool clearActiveFramebuffer(uint32_t rgba);
uint32_t consumeLocalToHostBytes(uint8_t *dst, uint32_t maxBytes);
@@ -227,13 +255,24 @@ private:
void vertexKick(bool drawing);
void processImageData(const uint8_t *data, uint32_t sizeBytes);
void performLocalToLocalTransfer();
void performLocalToHostToBuffer();
bool copyFrameToHostRgbaUnlocked(const GSFrameReg &frame,
uint32_t width,
uint32_t height,
std::vector<uint8_t> &outPixels,
bool preserveAlpha = false,
bool useLocalMemoryLayout = false,
bool frameBaseIsPages = true,
uint32_t sourceOriginX = 0u,
uint32_t sourceOriginY = 0u) const;
GSContext &activeContext();
uint8_t *m_vram = nullptr;
uint32_t m_vramSize = 0;
struct GSRegisters *m_privRegs = nullptr;
mutable std::recursive_mutex m_stateMutex;
GSContext m_ctx[2];
GSPrimReg m_prim{};
@@ -245,6 +284,9 @@ private:
uint8_t m_curFog = 0;
bool m_prmodecont = true;
bool m_pabe = false;
GSTexaReg m_texa{0u, false, 0u};
GSTexClutReg m_texclut{0u, 0u, 0u};
GSBitBltBuf m_bitbltbuf{};
GSTrxPos m_trxpos{};
@@ -261,6 +303,16 @@ private:
std::vector<uint8_t> m_displaySnapshot;
std::mutex m_snapshotMutex;
uint32_t m_lastDisplayBaseBytes = 0;
GSFrameReg m_preferredDisplaySourceFrame{};
uint32_t m_preferredDisplayDestFbp = 0;
bool m_hasPreferredDisplaySource = false;
std::vector<uint8_t> m_hostPresentationFrame;
uint32_t m_hostPresentationWidth = 0;
uint32_t m_hostPresentationHeight = 0;
uint32_t m_hostPresentationDisplayFbp = 0;
uint32_t m_hostPresentationSourceFbp = 0;
bool m_hostPresentationUsedPreferred = false;
bool m_hasHostPresentationFrame = false;
std::vector<uint8_t> m_localToHostBuffer;
size_t m_localToHostReadPos = 0;
@@ -0,0 +1,96 @@
#ifndef PS2_GS_PSMCT16_H
#define PS2_GS_PSMCT16_H
#include <cstdint>
namespace GSPSMCT16
{
static constexpr uint8_t blockTable16[8][4] = {
{0, 2, 8, 10},
{1, 3, 9, 11},
{4, 6, 12, 14},
{5, 7, 13, 15},
{16, 18, 24, 26},
{17, 19, 25, 27},
{20, 22, 28, 30},
{21, 23, 29, 31},
};
static constexpr uint8_t blockTable16S[8][4] = {
{0, 2, 16, 18},
{1, 3, 17, 19},
{8, 10, 24, 26},
{9, 11, 25, 27},
{4, 6, 20, 22},
{5, 7, 21, 23},
{12, 14, 28, 30},
{13, 15, 29, 31},
};
static constexpr uint8_t blockTableZ16[8][4] = {
{24, 26, 16, 18},
{25, 27, 17, 19},
{28, 30, 20, 22},
{29, 31, 21, 23},
{8, 10, 0, 2},
{9, 11, 1, 3},
{12, 14, 4, 6},
{13, 15, 5, 7},
};
static constexpr uint8_t blockTableZ16S[8][4] = {
{24, 26, 8, 10},
{25, 27, 9, 11},
{16, 18, 0, 2},
{17, 19, 1, 3},
{28, 30, 12, 14},
{29, 31, 13, 15},
{20, 22, 4, 6},
{21, 23, 5, 7},
};
static constexpr uint8_t columnTable16[2][16] = {
{0, 2, 8, 10, 16, 18, 24, 26, 1, 3, 9, 11, 17, 19, 25, 27},
{4, 6, 12, 14, 20, 22, 28, 30, 5, 7, 13, 15, 21, 23, 29, 31},
};
inline uint32_t addrPSMCT16Like(uint32_t block,
uint32_t width,
uint32_t x,
uint32_t y,
const uint8_t (&blockTable)[8][4])
{
const uint32_t pagesPerRow = (width != 0u) ? width : 1u;
const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 6u);
const uint32_t blockId = (block & 0x1Fu) + blockTable[(y >> 3u) & 0x7u][(x >> 4u) & 0x3u];
const uint32_t pageOffset = (blockId >> 5u) << 13u;
const uint32_t localBlock = blockId & 0x1Fu;
const uint32_t columnOffset = ((y >> 1u) & 0x3u) * 64u;
return (page << 13u) + pageOffset + localBlock * 256u + columnOffset +
static_cast<uint32_t>(columnTable16[y & 0x1u][x & 0x0Fu]) * 2u;
}
inline uint32_t addrPSMCT16(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTable16);
}
inline uint32_t addrPSMCT16S(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTable16S);
}
inline uint32_t addrPSMZ16(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTableZ16);
}
inline uint32_t addrPSMZ16S(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTableZ16S);
}
}
#endif
@@ -0,0 +1,40 @@
#ifndef PS2_GS_PSMCT32_H
#define PS2_GS_PSMCT32_H
#include <cstdint>
namespace GSPSMCT32
{
static constexpr uint8_t blockTable32[4][8] = {
{0, 1, 4, 5, 16, 17, 20, 21},
{2, 3, 6, 7, 18, 19, 22, 23},
{8, 9, 12, 13, 24, 25, 28, 29},
{10, 11, 14, 15, 26, 27, 30, 31},
};
static constexpr uint8_t columnTable32[8][8] = {
{0, 1, 4, 5, 8, 9, 12, 13},
{2, 3, 6, 7, 10, 11, 14, 15},
{16, 17, 20, 21, 24, 25, 28, 29},
{18, 19, 22, 23, 26, 27, 30, 31},
{32, 33, 36, 37, 40, 41, 44, 45},
{34, 35, 38, 39, 42, 43, 46, 47},
{48, 49, 52, 53, 56, 57, 60, 61},
{50, 51, 54, 55, 58, 59, 62, 63},
};
inline uint32_t addrPSMCT32(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
const uint32_t pagesPerRow = (width != 0u) ? width : 1u;
const uint32_t page = (block >> 5u) + (y >> 5u) * pagesPerRow + (x >> 6u);
const uint32_t blockId = (block & 0x1Fu) + blockTable32[(y >> 3u) & 3u][(x >> 3u) & 7u];
const uint32_t pageOffset = (blockId >> 5u) << 13u;
const uint32_t localBlock = blockId & 0x1Fu;
return (page << 13u) + pageOffset + localBlock * 256u +
static_cast<uint32_t>(columnTable32[y & 0x7u][x & 0x7u]) * 4u;
}
}
#endif
@@ -0,0 +1,51 @@
#ifndef PS2_GS_PSMT4_H
#define PS2_GS_PSMT4_H
#include <cstdint>
namespace GSPSMT4
{
static constexpr uint8_t blockTable4[8][4] = {
{0, 2, 8, 10},
{1, 3, 9, 11},
{4, 6, 12, 14},
{5, 7, 13, 15},
{16, 18, 24, 26},
{17, 19, 25, 27},
{20, 22, 28, 30},
{21, 23, 29, 31},
};
static const uint16_t columnTable4[16][32] = {
{0, 8, 32, 40, 64, 72, 96, 104, 2, 10, 34, 42, 66, 74, 98, 106, 4, 12, 36, 44, 68, 76, 100, 108, 6, 14, 38, 46, 70, 78, 102, 110},
{16, 24, 48, 56, 80, 88, 112, 120, 18, 26, 50, 58, 82, 90, 114, 122, 20, 28, 52, 60, 84, 92, 116, 124, 22, 30, 54, 62, 86, 94, 118, 126},
{65, 73, 97, 105, 1, 9, 33, 41, 67, 75, 99, 107, 3, 11, 35, 43, 69, 77, 101, 109, 5, 13, 37, 45, 71, 79, 103, 111, 7, 15, 39, 47},
{81, 89, 113, 121, 17, 25, 49, 57, 83, 91, 115, 123, 19, 27, 51, 59, 85, 93, 117, 125, 21, 29, 53, 61, 87, 95, 119, 127, 23, 31, 55, 63},
{192, 200, 224, 232, 128, 136, 160, 168, 194, 202, 226, 234, 130, 138, 162, 170, 196, 204, 228, 236, 132, 140, 164, 172, 198, 206, 230, 238, 134, 142, 166, 174},
{208, 216, 240, 248, 144, 152, 176, 184, 210, 218, 242, 250, 146, 154, 178, 186, 212, 220, 244, 252, 148, 156, 180, 188, 214, 222, 246, 254, 150, 158, 182, 190},
{129, 137, 161, 169, 193, 201, 225, 233, 131, 139, 163, 171, 195, 203, 227, 235, 133, 141, 165, 173, 197, 205, 229, 237, 135, 143, 167, 175, 199, 207, 231, 239},
{145, 153, 177, 185, 209, 217, 241, 249, 147, 155, 179, 187, 211, 219, 243, 251, 149, 157, 181, 189, 213, 221, 245, 253, 151, 159, 183, 191, 215, 223, 247, 255},
{256, 264, 288, 296, 320, 328, 352, 360, 258, 266, 290, 298, 322, 330, 354, 362, 260, 268, 292, 300, 324, 332, 356, 364, 262, 270, 294, 302, 326, 334, 358, 366},
{272, 280, 304, 312, 336, 344, 368, 376, 274, 282, 306, 314, 338, 346, 370, 378, 276, 284, 308, 316, 340, 348, 372, 380, 278, 286, 310, 318, 342, 350, 374, 382},
{321, 329, 353, 361, 257, 265, 289, 297, 323, 331, 355, 363, 259, 267, 291, 299, 325, 333, 357, 365, 261, 269, 293, 301, 327, 335, 359, 367, 263, 271, 295, 303},
{337, 345, 369, 377, 273, 281, 305, 313, 339, 347, 371, 379, 275, 283, 307, 315, 341, 349, 373, 381, 277, 285, 309, 317, 343, 351, 375, 383, 279, 287, 311, 319},
{448, 456, 480, 488, 384, 392, 416, 424, 450, 458, 482, 490, 386, 394, 418, 426, 452, 460, 484, 492, 388, 396, 420, 428, 454, 462, 486, 494, 390, 398, 422, 430},
{464, 472, 496, 504, 400, 408, 432, 440, 466, 474, 498, 506, 402, 410, 434, 442, 468, 476, 500, 508, 404, 412, 436, 444, 470, 478, 502, 510, 406, 414, 438, 446},
{385, 393, 417, 425, 449, 457, 481, 489, 387, 395, 419, 427, 451, 459, 483, 491, 389, 397, 421, 429, 453, 461, 485, 493, 391, 399, 423, 431, 455, 463, 487, 495},
{401, 409, 433, 441, 465, 473, 497, 505, 403, 411, 435, 443, 467, 475, 499, 507, 405, 413, 437, 445, 469, 477, 501, 509, 407, 415, 439, 447, 471, 479, 503, 511},
};
inline uint32_t addrPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u;
const uint32_t page = (block >> 5u) + (y >> 7u) * pagesPerRow + (x >> 7u);
const uint32_t blockId = (block & 0x1Fu) + blockTable4[(y >> 4u) & 7u][(x >> 5u) & 3u];
const uint32_t pageOffset = (blockId >> 5u) << 14u;
const uint32_t localBlock = blockId & 0x1Fu;
return (page << 14u) + pageOffset + localBlock * 512u + columnTable4[y & 0x0Fu][x & 0x1Fu];
}
}
#endif
@@ -0,0 +1,47 @@
#ifndef PS2_GS_PSMT8_H
#define PS2_GS_PSMT8_H
#include <cstdint>
namespace GSPSMT8
{
static constexpr uint8_t blockTable8[4][8] = {
{0, 1, 4, 5, 16, 17, 20, 21},
{2, 3, 6, 7, 18, 19, 22, 23},
{8, 9, 12, 13, 24, 25, 28, 29},
{10, 11, 14, 15, 26, 27, 30, 31},
};
static constexpr uint8_t columnTable8[16][16] = {
{0, 4, 16, 20, 32, 36, 48, 52, 2, 6, 18, 22, 34, 38, 50, 54},
{8, 12, 24, 28, 40, 44, 56, 60, 10, 14, 26, 30, 42, 46, 58, 62},
{33, 37, 49, 53, 1, 5, 17, 21, 35, 39, 51, 55, 3, 7, 19, 23},
{41, 45, 57, 61, 9, 13, 25, 29, 43, 47, 59, 63, 11, 15, 27, 31},
{96, 100, 112, 116, 64, 68, 80, 84, 98, 102, 114, 118, 66, 70, 82, 86},
{104, 108, 120, 124, 72, 76, 88, 92, 106, 110, 122, 126, 74, 78, 90, 94},
{65, 69, 81, 85, 97, 101, 113, 117, 67, 71, 83, 87, 99, 103, 115, 119},
{73, 77, 89, 93, 105, 109, 121, 125, 75, 79, 91, 95, 107, 111, 123, 127},
{128, 132, 144, 148, 160, 164, 176, 180, 130, 134, 146, 150, 162, 166, 178, 182},
{136, 140, 152, 156, 168, 172, 184, 188, 138, 142, 154, 158, 170, 174, 186, 190},
{161, 165, 177, 181, 129, 133, 145, 149, 163, 167, 179, 183, 131, 135, 147, 151},
{169, 173, 185, 189, 137, 141, 153, 157, 171, 175, 187, 191, 139, 143, 155, 159},
{224, 228, 240, 244, 192, 196, 208, 212, 226, 230, 242, 246, 194, 198, 210, 214},
{232, 236, 248, 252, 200, 204, 216, 220, 234, 238, 250, 254, 202, 206, 218, 222},
{193, 197, 209, 213, 225, 229, 241, 245, 195, 199, 211, 215, 227, 231, 243, 247},
{201, 205, 217, 221, 233, 237, 249, 253, 203, 207, 219, 223, 235, 239, 251, 255},
};
inline uint32_t addrPSMT8(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u;
const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 7u);
const uint32_t blockId = (block & 0x1Fu) + blockTable8[(y >> 4) & 3u][(x >> 4) & 7u];
const uint32_t pageOffset = (blockId >> 5u) << 13u;
const uint32_t localBlock = blockId & 0x1Fu;
return (page << 13u) + pageOffset + localBlock * 256u + columnTable8[y & 0x0Fu][x & 0x0Fu];
}
}
#endif
@@ -10,10 +10,11 @@ class GSRasterizer
public:
void drawPrimitive(GS *gs);
void writePixel(GS *gs, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
uint32_t sampleTexture(GS *gs, float s, float t, uint16_t u, uint16_t v);
uint32_t sampleTexture(GS *gs, float s, float t, float q, uint16_t u, uint16_t v);
uint32_t readTexelPSMCT32(GS *gs, uint32_t tbp0, uint32_t tbw, int texU, int texV);
uint32_t readTexelPSMCT16(GS *gs, uint32_t tbp0, uint32_t tbw, int texU, int texV);
uint32_t readTexelPSMT4(GS *gs, uint32_t tbp0, uint32_t tbw, int texU, int texV);
uint32_t lookupCLUT(GS *gs, uint8_t index, uint32_t cbp, uint8_t cpsm, uint8_t csa);
uint32_t lookupCLUT(GS *gs, uint8_t index, uint32_t cbp, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm);
private:
void drawSprite(GS *gs);
+36
View File
@@ -0,0 +1,36 @@
#ifndef PS2_IOP_H
#define PS2_IOP_H
#include <cstdint>
class PS2Runtime;
constexpr uint32_t IOP_SID_SNDDRV_COMMAND = 0x00000000u;
constexpr uint32_t IOP_SID_SNDDRV_STATE = 0x00000001u;
constexpr uint32_t IOP_SID_LIBSD = 0x80000701u;
constexpr uint32_t IOP_RPC_SNDDRV_SUBMIT = 0x00000000u;
constexpr uint32_t IOP_RPC_SNDDRV_GET_STATUS_ADDR = 0x00000012u;
constexpr uint32_t IOP_RPC_SNDDRV_GET_ADDR_TABLE = 0x00000013u;
class ps2_iop
{
public:
ps2_iop();
~ps2_iop() = default;
void init(uint8_t *rdram);
void reset();
bool handleRPC(PS2Runtime *runtime,
uint32_t sid, uint32_t rpcNum,
uint32_t sendBufAddr, uint32_t sendSize,
uint32_t recvBufAddr, uint32_t recvSize,
uint32_t &resultPtr,
bool &signalNowaitCompletion);
private:
uint8_t *m_rdram = nullptr;
};
#endif
@@ -23,6 +23,7 @@ constexpr uint32_t PS2_RAM_SIZE = 32u * 1024u * 1024u; // 32MB
constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1u; // Mask for 32MB alignment
constexpr uint32_t PS2_RAM_BASE = 0x00000000; // Physical base of RDRAM
constexpr uint32_t PS2_SCRATCHPAD_BASE = 0x70000000;
constexpr uint32_t PS2_SCRATCHPAD_ALIAS_BASE = 0xF0000000;
constexpr uint32_t PS2_SCRATCHPAD_SIZE = 16u * 1024u; // 16KB
constexpr uint32_t PS2_IO_BASE = 0x10000000; // Base for many I/O regs (Timers, DMAC, INTC)
constexpr uint32_t PS2_IO_SIZE = 0x10000; // 64KB
@@ -81,12 +82,40 @@ inline uint8_t *ps2GetScratchpadHostPtr()
return ps2ScratchpadHostPtrStorage().load(std::memory_order_relaxed);
}
inline bool ps2ResolveGuestPointer(uint32_t addr, uint32_t &offset, bool &scratch)
inline bool ps2IsScratchpadAddress(uint32_t addr)
{
if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
{
return true;
}
if ((addr & 0x80000000u) != 0u)
{
const uint32_t lower = addr & 0x7FFFFFFFu;
return lower >= PS2_SCRATCHPAD_BASE &&
lower < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE);
}
return false;
}
inline uint32_t ps2ScratchpadOffset(uint32_t addr)
{
if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
{
return addr - PS2_SCRATCHPAD_BASE;
}
const uint32_t lower = addr & 0x7FFFFFFFu;
return lower - PS2_SCRATCHPAD_BASE;
}
inline bool ps2ResolveGuestPointer(uint32_t addr, uint32_t &offset, bool &scratch)
{
if (ps2IsScratchpadAddress(addr))
{
scratch = true;
offset = addr - PS2_SCRATCHPAD_BASE;
offset = ps2ScratchpadOffset(addr);
return true;
}
@@ -276,6 +305,8 @@ public:
using Vu1MscalCallback = std::function<void(uint32_t startPC, uint32_t itop)>;
void setVu1MscalCallback(Vu1MscalCallback cb) { m_vu1MscalCallback = std::move(cb); }
using Vu1MscntCallback = std::function<void(uint32_t itop)>;
void setVu1MscntCallback(Vu1MscntCallback cb) { m_vu1MscntCallback = std::move(cb); }
uint8_t *getVU1Code() { return m_vu1Code; }
const uint8_t *getVU1Code() const { return m_vu1Code; }
@@ -344,10 +375,13 @@ public:
GifPacketCallback m_gifPacketCallback;
GifArbiter *m_gifArbiter = nullptr;
Vu1MscalCallback m_vu1MscalCallback;
Vu1MscntCallback m_vu1MscntCallback;
uint8_t *m_vu1Code = nullptr;
uint8_t *m_vu1Data = nullptr;
bool m_path3Masked = false;
uint32_t m_vif1PendingPath2ImageQwc = 0u;
bool m_vif1PendingPath2DirectHl = false;
std::vector<std::vector<uint8_t>> m_path3MaskedFifo;
struct PendingTransfer
-108
View File
@@ -1,108 +0,0 @@
#include "ps2_runtime.h"
#include <iostream>
#include <string>
// Example of how to use the PS2 runtime with recompiled code
// Stub implementation for PS2 syscalls
void syscall(uint8_t *rdram, R5900Context *ctx)
{
uint32_t syscallNum = ctx->r[4].m128i_u32[0];
std::cout << "Syscall " << syscallNum << " called" << std::endl;
switch (syscallNum)
{
case 0x01: // Exit program
std::cout << "Program requested exit with code: " << ctx->r[5].m128i_u32[0] << std::endl;
break;
case 0x3C: // PutChar - print a character to stdout
std::cout << (char)ctx->r[5].m128i_u32[0];
break;
case 0x3D: // PutString - print a string to stdout
{
uint32_t strAddr = ctx->r[5].m128i_u32[0];
if (strAddr == 0)
{
std::cout << "(null)";
}
else
{
uint32_t physAddr = strAddr & 0x1FFFFFFF;
const char *str = reinterpret_cast<const char *>(rdram + physAddr);
std::cout << str;
}
}
break;
default:
std::cout << "Unhandled syscall: " << syscallNum << std::endl;
break;
}
}
// Example implementation of FlushCache
void FlushCache(uint8_t *rdram, R5900Context *ctx)
{
uint32_t cacheType = ctx->r[4].m128i_u32[0];
std::cout << "FlushCache called with type: " << cacheType << std::endl;
}
// Example implementation of a recompiled function
void recompiled_main(uint8_t *rdram, R5900Context *ctx)
{
std::cout << "Running recompiled main function" << std::endl;
// Example of memory access
uint32_t addr = 0x100000; // Some address in memory
uint32_t physAddr = addr & 0x1FFFFFFF;
uint32_t value = *reinterpret_cast<uint32_t *>(rdram + physAddr);
std::cout << "Value at 0x" << std::hex << addr << " = 0x" << value << std::dec << std::endl;
// Example of register manipulation
ctx->r[2] = _mm_set1_epi32(0x12345678); // Set register v0
ctx->r[4] = _mm_set1_epi32(0x3D); // Set register a0 for syscall (PutString)
ctx->r[5] = _mm_set1_epi32(0x10000); // Set register a1 with string address
// Call a "syscall" function
syscall(rdram, ctx);
// Example of returning a value
ctx->r[2] = _mm_set1_epi32(0); // Return 0 (success)
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << "Usage: " << argv[0] << " <elf_file>" << std::endl;
return 1;
}
std::string elfPath = argv[1];
PS2Runtime runtime;
if (!runtime.initialize())
{
std::cerr << "Failed to initialize PS2 runtime" << std::endl;
return 1;
}
// Register built-in functions
runtime.registerFunction(0x00000001, syscall);
runtime.registerFunction(0x00000002, FlushCache);
runtime.registerFunction(0x00100000, recompiled_main); // Example address for main
// Load the ELF file
if (!runtime.loadELF(elfPath))
{
std::cerr << "Failed to load ELF file: " << elfPath << std::endl;
return 1;
}
// Run the program
runtime.run();
return 0;
}
+549
View File
@@ -0,0 +1,549 @@
#include "Common.h"
#include "Audio.h"
namespace ps2_stubs
{
namespace
{
constexpr uint32_t kLibSdCmdSetParam = 0x8010u;
constexpr uint32_t kLibSdCmdBlockTrans = 0x80D0u;
constexpr uint32_t kLibSdCmdBlockTransAlt = 0x80E0u;
constexpr uint32_t kAudioPositionMask = 0x00FFFFFFu;
struct AudioStubState
{
bool initialized = false;
uint32_t currentBlockBase = 0u;
uint32_t currentBlockSize = 0u;
uint32_t currentPauseBase = 0u;
};
std::mutex g_audio_stub_mutex;
AudioStubState g_audio_stub_state;
void resetAudioStubStateUnlocked()
{
g_audio_stub_state = {};
}
}
void resetAudioStubState()
{
std::lock_guard<std::mutex> lock(g_audio_stub_mutex);
resetAudioStubStateUnlocked();
}
void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSdCallBack", rdram, ctx, runtime);
}
void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t cmd = getRegU32(ctx, 5);
const uint32_t cmdArg0 = getRegU32(ctx, 6);
const uint32_t cmdArg1 = getRegU32(ctx, 7);
const uint32_t sp = getRegU32(ctx, 29);
const uint32_t arg4Reg = getRegU32(ctx, 8);
const uint32_t arg5Reg = getRegU32(ctx, 9);
const uint32_t arg6Reg = getRegU32(ctx, 10);
const uint32_t arg4Stk = FAST_READ32(sp + 0x10u);
const uint32_t arg5Stk = FAST_READ32(sp + 0x14u);
const uint32_t arg6Stk = FAST_READ32(sp + 0x18u);
const uint32_t arg4 = (arg4Reg != 0u) ? arg4Reg : arg4Stk;
const uint32_t arg5 = (arg5Reg != 0u) ? arg5Reg : arg5Stk;
const uint32_t arg6 = (arg6Reg != 0u) ? arg6Reg : arg6Stk;
std::lock_guard<std::mutex> lock(g_audio_stub_mutex);
g_audio_stub_state.initialized = true;
if (cmd == kLibSdCmdBlockTrans || cmd == kLibSdCmdBlockTransAlt)
{
if (arg4 != 0u)
{
g_audio_stub_state.currentBlockBase = arg4 & kAudioPositionMask;
}
if (arg5 != 0u)
{
g_audio_stub_state.currentBlockSize = arg5;
}
if (arg6 != 0u)
{
g_audio_stub_state.currentPauseBase = arg6 & kAudioPositionMask;
}
}
else if (cmd == kLibSdCmdSetParam)
{
(void)cmdArg0;
(void)cmdArg1;
}
// Some games only sample the low 24 bits of the reported SPU transfer head.
// Returning the last configured transfer base keeps the ring-buffer math
// stable without emulating SPU DMA progress.
setReturnU32(ctx, g_audio_stub_state.currentBlockBase & kAudioPositionMask);
}
void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_audio_stub_mutex);
resetAudioStubStateUnlocked();
g_audio_stub_state.initialized = true;
setReturnS32(ctx, 0);
}
void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime);
}
void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime);
}
void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime);
}
void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime);
}
void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime);
}
void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime);
}
void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime);
}
void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime);
}
void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime);
}
void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime);
}
void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime);
}
void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime);
}
void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime);
}
void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime);
}
void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime);
}
void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime);
}
void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime);
}
void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime);
}
void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime);
}
void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime);
}
void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime);
}
void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime);
}
void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime);
}
void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime);
}
void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime);
}
void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime);
}
void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime);
}
void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime);
}
void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime);
}
void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime);
}
void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime);
}
void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime);
}
void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime);
}
void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime);
}
void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime);
}
void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime);
}
void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime);
}
void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime);
}
void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime);
}
void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime);
}
void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime);
}
void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime);
}
void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime);
}
void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime);
}
void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime);
}
void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime);
}
void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime);
}
void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime);
}
void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime);
}
void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime);
}
void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime);
}
void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime);
}
void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime);
}
void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime);
}
void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime);
}
void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime);
}
void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime);
}
void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime);
}
void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime);
}
void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime);
}
void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime);
}
void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime);
}
void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime);
}
void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime);
}
void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime);
}
void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime);
}
void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime);
}
void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime);
}
void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime);
}
void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime);
}
void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime);
}
void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime);
}
void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime);
}
void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime);
}
void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime);
}
void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime);
}
void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime);
}
void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime);
}
void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime);
}
void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime);
}
void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime);
}
void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime);
}
void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime);
}
void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime);
}
void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime);
}
void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime);
}
void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime);
}
void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime);
}
void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime);
}
}
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void resetAudioStubState();
void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+559
View File
@@ -0,0 +1,559 @@
#include "Common.h"
#include "CD.h"
namespace ps2_stubs
{
void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t a0 = getRegU32(ctx, 4); // usually lbn
const uint32_t a1 = getRegU32(ctx, 5); // usually sector count
const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer
struct CdReadArgs
{
uint32_t lbn = 0;
uint32_t sectors = 0;
uint32_t buf = 0;
const char *tag = "";
};
auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t
{
const uint64_t requested = static_cast<uint64_t>(sectors) * static_cast<uint64_t>(kCdSectorSize);
if (requested == 0)
{
return 0;
}
const uint64_t maxBytes = static_cast<uint64_t>(PS2_RAM_SIZE - offset);
const uint64_t clamped = std::min<uint64_t>(requested, maxBytes);
return static_cast<size_t>(clamped);
};
auto tryRead = [&](const CdReadArgs &args) -> bool
{
const uint32_t offset = args.buf & PS2_RAM_MASK;
const size_t bytes = clampReadBytes(args.sectors, offset);
if (bytes == 0)
{
return true;
}
return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes);
};
CdReadArgs selected{a0, a1, a2, "a0/a1/a2"};
bool ok = tryRead(selected);
if (!ok)
{
// Some game-side wrappers use a nonstandard register layout.
// If primary decode does not resolve to a known LBN, try safe alternatives.
constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize;
if (!isResolvableCdLbn(selected.lbn))
{
const std::array<CdReadArgs, 5> alternatives = {
CdReadArgs{a2, a1, a0, "a2/a1/a0"},
CdReadArgs{a0, a2, a1, "a0/a2/a1"},
CdReadArgs{a1, a0, a2, "a1/a0/a2"},
CdReadArgs{a1, a2, a0, "a1/a2/a0"},
CdReadArgs{a2, a0, a1, "a2/a0/a1"}};
for (const CdReadArgs &candidate : alternatives)
{
if (candidate.sectors > kMaxReasonableSectors)
{
continue;
}
if (!isResolvableCdLbn(candidate.lbn))
{
continue;
}
if (tryRead(candidate))
{
static uint32_t recoverLogCount = 0;
if (recoverLogCount < 16)
{
RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag
<< " (pc=0x" << std::hex << ctx->pc
<< " ra=0x" << getRegU32(ctx, 31)
<< " a0=0x" << a0
<< " a1=0x" << a1
<< " a2=0x" << a2 << std::dec << ")" << std::endl);
++recoverLogCount;
}
selected = candidate;
ok = true;
break;
}
}
}
if (!ok)
{
const uint32_t offset = a2 & PS2_RAM_MASK;
const size_t bytes = clampReadBytes(a1, offset);
if (bytes > 0)
{
std::memset(rdram + offset, 0, bytes);
}
static uint32_t unresolvedLogCount = 0;
if (unresolvedLogCount < 32)
{
std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc
<< " ra=0x" << getRegU32(ctx, 31)
<< " a0=0x" << a0
<< " a1=0x" << a1
<< " a2=0x" << a2 << std::dec << std::endl;
++unresolvedLogCount;
}
}
}
if (ok)
{
g_cdStreamingLbn = selected.lbn + selected.sectors;
setReturnS32(ctx, 1); // command accepted/success
return;
}
setReturnS32(ctx, 0);
}
void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0); // 0 = completed/not busy
}
void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, g_lastCdError);
}
void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceCdRI", rdram, ctx, runtime);
}
void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceCdRM", rdram, ctx, runtime);
}
void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 2);
}
void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
// SCECdPS2DVD
setReturnS32(ctx, 0x14);
}
void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, g_cdStreamingLbn);
}
void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t tocAddr = getRegU32(ctx, 4);
if (uint8_t *toc = getMemPtr(rdram, tocAddr))
{
std::memset(toc, 0, 1024);
}
setReturnS32(ctx, 1);
}
void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
g_cdInitialized = true;
g_lastCdError = 0;
setReturnS32(ctx, 1);
}
void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t lsn = getRegU32(ctx, 4);
uint32_t posAddr = getRegU32(ctx, 5);
uint8_t *pos = getMemPtr(rdram, posAddr);
if (!pos)
{
setReturnS32(ctx, 0);
return;
}
uint32_t adjusted = lsn + 150;
const uint32_t minutes = adjusted / (60 * 75);
adjusted %= (60 * 75);
const uint32_t seconds = adjusted / 75;
const uint32_t sectors = adjusted % 75;
pos[0] = toBcd(minutes);
pos[1] = toBcd(seconds);
pos[2] = toBcd(sectors);
pos[3] = 0;
setReturnS32(ctx, 1);
}
void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
g_cdMode = getRegU32(ctx, 4);
setReturnS32(ctx, 1);
}
void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 2);
}
void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t posAddr = getRegU32(ctx, 4);
const uint8_t *pos = getConstMemPtr(rdram, posAddr);
if (!pos)
{
setReturnS32(ctx, -1);
return;
}
const uint32_t minutes = fromBcd(pos[0]);
const uint32_t seconds = fromBcd(pos[1]);
const uint32_t sectors = fromBcd(pos[2]);
const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors;
const int32_t lsn = static_cast<int32_t>(absolute) - 150;
setReturnS32(ctx, lsn);
}
void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t chainAddr = getRegU32(ctx, 4);
bool ok = true;
for (int i = 0; i < 64; ++i)
{
uint32_t *entry = reinterpret_cast<uint32_t *>(getMemPtr(rdram, chainAddr + (i * 16)));
if (!entry)
{
ok = false;
break;
}
const uint32_t lbn = entry[0];
const uint32_t sectors = entry[1];
const uint32_t buf = entry[2];
if (lbn == 0xFFFFFFFFu || sectors == 0)
{
break;
}
uint32_t offset = buf & PS2_RAM_MASK;
size_t bytes = static_cast<size_t>(sectors) * kCdSectorSize;
const size_t maxBytes = PS2_RAM_SIZE - offset;
if (bytes > maxBytes)
{
bytes = maxBytes;
}
if (!readCdSectors(lbn, sectors, rdram + offset, bytes))
{
ok = false;
break;
}
g_cdStreamingLbn = lbn + sectors;
}
setReturnS32(ctx, ok ? 1 : 0);
}
void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t clockAddr = getRegU32(ctx, 4);
uint8_t *clockData = getMemPtr(rdram, clockAddr);
if (!clockData)
{
setReturnS32(ctx, 0);
return;
}
std::time_t now = std::time(nullptr);
std::tm localTm{};
#ifdef _WIN32
localtime_s(&localTm, &now);
#else
localtime_r(&now, &localTm);
#endif
// sceCdCLOCK format (BCD fields).
clockData[0] = 0;
clockData[1] = toBcd(static_cast<uint32_t>(localTm.tm_sec));
clockData[2] = toBcd(static_cast<uint32_t>(localTm.tm_min));
clockData[3] = toBcd(static_cast<uint32_t>(localTm.tm_hour));
clockData[4] = 0;
clockData[5] = toBcd(static_cast<uint32_t>(localTm.tm_mday));
clockData[6] = toBcd(static_cast<uint32_t>(localTm.tm_mon + 1));
clockData[7] = toBcd(static_cast<uint32_t>((localTm.tm_year + 1900) % 100));
setReturnS32(ctx, 1);
}
void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
sceCdRead(rdram, ctx, runtime);
}
void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t fileAddr = getRegU32(ctx, 4);
uint32_t pathAddr = getRegU32(ctx, 5);
const std::string path = readPs2CStringBounded(rdram, pathAddr, 260);
const std::string normalizedPath = normalizeCdPathNoPrefix(path);
static uint32_t traceCount = 0;
const uint32_t callerRa = getRegU32(ctx, 31);
const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u);
if (shouldTrace)
{
RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc
<< " ra=0x" << callerRa
<< " file=0x" << fileAddr
<< " pathAddr=0x" << pathAddr
<< " path=\"" << sanitizeForLog(path) << "\""
<< std::dec << std::endl);
}
++traceCount;
if (path.empty())
{
static uint32_t emptyPathCount = 0;
if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u)
{
std::ostringstream preview;
preview << std::hex;
for (uint32_t i = 0; i < 16; ++i)
{
const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i);
preview << (i == 0 ? "" : " ") << static_cast<uint32_t>(byte);
}
std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr
<< " preview=" << preview.str()
<< " ra=0x" << callerRa << std::dec << std::endl;
}
++emptyPathCount;
g_lastCdError = -1;
setReturnS32(ctx, 0);
return;
}
if (normalizedPath.empty())
{
static uint32_t emptyNormalizedCount = 0;
if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u)
{
std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path)
<< " (normalized path is empty, root: " << getCdRootPath().string() << ")"
<< std::endl;
}
++emptyNormalizedCount;
g_lastCdError = -1;
setReturnS32(ctx, 0);
return;
}
CdFileEntry entry;
bool found = registerCdFile(path, entry);
CdFileEntry resolvedEntry = entry;
std::string resolvedPath;
if (!found)
{
static std::string lastFailedPath;
static uint32_t samePathFailCount = 0;
if (path == lastFailedPath)
{
++samePathFailCount;
}
else
{
lastFailedPath = path;
samePathFailCount = 1;
}
if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u)
{
std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path)
<< " (root: " << getCdRootPath().string()
<< ", repeat=" << samePathFailCount << ")" << std::endl;
}
setReturnS32(ctx, 0);
return;
}
if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry))
{
g_lastCdError = -1;
setReturnS32(ctx, 0);
return;
}
g_cdStreamingLbn = resolvedEntry.baseLbn;
if (shouldTrace)
{
RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path)
<< "\" lsn=0x" << std::hex << resolvedEntry.baseLbn
<< " size=0x" << resolvedEntry.sizeBytes
<< " sectors=0x" << resolvedEntry.sectors
<< std::dec << std::endl);
}
setReturnS32(ctx, 1);
}
void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
g_cdStreamingLbn = getRegU32(ctx, 4);
setReturnS32(ctx, 1);
}
void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, g_cdInitialized ? 6 : 0);
}
void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t sectors = getRegU32(ctx, 4);
uint32_t buf = getRegU32(ctx, 5);
uint32_t errAddr = getRegU32(ctx, 7);
uint32_t offset = buf & PS2_RAM_MASK;
size_t bytes = static_cast<size_t>(sectors) * kCdSectorSize;
const size_t maxBytes = PS2_RAM_SIZE - offset;
if (bytes > maxBytes)
{
bytes = maxBytes;
}
const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes);
if (ok)
{
g_cdStreamingLbn += sectors;
}
if (int32_t *err = reinterpret_cast<int32_t *>(getMemPtr(rdram, errAddr)); err)
{
*err = ok ? 0 : g_lastCdError;
}
setReturnS32(ctx, ok ? static_cast<int32_t>(sectors) : 0);
}
void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
g_cdStreamingLbn = getRegU32(ctx, 4);
setReturnS32(ctx, 1);
}
void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
g_cdStreamingLbn = getRegU32(ctx, 4);
setReturnS32(ctx, 1);
}
void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
g_cdStreamingLbn = getRegU32(ctx, 4);
setReturnS32(ctx, 1);
}
void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t statusPtr = getRegU32(ctx, 5);
if (uint32_t *status = reinterpret_cast<uint32_t *>(getMemPtr(rdram, statusPtr)); status)
{
*status = 0;
}
setReturnS32(ctx, 1);
}
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "ps2_stubs.h"
#include "ps2_runtime.h"
#include "ps2_runtime_macros.h"
#include "ps2_syscalls.h"
#include "Unimplemented.h"
#include <iostream>
#include <algorithm>
#include <array>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <filesystem>
#include <mutex>
#include <atomic>
#include <limits>
#include "ps2_host_backend.h"
#include "Helpers/Support.h"
@@ -0,0 +1,123 @@
#include "Common.h"
#include "Compatibility.h"
#include "ps2_log.h"
namespace ps2_stubs
{
void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t count = getRegU32(ctx, 5); // $a1
const uint32_t size = getRegU32(ctx, 6); // $a2
const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u;
setReturnU32(ctx, guestAddr);
}
void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, 0u);
ctx->pc = getRegU32(ctx, 31);
}
void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, 1u);
ctx->pc = getRegU32(ctx, 31);
}
void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, getRegU32(ctx, 4));
ctx->pc = getRegU32(ctx, 31);
}
void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t guestAddr = getRegU32(ctx, 5); // $a1
if (runtime && guestAddr != 0u)
{
runtime->guestFree(guestAddr);
}
}
void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t size = getRegU32(ctx, 5); // $a1
const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u;
setReturnU32(ctx, guestAddr);
}
void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t wcAddr = getRegU32(ctx, 5); // $a1
const uint32_t strAddr = getRegU32(ctx, 6); // $a2
const int32_t n = static_cast<int32_t>(getRegU32(ctx, 7)); // $a3
if (n <= 0 || strAddr == 0u)
{
setReturnS32(ctx, 0);
return;
}
const uint8_t *src = getConstMemPtr(rdram, strAddr);
if (!src)
{
setReturnS32(ctx, -1);
return;
}
const uint8_t ch = *src;
if (wcAddr != 0u)
{
if (uint8_t *dst = getMemPtr(rdram, wcAddr))
{
const uint32_t out = static_cast<uint32_t>(ch);
std::memcpy(dst, &out, sizeof(out));
}
}
setReturnS32(ctx, (ch == 0u) ? 0 : 1);
}
void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t format_addr = getRegU32(ctx, 5); // $a1
const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024);
int ret = -1;
if (format_addr != 0)
{
std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2);
if (rendered.size() > 2048)
{
rendered.resize(2048);
}
PS2_IF_AGRESSIVE_LOGS({
const std::string logLine = sanitizeForLog(rendered);
uint32_t count = 0;
{
std::lock_guard<std::mutex> lock(g_printfLogMutex);
count = ++g_printfLogCount;
}
if (count <= kMaxPrintfLogs)
{
RUNTIME_LOG("PS2 printf: " << logLine);
RUNTIME_LOG(std::flush);
}
else if (count == kMaxPrintfLogs + 1)
{
std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl;
}
});
ret = static_cast<int>(rendered.size());
}
else
{
std::cerr << "printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl;
}
setReturnS32(ctx, ret);
}
}
@@ -0,0 +1,16 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+236
View File
@@ -0,0 +1,236 @@
#include "Common.h"
#include "DMA.h"
namespace ps2_stubs
{
namespace
{
struct SceDmaEnv
{
uint8_t sts = 0;
uint8_t std = 0;
uint8_t mfd = 0;
uint8_t rele = 0;
uint32_t pcr = 0;
uint32_t sqwc = 0;
uint32_t rbor = 0;
uint32_t rbsr = 0;
};
static_assert(sizeof(SceDmaEnv) == 0x14, "sceDmaEnv must match the guest ABI");
constexpr uint32_t DMA_REG_CTRL = 0x1000E000u;
constexpr uint32_t DMA_REG_PCR = 0x1000E020u;
constexpr uint32_t DMA_REG_SQWC = 0x1000E030u;
constexpr uint32_t DMA_REG_RBSR = 0x1000E040u;
constexpr uint32_t DMA_REG_RBOR = 0x1000E050u;
constexpr uint32_t DMA_REG_STADR = 0x1000E060u;
constexpr std::array<uint8_t, 10> kStsTable = {0u, 0u, 0u, 3u, 0u, 1u, 0u, 0u, 2u, 0u};
constexpr std::array<uint8_t, 10> kStdTable = {0u, 1u, 2u, 0u, 0u, 0u, 3u, 0u, 0u, 0u};
constexpr std::array<uint8_t, 10> kMfdTable = {0u, 2u, 3u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
std::mutex g_dmaEnvMutex;
SceDmaEnv g_dmaCurrentEnv;
}
void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, getRegU32(ctx, 4));
}
void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaCallback", rdram, ctx, runtime);
}
void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaDebug", rdram, ctx, runtime);
}
void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t chanArg = getRegU32(ctx, 4);
const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg);
setReturnU32(ctx, channelBase);
}
void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t envAddr = getRegU32(ctx, 4);
if (uint8_t *dst = getMemPtr(rdram, envAddr))
{
std::lock_guard<std::mutex> lock(g_dmaEnvMutex);
std::memcpy(dst, &g_dmaCurrentEnv, sizeof(g_dmaCurrentEnv));
}
setReturnU32(ctx, envAddr);
}
void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime);
}
void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaPause", rdram, ctx, runtime);
}
void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t envAddr = getRegU32(ctx, 4);
const uint8_t *src = getConstMemPtr(rdram, envAddr);
if (!src || !runtime)
{
setReturnS32(ctx, -1);
return;
}
SceDmaEnv env{};
std::memcpy(&env, src, sizeof(env));
if (env.sts >= kStsTable.size())
{
setReturnS32(ctx, -1);
return;
}
if (env.std >= kStdTable.size())
{
setReturnS32(ctx, -2);
return;
}
if (env.mfd >= kMfdTable.size())
{
setReturnS32(ctx, -3);
return;
}
if (env.rele >= 7u)
{
setReturnS32(ctx, -4);
return;
}
PS2Memory &mem = runtime->memory();
uint32_t ctrl = mem.readIORegister(DMA_REG_CTRL);
ctrl = (ctrl & 0xFFFFFFCFu) | (static_cast<uint32_t>(kStsTable[env.sts]) << 4);
ctrl = (ctrl & 0xFFFFFF3Fu) | (static_cast<uint32_t>(kStdTable[env.std]) << 6);
ctrl = (ctrl & 0xFFFFFFF3u) | (static_cast<uint32_t>(kMfdTable[env.mfd]) << 2);
if (env.rele == 0u)
{
ctrl &= 0xFFFFFFFDu;
}
else
{
ctrl = ((ctrl | 0x2u) & 0xFFFFFCFFu) | ((static_cast<uint32_t>(env.rele - 1u) & 0x7u) << 8);
}
mem.writeIORegister(DMA_REG_CTRL, ctrl);
mem.writeIORegister(DMA_REG_PCR, env.pcr);
mem.writeIORegister(DMA_REG_SQWC, env.sqwc);
mem.writeIORegister(DMA_REG_RBOR, env.rbor);
mem.writeIORegister(DMA_REG_RBSR, env.rbsr);
{
std::lock_guard<std::mutex> lock(g_dmaEnvMutex);
g_dmaCurrentEnv = env;
}
setReturnS32(ctx, 0);
}
void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t newAddr = getRegU32(ctx, 4);
uint32_t oldAddr = 0;
if (runtime)
{
PS2Memory &mem = runtime->memory();
oldAddr = mem.readIORegister(DMA_REG_STADR);
if (newAddr != 0xFFFFFFFFu)
{
mem.writeIORegister(DMA_REG_STADR, newAddr);
}
}
setReturnU32(ctx, oldAddr);
}
void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaRecv", rdram, ctx, runtime);
}
void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime);
}
void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime);
}
void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
if (runtime)
{
PS2Memory &mem = runtime->memory();
// libdma reset leaves the controller runnable; DMAE must be re-enabled or chain submissions will be accepted but never execute.
mem.writeIORegister(DMA_REG_CTRL, 0u);
mem.writeIORegister(DMA_REG_PCR, 0u);
mem.writeIORegister(DMA_REG_SQWC, 0u);
mem.writeIORegister(DMA_REG_RBOR, 0u);
mem.writeIORegister(DMA_REG_RBSR, 0u);
mem.writeIORegister(DMA_REG_STADR, 0u);
mem.writeIORegister(DMA_REG_CTRL, 1u);
}
{
std::lock_guard<std::mutex> lock(g_dmaEnvMutex);
g_dmaCurrentEnv = {};
}
setReturnS32(ctx, 0);
}
void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaRestart", rdram, ctx, runtime);
}
void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false));
}
void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false));
}
void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false));
}
void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true));
}
void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime));
}
void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime));
}
void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDmaWatch", rdram, ctx, runtime);
}
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,50 @@
#include "Common.h"
#include "Deci2.h"
namespace ps2_stubs
{
void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2Close", rdram, ctx, runtime);
}
void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime);
}
void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime);
}
void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime);
}
void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime);
}
void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime);
}
void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2Open", rdram, ctx, runtime);
}
void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime);
}
void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime);
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+151
View File
@@ -0,0 +1,151 @@
#include "Common.h"
#include "FileIO.h"
namespace ps2_stubs
{
void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceFsDbChk", rdram, ctx, runtime);
}
void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceFsIntrSigSema", rdram, ctx, runtime);
}
void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceFsSemExit", rdram, ctx, runtime);
}
void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceFsSemInit", rdram, ctx, runtime);
}
void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceFsSigSema", rdram, ctx, runtime);
}
void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioClose(rdram, ctx, runtime);
}
void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t statAddr = getRegU32(ctx, 5);
if (uint8_t *statBuf = getMemPtr(rdram, statAddr))
{
std::memset(statBuf, 0, 128);
setReturnS32(ctx, 0);
return;
}
setReturnS32(ctx, -1);
}
void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioLseek(rdram, ctx, runtime);
}
void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioOpen(rdram, ctx, runtime);
}
void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioRead(rdram, ctx, runtime);
}
void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioClose(rdram, ctx, runtime);
}
void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int32_t cmd = static_cast<int32_t>(getRegU32(ctx, 5));
const uint32_t argAddr = getRegU32(ctx, 6);
// HTCI wait paths poll sceIoctl(fd, 1, &state) and expect state to move
// away from 1 once host-side I/O is no longer busy.
if (cmd == 1 && argAddr != 0u)
{
uint8_t *argPtr = getMemPtr(rdram, argAddr);
if (!argPtr)
{
setReturnS32(ctx, -1);
return;
}
const uint32_t ready = 0u;
std::memcpy(argPtr, &ready, sizeof(ready));
}
setReturnS32(ctx, 0);
}
void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioLseek(rdram, ctx, runtime);
}
void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioOpen(rdram, ctx, runtime);
}
void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioRead(rdram, ctx, runtime);
}
void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioWrite(rdram, ctx, runtime);
}
void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t statAddr = getRegU32(ctx, 5);
uint8_t *statBuf = getMemPtr(rdram, statAddr);
if (!statBuf)
{
setReturnS32(ctx, -1);
return;
}
// Minimal fake stat payload: zeroed structure indicates a valid, readable file.
std::memset(statBuf, 0, 128);
setReturnS32(ctx, 0);
}
void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::fioWrite(rdram, ctx, runtime);
}
void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount < 8)
{
RUNTIME_LOG("ps2_stub cvFsSetDefDev");
++logCount;
}
setReturnS32(ctx, 0);
}
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+667
View File
@@ -0,0 +1,667 @@
#include "Common.h"
#include "Font.h"
namespace ps2_stubs
{
static void writeU32AtGp(uint8_t *rdram, uint32_t gp, int32_t offset, uint32_t value)
{
const uint32_t addr = gp + static_cast<uint32_t>(offset);
if (uint8_t *p = getMemPtr(rdram, addr))
*reinterpret_cast<uint32_t *>(p) = value;
}
void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t gp = getRegU32(ctx, 28);
const uint32_t a0 = getRegU32(ctx, 4);
const uint32_t a1 = getRegU32(ctx, 5);
const uint32_t a2 = getRegU32(ctx, 6);
const uint32_t a3 = getRegU32(ctx, 7);
writeU32AtGp(rdram, gp, -0x7b60, a1);
writeU32AtGp(rdram, gp, -0x7b5c, a2);
writeU32AtGp(rdram, gp, -0x7b64, a0);
writeU32AtGp(rdram, gp, -0x7c98, a3);
writeU32AtGp(rdram, gp, -0x7b4c, 0x7f7f7f7f);
writeU32AtGp(rdram, gp, -0x7b50, 0x3f800000);
writeU32AtGp(rdram, gp, -0x7b54, 0x3f800000);
writeU32AtGp(rdram, gp, -0x7b58, 0);
if (runtime && a0 != 0u)
{
if ((a0 * 256u) + 64u <= PS2_GS_VRAM_SIZE)
{
uint32_t clutData[16];
for (uint32_t i = 0; i < 16u; ++i)
{
uint8_t alpha = static_cast<uint8_t>((i * 0x80u) / 15u);
clutData[i] = (i == 0)
? 0x00000000u
: (0x80u | (0x80u << 8) | (0x80u << 16) | (static_cast<uint32_t>(alpha) << 24));
}
constexpr uint32_t kClutQwc = 4u;
constexpr uint32_t kHeaderQwc = 6u;
constexpr uint32_t kTotalQwc = kHeaderQwc + kClutQwc;
uint32_t pktAddr = runtime->guestMalloc(kTotalQwc * 16u, 16u);
if (pktAddr != 0u)
{
uint8_t *pkt = getMemPtr(rdram, pktAddr);
if (pkt)
{
uint64_t *q = reinterpret_cast<uint64_t *>(pkt);
const uint32_t dbp = a0 & 0x3FFFu;
constexpr uint8_t psm = 0u;
q[0] = makeGiftagAplusD(4u);
q[1] = 0xEULL;
q[2] = (static_cast<uint64_t>(dbp) << 32) | (1ULL << 48) | (static_cast<uint64_t>(psm) << 56);
q[3] = 0x50ULL;
q[4] = 0ULL;
q[5] = 0x51ULL;
q[6] = 16ULL | (1ULL << 32);
q[7] = 0x52ULL;
q[8] = 0ULL;
q[9] = 0x53ULL;
q[10] = (2ULL << 58) | (kClutQwc & 0x7FFF) | (1ULL << 15);
q[11] = 0ULL;
std::memcpy(pkt + 12u * 8u, clutData, 64u);
constexpr uint32_t GIF_CHANNEL = 0x1000A000;
constexpr uint32_t CHCR_STR_MODE0 = 0x101u;
runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr);
runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, kTotalQwc & 0xFFFFu);
runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0);
runtime->memory().processPendingTransfers();
}
}
}
}
setReturnS32(ctx, static_cast<int32_t>(a0 + 4));
}
void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static constexpr uint32_t kFontBase = 0x176148u;
static constexpr uint32_t kFontEntrySz = 0x24u;
const uint32_t fontDataAddr = getRegU32(ctx, 4);
const int fontId = static_cast<int>(getRegU32(ctx, 5));
const int tbp0 = static_cast<int>(getRegU32(ctx, 7));
if (!fontDataAddr || !runtime)
{
setReturnS32(ctx, tbp0);
return;
}
const uint8_t *fontPtr = getConstMemPtr(rdram, fontDataAddr);
if (!fontPtr)
{
setReturnS32(ctx, tbp0);
return;
}
int width = static_cast<int>(*reinterpret_cast<const uint32_t *>(fontPtr + 0x00u));
int height = static_cast<int>(*reinterpret_cast<const uint32_t *>(fontPtr + 0x04u));
uint32_t raw8 = *reinterpret_cast<const uint32_t *>(fontPtr + 0x08u);
int fontDataSz = static_cast<int>(*reinterpret_cast<const uint32_t *>(fontPtr + 0x0cu));
uint32_t pointsize = raw8;
uint32_t fontOff = static_cast<uint32_t>(fontId * static_cast<int>(kFontEntrySz));
if (raw8 & 0x40000000u)
{
pointsize = raw8 - 0x40000000u;
if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u))
*reinterpret_cast<uint32_t *>(p) = 1u;
}
else
{
if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u))
*reinterpret_cast<uint32_t *>(p) = 0u;
}
int tw = (width >= 0) ? (width >> 6) : ((width + 0x3f) >> 6);
int qwc = (fontDataSz >= 0) ? (fontDataSz >> 4) : ((fontDataSz + 0xf) >> 4);
uint32_t glyphSrc = fontDataAddr + static_cast<uint32_t>(fontDataSz) + 0x10u;
uint32_t glyphAlloc = runtime->guestMalloc(0x2010u, 0x40u);
if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff))
*reinterpret_cast<uint32_t *>(p) = glyphAlloc;
if (glyphAlloc != 0u)
{
uint8_t *dst = getMemPtr(rdram, glyphAlloc);
const uint8_t *src = getConstMemPtr(rdram, glyphSrc);
if (dst && src)
std::memcpy(dst, src, 0x2010u);
}
uint32_t isDoubleByte = 0;
if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff + 0x20u))
isDoubleByte = *reinterpret_cast<const uint32_t *>(p);
if (isDoubleByte == 0u)
{
uint32_t kernSrc = glyphSrc + 0x2010u;
uint32_t kernAlloc = runtime->guestMalloc(0xc400u, 0x40u);
if (glyphAlloc != 0u)
*reinterpret_cast<uint32_t *>(getMemPtr(rdram, glyphAlloc + 0x2000u)) = kernAlloc;
if (kernAlloc != 0u)
{
uint8_t *dst = getMemPtr(rdram, kernAlloc);
const uint8_t *src = getConstMemPtr(rdram, kernSrc);
if (dst && src)
std::memcpy(dst, src, 0xc400u);
}
}
auto writeFontField = [&](uint32_t off, uint32_t val)
{
if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + off))
*reinterpret_cast<uint32_t *>(p) = val;
};
writeFontField(0x18u, pointsize);
writeFontField(0x08u, static_cast<uint32_t>(tbp0));
writeFontField(0x0cu, static_cast<uint32_t>(tw));
int logW = 0;
for (int w = width; w != 1 && w != 0; w = static_cast<int>(static_cast<uint32_t>(w) >> 1))
logW++;
writeFontField(0x10u, static_cast<uint32_t>(logW));
int logH = 0;
for (int h = height; h != 1 && h != 0; h = static_cast<int>(static_cast<uint32_t>(h) >> 1))
logH++;
writeFontField(0x14u, static_cast<uint32_t>(logH));
writeFontField(0x04u, 0u);
writeFontField(0x1cu, getRegU32(ctx, 6));
if (qwc > 0)
{
const uint32_t imageBytes = static_cast<uint32_t>(qwc) * 16u;
const uint8_t psm = 20u;
const uint32_t headerQwc = 12u;
const uint32_t imageQwc = static_cast<uint32_t>(qwc);
const uint32_t totalQwc = headerQwc + imageQwc;
uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u);
if (pktAddr != 0u)
{
uint8_t *pkt = getMemPtr(rdram, pktAddr);
const uint8_t *imgSrc = getConstMemPtr(rdram, fontDataAddr + 0x10u);
if (pkt && imgSrc)
{
uint64_t *q = reinterpret_cast<uint64_t *>(pkt);
const uint32_t dbp = static_cast<uint32_t>(tbp0) & 0x3FFFu;
const uint32_t dbw = static_cast<uint32_t>(tw > 0 ? tw : 1) & 0x3Fu;
const uint32_t rrw = static_cast<uint32_t>(width > 0 ? width : 64);
const uint32_t rrh = static_cast<uint32_t>(height > 0 ? height : 1);
q[0] = makeGiftagAplusD(4u);
q[1] = 0xEULL;
q[2] = (static_cast<uint64_t>(psm) << 24) | (1ULL << 16) |
(static_cast<uint64_t>(dbp) << 32) | (static_cast<uint64_t>(dbw) << 48) |
(static_cast<uint64_t>(psm) << 56);
q[3] = 0x50ULL;
q[4] = 0ULL;
q[5] = 0x51ULL;
q[6] = (static_cast<uint64_t>(rrh) << 32) | static_cast<uint64_t>(rrw);
q[7] = 0x52ULL;
q[8] = 0ULL;
q[9] = 0x53ULL;
q[10] = (2ULL << 58) | (imageQwc & 0x7FFF) | (1ULL << 15);
q[11] = 0ULL;
std::memcpy(pkt + 12 * 8, imgSrc, imageBytes);
constexpr uint32_t GIF_CHANNEL = 0x1000A000;
constexpr uint32_t CHCR_STR_MODE0 = 0x101u;
runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr);
runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu);
runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0);
}
}
}
int retTbp = tbp0 + ((fontDataSz >= 0 ? fontDataSz : fontDataSz + 0x7f) >> 7);
setReturnS32(ctx, retTbp);
}
static constexpr uint32_t kFontBase = 0x176148u;
static constexpr uint32_t kFontEntrySz = 0x24u;
void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const float sclx = ctx->f[12];
const float scly = ctx->f[13];
const uint32_t bufAddr = getRegU32(ctx, 4);
const uint64_t paramX = GPR_U64(ctx, 5);
const int64_t paramY = GPR_S64(ctx, 6);
const int paramW = static_cast<int>(getRegU32(ctx, 7));
const int paramH = static_cast<int>(getRegU32(ctx, 8));
const uint32_t colour = getRegU32(ctx, 9);
const int alignCh = static_cast<int8_t>(getRegU32(ctx, 10) & 0xffu);
const int fontId = static_cast<int>(getRegU32(ctx, 11));
const uint32_t sp = getRegU32(ctx, 29);
const uint32_t strAddr = FAST_READ32(sp + 0x00u);
const uint32_t param14 = FAST_READ32(sp + 0x18u);
if (bufAddr == 0u)
{
setReturnS32(ctx, 0);
ctx->pc = getRegU32(ctx, 31);
return;
}
const uint32_t gp = getRegU32(ctx, 28);
const uint32_t fontModeAdj = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7c98)));
const uint32_t shiftAmt = fontModeAdj & 0x1fu;
const int scrHeight = static_cast<int>(FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b5c))));
const int scrWidth = static_cast<int>(FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b60))));
const uint32_t fontClut = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b64)));
const uint32_t fontOff = static_cast<uint32_t>(fontId * static_cast<int>(kFontEntrySz));
const int lineH = static_cast<int>(FAST_READ32(kFontBase + fontOff + 0x18u));
int iVar21 = 0;
int iStack_dc = 0;
uint32_t uStack_d8 = 0;
int iVar15 = 0;
int16_t sVar8;
{
int yStepRaw = static_cast<int>(static_cast<float>((lineH + 6) * 16) * scly);
sVar8 = static_cast<int16_t>((static_cast<int>(paramY) + 0x700) * 16) + static_cast<int16_t>(yStepRaw >> static_cast<int>(shiftAmt));
}
int16_t baseX = static_cast<int16_t>((static_cast<int>(paramX) + 0x6c0) * 16);
if (param14 != 0u)
{
int64_t clipY1 = static_cast<int64_t>(static_cast<int>(paramY) + paramH);
int64_t clipX1 = static_cast<int64_t>(static_cast<int>(paramX) + paramW);
if (clipY1 > scrHeight - 1)
clipY1 = static_cast<int64_t>(scrHeight - 1);
if (clipX1 > scrWidth - 1)
clipX1 = static_cast<int64_t>(scrWidth - 1);
int64_t clipY0 = 0;
if (paramY > 0)
clipY0 = paramY;
uint64_t clipX0 = 0;
if (static_cast<int64_t>(paramX) > 0)
clipX0 = paramX;
uint64_t scissor = clipX0 | (static_cast<uint64_t>(static_cast<uint32_t>(clipX1)) << 16) | (static_cast<uint64_t>(static_cast<uint32_t>(clipY0)) << 32) | (static_cast<uint64_t>(static_cast<uint32_t>(clipY1)) << 48);
FAST_WRITE64(bufAddr + 0x00, 0x1000000000000005ull);
FAST_WRITE64(bufAddr + 0x08, 0x0eull);
FAST_WRITE64(bufAddr + 0x10, scissor);
FAST_WRITE64(bufAddr + 0x18, 0x40ull);
FAST_WRITE64(bufAddr + 0x20, 0x20000ull);
FAST_WRITE64(bufAddr + 0x28, 0x47ull);
FAST_WRITE64(bufAddr + 0x30, 0x44ull);
FAST_WRITE64(bufAddr + 0x38, 0x42ull);
FAST_WRITE64(bufAddr + 0x40, 0x160ull);
FAST_WRITE64(bufAddr + 0x48, 0x14ull);
FAST_WRITE64(bufAddr + 0x50, 0x156ull);
FAST_WRITE64(bufAddr + 0x58, 0ull);
FAST_WRITE64(bufAddr + 0x60, 0x1000000000000001ull);
FAST_WRITE64(bufAddr + 0x68, 0x0eull);
uint64_t iVar5 = static_cast<uint64_t>(FAST_READ32(kFontBase + fontOff + 0x08u));
uint64_t iVar22 = static_cast<uint64_t>(FAST_READ32(kFontBase + fontOff + 0x0cu));
uint64_t iVar3 = static_cast<uint64_t>(FAST_READ32(kFontBase + fontOff + 0x10u));
uint64_t iVar4 = static_cast<uint64_t>(FAST_READ32(kFontBase + fontOff + 0x14u));
uint64_t tex0 = iVar5 | 0x2000000000000000ull | (iVar22 << 14) | 0x400000000ull | (iVar3 << 26) | 0x1400000ull | (iVar4 << 30) | (static_cast<uint64_t>(fontClut) << 37);
FAST_WRITE64(bufAddr + 0x70, tex0);
FAST_WRITE64(bufAddr + 0x78, 6ull);
FAST_WRITE64(bufAddr + 0x80, 0x1000000000000001ull);
FAST_WRITE64(bufAddr + 0x88, 0x0eull);
FAST_WRITE64(bufAddr + 0x90, static_cast<uint64_t>(colour));
FAST_WRITE64(bufAddr + 0x98, 1ull);
iVar21 = 10;
}
int iVar22_qw = iVar21 + 1;
uint32_t s2 = bufAddr + static_cast<uint32_t>(iVar22_qw * 16);
uint32_t uVar20 = 0;
size_t sLen = 0;
{
const char *hostStr = reinterpret_cast<const char *>(getConstMemPtr(rdram, strAddr));
if (hostStr)
sLen = ::strlen(hostStr);
}
while (uVar20 < sLen)
{
uint8_t bVar1 = FAST_READ8(strAddr + uVar20);
uint32_t uVar9 = static_cast<uint32_t>(bVar1);
int8_t chSigned = static_cast<int8_t>(bVar1);
if (uStack_d8 < 0x21u)
{
goto label_check_printable;
}
if (uVar9 > 0x20u)
{
uint32_t dat176168 = FAST_READ32(kFontBase + fontOff + 0x20u);
if (dat176168 == 0u)
{
uint32_t fontPtr0 = FAST_READ32(kFontBase + fontOff);
uint32_t tableAddr = FAST_READ32(fontPtr0 + 0x2000u);
int8_t kern = static_cast<int8_t>(FAST_READ8(tableAddr - 0x1c20u + uStack_d8 * 0xe0u + uVar9));
iVar15 += static_cast<int>(static_cast<float>(static_cast<int>(kern)) * sclx);
}
goto label_check_printable;
}
goto label_space;
label_check_printable:
if (uVar9 < 0x21u)
{
goto label_space;
}
{
int glyphIdx = static_cast<int>(chSigned);
uint32_t iVar19_off = static_cast<uint32_t>(glyphIdx * 0x20);
if (param14 != 0u)
{
uint32_t fontPtr = FAST_READ32(kFontBase + fontOff);
int16_t sVar7 = baseX + static_cast<int16_t>(iVar15);
iVar22_qw += 2;
iStack_dc += 1;
uint16_t wU0 = FAST_READ16(fontPtr + iVar19_off + 0);
uint16_t wV0 = FAST_READ16(fontPtr + iVar19_off + 2);
FAST_WRITE16(s2 + 0x00, wU0);
FAST_WRITE16(s2 + 0x02, wV0);
int16_t dx0 = static_cast<int16_t>(FAST_READ16(fontPtr + iVar19_off + 8));
int16_t dy0 = static_cast<int16_t>(FAST_READ16(fontPtr + iVar19_off + 10));
uint16_t wX0 = static_cast<uint16_t>(sVar7 + static_cast<int16_t>(static_cast<int>(static_cast<float>(static_cast<int>(dx0)) * sclx)));
int yVal0 = static_cast<int>(static_cast<float>(static_cast<int>(dy0)) * scly) >> static_cast<int>(shiftAmt);
uint16_t wY0 = static_cast<uint16_t>(sVar8 + static_cast<int16_t>(yVal0));
FAST_WRITE16(s2 + 0x08, wX0);
FAST_WRITE16(s2 + 0x0a, wY0);
FAST_WRITE32(s2 + 0x0c, 1u);
s2 += 0x10u;
uint16_t wU1 = FAST_READ16(fontPtr + iVar19_off + 4);
uint16_t wV1 = FAST_READ16(fontPtr + iVar19_off + 6);
FAST_WRITE16(s2 + 0x00, wU1);
FAST_WRITE16(s2 + 0x02, wV1);
int16_t dx1 = static_cast<int16_t>(FAST_READ16(fontPtr + iVar19_off + 12));
int16_t dy1 = static_cast<int16_t>(FAST_READ16(fontPtr + iVar19_off + 14));
uint16_t wX1 = static_cast<uint16_t>(sVar7 + static_cast<int16_t>(static_cast<int>(static_cast<float>(static_cast<int>(dx1)) * sclx)));
int yVal1 = static_cast<int>(static_cast<float>(static_cast<int>(dy1)) * scly) >> static_cast<int>(shiftAmt);
uint16_t wY1 = static_cast<uint16_t>(sVar8 + static_cast<int16_t>(yVal1));
FAST_WRITE16(s2 + 0x08, wX1);
FAST_WRITE16(s2 + 0x0a, wY1);
FAST_WRITE32(s2 + 0x0c, 1u);
s2 += 0x10u;
}
{
uint32_t fontPtr = FAST_READ32(kFontBase + fontOff);
uint32_t advOff = static_cast<uint32_t>((glyphIdx * 2 + 1) * 16 + 8);
int16_t advW = static_cast<int16_t>(FAST_READ16(fontPtr + advOff));
iVar15 += static_cast<int>(static_cast<float>(static_cast<int>(advW)) * sclx);
}
}
goto label_next;
label_space:
{
int spaceW = static_cast<int>(FAST_READ32(kFontBase + fontOff + 0x1cu));
iVar15 += static_cast<int>(static_cast<float>(spaceW) * sclx);
}
label_next:
uStack_d8 = uVar9;
uVar20++;
}
if (param14 != 0u)
{
if (alignCh != 'L')
{
if (alignCh == 'C' || alignCh == 'R')
{
int shift = paramW * 16 - iVar15;
if (alignCh == 'C')
shift >>= 1;
if (iStack_dc > 0)
{
uint32_t adj = bufAddr + static_cast<uint32_t>(iVar21 * 16) + 0x20u;
for (int k = 0; k < iStack_dc; k++)
{
int16_t oldX0 = static_cast<int16_t>(FAST_READ16(adj - 8u));
int16_t oldX1 = static_cast<int16_t>(FAST_READ16(adj + 8u));
FAST_WRITE16(adj - 8u, static_cast<uint16_t>(oldX0 + static_cast<int16_t>(shift)));
FAST_WRITE16(adj + 8u, static_cast<uint16_t>(oldX1 + static_cast<int16_t>(shift)));
adj += 0x20u;
}
}
}
else if (alignCh == 'J' && sLen > 1)
{
int iVar19_div = static_cast<int>(sLen) - 1;
if (iVar19_div == 0)
iVar19_div = 1;
int spacePer = (paramW * 16 - iVar15) / iVar19_div;
uint32_t adj = bufAddr + static_cast<uint32_t>(iVar21 * 16) + 0x20u;
int accum = 0;
for (uint32_t jj = 0; jj < sLen; jj++)
{
int8_t jch = static_cast<int8_t>(FAST_READ8(strAddr + jj));
if (jch > 0x20)
{
int16_t oldX0 = static_cast<int16_t>(FAST_READ16(adj - 8u));
int16_t oldX1 = static_cast<int16_t>(FAST_READ16(adj + 8u));
FAST_WRITE16(adj - 8u, static_cast<uint16_t>(oldX0 + static_cast<int16_t>(accum)));
FAST_WRITE16(adj + 8u, static_cast<uint16_t>(oldX1 + static_cast<int16_t>(accum)));
adj += 0x20u;
}
accum += spacePer;
}
}
}
if (param14 != 0u)
{
uint32_t tagAddr = bufAddr + static_cast<uint32_t>(iVar21 * 16);
FAST_WRITE64(tagAddr + 0x00, static_cast<uint64_t>(static_cast<uint32_t>(iStack_dc)) | 0x4400000000000000ull);
FAST_WRITE64(tagAddr + 0x08, 0x5353ull);
uint32_t endAddr = bufAddr + static_cast<uint32_t>(iVar22_qw * 16);
FAST_WRITE64(endAddr + 0x00, 0x1000000000008001ull);
FAST_WRITE64(endAddr + 0x08, 0x0eull);
int iVar19_end = iVar22_qw + 1;
uint32_t endAddr2 = bufAddr + static_cast<uint32_t>(iVar19_end * 16);
FAST_WRITE64(endAddr2 + 0x00, 0x01ff0000027f0000ull);
FAST_WRITE64(endAddr2 + 0x08, 0x40ull);
iVar22_qw += 2;
}
}
int ret = 0;
if (param14 != 0u)
ret = iVar22_qw;
setReturnS32(ctx, ret);
ctx->pc = getRegU32(ctx, 31);
}
void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t oldSp = getRegU32(ctx, 29);
const uint32_t frame = oldSp - 0x900u;
const uint32_t bufAddr = getRegU32(ctx, 4);
const uint32_t paramX = getRegU32(ctx, 5);
const uint32_t paramY = getRegU32(ctx, 6);
const uint32_t fmtAddr = getRegU32(ctx, 7);
const uint8_t *callerVa = getConstMemPtr(rdram, oldSp + 16u);
uint8_t *frameVa = getMemPtr(rdram, frame + 0x8f8u);
if (callerVa && frameVa)
std::memcpy(frameVa, callerVa, 64u);
SET_GPR_U32(ctx, 4, frame + 0x20u);
SET_GPR_U32(ctx, 5, fmtAddr);
SET_GPR_U32(ctx, 6, frame + 0x8f8u);
vsprintf(rdram, ctx, runtime);
const uint32_t gp = getRegU32(ctx, 28);
uint32_t defaultSclxBits = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b54)));
uint32_t defaultSclyBits = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b50)));
uint32_t defaultColour = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b4c)));
uint32_t defaultFontId = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b58)));
uint32_t scrWidth = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b60)));
uint32_t scrHeight = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b5c)));
std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float));
std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float));
FAST_WRITE32(frame + 0x00u, frame + 0x20u);
FAST_WRITE32(frame + 0x08u, frame + 0x820u);
FAST_WRITE32(frame + 0x10u, frame + 0x824u);
FAST_WRITE32(frame + 0x18u, 1u);
SET_GPR_U32(ctx, 29, frame);
SET_GPR_U32(ctx, 4, bufAddr);
SET_GPR_U32(ctx, 5, paramX);
SET_GPR_U32(ctx, 6, paramY);
SET_GPR_U32(ctx, 7, scrWidth);
SET_GPR_U32(ctx, 8, scrHeight);
SET_GPR_U32(ctx, 9, defaultColour);
SET_GPR_U32(ctx, 10, 0x4cu);
SET_GPR_U32(ctx, 11, defaultFontId);
sceeFontGenerateString(rdram, ctx, runtime);
SET_GPR_U32(ctx, 29, oldSp);
ctx->pc = getRegU32(ctx, 31);
}
void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t oldSp = getRegU32(ctx, 29);
const uint32_t frame = oldSp - 0x900u;
const uint32_t bufAddr = getRegU32(ctx, 4);
const uint32_t paramX = getRegU32(ctx, 5);
const uint32_t paramY = getRegU32(ctx, 6);
const uint32_t paramW = getRegU32(ctx, 7);
const uint32_t paramH = getRegU32(ctx, 8);
const uint32_t alignRaw = getRegU32(ctx, 9);
const uint32_t fmtAddr = getRegU32(ctx, 10);
const uint64_t param8 = GPR_U64(ctx, 11);
int8_t alignChar = static_cast<int8_t>(alignRaw & 0xffu);
FAST_WRITE64(frame + 0x8f8u, param8);
SET_GPR_U32(ctx, 4, frame + 0x20u);
SET_GPR_U32(ctx, 5, fmtAddr);
SET_GPR_U32(ctx, 6, frame + 0x8f8u);
vsprintf(rdram, ctx, runtime);
const uint32_t gp = getRegU32(ctx, 28);
uint32_t defaultSclxBits = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b54)));
uint32_t defaultSclyBits = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b50)));
uint32_t defaultColour = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b4c)));
uint32_t defaultFontId = FAST_READ32(gp + static_cast<uint32_t>(static_cast<int32_t>(-0x7b58)));
std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float));
std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float));
FAST_WRITE32(frame + 0x00u, frame + 0x20u);
FAST_WRITE32(frame + 0x08u, frame + 0x820u);
FAST_WRITE32(frame + 0x10u, frame + 0x824u);
FAST_WRITE32(frame + 0x18u, 1u);
SET_GPR_U32(ctx, 29, frame);
SET_GPR_U32(ctx, 4, bufAddr);
SET_GPR_U32(ctx, 5, paramX);
SET_GPR_U32(ctx, 6, paramY);
SET_GPR_U32(ctx, 7, paramW);
SET_GPR_U32(ctx, 8, paramH);
SET_GPR_U32(ctx, 9, defaultColour);
SET_GPR_U32(ctx, 10, static_cast<uint32_t>(static_cast<uint8_t>(alignChar)));
SET_GPR_U32(ctx, 11, defaultFontId);
sceeFontGenerateString(rdram, ctx, runtime);
SET_GPR_U32(ctx, 29, oldSp);
ctx->pc = getRegU32(ctx, 31);
}
void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static constexpr uint32_t kFontBase = 0x176148u;
static constexpr uint32_t kFontEntrySz = 0x24u;
const int fontId = static_cast<int>(getRegU32(ctx, 4));
const uint32_t fontOff = static_cast<uint32_t>(fontId * static_cast<int>(kFontEntrySz));
uint32_t glyphPtr = 0;
if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff))
glyphPtr = *reinterpret_cast<const uint32_t *>(p);
if (glyphPtr != 0u)
{
if (runtime)
{
uint32_t kernPtr = 0;
if (const uint8_t *kp = getConstMemPtr(rdram, glyphPtr + 0x2000u))
kernPtr = *reinterpret_cast<const uint32_t *>(kp);
if (kernPtr != 0u)
runtime->guestFree(kernPtr);
runtime->guestFree(glyphPtr);
}
if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff))
*reinterpret_cast<uint32_t *>(p) = 0u;
setReturnS32(ctx, 0);
}
else
{
setReturnS32(ctx, -1);
}
}
void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t gp = getRegU32(ctx, 28);
writeU32AtGp(rdram, gp, -0x7b4c, getRegU32(ctx, 4));
}
void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t gp = getRegU32(ctx, 28);
writeU32AtGp(rdram, gp, -0x7c98, getRegU32(ctx, 4));
setReturnS32(ctx, 0);
}
void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t gp = getRegU32(ctx, 28);
writeU32AtGp(rdram, gp, -0x7b58, getRegU32(ctx, 4));
}
void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t gp = getRegU32(ctx, 28);
uint32_t sclx_bits, scly_bits;
std::memcpy(&sclx_bits, &ctx->f[12], sizeof(float));
std::memcpy(&scly_bits, &ctx->f[13], sizeof(float));
writeU32AtGp(rdram, gp, -0x7b54, sclx_bits);
writeU32AtGp(rdram, gp, -0x7b50, scly_bits);
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void resetGsSyncVCallbackState();
void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick);
void sceGifPkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkAddGsData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkRef(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkRefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGifPkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkCall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkCloseDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkOpenDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVif1PkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -1,6 +1,5 @@
#ifndef PS2_CD_REMAP_IDX_TO_AFS
#define PS2_CD_REMAP_IDX_TO_AFS 1
#endif
#include <algorithm>
#include <cctype>
namespace
{
@@ -17,12 +16,10 @@ namespace
std::unordered_map<std::string, CdFileEntry> g_cdFilesByKey;
std::unordered_map<std::string, std::filesystem::path> g_cdLeafIndex;
std::unordered_map<std::string, std::filesystem::path> g_cdLoosePathIndex;
std::filesystem::path g_cdLeafIndexRoot;
bool g_cdLeafIndexBuilt = false;
uint32_t g_nextPseudoLbn = kCdPseudoLbnStart;
std::filesystem::path g_cdAutoImagePath;
std::filesystem::path g_cdAutoImageRoot;
bool g_cdAutoImageSearched = false;
std::filesystem::path g_cdImageSizePath;
uint64_t g_cdImageSizeBytes = 0;
bool g_cdImageSizeValid = false;
@@ -109,6 +106,63 @@ namespace
return path;
}
std::string normalizeCdLooseNumericKey(std::string value)
{
value = toLowerAscii(stripIsoVersionSuffix(normalizePathSeparators(std::move(value))));
std::string normalized;
normalized.reserve(value.size());
for (std::size_t i = 0; i < value.size();)
{
if (!std::isdigit(static_cast<unsigned char>(value[i])))
{
normalized.push_back(value[i]);
++i;
continue;
}
std::size_t end = i + 1;
while (end < value.size() && std::isdigit(static_cast<unsigned char>(value[end])))
{
++end;
}
std::size_t firstNonZero = i;
while (firstNonZero + 1 < end && value[firstNonZero] == '0')
{
++firstNonZero;
}
normalized.append(value, firstNonZero, end - firstNonZero);
i = end;
}
return normalized;
}
std::string cdLoosePathKeyFromRelative(const std::filesystem::path &relative)
{
const std::string normalized = normalizeCdPathNoPrefix(relative.generic_string());
if (normalized.empty())
{
throw std::runtime_error("cdLoosePathKeyFromRelative: normalized path is empty");
}
const std::filesystem::path relPath(normalized);
std::string parent = toLowerAscii(normalizePathSeparators(relPath.parent_path().generic_string()));
const std::string leaf = normalizeCdLooseNumericKey(relPath.filename().string());
if (parent.empty())
{
return leaf;
}
return parent + "/" + leaf;
}
std::string cdLoosePathKey(const std::string &ps2Path)
{
return cdLoosePathKeyFromRelative(std::filesystem::path(normalizeCdPathNoPrefix(ps2Path)));
}
std::filesystem::path getCdRootPath()
{
const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths();
@@ -126,150 +180,9 @@ namespace
return ec ? std::filesystem::path(".") : cwd.lexically_normal();
}
bool hasCdImageExtension(const std::filesystem::path &path)
{
const std::string ext = toLowerAscii(path.extension().string());
return ext == ".iso" || ext == ".bin" || ext == ".img" || ext == ".mdf" || ext == ".nrg";
}
bool trySelectBestDiscImageFromDirectory(const std::filesystem::path &dir,
std::filesystem::path &pathOut)
{
std::error_code ec;
if (!std::filesystem::exists(dir, ec) || ec || !std::filesystem::is_directory(dir, ec))
{
return false;
}
std::filesystem::path bestPath;
uint64_t bestSize = 0;
for (const auto &entry : std::filesystem::directory_iterator(
dir, std::filesystem::directory_options::skip_permission_denied, ec))
{
if (ec)
{
break;
}
if (!entry.is_regular_file())
{
continue;
}
if (!hasCdImageExtension(entry.path()))
{
continue;
}
std::error_code sizeEc;
const uint64_t size = static_cast<uint64_t>(entry.file_size(sizeEc));
if (sizeEc || size < (64ull * 1024ull * 1024ull))
{
continue;
}
if (size > bestSize)
{
bestSize = size;
bestPath = entry.path();
}
}
if (bestPath.empty())
{
return false;
}
pathOut = bestPath;
return true;
}
std::filesystem::path autoDetectCdImagePath()
{
const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths();
std::vector<std::filesystem::path> roots;
const std::filesystem::path cdRoot = getCdRootPath();
if (!cdRoot.empty())
{
roots.push_back(cdRoot);
std::filesystem::path parent = cdRoot;
for (int i = 0; i < 4; ++i)
{
parent = parent.parent_path();
if (parent.empty())
{
break;
}
roots.push_back(parent);
}
}
if (!paths.hostRoot.empty())
{
roots.push_back(paths.hostRoot);
}
if (!paths.elfDirectory.empty())
{
roots.push_back(paths.elfDirectory);
}
std::filesystem::path bestPath;
uint64_t bestSize = 0;
std::unordered_set<std::string> seenRoots;
for (const std::filesystem::path &root : roots)
{
if (root.empty())
{
continue;
}
const std::string key = toLowerAscii(root.lexically_normal().string());
if (!seenRoots.emplace(key).second)
{
continue;
}
std::filesystem::path candidate;
if (!trySelectBestDiscImageFromDirectory(root, candidate))
{
continue;
}
std::error_code sizeEc;
const uint64_t size = static_cast<uint64_t>(std::filesystem::file_size(candidate, sizeEc));
if (sizeEc || size <= bestSize)
{
continue;
}
bestSize = size;
bestPath = candidate;
}
if (!bestPath.empty())
{
std::cout << "[CD] Auto-detected disc image: " << bestPath.string() << std::endl;
}
return bestPath;
}
std::filesystem::path getCdImagePath()
{
const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths();
if (!paths.cdImage.empty())
{
return paths.cdImage;
}
const std::filesystem::path cdRoot = getCdRootPath();
if (!g_cdAutoImageSearched || g_cdAutoImageRoot != cdRoot)
{
g_cdAutoImageRoot = cdRoot;
g_cdAutoImagePath = autoDetectCdImagePath();
g_cdAutoImageSearched = true;
}
return g_cdAutoImagePath;
return PS2Runtime::getIoPaths().cdImage;
}
bool tryGetCdImageTotalSectors(uint64_t &totalSectorsOut)
@@ -375,6 +288,7 @@ namespace
}
g_cdLeafIndex.clear();
g_cdLoosePathIndex.clear();
g_cdLeafIndexRoot = root;
g_cdLeafIndexBuilt = true;
@@ -398,6 +312,17 @@ namespace
const std::string leaf = toLowerAscii(entry.path().filename().string());
g_cdLeafIndex.emplace(leaf, entry.path());
std::error_code relEc;
const std::filesystem::path relative = std::filesystem::relative(entry.path(), root, relEc);
if (!relEc)
{
const std::string looseKey = cdLoosePathKeyFromRelative(relative);
if (!looseKey.empty())
{
g_cdLoosePathIndex.emplace(looseKey, entry.path());
}
}
}
}
@@ -442,8 +367,18 @@ namespace
}
else
{
g_lastCdError = -1;
return false;
const std::string looseKey = cdLoosePathKey(ps2Path);
auto looseIt = g_cdLoosePathIndex.find(looseKey);
if (looseIt != g_cdLoosePathIndex.end())
{
path = looseIt->second;
ec.clear();
}
else
{
g_lastCdError = -1;
return false;
}
}
}
}
@@ -584,73 +519,6 @@ namespace
return true;
}
bool hostFileHasAfsMagic(const std::filesystem::path &path)
{
std::ifstream file(path, std::ios::binary);
if (!file.is_open())
{
return false;
}
char magic[4] = {};
file.read(magic, sizeof(magic));
if (file.gcount() < 3)
{
return false;
}
return magic[0] == 'A' && magic[1] == 'F' && magic[2] == 'S';
}
bool tryRemapGdInitSearchToAfs(const std::string &ps2Path,
uint32_t callerRa,
const CdFileEntry &foundEntry,
CdFileEntry &entryOut,
std::string &resolvedPathOut)
{
#if !PS2_CD_REMAP_IDX_TO_AFS
{
return false;
}
#endif
if (callerRa != 0x2d9444u)
{
return false;
}
std::filesystem::path relative(normalizeCdPathNoPrefix(ps2Path));
const std::string ext = toLowerAscii(relative.extension().string());
const std::string leaf = toLowerAscii(relative.filename().string());
if (ext == ".idx")
{
if (foundEntry.sizeBytes > (kCdSectorSize * 8u))
{
return false;
}
std::filesystem::path afsRelative = relative;
afsRelative.replace_extension(".AFS");
CdFileEntry afsEntry;
if (!registerCdFile(afsRelative.generic_string(), afsEntry))
{
return false;
}
if (!hostFileHasAfsMagic(afsEntry.hostPath))
{
return false;
}
entryOut = afsEntry;
resolvedPathOut = afsRelative.generic_string();
return true;
}
return false;
}
uint8_t toBcd(uint32_t value)
{
const uint32_t clamped = value % 100;
@@ -925,13 +793,13 @@ namespace
private:
uint32_t readWordAtSlot(uint32_t slotIndex) const
{
if (slotIndex < 4u)
if (slotIndex < 8u)
{
// slot0..slot3 -> a0..a3 (r4..r7)
// EE calls use eight integer argument registers (a0-a3, t0-t3 / r4-r11).
return getRegU32(m_ctx, 4 + static_cast<int>(slotIndex));
}
const uint32_t stackIndex = slotIndex - 4u;
const uint32_t stackIndex = slotIndex - 8u;
const uint32_t stackAddr = m_stackBase + stackIndex * 4u;
uint32_t value = 0;
(void)tryReadWordFromGuest(m_rdram, m_runtime, stackAddr, value);
@@ -1006,15 +874,20 @@ namespace
int parsedWidth = -1;
int parsedPrecision = -1;
bool widthSpecified = false;
bool precisionSpecified = false;
std::string parsedFlags;
while (*p && std::strchr("-+ #0", *p))
{
parsedFlags.push_back(*p);
++p;
}
if (*p == '*')
{
parsedWidth = static_cast<int32_t>(nextU32());
widthSpecified = true;
++p;
}
else
@@ -1022,6 +895,7 @@ namespace
if (*p && std::isdigit(static_cast<unsigned char>(*p)))
{
parsedWidth = 0;
widthSpecified = true;
}
while (*p && std::isdigit(static_cast<unsigned char>(*p)))
{
@@ -1033,6 +907,7 @@ namespace
if (*p == '.')
{
++p;
precisionSpecified = true;
if (*p == '*')
{
parsedPrecision = static_cast<int32_t>(nextU32());
@@ -1052,7 +927,6 @@ namespace
{
parsedPrecision = -1;
}
(void)parsedWidth;
enum class LengthMod
{
@@ -1121,6 +995,51 @@ namespace
break;
}
auto buildHostSpec = [&](char spec, const char *lengthOverride = nullptr) -> std::string
{
std::string specText;
specText.reserve(32);
specText.push_back('%');
if (widthSpecified && parsedWidth < 0 &&
parsedFlags.find('-') == std::string::npos)
{
specText.push_back('-');
}
specText.append(parsedFlags);
if (widthSpecified)
{
const int hostWidth = (parsedWidth < 0) ? -parsedWidth : parsedWidth;
specText.append(std::to_string(hostWidth));
}
if (precisionSpecified)
{
specText.push_back('.');
specText.append(std::to_string(std::max(parsedPrecision, 0)));
}
if (lengthOverride != nullptr)
{
specText.append(lengthOverride);
}
specText.push_back(spec);
return specText;
};
auto appendFormatted = [&](const std::string &specText, auto value) -> bool
{
const int needed = std::snprintf(nullptr, 0, specText.c_str(), value);
if (needed < 0)
{
return false;
}
std::string chunk(static_cast<size_t>(needed), '\0');
std::snprintf(chunk.data(), chunk.size() + 1u, specText.c_str(), value);
out.append(chunk);
return true;
};
const bool use64Integer = (length == LengthMod::LL || length == LengthMod::J);
auto readUnsignedInteger = [&]() -> uint64_t
{
@@ -1141,59 +1060,72 @@ namespace
case 's':
{
const uint32_t strAddr = nextU32();
if (strAddr == 0)
const char *text = "(null)";
std::string ownedText;
if (strAddr != 0u)
{
out.append("(null)");
ownedText = readString(strAddr);
text = ownedText.c_str();
}
else
if (!appendFormatted(buildHostSpec(spec), text))
{
std::string str = readString(strAddr);
if (parsedPrecision >= 0 &&
str.size() > static_cast<size_t>(parsedPrecision))
{
str.resize(static_cast<size_t>(parsedPrecision));
}
out.append(str);
out.append(text);
}
break;
}
case 'c':
{
const char ch = static_cast<char>(nextU32() & 0xFF);
out.push_back(ch);
const int ch = static_cast<int>(nextU32() & 0xFFu);
if (!appendFormatted(buildHostSpec(spec), ch))
{
out.push_back(static_cast<char>(ch));
}
break;
}
case 'd':
case 'i':
out.append(std::to_string(readSignedInteger()));
break;
case 'u':
out.append(std::to_string(readUnsignedInteger()));
break;
case 'x':
case 'X':
{
std::ostringstream ss;
if (spec == 'X')
const long long value = static_cast<long long>(readSignedInteger());
if (!appendFormatted(buildHostSpec(spec, "ll"), value))
{
ss.setf(std::ios::uppercase);
out.append(std::to_string(value));
}
ss << std::hex << readUnsignedInteger();
out.append(ss.str());
break;
}
case 'u':
case 'x':
case 'X':
case 'o':
{
std::ostringstream ss;
ss << std::oct << readUnsignedInteger();
out.append(ss.str());
const unsigned long long value = static_cast<unsigned long long>(readUnsignedInteger());
if (!appendFormatted(buildHostSpec(spec, "ll"), value))
{
std::ostringstream ss;
if (spec == 'o')
{
ss << std::oct << value;
}
else
{
if (spec == 'X')
{
ss.setf(std::ios::uppercase);
}
ss << std::hex << value;
}
out.append(ss.str());
}
break;
}
case 'p':
{
std::ostringstream ss;
ss << "0x" << std::hex << nextU32();
out.append(ss.str());
const uint32_t ptrValue = nextU32();
if (!appendFormatted(buildHostSpec(spec), reinterpret_cast<void *>(static_cast<uintptr_t>(ptrValue))))
{
std::ostringstream ss;
ss << "0x" << std::hex << ptrValue;
out.append(ss.str());
}
break;
}
case 'f':
@@ -1208,9 +1140,17 @@ namespace
const uint64_t bits = nextU64();
double value = 0.0;
std::memcpy(&value, &bits, sizeof(value));
char numBuf[128];
std::snprintf(numBuf, sizeof(numBuf), "%g", value);
out.append(numBuf);
if (length == LengthMod::BigL)
{
if (!appendFormatted(buildHostSpec(spec, "L"), static_cast<long double>(value)))
{
out.append(std::to_string(value));
}
}
else if (!appendFormatted(buildHostSpec(spec), value))
{
out.append(std::to_string(value));
}
break;
}
case 'n':
@@ -1412,16 +1352,38 @@ namespace
mem.writeIORegister(channelBase + 0x10u, madr);
mem.writeIORegister(channelBase + 0x30u, tadr);
mem.writeIORegister(channelBase + 0x00u, chcr);
mem.processPendingTransfers();
std::lock_guard<std::mutex> lock(g_dmaStubMutex);
g_dmaPendingPolls[channelBase] = 1;
if (g_dmaStubLogCount < kMaxDmaStubLogs)
{
std::cout << "[sceDmaSend] ch=0x" << std::hex << channelBase
RUNTIME_LOG("[sceDmaSend] ch=0x" << std::hex << channelBase
<< " madr=0x" << madr
<< " qwc=0x" << qwc
<< " tadr=0x" << tadr
<< " chcr=0x" << chcr << std::dec << std::endl;
<< " chcr=0x" << chcr << std::dec << std::endl);
if (!preferNormalCount && (channelBase == 0x10009000u || channelBase == 0x1000A000u))
{
if (const uint8_t *tagPtr = getConstMemPtr(rdram, tadr))
{
uint64_t tagLo = 0u;
std::memcpy(&tagLo, tagPtr, sizeof(tagLo));
uint32_t w2 = 0u;
uint32_t w3 = 0u;
std::memcpy(&w2, tagPtr + 8u, sizeof(w2));
std::memcpy(&w3, tagPtr + 12u, sizeof(w3));
RUNTIME_LOG("[sceDmaSend:head] ch=0x" << std::hex << channelBase
<< " tagQwc=0x" << static_cast<uint32_t>(tagLo & 0xFFFFu)
<< " id=0x" << static_cast<uint32_t>((tagLo >> 28u) & 0x7u)
<< " irq=0x" << static_cast<uint32_t>((tagLo >> 31u) & 0x1u)
<< " addr=0x" << static_cast<uint32_t>((tagLo >> 32u) & 0x7FFFFFFFu)
<< " w2=0x" << w2
<< " w3=0x" << w3
<< std::dec << std::endl);
}
}
++g_dmaStubLogCount;
}
@@ -1492,6 +1454,76 @@ namespace
uint64_t bgcolor;
};
struct GsGiftagMem
{
uint64_t lo;
uint64_t hi;
};
struct GsRegPairMem
{
uint64_t value;
uint64_t reg;
};
struct GsDrawEnv1Mem
{
GsRegPairMem frame1;
GsRegPairMem zbuf1;
GsRegPairMem xyoffset1;
GsRegPairMem scissor1;
GsRegPairMem prmodecont;
GsRegPairMem colclamp;
GsRegPairMem dthe;
GsRegPairMem test1;
};
struct GsDrawEnv2Mem
{
GsRegPairMem frame2;
GsRegPairMem zbuf2;
GsRegPairMem xyoffset2;
GsRegPairMem scissor2;
GsRegPairMem prmodecont;
GsRegPairMem colclamp;
GsRegPairMem dthe;
GsRegPairMem test2;
};
struct GsClearMem
{
GsRegPairMem testa;
GsRegPairMem prim;
GsRegPairMem rgbaq;
GsRegPairMem xyz2a;
GsRegPairMem xyz2b;
GsRegPairMem testb;
};
struct GsDBuffDcMem
{
GsDispEnvMem disp[2];
GsGiftagMem giftag0;
GsDrawEnv1Mem draw01;
GsDrawEnv2Mem draw02;
GsClearMem clear0;
GsGiftagMem giftag1;
GsDrawEnv1Mem draw11;
GsDrawEnv2Mem draw12;
GsClearMem clear1;
};
struct GsDBuffMem
{
GsDispEnvMem disp[2];
GsGiftagMem giftag0;
GsDrawEnv1Mem draw0;
GsClearMem clear0;
GsGiftagMem giftag1;
GsDrawEnv1Mem draw1;
GsClearMem clear1;
};
struct GsImageMem
{
uint16_t x;
@@ -1503,33 +1535,14 @@ namespace
uint8_t psm;
};
#pragma pack(push, 1)
struct GsDrawEnvMem
{
uint16_t offset_x;
uint16_t offset_y;
uint16_t clip_x;
uint16_t clip_y;
uint16_t clip_w;
uint16_t clip_h;
uint16_t vram_addr;
uint8_t fbw;
uint8_t psm;
uint16_t vram_x;
uint16_t vram_y;
uint32_t draw_mask;
uint8_t auto_clear;
uint8_t pad[3];
uint8_t bg_r;
uint8_t bg_g;
uint8_t bg_b;
uint8_t bg_a;
float bg_q;
};
#pragma pack(pop)
static_assert(sizeof(GsImageMem) == 12, "GsImageMem size mismatch");
static_assert(sizeof(GsDrawEnvMem) == 36, "GsDrawEnvMem size mismatch");
static_assert(sizeof(GsDispEnvMem) == 40, "GsDispEnvMem size mismatch");
static_assert(sizeof(GsGiftagMem) == 16, "GsGiftagMem size mismatch");
static_assert(sizeof(GsRegPairMem) == 16, "GsRegPairMem size mismatch");
static_assert(sizeof(GsDrawEnv1Mem) == 128, "GsDrawEnv1Mem size mismatch");
static_assert(sizeof(GsDrawEnv2Mem) == 128, "GsDrawEnv2Mem size mismatch");
static_assert(sizeof(GsClearMem) == 96, "GsClearMem size mismatch");
static_assert(sizeof(GsDBuffDcMem) == 0x330, "GsDBuffDcMem size mismatch");
constexpr uint32_t kGsParamScratchOffset = 0x100;
GsGParam g_gparam{1, 2, 1, 3}; // Default: interlaced NTSC, frame mode.
@@ -1564,6 +1577,53 @@ namespace
(static_cast<uint64_t>(dh & 0x07FF) << 44);
}
static uint64_t makeFrame(uint32_t fbp, uint32_t fbw, uint32_t psm, uint32_t fbmsk)
{
return (static_cast<uint64_t>(fbp & 0x1FFu) << 0) |
(static_cast<uint64_t>(fbw & 0x3Fu) << 16) |
(static_cast<uint64_t>(psm & 0x3Fu) << 24) |
(static_cast<uint64_t>(fbmsk) << 32);
}
static uint64_t makeZbuf(uint32_t zbp, uint32_t psm, bool zmsk)
{
return (static_cast<uint64_t>(zbp & 0x1FFu) << 0) |
(static_cast<uint64_t>(psm & 0xFu) << 24) |
(static_cast<uint64_t>(zmsk ? 1u : 0u) << 32);
}
static uint64_t makeXYOffset(int32_t width, int32_t height)
{
const int32_t offX = 0x800 - (width >> 1);
const int32_t offY = 0x800 - (height >> 1);
return (static_cast<uint64_t>(static_cast<uint32_t>(offY) & 0xFFFFu) << 36) |
(static_cast<uint64_t>(static_cast<uint32_t>(offX) & 0xFFFFu) << 4);
}
static uint64_t makeScissor(int32_t width, int32_t height)
{
return (static_cast<uint64_t>(0u) << 0) |
(static_cast<uint64_t>(static_cast<uint32_t>(width - 1) & 0x7FFu) << 16) |
(static_cast<uint64_t>(0u) << 32) |
(static_cast<uint64_t>(static_cast<uint32_t>(height - 1) & 0x7FFu) << 48);
}
static uint64_t makeTest(uint32_t ztest)
{
if ((ztest & 0x3u) == 0u)
{
return 0x30000ULL;
}
return (static_cast<uint64_t>(ztest & 0x3u) << 17) | 0x10000ULL;
}
static uint64_t makeGiftagAplusD(uint32_t nloop)
{
return (static_cast<uint64_t>(nloop & 0x7FFFu) << 0) |
(static_cast<uint64_t>(1u) << 15) |
(static_cast<uint64_t>(1u) << 60);
}
static uint32_t readStackU32(uint8_t *rdram, R5900Context *ctx, uint32_t offset)
{
uint32_t sp = getRegU32(ctx, 29);
@@ -1712,6 +1772,119 @@ namespace
return true;
}
static bool readGsDBuffDc(uint8_t *rdram, uint32_t addr, GsDBuffDcMem &out)
{
const uint8_t *ptr = getConstMemPtr(rdram, addr);
if (!ptr)
return false;
std::memcpy(&out, ptr, sizeof(out));
return true;
}
static bool readGsDBuff(uint8_t* rdram, uint32_t addr, GsDBuffMem& out)
{
const uint8_t* ptr = getConstMemPtr(rdram, addr);
if (!ptr)
return false;
std::memcpy(&out, ptr, sizeof(out));
return true;
}
static bool writeGsDBuffDc(uint8_t *rdram, uint32_t addr, const GsDBuffDcMem &db)
{
uint8_t *ptr = getMemPtr(rdram, addr);
if (!ptr)
return false;
std::memcpy(ptr, &db, sizeof(db));
return true;
}
static bool writeGsDBuff(uint8_t* rdram, uint32_t addr, const GsDBuffMem& db)
{
uint8_t* ptr = getMemPtr(rdram, addr);
if (!ptr)
return false;
std::memcpy(ptr, &db, sizeof(db));
return true;
}
static bool readGsRegPairs(uint8_t *rdram, uint32_t addr, GsRegPairMem *pairs, size_t pairCount)
{
if (!pairs || pairCount == 0u)
return false;
const uint8_t *ptr = getConstMemPtr(rdram, addr);
if (!ptr)
return false;
std::memcpy(pairs, ptr, pairCount * sizeof(GsRegPairMem));
return true;
}
static void applyGsDispEnv(PS2Runtime *runtime, const GsDispEnvMem &env)
{
if (!runtime || !runtime->syncCoreSubsystems())
return;
auto &regs = runtime->memory().gs();
regs.pmode = env.pmode;
regs.smode2 = env.smode2;
regs.dispfb1 = env.dispfb;
regs.display1 = env.display;
regs.dispfb2 = env.dispfb;
regs.display2 = env.display;
regs.bgcolor = env.bgcolor;
}
static void applyGsRegPairs(PS2Runtime *runtime, const GsRegPairMem *pairs, size_t pairCount)
{
if (!runtime || !pairs || !runtime->syncCoreSubsystems())
return;
for (size_t i = 0; i < pairCount; ++i)
{
runtime->gs().writeRegister(static_cast<uint8_t>(pairs[i].reg & 0xFFu), pairs[i].value);
}
}
static void seedGsDrawEnv1(GsDrawEnv1Mem &env,
int32_t width,
int32_t height,
uint32_t fbp,
uint32_t fbw,
uint32_t psm,
uint32_t zbp,
uint32_t zpsm,
uint32_t ztest,
bool dthe)
{
env.frame1 = {makeFrame(fbp, fbw, psm, 0u), GS_REG_FRAME_1};
env.zbuf1 = {makeZbuf(zbp, zpsm, (ztest & 0x3u) == 0u), GS_REG_ZBUF_1};
env.xyoffset1 = {makeXYOffset(width, height), GS_REG_XYOFFSET_1};
env.scissor1 = {makeScissor(width, height), GS_REG_SCISSOR_1};
env.prmodecont = {1u, GS_REG_PRMODECONT};
env.colclamp = {1u, GS_REG_COLCLAMP};
env.dthe = {dthe ? 1u : 0u, GS_REG_DTHE};
env.test1 = {makeTest(ztest), GS_REG_TEST_1};
}
static void seedGsDrawEnv2(GsDrawEnv2Mem &env,
int32_t width,
int32_t height,
uint32_t fbp,
uint32_t fbw,
uint32_t psm,
uint32_t zbp,
uint32_t zpsm,
uint32_t ztest,
bool dthe)
{
env.frame2 = {makeFrame(fbp, fbw, psm, 0u), GS_REG_FRAME_2};
env.zbuf2 = {makeZbuf(zbp, zpsm, (ztest & 0x3u) == 0u), GS_REG_ZBUF_2};
env.xyoffset2 = {makeXYOffset(width, height), GS_REG_XYOFFSET_2};
env.scissor2 = {makeScissor(width, height), GS_REG_SCISSOR_2};
env.prmodecont = {1u, GS_REG_PRMODECONT};
env.colclamp = {1u, GS_REG_COLCLAMP};
env.dthe = {dthe ? 1u : 0u, GS_REG_DTHE};
env.test2 = {makeTest(ztest), GS_REG_TEST_2};
}
static uint32_t writeGsGParamToScratch(PS2Runtime *runtime)
{
if (!runtime)
+94
View File
@@ -0,0 +1,94 @@
#include "Common.h"
#include "IPU.h"
namespace ps2_stubs
{
void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static constexpr uint32_t REG_IPU_CTRL = 0x10002010u;
static constexpr uint32_t REG_IPU_CMD = 0x10002000u;
static constexpr uint32_t REG_IPU_IN_FIFO = 0x10007010u;
static constexpr uint32_t IQVAL_BASE = 0x1721e0u;
static constexpr uint32_t VQVAL_BASE = 0x172230u;
static constexpr uint32_t SETD4_CHCR_ENTRY = 0x126428u;
if (!runtime)
return;
if (!runtime->memory().getRDRAM())
{
if (!runtime->memory().initialize())
{
setReturnS32(ctx, -1);
return;
}
}
if (!runtime->syncCoreSubsystems())
{
setReturnS32(ctx, -1);
return;
}
PS2Memory &mem = runtime->memory();
if (runtime->hasFunction(SETD4_CHCR_ENTRY))
{
auto setD4 = runtime->lookupFunction(SETD4_CHCR_ENTRY);
ctx->r[4] = _mm_set_epi64x(0, 1);
{
PS2Runtime::GuestExecutionScope guestExecution(runtime);
setD4(rdram, ctx, runtime);
}
}
mem.write32(REG_IPU_CTRL, 0x40000000u);
mem.write32(REG_IPU_CMD, 0u);
__m128i v;
v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x00u);
mem.write128(REG_IPU_IN_FIFO, v);
v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x10u);
mem.write128(REG_IPU_IN_FIFO, v);
v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x20u);
mem.write128(REG_IPU_IN_FIFO, v);
v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x30u);
mem.write128(REG_IPU_IN_FIFO, v);
v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x40u);
mem.write128(REG_IPU_IN_FIFO, v);
mem.write128(REG_IPU_IN_FIFO, v);
mem.write128(REG_IPU_IN_FIFO, v);
mem.write128(REG_IPU_IN_FIFO, v);
mem.write32(REG_IPU_CMD, 0x50000000u);
mem.write32(REG_IPU_CMD, 0x58000000u);
v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x00u);
mem.write128(REG_IPU_IN_FIFO, v);
v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x10u);
mem.write128(REG_IPU_IN_FIFO, v);
mem.write32(REG_IPU_CMD, 0x60000000u);
mem.write32(REG_IPU_CMD, 0x90000000u);
mem.write32(REG_IPU_CTRL, 0x40000000u);
mem.write32(REG_IPU_CMD, 0u);
setReturnS32(ctx, 0);
}
void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
File diff suppressed because it is too large Load Diff
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strncpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strlen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strncmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strcat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strncat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strrchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strstr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void snprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void puts(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fclose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fwrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ftell(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fflush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sqrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void __kernel_sinf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void cos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void __kernel_cosf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void __ieee754_rem_pio2f(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void tan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void atan2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void pow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void exp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void log(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void log10(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ceil(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void floor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+421
View File
@@ -0,0 +1,421 @@
#include "Common.h"
#include "MPEG.h"
namespace ps2_stubs
{
namespace
{
struct MpegRegisteredCallback
{
uint32_t type = 0u;
uint32_t func = 0u;
uint32_t data = 0u;
uint32_t handle = 0u;
};
struct MpegPlaybackState
{
uint32_t picturesServed = 0u;
};
struct MpegStubState
{
bool initialized = false;
uint32_t nextCallbackHandle = 1u;
std::unordered_map<uint32_t, std::vector<MpegRegisteredCallback>> callbacksByMpeg;
std::unordered_map<uint32_t, MpegPlaybackState> playbackByMpeg;
PS2MpegCompatLayout compat;
};
std::mutex g_mpeg_stub_mutex;
MpegStubState g_mpeg_stub_state;
constexpr uint32_t kStubMovieWidth = 320u;
constexpr uint32_t kStubMovieHeight = 240u;
uint32_t mpegCompatSyntheticFrames(const PS2MpegCompatLayout &layout)
{
return layout.syntheticFramesBeforeEnd != 0u ? layout.syntheticFramesBeforeEnd : 1u;
}
MpegPlaybackState &getPlaybackState(uint32_t mpegAddr)
{
return g_mpeg_stub_state.playbackByMpeg[mpegAddr];
}
void resetMpegStubStateUnlocked()
{
const PS2MpegCompatLayout compat = g_mpeg_stub_state.compat;
g_mpeg_stub_state.initialized = false;
g_mpeg_stub_state.nextCallbackHandle = 1u;
g_mpeg_stub_state.callbacksByMpeg.clear();
g_mpeg_stub_state.playbackByMpeg.clear();
g_mpeg_stub_state.compat = compat;
}
}
void setMpegCompatLayout(const PS2MpegCompatLayout &layout)
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.compat = layout;
}
void clearMpegCompatLayout()
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.compat = {};
}
void resetMpegStubState()
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
resetMpegStubStateUnlocked();
}
void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegFlush", rdram, ctx, runtime);
}
void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime);
}
void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t mpegAddr = getRegU32(ctx, 4);
const uint32_t callbackType = getRegU32(ctx, 5);
const uint32_t callbackFunc = getRegU32(ctx, 6);
const uint32_t callbackData = getRegU32(ctx, 7);
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.initialized = true;
(void)getPlaybackState(mpegAddr);
const uint32_t handle = g_mpeg_stub_state.nextCallbackHandle++;
g_mpeg_stub_state.callbacksByMpeg[mpegAddr].push_back(
MpegRegisteredCallback{callbackType, callbackFunc, callbackData, handle});
setReturnU32(ctx, handle);
}
void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnU32(ctx, 0u);
}
void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)ctx;
(void)runtime;
static const uint32_t kRefGlobalAddrs[] = {
0x171800u, 0x17180Cu, 0x171818u, 0x171804u, 0x171810u, 0x17181Cu};
for (uint32_t addr : kRefGlobalAddrs)
{
uint8_t *p = getMemPtr(rdram, addr);
if (!p)
continue;
uint32_t ptr = *reinterpret_cast<uint32_t *>(p);
if (ptr != 0u)
{
uint8_t *q = getMemPtr(rdram, ptr + 0x28u);
if (q)
*reinterpret_cast<uint32_t *>(q) = 0u;
}
}
setReturnU32(ctx, 1u);
}
static void mpegGuestWrite32(uint8_t *rdram, uint32_t addr, uint32_t value)
{
if (uint8_t *p = getMemPtr(rdram, addr))
*reinterpret_cast<uint32_t *>(p) = value;
}
static void mpegGuestWrite64(uint8_t *rdram, uint32_t addr, uint64_t value)
{
if (uint8_t *p = getMemPtr(rdram, addr))
{
*reinterpret_cast<uint32_t *>(p) = static_cast<uint32_t>(value);
*reinterpret_cast<uint32_t *>(p + 4) = static_cast<uint32_t>(value >> 32);
}
}
void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t param_1 = getRegU32(ctx, 4); // a0
const uint32_t param_2 = getRegU32(ctx, 5); // a1
const uint32_t param_3 = getRegU32(ctx, 6); // a2
const uint32_t uVar3 = (param_2 + 3u) & 0xFFFFFFFCu;
const int32_t iVar2_signed = static_cast<int32_t>(param_3) - static_cast<int32_t>(uVar3 - param_2);
if (iVar2_signed <= 0x117)
{
setReturnU32(ctx, 0u);
return;
}
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
getPlaybackState(param_1) = {};
}
const uint32_t puVar4 = uVar3 + 0x108u;
const uint32_t innerSize = static_cast<uint32_t>(iVar2_signed) - 0x118u;
mpegGuestWrite32(rdram, param_1 + 0x40, uVar3);
const uint32_t a1_init = uVar3 + 0x118u;
mpegGuestWrite32(rdram, puVar4 + 0x0, a1_init);
mpegGuestWrite32(rdram, puVar4 + 0x4, innerSize);
mpegGuestWrite32(rdram, puVar4 + 0x8, a1_init);
mpegGuestWrite32(rdram, puVar4 + 0xC, a1_init);
const uint32_t allocResult = runtime ? runtime->guestMalloc(0x600, 8u) : (uVar3 + 0x200u);
mpegGuestWrite32(rdram, uVar3 + 0x44, allocResult);
// param_1[0..2] = 0; param_1[4..0xe] = 0xffffffff/0 as per decompilation
mpegGuestWrite32(rdram, param_1 + 0x00, 0);
mpegGuestWrite32(rdram, param_1 + 0x04, 0);
mpegGuestWrite32(rdram, param_1 + 0x08, 0);
mpegGuestWrite64(rdram, param_1 + 0x10, 0xFFFFFFFFFFFFFFFFULL);
mpegGuestWrite64(rdram, param_1 + 0x18, 0xFFFFFFFFFFFFFFFFULL);
mpegGuestWrite64(rdram, param_1 + 0x20, 0);
mpegGuestWrite64(rdram, param_1 + 0x28, 0xFFFFFFFFFFFFFFFFULL);
mpegGuestWrite64(rdram, param_1 + 0x30, 0xFFFFFFFFFFFFFFFFULL);
mpegGuestWrite64(rdram, param_1 + 0x38, 0);
static const unsigned s_zeroOffsets[] = {
0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0, 0xE4, 0xE8, 0xF8,
0x0C, 0x14, 0x2C, 0x34, 0x3C,
0x48, 0xFC, 0x100, 0x104, 0x70, 0x90, 0xAC};
for (unsigned off : s_zeroOffsets)
mpegGuestWrite32(rdram, uVar3 + off, 0u);
mpegGuestWrite64(rdram, uVar3 + 0x78, 0);
mpegGuestWrite64(rdram, uVar3 + 0x88, 0);
mpegGuestWrite64(rdram, uVar3 + 0xF0, 0xFFFFFFFFFFFFFFFFULL);
mpegGuestWrite32(rdram, uVar3 + 0x1C, 0x1209F8u);
mpegGuestWrite32(rdram, uVar3 + 0x24, 0x120A08u);
mpegGuestWrite32(rdram, uVar3 + 0xB0, 1u);
mpegGuestWrite32(rdram, uVar3 + 0x9C, 0xFFFFFFFFu);
mpegGuestWrite32(rdram, uVar3 + 0x80, 0xFFFFFFFFu);
mpegGuestWrite32(rdram, uVar3 + 0x94, 0xFFFFFFFFu);
mpegGuestWrite32(rdram, uVar3 + 0x98, 0xFFFFFFFFu);
mpegGuestWrite32(rdram, 0x1717BCu, param_1);
static const uint32_t s_refValues[] = {
0x171A50u, 0x171C58u, 0x171CC0u, 0x171D28u, 0x171D90u,
0x171AB8u, 0x171B20u, 0x171B88u, 0x171BF0u};
for (unsigned i = 0; i < 9u; ++i)
mpegGuestWrite32(rdram, 0x171800u + i * 4u, s_refValues[i]);
uint32_t setDynamicRet = a1_init;
if (uint8_t *p = getMemPtr(rdram, puVar4 + 8))
setDynamicRet = *reinterpret_cast<uint32_t *>(p);
mpegGuestWrite32(rdram, puVar4 + 12, setDynamicRet);
setReturnU32(ctx, setDynamicRet);
}
void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t mpegAddr = getRegU32(ctx, 4);
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.callbacksByMpeg.erase(mpegAddr);
g_mpeg_stub_state.playbackByMpeg.erase(mpegAddr);
setReturnU32(ctx, 0u);
}
void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime);
}
void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t availableBytes = getRegU32(ctx, 6);
setReturnS32(ctx, static_cast<int32_t>(availableBytes));
}
void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime);
}
void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime);
}
void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime);
}
void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime);
}
void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime);
}
void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t mpegAddr = getRegU32(ctx, 4);
uint32_t picturesServed = 0u;
PS2MpegCompatLayout compat{};
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
MpegPlaybackState &playback = getPlaybackState(mpegAddr);
mpegGuestWrite32(rdram, mpegAddr + 0x00u, kStubMovieWidth);
mpegGuestWrite32(rdram, mpegAddr + 0x04u, kStubMovieHeight);
mpegGuestWrite32(rdram, mpegAddr + 0x08u, playback.picturesServed);
picturesServed = playback.picturesServed;
compat = g_mpeg_stub_state.compat;
playback.picturesServed += 1u;
}
if (uint8_t *base = getMemPtr(rdram, mpegAddr))
{
const uint32_t iVar1 = *reinterpret_cast<uint32_t *>(base + 0x40);
if (uint8_t *inner = getMemPtr(rdram, iVar1))
{
*reinterpret_cast<uint32_t *>(inner + 0xb0) = 1;
*reinterpret_cast<uint32_t *>(inner + 0xd8) = (getRegU32(ctx, 5) & 0x0FFFFFFFu) | 0x20000000u;
*reinterpret_cast<uint32_t *>(inner + 0xe4) = getRegU32(ctx, 6);
*reinterpret_cast<uint32_t *>(inner + 0xdc) = 0;
*reinterpret_cast<uint32_t *>(inner + 0xe0) = 0;
}
}
if (compat.matchesMpegObject(mpegAddr) &&
compat.hasFinishTargets() &&
(picturesServed + 1u) >= mpegCompatSyntheticFrames(compat))
{
// No decoder yet: synthesize a safe frame so the guest can
// initialize its movie presentation path, then mark playback finished.
if (compat.videoStateAddr != 0u)
{
mpegGuestWrite32(rdram, compat.videoStateAddr, compat.finishedVideoStateValue);
}
if (compat.movieStateAddr != 0u)
{
mpegGuestWrite32(rdram, compat.movieStateAddr, compat.finishedMovieStateValue);
}
}
setReturnU32(ctx, 0u);
}
void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime);
}
void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime);
}
void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
resetMpegStubStateUnlocked();
g_mpeg_stub_state.initialized = true;
setReturnU32(ctx, 0u);
}
void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t mpegAddr = getRegU32(ctx, 4);
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.initialized = true;
const MpegPlaybackState &playback = getPlaybackState(mpegAddr);
if (g_mpeg_stub_state.compat.matchesMpegObject(mpegAddr))
{
setReturnS32(ctx, playback.picturesServed >= mpegCompatSyntheticFrames(g_mpeg_stub_state.compat) ? 1 : 0);
return;
}
// Generic fallback: keep decode threads alive until a game-specific path
// decides to stop playback.
setReturnS32(ctx, 0);
}
void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime);
}
void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t param_1 = getRegU32(ctx, 4);
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.playbackByMpeg[param_1] = {};
}
uint8_t *base = getMemPtr(rdram, param_1);
if (!base)
{
return;
}
uint32_t inner = *reinterpret_cast<uint32_t *>(base + 0x40);
if (inner == 0u)
return;
mpegGuestWrite32(rdram, param_1 + 0x00u, 0u);
mpegGuestWrite32(rdram, param_1 + 0x04u, 0u);
mpegGuestWrite32(rdram, param_1 + 0x08u, 0u);
mpegGuestWrite32(rdram, inner + 0x00, 0u);
mpegGuestWrite32(rdram, inner + 0x04, 0u);
mpegGuestWrite32(rdram, inner + 0x08, 0u);
mpegGuestWrite32(rdram, param_1 + 0x08, 0u);
mpegGuestWrite32(rdram, inner + 0x80, 0xFFFFFFFFu);
mpegGuestWrite32(rdram, inner + 0xAC, 0u);
mpegGuestWrite32(rdram, 0x171904u, 0u);
}
void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime);
}
void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime);
}
void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime);
}
void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime);
}
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void resetMpegStubState();
void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+759
View File
@@ -0,0 +1,759 @@
#include "Common.h"
#include "Pad.h"
namespace ps2_stubs
{
namespace
{
constexpr uint8_t kPadModeDigital = 0x41;
constexpr uint8_t kPadModeDualShock = 0x73;
constexpr uint8_t kPadAnalogCenter = 0x80;
constexpr int32_t kPadTypeDigital = 4;
constexpr int32_t kPadTypeDualShock = 7;
constexpr int32_t kPadStateDisconnected = 0;
constexpr int32_t kPadStateStable = 6;
constexpr size_t kPadPortCount = 2;
constexpr size_t kPadSlotCount = 1;
constexpr uint16_t kPadBtnSelect = 1u << 0;
constexpr uint16_t kPadBtnL3 = 1u << 1;
constexpr uint16_t kPadBtnR3 = 1u << 2;
constexpr uint16_t kPadBtnStart = 1u << 3;
constexpr uint16_t kPadBtnUp = 1u << 4;
constexpr uint16_t kPadBtnRight = 1u << 5;
constexpr uint16_t kPadBtnDown = 1u << 6;
constexpr uint16_t kPadBtnLeft = 1u << 7;
constexpr uint16_t kPadBtnL2 = 1u << 8;
constexpr uint16_t kPadBtnR2 = 1u << 9;
constexpr uint16_t kPadBtnL1 = 1u << 10;
constexpr uint16_t kPadBtnR1 = 1u << 11;
constexpr uint16_t kPadBtnTriangle = 1u << 12;
constexpr uint16_t kPadBtnCircle = 1u << 13;
constexpr uint16_t kPadBtnCross = 1u << 14;
constexpr uint16_t kPadBtnSquare = 1u << 15;
struct PadInputState
{
uint16_t buttons = 0xFFFF; // active-low
uint8_t rx = kPadAnalogCenter;
uint8_t ry = kPadAnalogCenter;
uint8_t lx = kPadAnalogCenter;
uint8_t ly = kPadAnalogCenter;
};
struct PadPortState
{
bool open = false;
bool analogMode = true;
bool pressureEnabled = false;
uint16_t buttonMask = 0xFFFFu;
uint32_t dmaAddr = 0u;
uint32_t reqState = 0u;
};
std::mutex g_padOverrideMutex;
std::mutex g_padStateMutex;
bool g_padOverrideEnabled = false;
PadInputState g_padOverrideState{};
PadPortState g_padPorts[kPadPortCount]{};
int g_padReadLogCount = 0;
uint8_t axisToByte(float axis)
{
axis = std::clamp(axis, -1.0f, 1.0f);
const float mapped = (axis + 1.0f) * 127.5f;
return static_cast<uint8_t>(std::lround(mapped));
}
void setButton(PadInputState &state, uint16_t mask, bool pressed)
{
if (pressed)
{
state.buttons = static_cast<uint16_t>(state.buttons & ~mask);
}
}
int findFirstGamepad()
{
for (int i = 0; i < 4; ++i)
{
if (IsGamepadAvailable(i))
{
return i;
}
}
return -1;
}
void applyGamepadState(PadInputState &state)
{
if (!IsWindowReady())
{
return;
}
const int gamepad = findFirstGamepad();
if (gamepad < 0)
{
return;
}
// Raylib mapping (PS2 -> raylib buttons/axes):
// D-Pad -> LEFT_FACE_*, Cross/Circle/Square/Triangle -> RIGHT_FACE_*
// L1/R1 -> TRIGGER_1, L2/R2 -> TRIGGER_2, L3/R3 -> THUMB
// Select/Start -> MIDDLE_LEFT/MIDDLE_RIGHT
state.lx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X));
state.ly = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y));
state.rx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X));
state.ry = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y));
setButton(state, kPadBtnUp, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP));
setButton(state, kPadBtnDown, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN));
setButton(state, kPadBtnLeft, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT));
setButton(state, kPadBtnRight, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT));
setButton(state, kPadBtnCross, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN));
setButton(state, kPadBtnCircle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT));
setButton(state, kPadBtnSquare, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT));
setButton(state, kPadBtnTriangle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP));
setButton(state, kPadBtnL1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1));
setButton(state, kPadBtnR1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1));
setButton(state, kPadBtnL2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2));
setButton(state, kPadBtnR2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2));
setButton(state, kPadBtnL3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB));
setButton(state, kPadBtnR3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB));
setButton(state, kPadBtnSelect, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT));
setButton(state, kPadBtnStart, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT));
}
void applyKeyboardState(PadInputState &state, bool allowAnalog)
{
if (!IsWindowReady())
{
return;
}
// Keyboard mapping (PS2 -> keys):
// D-Pad: arrows, Square/Cross/Circle/Triangle: Z/X/C/V
// L1/R1: Q/E, L2/R2: 1/3, Start/Select: Enter/RightShift
// L3/R3: LeftCtrl/RightCtrl, Analog left: WASD
setButton(state, kPadBtnUp, IsKeyDown(KEY_UP));
setButton(state, kPadBtnDown, IsKeyDown(KEY_DOWN));
setButton(state, kPadBtnLeft, IsKeyDown(KEY_LEFT));
setButton(state, kPadBtnRight, IsKeyDown(KEY_RIGHT));
setButton(state, kPadBtnSquare, IsKeyDown(KEY_Z));
setButton(state, kPadBtnCross, IsKeyDown(KEY_X));
setButton(state, kPadBtnCircle, IsKeyDown(KEY_C));
setButton(state, kPadBtnTriangle, IsKeyDown(KEY_V));
setButton(state, kPadBtnL1, IsKeyDown(KEY_Q));
setButton(state, kPadBtnR1, IsKeyDown(KEY_E));
setButton(state, kPadBtnL2, IsKeyDown(KEY_ONE));
setButton(state, kPadBtnR2, IsKeyDown(KEY_THREE));
setButton(state, kPadBtnStart, IsKeyDown(KEY_ENTER));
setButton(state, kPadBtnSelect, IsKeyDown(KEY_RIGHT_SHIFT));
setButton(state, kPadBtnL3, IsKeyDown(KEY_LEFT_CONTROL));
setButton(state, kPadBtnR3, IsKeyDown(KEY_RIGHT_CONTROL));
if (!allowAnalog)
{
return;
}
float ax = 0.0f;
float ay = 0.0f;
if (IsKeyDown(KEY_D))
ax += 1.0f;
if (IsKeyDown(KEY_A))
ax -= 1.0f;
if (IsKeyDown(KEY_S))
ay += 1.0f;
if (IsKeyDown(KEY_W))
ay -= 1.0f;
if (ax != 0.0f || ay != 0.0f)
{
state.lx = axisToByte(ax);
state.ly = axisToByte(ay);
}
}
void resetPadStateLocked()
{
for (PadPortState &portState : g_padPorts)
{
portState = PadPortState{};
}
}
PadPortState *lookupPadPortStateLocked(int port, int slot)
{
if (port < 0 || port >= static_cast<int>(kPadPortCount))
{
return nullptr;
}
if (slot < 0 || slot >= static_cast<int>(kPadSlotCount))
{
return nullptr;
}
return &g_padPorts[port];
}
void initializePadPortLocked(PadPortState &portState, uint32_t dmaAddr)
{
portState.open = true;
portState.analogMode = true;
portState.pressureEnabled = false;
portState.buttonMask = 0xFFFFu;
portState.dmaAddr = dmaAddr;
portState.reqState = 0u;
}
uint8_t pressureValue(const PadInputState &state, const PadPortState &portState, uint16_t mask)
{
if (!portState.pressureEnabled)
{
return 0u;
}
if ((portState.buttonMask & mask) == 0u)
{
return 0u;
}
return ((state.buttons & mask) == 0u) ? 0xFFu : 0u;
}
void fillPadStatus(uint8_t *data, const PadInputState &state, const PadPortState &portState)
{
std::memset(data, 0, 32);
data[1] = portState.analogMode ? kPadModeDualShock : kPadModeDigital;
data[2] = static_cast<uint8_t>(state.buttons & 0xFFu);
data[3] = static_cast<uint8_t>((state.buttons >> 8) & 0xFFu);
data[4] = state.rx;
data[5] = state.ry;
data[6] = state.lx;
data[7] = state.ly;
data[8] = pressureValue(state, portState, kPadBtnRight);
data[9] = pressureValue(state, portState, kPadBtnLeft);
data[10] = pressureValue(state, portState, kPadBtnUp);
data[11] = pressureValue(state, portState, kPadBtnDown);
data[12] = pressureValue(state, portState, kPadBtnTriangle);
data[13] = pressureValue(state, portState, kPadBtnCircle);
data[14] = pressureValue(state, portState, kPadBtnCross);
data[15] = pressureValue(state, portState, kPadBtnSquare);
data[16] = pressureValue(state, portState, kPadBtnL1);
data[17] = pressureValue(state, portState, kPadBtnL2);
data[18] = pressureValue(state, portState, kPadBtnR1);
data[19] = pressureValue(state, portState, kPadBtnR2);
}
bool readPadPortData(int port, int slot, PS2Runtime *runtime, uint8_t *outData)
{
if (!outData)
{
return false;
}
PadPortState portState;
{
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *sharedPortState = lookupPadPortStateLocked(port, slot);
if (!sharedPortState || !sharedPortState->open)
{
return false;
}
portState = *sharedPortState;
}
PadInputState state;
bool useOverride = false;
{
std::lock_guard<std::mutex> lock(g_padOverrideMutex);
if (g_padOverrideEnabled)
{
state = g_padOverrideState;
useOverride = true;
}
}
if (!useOverride)
{
uint8_t backendData[32]{};
if (runtime && runtime->padBackend().readState(port, slot, backendData, sizeof(backendData)))
{
state.buttons = static_cast<uint16_t>(backendData[2] | (backendData[3] << 8));
state.rx = backendData[4];
state.ry = backendData[5];
state.lx = backendData[6];
state.ly = backendData[7];
}
else
{
applyGamepadState(state);
applyKeyboardState(state, portState.analogMode);
}
}
fillPadStatus(outData, state, portState);
return true;
}
}
void PadSyncCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
{
std::lock_guard<std::mutex> lock(g_padStateMutex);
resetPadStateLocked();
}
setReturnS32(ctx, 1);
}
void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState || !portState->open)
{
setReturnS32(ctx, 0);
return;
}
portState->pressureEnabled = true;
portState->reqState = 0u;
setReturnS32(ctx, 1);
}
void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState || !portState->open)
{
setReturnS32(ctx, 0);
return;
}
portState->pressureEnabled = false;
portState->reqState = 0u;
setReturnS32(ctx, 1);
}
void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
const uint16_t mask = portState ? portState->buttonMask : 0xFFFFu;
setReturnS32(ctx, static_cast<int32_t>(mask));
}
void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
const uint32_t dmaAddr = portState ? portState->dmaAddr : getRegU32(ctx, 6);
setReturnU32(ctx, dmaAddr);
}
void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
static std::atomic<uint32_t> frameCount{0};
setReturnU32(ctx, frameCount++);
}
void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
// Arbitrary non-zero module version.
setReturnS32(ctx, 0x0200);
}
void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, 2);
}
void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
setReturnS32(ctx, static_cast<int32_t>(portState ? portState->reqState : 0u));
}
void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
// Most games use one slot unless multitap is active.
setReturnS32(ctx, 1);
}
void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
setReturnS32(ctx, (portState && portState->open) ? kPadStateStable : kPadStateDisconnected);
}
void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int32_t act = static_cast<int32_t>(getRegU32(ctx, 6));
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState || !portState->open)
{
setReturnS32(ctx, 0);
return;
}
if (act < 0)
{
setReturnS32(ctx, 2); // small + large motors
return;
}
setReturnS32(ctx, (act < 2) ? 1 : 0);
}
void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
// No combined modes reported.
setReturnS32(ctx, 0);
}
void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const int32_t infoMode = static_cast<int32_t>(getRegU32(ctx, 6)); // a2
const int32_t index = static_cast<int32_t>(getRegU32(ctx, 7)); // a3
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState || !portState->open)
{
setReturnS32(ctx, 0);
return;
}
const int32_t currentId = portState->analogMode ? kPadTypeDualShock : kPadTypeDigital;
switch (infoMode)
{
case 1: // PAD_MODECURID
setReturnS32(ctx, currentId);
return;
case 2: // PAD_MODECUREXID
setReturnS32(ctx, currentId);
return;
case 3: // PAD_MODECUROFFS
setReturnS32(ctx, 0);
return;
case 4: // PAD_MODETABLE
if (index == -1)
{
setReturnS32(ctx, 1); // one available mode
}
else if (index == 0)
{
setReturnS32(ctx, currentId);
}
else
{
setReturnS32(ctx, 0);
}
return;
default:
setReturnS32(ctx, 0);
return;
}
}
void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
const PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
setReturnS32(ctx, (portState && portState->open) ? 1 : 0);
}
void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
{
std::lock_guard<std::mutex> lock(g_padStateMutex);
resetPadStateLocked();
}
setReturnS32(ctx, 1);
}
void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
scePadInit(rdram, ctx, runtime);
}
void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState)
{
setReturnS32(ctx, 0);
return;
}
portState->open = false;
portState->pressureEnabled = false;
portState->reqState = 0u;
setReturnS32(ctx, 1);
}
void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t dmaAddr = getRegU32(ctx, 6);
uint8_t *dmaStr = getMemPtr(rdram, dmaAddr);
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState || (dmaAddr != 0u && !dmaStr))
{
setReturnS32(ctx, 0);
return;
}
portState->open = true;
portState->analogMode = true;
portState->pressureEnabled = false;
portState->buttonMask = 0xFFFFu;
portState->dmaAddr = dmaAddr;
portState->reqState = 0u;
if (dmaStr)
{
std::memset(dmaStr, 0, 32);
}
setReturnS32(ctx, 1);
}
void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int port = static_cast<int>(getRegU32(ctx, 4));
const int slot = static_cast<int>(getRegU32(ctx, 5));
const uint32_t dataAddr = getRegU32(ctx, 6);
uint8_t *data = getMemPtr(rdram, dataAddr);
if (!data)
{
setReturnS32(ctx, 0);
return;
}
if (!readPadPortData(port, slot, runtime, data))
{
setReturnS32(ctx, 0);
return;
}
if (g_padReadLogCount < 48)
{
const int gamepad = findFirstGamepad();
const bool gamepadStartPressed =
(gamepad >= 0) && IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT);
const bool startPressed = (data[2] != 0xFFu || data[3] != 0xFFu ||
IsKeyDown(KEY_ENTER) || gamepadStartPressed);
if (startPressed)
{
const uint32_t guestButtons =
(static_cast<uint32_t>(static_cast<uint8_t>(data[2] ^ 0xFFu)) << 8) |
static_cast<uint32_t>(static_cast<uint8_t>(data[3] ^ 0xFFu));
std::printf("[padread] port=%d slot=%d data2=0x%02x data3=0x%02x guestButtons=0x%04x enter=%d gamepadStart=%d\n",
port, slot, data[2], data[3], guestButtons,
IsKeyDown(KEY_ENTER) ? 1 : 0, gamepadStartPressed ? 1 : 0);
++g_padReadLogCount;
}
}
setReturnS32(ctx, 1);
}
void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t state = getRegU32(ctx, 4);
const uint32_t strAddr = getRegU32(ctx, 5);
char *buf = reinterpret_cast<char *>(getMemPtr(rdram, strAddr));
if (!buf)
{
setReturnS32(ctx, -1);
return;
}
const char *text = (state == 0) ? "COMPLETE" : "BUSY";
std::strncpy(buf, text, 31);
buf[31] = '\0';
setReturnS32(ctx, 0);
}
void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, 1);
}
void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, 1);
}
void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (portState && portState->open)
{
portState->buttonMask = static_cast<uint16_t>(getRegU32(ctx, 6));
portState->reqState = 0u;
}
setReturnS32(ctx, 1);
}
void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (!portState || !portState->open)
{
setReturnS32(ctx, 0);
return;
}
portState->analogMode = (getRegU32(ctx, 6) != 0u);
portState->reqState = 0u;
setReturnS32(ctx, 1);
}
void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
std::lock_guard<std::mutex> lock(g_padStateMutex);
PadPortState *portState = lookupPadPortStateLocked(static_cast<int>(getRegU32(ctx, 4)),
static_cast<int>(getRegU32(ctx, 5)));
if (portState && portState->open)
{
portState->reqState = static_cast<uint32_t>(getRegU32(ctx, 6) ? 1u : 0u);
}
setReturnS32(ctx, 1);
}
void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, 1);
}
void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, 0);
}
void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t state = getRegU32(ctx, 4);
const uint32_t strAddr = getRegU32(ctx, 5);
char *buf = reinterpret_cast<char *>(getMemPtr(rdram, strAddr));
if (!buf)
{
setReturnS32(ctx, -1);
return;
}
const char *text = "UNKNOWN";
if (state == 6)
{
text = "STABLE";
}
else if (state == 1)
{
text = "FINDPAD";
}
else if (state == 0)
{
text = "DISCONNECTED";
}
std::strncpy(buf, text, 31);
buf[31] = '\0';
setReturnS32(ctx, 0);
}
void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry)
{
std::lock_guard<std::mutex> lock(g_padOverrideMutex);
g_padOverrideEnabled = true;
g_padOverrideState.buttons = buttons;
g_padOverrideState.lx = lx;
g_padOverrideState.ly = ly;
g_padOverrideState.rx = rx;
g_padOverrideState.ry = ry;
}
void clearPadOverrideState()
{
std::lock_guard<std::mutex> lock(g_padOverrideMutex);
g_padOverrideEnabled = false;
g_padOverrideState = PadInputState{};
}
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void PadSyncCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry);
void clearPadOverrideState();
}
+20
View File
@@ -0,0 +1,20 @@
#include "Common.h"
#include "RPC.h"
namespace ps2_stubs
{
void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceRpcFreePacket", rdram, ctx, runtime);
}
void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceRpcGetFPacket", rdram, ctx, runtime);
}
void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceRpcGetFPacket2", rdram, ctx, runtime);
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+789
View File
@@ -0,0 +1,789 @@
#include "Common.h"
#include "SIF.h"
#include "../Syscalls/RPC.h"
#include <map>
namespace ps2_stubs
{
void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSifCmdIntrHdlr", rdram, ctx, runtime);
}
void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifLoadModule(rdram, ctx, runtime);
}
void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t srcAddr = getRegU32(ctx, 7); // $a3
const uint32_t dstAddr = readStackU32(rdram, ctx, 16);
const uint32_t size = readStackU32(rdram, ctx, 20);
if (size != 0u && srcAddr != 0u && dstAddr != 0u)
{
for (uint32_t i = 0; i < size; ++i)
{
const uint8_t *src = getConstMemPtr(rdram, srcAddr + i);
uint8_t *dst = getMemPtr(rdram, dstAddr + i);
if (!src || !dst)
{
break;
}
*dst = *src;
}
}
setReturnS32(ctx, 1);
}
namespace
{
struct Ps2SifDmaTransfer
{
uint32_t src = 0;
uint32_t dest = 0;
int32_t size = 0;
int32_t attr = 0;
};
static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected SIF DMA descriptor size");
std::mutex g_sifDmaTransferMutex;
uint32_t g_nextSifDmaTransferId = 1u;
std::mutex g_sifCmdStateMutex;
std::mutex g_sifHeapMutex;
std::unordered_map<uint32_t, uint32_t> g_sifRegs;
std::unordered_map<uint32_t, uint32_t> g_sifSregs;
std::unordered_map<uint32_t, uint32_t> g_sifCmdHandlers;
std::map<uint32_t, uint32_t> g_sifHeapAllocations;
uint32_t g_sifCmdBuffer = 0u;
uint32_t g_sifSysCmdBuffer = 0u;
bool g_sifCmdInitialized = false;
uint32_t g_sifGetRegLogCount = 0u;
uint32_t g_sifSetRegLogCount = 0u;
constexpr uint32_t kSifRegBootStatus = 0x4u;
constexpr uint32_t kSifRegMainAddr = 0x80000000u;
constexpr uint32_t kSifRegSubAddr = 0x80000001u;
constexpr uint32_t kSifRegMsCom = 0x80000002u;
constexpr uint32_t kSifBootReadyMask = 0x00020000u;
void seedDefaultSifRegsLocked()
{
g_sifRegs.clear();
g_sifSregs.clear();
g_sifCmdHandlers.clear();
g_sifCmdBuffer = 0u;
g_sifSysCmdBuffer = 0u;
g_sifCmdInitialized = false;
g_sifGetRegLogCount = 0u;
g_sifSetRegLogCount = 0u;
g_sifRegs[kSifRegBootStatus] = kSifBootReadyMask;
g_sifRegs[kSifRegMainAddr] = 0u;
g_sifRegs[kSifRegSubAddr] = 0u;
g_sifRegs[kSifRegMsCom] = 0u;
}
bool shouldTraceSifReg(uint32_t reg)
{
switch (reg)
{
case 0x2u:
case 0x4u:
case 0x80000000u:
case 0x80000001u:
case 0x80000002u:
return true;
default:
return false;
}
}
struct SifStateInitializer
{
SifStateInitializer()
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
seedDefaultSifRegsLocked();
}
} g_sifStateInitializer;
uint32_t allocateSifDmaTransferId()
{
std::lock_guard<std::mutex> lock(g_sifDmaTransferMutex);
uint32_t id = g_nextSifDmaTransferId++;
if (id == 0u)
{
id = g_nextSifDmaTransferId++;
}
return id;
}
uint32_t alignIopHeapSize(uint32_t size)
{
return (size + (kIopHeapAlign - 1u)) & ~(kIopHeapAlign - 1u);
}
uint32_t allocateSifHeapBlock(uint32_t requestSize)
{
const uint32_t alignedSize = alignIopHeapSize(requestSize);
if (alignedSize == 0u)
{
return 0u;
}
std::lock_guard<std::mutex> lock(g_sifHeapMutex);
uint32_t candidate = kIopHeapBase;
for (const auto &[addr, size] : g_sifHeapAllocations)
{
if (candidate + alignedSize <= addr)
{
break;
}
const uint32_t blockEnd = alignIopHeapSize(addr + size);
if (blockEnd > candidate)
{
candidate = blockEnd;
}
}
if (candidate < kIopHeapBase || candidate + alignedSize > kIopHeapLimit)
{
return 0u;
}
g_sifHeapAllocations[candidate] = alignedSize;
g_iopHeapNext = candidate + alignedSize;
return candidate;
}
bool freeSifHeapBlock(uint32_t addr)
{
std::lock_guard<std::mutex> lock(g_sifHeapMutex);
const auto it = g_sifHeapAllocations.find(addr);
if (it == g_sifHeapAllocations.end())
{
return false;
}
g_sifHeapAllocations.erase(it);
if (g_sifHeapAllocations.empty())
{
g_iopHeapNext = kIopHeapBase;
}
return true;
}
void resetSifHeapState()
{
std::lock_guard<std::mutex> lock(g_sifHeapMutex);
g_sifHeapAllocations.clear();
g_iopHeapNext = kIopHeapBase;
}
bool isCopyableGuestAddress(uint32_t addr)
{
if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
{
return true;
}
if (addr < 0x20000000u)
{
return true;
}
if (addr >= 0x20000000u && addr < 0x40000000u)
{
return true;
}
if (addr >= 0x80000000u && addr < 0xC0000000u)
{
return true;
}
return false;
}
bool canCopyGuestByteRange(const uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes)
{
if (!rdram)
{
return false;
}
if (sizeBytes == 0u)
{
return true;
}
for (uint32_t i = 0u; i < sizeBytes; ++i)
{
const uint32_t srcByteAddr = srcAddr + i;
const uint32_t dstByteAddr = dstAddr + i;
if (!isCopyableGuestAddress(srcByteAddr) || !isCopyableGuestAddress(dstByteAddr))
{
return false;
}
const uint8_t *src = getConstMemPtr(rdram, srcByteAddr);
const uint8_t *dst = getConstMemPtr(rdram, dstByteAddr);
if (!src || !dst)
{
return false;
}
}
return true;
}
bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes)
{
if (!canCopyGuestByteRange(rdram, dstAddr, srcAddr, sizeBytes))
{
return false;
}
if (sizeBytes == 0u)
{
return true;
}
const uint64_t srcBegin = srcAddr;
const uint64_t srcEnd = srcBegin + static_cast<uint64_t>(sizeBytes);
const uint64_t dstBegin = dstAddr;
const bool copyBackward = (dstBegin > srcBegin) && (dstBegin < srcEnd);
if (copyBackward)
{
for (uint32_t i = sizeBytes; i > 0u; --i)
{
const uint32_t index = i - 1u;
const uint8_t *src = getConstMemPtr(rdram, srcAddr + index);
uint8_t *dst = getMemPtr(rdram, dstAddr + index);
if (!src || !dst)
{
return false;
}
*dst = *src;
}
return true;
}
for (uint32_t i = 0; i < sizeBytes; ++i)
{
const uint8_t *src = getConstMemPtr(rdram, srcAddr + i);
uint8_t *dst = getMemPtr(rdram, dstAddr + i);
if (!src || !dst)
{
return false;
}
*dst = *src;
}
return true;
}
}
void resetSifState()
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
seedDefaultSifRegsLocked();
resetSifHeapState();
}
void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cid = getRegU32(ctx, 4);
const uint32_t handler = getRegU32(ctx, 5);
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
g_sifCmdHandlers[cid] = handler;
setReturnS32(ctx, 0);
}
void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t reqSize = getRegU32(ctx, 4);
setReturnU32(ctx, allocateSifHeapBlock(reqSize));
}
void sceSifAllocSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t size = getRegU32(ctx, 5);
setReturnU32(ctx, allocateSifHeapBlock(size));
}
void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifBindRpc(rdram, ctx, runtime);
}
void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime);
}
void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
(void)getRegU32(ctx, 4); // trid
// Transfers are applied immediately by sceSifSetDma in this runtime.
setReturnS32(ctx, -1);
}
void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
seedDefaultSifRegsLocked();
setReturnS32(ctx, 0);
}
void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t addr = getRegU32(ctx, 4);
setReturnS32(ctx, freeSifHeapBlock(addr) ? 0 : -1);
}
void sceSifFreeSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
const uint32_t addr = getRegU32(ctx, 4);
setReturnS32(ctx, freeSifHeapBlock(addr) ? 0 : -1);
}
void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
setReturnU32(ctx, g_sifCmdBuffer);
}
void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, getRegU32(ctx, 4));
}
void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t rdAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
const uint32_t dstAddr = getRegU32(ctx, 6);
const int32_t sizeSigned = static_cast<int32_t>(getRegU32(ctx, 7));
if (sizeSigned <= 0)
{
setReturnS32(ctx, 0);
return;
}
const uint32_t size = static_cast<uint32_t>(sizeSigned);
if (size > PS2_RAM_SIZE)
{
static uint32_t warnCount = 0;
if (warnCount < 32u)
{
std::cerr << "sceSifGetOtherData rejected oversized transfer size=0x"
<< std::hex << size << std::dec << std::endl;
++warnCount;
}
setReturnS32(ctx, -1);
return;
}
ps2_syscalls::prepareSoundDriverStatusTransfer(rdram, srcAddr, size);
if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size))
{
static uint32_t warnCount = 0;
if (warnCount < 32u)
{
std::cerr << "sceSifGetOtherData copy failed src=0x" << std::hex << srcAddr
<< " dst=0x" << dstAddr
<< " size=0x" << size
<< std::dec << std::endl;
++warnCount;
}
setReturnS32(ctx, -1);
return;
}
// SifRpcReceiveData_t keeps src/dest/size at offsets 0x10/0x14/0x18.
if (uint8_t *rd = getMemPtr(rdram, rdAddr))
{
std::memcpy(rd + 0x10u, &srcAddr, sizeof(srcAddr));
std::memcpy(rd + 0x14u, &dstAddr, sizeof(dstAddr));
std::memcpy(rd + 0x18u, &size, sizeof(size));
}
ps2_syscalls::finalizeSoundDriverStatusTransfer(rdram, srcAddr, dstAddr, size);
setReturnS32(ctx, 0);
}
void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t reg = getRegU32(ctx, 4);
uint32_t value = 0u;
bool shouldLog = false;
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
auto it = g_sifRegs.find(reg);
if (it != g_sifRegs.end())
{
value = it->second;
}
shouldLog = shouldTraceSifReg(reg) && g_sifGetRegLogCount < 128u;
if (shouldLog)
{
++g_sifGetRegLogCount;
}
}
if (shouldLog)
{
auto flags = std::cerr.flags();
std::cerr << "[sceSifGetReg] reg=0x" << std::hex << reg
<< " value=0x" << value
<< " pc=0x" << (ctx ? ctx->pc : 0u)
<< " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u)
<< std::dec << std::endl;
std::cerr.flags(flags);
}
setReturnU32(ctx, value);
}
void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t reg = getRegU32(ctx, 4);
uint32_t value = 0u;
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
auto it = g_sifSregs.find(reg);
if (it != g_sifSregs.end())
{
value = it->second;
}
}
setReturnU32(ctx, value);
}
void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
g_sifCmdInitialized = true;
setReturnS32(ctx, 0);
}
void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
resetSifHeapState();
setReturnS32(ctx, 0);
}
void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifInitRpc(rdram, ctx, runtime);
}
void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime);
}
void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime);
}
void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime);
}
void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime);
}
void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cid = getRegU32(ctx, 4);
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
g_sifCmdHandlers.erase(cid);
setReturnS32(ctx, 0);
}
void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime);
}
void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime);
}
void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t newBuffer = getRegU32(ctx, 4);
uint32_t prev = 0u;
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
prev = g_sifCmdBuffer;
g_sifCmdBuffer = newBuffer;
}
setReturnU32(ctx, prev);
}
void isceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
sceSifSetDChain(rdram, ctx, runtime);
}
void isceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
sceSifSetDma(rdram, ctx, runtime);
}
void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, 0);
}
void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t dmatAddr = getRegU32(ctx, 4);
const uint32_t count = getRegU32(ctx, 5);
if (!dmatAddr || count == 0u || count > 32u)
{
setReturnS32(ctx, 0);
return;
}
std::array<Ps2SifDmaTransfer, 32u> pending{};
uint32_t pendingCount = 0u;
bool ok = true;
for (uint32_t i = 0; i < count; ++i)
{
const uint32_t entryAddr = dmatAddr + (i * static_cast<uint32_t>(sizeof(Ps2SifDmaTransfer)));
const uint8_t *entry = getConstMemPtr(rdram, entryAddr);
if (!entry)
{
ok = false;
break;
}
Ps2SifDmaTransfer xfer{};
std::memcpy(&xfer, entry, sizeof(xfer));
if (xfer.size <= 0)
{
continue;
}
const uint32_t sizeBytes = static_cast<uint32_t>(xfer.size);
if (sizeBytes > PS2_RAM_SIZE)
{
ok = false;
break;
}
if (!canCopyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes))
{
ok = false;
break;
}
pending[pendingCount++] = xfer;
}
if (ok)
{
for (uint32_t i = 0; i < pendingCount; ++i)
{
const Ps2SifDmaTransfer &xfer = pending[i];
if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast<uint32_t>(xfer.size)))
{
ok = false;
break;
}
ps2_syscalls::noteDtxSifDmaTransfer(
rdram,
xfer.src,
xfer.dest,
static_cast<uint32_t>(xfer.size));
}
}
if (!ok)
{
static uint32_t warnCount = 0;
if (warnCount < 32u)
{
std::cerr << "sceSifSetDma failed dmat=0x" << std::hex << dmatAddr
<< " count=0x" << count
<< std::dec << std::endl;
++warnCount;
}
setReturnS32(ctx, 0);
return;
}
ps2_syscalls::dispatchDmacHandlersForCause(rdram, runtime, 5u);
setReturnS32(ctx, static_cast<int32_t>(allocateSifDmaTransferId()));
}
void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, getRegU32(ctx, 5));
}
void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t reg = getRegU32(ctx, 4);
const uint32_t value = getRegU32(ctx, 5);
uint32_t prev = 0u;
bool shouldLog = false;
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
auto it = g_sifRegs.find(reg);
if (it != g_sifRegs.end())
{
prev = it->second;
}
g_sifRegs[reg] = value;
shouldLog = shouldTraceSifReg(reg) && g_sifSetRegLogCount < 128u;
if (shouldLog)
{
++g_sifSetRegLogCount;
}
}
if (shouldLog)
{
auto flags = std::cerr.flags();
std::cerr << "[sceSifSetReg] reg=0x" << std::hex << reg
<< " prev=0x" << prev
<< " value=0x" << value
<< " pc=0x" << (ctx ? ctx->pc : 0u)
<< " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u)
<< std::dec << std::endl;
std::cerr.flags(flags);
}
setReturnU32(ctx, prev);
}
void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime);
}
void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t reg = getRegU32(ctx, 4);
const uint32_t value = getRegU32(ctx, 5);
uint32_t prev = 0u;
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
auto it = g_sifSregs.find(reg);
if (it != g_sifSregs.end())
{
prev = it->second;
}
g_sifSregs[reg] = value;
}
setReturnU32(ctx, prev);
}
void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t newBuffer = getRegU32(ctx, 4);
uint32_t prev = 0u;
{
std::lock_guard<std::mutex> lock(g_sifCmdStateMutex);
prev = g_sifSysCmdBuffer;
g_sifSysCmdBuffer = newBuffer;
}
setReturnU32(ctx, prev);
}
void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
}
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void resetSifState();
void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifAllocSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifFreeSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void isceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void isceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,61 @@
#include "Common.h"
#include "System.h"
namespace ps2_stubs
{
void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount < 8)
{
RUNTIME_LOG("ps2_stub builtin_set_imask");
++logCount;
}
setReturnS32(ctx, 0);
}
void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
if (runtime)
{
runtime->requestStop();
}
setReturnS32(ctx, 0);
}
void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1);
}
void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime);
}
void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceSetPtm", rdram, ctx, runtime);
}
void sceDevVif0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceDevVu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDevVif0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceDevVu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+59
View File
@@ -0,0 +1,59 @@
#include "Common.h"
#include "TTY.h"
#include "ps2_log.h"
namespace ps2_stubs
{
void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t format_addr = getRegU32(ctx, 4);
const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024);
if (format_addr == 0)
return;
std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1);
if (rendered.size() > 2048)
rendered.resize(2048);
PS2_IF_AGRESSIVE_LOGS({
const std::string logLine = sanitizeForLog(rendered);
uint32_t count = 0;
{
std::lock_guard<std::mutex> lock(g_printfLogMutex);
count = ++g_printfLogCount;
}
if (count <= kMaxPrintfLogs)
{
RUNTIME_LOG("PS2 scePrintf: " << logLine);
RUNTIME_LOG(std::flush);
}
else if (count == kMaxPrintfLogs + 1)
{
std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl;
}
});
}
void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceResetttyinit", rdram, ctx, runtime);
}
void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceTtyHandler", rdram, ctx, runtime);
}
void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceTtyInit", rdram, ctx, runtime);
}
void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceTtyRead", rdram, ctx, runtime);
}
void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceTtyWrite", rdram, ctx, runtime);
}
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -1,36 +1,8 @@
#include "ps2_stubs.h"
#include "ps2_runtime.h"
#include "ps2_runtime_macros.h"
#include "ps2_syscalls.h"
#include <iostream>
#include <algorithm>
#include <array>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <filesystem>
#include <mutex>
#include <limits>
#include "stubs/helpers/ps2_stubs_helpers.inl"
#include "Common.h"
#include "Unimplemented.h"
namespace ps2_stubs
{
#include "stubs/ps2_stubs_libc.inl"
#include "stubs/ps2_stubs_ps2.inl"
#include "stubs/ps2_stubs_misc.inl"
#include "stubs/ps2_stubs_gs.inl"
#include "stubs/ps2_stubs_residentEvilCV.inl"
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("unknown", rdram, ctx, runtime);
@@ -71,7 +43,7 @@ namespace ps2_stubs
<< ", $a2=0x" << getRegU32(ctx, 6)
<< ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl;
setReturnS32(ctx, -1); // Return error
//TODO maybe a macro to disable the exception and just return an success just to see it where goes.
throw std::runtime_error("Unimplemented PS2 stub called: " + stubName);
}
}
@@ -0,0 +1,9 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
+584
View File
@@ -0,0 +1,584 @@
#include "Common.h"
#include "VU.h"
//TODO use glm
namespace ps2_stubs
{
void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime);
}
namespace
{
bool readVuVec4f(uint8_t *rdram, uint32_t addr, float (&out)[4])
{
const uint8_t *ptr = getConstMemPtr(rdram, addr);
if (!ptr)
{
return false;
}
std::memcpy(out, ptr, sizeof(out));
return true;
}
bool writeVuVec4f(uint8_t *rdram, uint32_t addr, const float (&in)[4])
{
uint8_t *ptr = getMemPtr(rdram, addr);
if (!ptr)
{
return false;
}
std::memcpy(ptr, in, sizeof(in));
return true;
}
bool readVuVec4i(uint8_t *rdram, uint32_t addr, int32_t (&out)[4])
{
const uint8_t *ptr = getConstMemPtr(rdram, addr);
if (!ptr)
{
return false;
}
std::memcpy(out, ptr, sizeof(out));
return true;
}
bool writeVuVec4i(uint8_t *rdram, uint32_t addr, const int32_t (&in)[4])
{
uint8_t *ptr = getMemPtr(rdram, addr);
if (!ptr)
{
return false;
}
std::memcpy(ptr, in, sizeof(in));
return true;
}
bool readVuMatrix4f(uint8_t *rdram, uint32_t addr, float (&out)[16])
{
const uint8_t *ptr = getConstMemPtr(rdram, addr);
if (!ptr)
{
return false;
}
std::memcpy(out, ptr, sizeof(out));
return true;
}
bool writeVuMatrix4f(uint8_t *rdram, uint32_t addr, const float (&in)[16])
{
uint8_t *ptr = getMemPtr(rdram, addr);
if (!ptr)
{
return false;
}
std::memcpy(ptr, in, sizeof(in));
return true;
}
void mulVuMatrix(const float (&lhs)[16], const float (&rhs)[16], float (&out)[16])
{
std::fill(std::begin(out), std::end(out), 0.0f);
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
for (int k = 0; k < 4; ++k)
{
out[4 * i + j] += rhs[4 * k + j] * lhs[4 * i + k];
}
}
}
}
void makeIdentityMatrix(float (&out)[16])
{
std::fill(std::begin(out), std::end(out), 0.0f);
out[0] = 1.0f;
out[5] = 1.0f;
out[10] = 1.0f;
out[15] = 1.0f;
}
}
void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t lhsAddr = getRegU32(ctx, 5);
const uint32_t rhsAddr = getRegU32(ctx, 6);
float lhs[4]{}, rhs[4]{}, out[4]{};
if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs))
{
for (int i = 0; i < 4; ++i)
{
out[i] = lhs[i] + rhs[i];
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t matrixAddr = getRegU32(ctx, 5);
const uint32_t srcAddr = getRegU32(ctx, 6);
float matrix[16]{};
float src[4]{};
float out[4]{};
if (readVuMatrix4f(rdram, matrixAddr, matrix) && readVuVec4f(rdram, srcAddr, src))
{
// Match libvux VuxApplyMatrix math while honoring the imported EE ABI:
// a0=out, a1=matrix, a2=vector.
out[0] = (matrix[0] * src[0]) + (matrix[4] * src[1]) + (matrix[8] * src[2]) + (matrix[12] * src[3]);
out[1] = (matrix[1] * src[0]) + (matrix[5] * src[1]) + (matrix[9] * src[2]) + (matrix[13] * src[3]);
out[2] = (matrix[2] * src[0]) + (matrix[6] * src[1]) + (matrix[10] * src[2]) + (matrix[14] * src[3]);
out[3] = (matrix[3] * src[0]) + (matrix[7] * src[1]) + (matrix[11] * src[2]) + (matrix[15] * src[3]);
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime);
}
void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime);
}
void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime);
}
void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime);
}
void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime);
}
void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
uint8_t *dst = getMemPtr(rdram, dstAddr);
const uint8_t *src = getConstMemPtr(rdram, srcAddr);
if (dst && src)
{
std::memcpy(dst, src, sizeof(float) * 16u);
}
setReturnS32(ctx, 0);
}
void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
uint8_t *dst = getMemPtr(rdram, dstAddr);
const uint8_t *src = getConstMemPtr(rdram, srcAddr);
if (dst && src)
{
std::memcpy(dst, src, sizeof(float) * 4u);
}
setReturnS32(ctx, 0);
}
void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
uint8_t *dst = getMemPtr(rdram, dstAddr);
const uint8_t *src = getConstMemPtr(rdram, srcAddr);
if (dst && src)
{
std::memcpy(dst, src, sizeof(float) * 3u);
}
setReturnS32(ctx, 0);
}
void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime);
}
void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime);
}
void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime);
}
void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
float src[4]{};
int32_t out[4]{};
if (readVuVec4f(rdram, srcAddr, src))
{
for (int i = 0; i < 4; ++i)
{
out[i] = static_cast<int32_t>(src[i]);
}
(void)writeVuVec4i(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
float src[4]{};
int32_t out[4]{};
if (readVuVec4f(rdram, srcAddr, src))
{
for (int i = 0; i < 4; ++i)
{
out[i] = static_cast<int32_t>(src[i] * 16.0f);
}
(void)writeVuVec4i(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t lhsAddr = getRegU32(ctx, 4);
const uint32_t rhsAddr = getRegU32(ctx, 5);
float lhs[4]{}, rhs[4]{};
float dot = 0.0f;
if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs))
{
dot = (lhs[0] * rhs[0]) + (lhs[1] * rhs[1]) + (lhs[2] * rhs[2]) + (lhs[3] * rhs[3]);
}
if (ctx)
{
ctx->f[0] = dot;
}
uint32_t raw = 0u;
std::memcpy(&raw, &dot, sizeof(raw));
setReturnU32(ctx, raw);
}
void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime);
}
void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime);
}
void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime);
}
void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
int32_t src[4]{};
float out[4]{};
if (readVuVec4i(rdram, srcAddr, src))
{
for (int i = 0; i < 4; ++i)
{
out[i] = static_cast<float>(src[i]);
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
int32_t src[4]{};
float out[4]{};
if (readVuVec4i(rdram, srcAddr, src))
{
for (int i = 0; i < 4; ++i)
{
out[i] = static_cast<float>(src[i]) / 4096.0f;
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
int32_t src[4]{};
float out[4]{};
if (readVuVec4i(rdram, srcAddr, src))
{
for (int i = 0; i < 4; ++i)
{
out[i] = static_cast<float>(src[i]) / 16.0f;
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime);
}
void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime);
}
void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime);
}
void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
float src[4]{}, out[4]{};
if (readVuVec4f(rdram, srcAddr, src))
{
const float len = std::sqrt((src[0] * src[0]) + (src[1] * src[1]) + (src[2] * src[2]) + (src[3] * src[3]));
if (len > 1.0e-6f)
{
const float invLen = 1.0f / len;
for (int i = 0; i < 4; ++i)
{
out[i] = src[i] * invLen;
}
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime);
}
void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t lhsAddr = getRegU32(ctx, 5);
const uint32_t rhsAddr = getRegU32(ctx, 6);
float lhs[4]{}, rhs[4]{}, out[4]{};
if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs))
{
out[0] = (lhs[1] * rhs[2]) - (lhs[2] * rhs[1]);
out[1] = (lhs[2] * rhs[0]) - (lhs[0] * rhs[2]);
out[2] = (lhs[0] * rhs[1]) - (lhs[1] * rhs[0]);
out[3] = 0.0f;
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime);
}
void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
const float angle = ctx ? ctx->f[12] : 0.0f;
float src[16]{}, rot[16]{}, out[16]{};
if (readVuMatrix4f(rdram, srcAddr, src))
{
makeIdentityMatrix(rot);
const float cs = std::cos(angle);
const float sn = std::sin(angle);
rot[5] = cs;
rot[6] = sn;
rot[9] = -sn;
rot[10] = cs;
mulVuMatrix(src, rot, out);
(void)writeVuMatrix4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
const float angle = ctx ? ctx->f[12] : 0.0f;
float src[16]{}, rot[16]{}, out[16]{};
if (readVuMatrix4f(rdram, srcAddr, src))
{
makeIdentityMatrix(rot);
const float cs = std::cos(angle);
const float sn = std::sin(angle);
rot[0] = cs;
rot[2] = -sn;
rot[8] = sn;
rot[10] = cs;
mulVuMatrix(src, rot, out);
(void)writeVuMatrix4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
const float angle = ctx ? ctx->f[12] : 0.0f;
float src[16]{}, rot[16]{}, out[16]{};
if (readVuMatrix4f(rdram, srcAddr, src))
{
makeIdentityMatrix(rot);
const float cs = std::cos(angle);
const float sn = std::sin(angle);
rot[0] = cs;
rot[1] = sn;
rot[4] = -sn;
rot[5] = cs;
mulVuMatrix(src, rot, out);
(void)writeVuMatrix4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime);
}
void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime);
}
void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
float src[4]{}, out[4]{};
float scale = ctx ? ctx->f[12] : 0.0f;
if (scale == 0.0f)
{
uint32_t raw = getRegU32(ctx, 6);
std::memcpy(&scale, &raw, sizeof(scale));
if (scale == 0.0f)
{
scale = static_cast<float>(getRegU32(ctx, 6));
}
}
if (readVuVec4f(rdram, srcAddr, src))
{
for (int i = 0; i < 4; ++i)
{
out[i] = src[i] * scale;
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime);
}
void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t lhsAddr = getRegU32(ctx, 5);
const uint32_t rhsAddr = getRegU32(ctx, 6);
float lhs[4]{}, rhs[4]{}, out[4]{};
if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs))
{
for (int i = 0; i < 4; ++i)
{
out[i] = lhs[i] - rhs[i];
}
(void)writeVuVec4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime);
}
void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4);
const uint32_t srcAddr = getRegU32(ctx, 5);
float src[16]{};
float out[16]{};
if (readVuMatrix4f(rdram, srcAddr, src))
{
for (int row = 0; row < 4; ++row)
{
for (int col = 0; col < 4; ++col)
{
out[4 * row + col] = src[4 * col + row];
}
}
(void)writeVuMatrix4f(rdram, dstAddr, out);
}
setReturnS32(ctx, 0);
}
void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst
alignas(16) const float identity[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast<const uint8_t *>(identity), sizeof(identity)))
{
static uint32_t warnCount = 0;
if (warnCount < 8)
{
std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x"
<< std::hex << dstAddr << std::dec << std::endl;
++warnCount;
}
}
setReturnS32(ctx, 0);
}
void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime);
}
}
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include "ps2_stubs.h"
namespace ps2_stubs
{
void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,36 @@
#include "ps2_syscalls.h"
#include "ps2_runtime.h"
#include "runtime/ps2_iop_audio.h"
#include "ps2_runtime_macros.h"
#include "ps2_stubs.h"
#include <iostream>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <thread>
#include <condition_variable>
#include <atomic>
#include <filesystem>
#include <chrono>
#include <ctime>
#include <memory>
#include <string>
#ifndef _WIN32
#include <unistd.h>
#include <sys/stat.h>
#endif
#include <ThreadNaming.h>
std::string translatePs2Path(const char *ps2Path);
#include "Helpers/Path.h"
#include "Helpers/State.h"
#include "Helpers/Loader.h"
#include "Helpers/Runtime.h"
@@ -1,47 +1,16 @@
#include "ps2_syscalls.h"
#include "ps2_runtime.h"
#include "ps2_iop_audio.h"
#include "ps2_runtime_macros.h"
#include "ps2_stubs.h"
#include <iostream>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <thread>
#include <condition_variable>
#include <atomic>
#include <filesystem>
#include <chrono>
#include <ctime>
#include <memory>
#include <string>
#ifndef _WIN32
#include <unistd.h> // for unlink,rmdir,chdir
#include <sys/stat.h> // for mkdir
#endif
#include <ThreadNaming.h>
std::string translatePs2Path(const char *ps2Path);
#include "syscalls/helpers/ps2_syscalls_helpers_path.inl"
#include "syscalls/helpers/ps2_syscalls_helpers_state.inl"
#include "syscalls/helpers/ps2_syscalls_helpers_loader.inl"
#include "syscalls/helpers/ps2_syscalls_helpers_runtime.inl"
#include "Common.h"
#include "Dispatcher.h"
#include "System.h"
namespace ps2_syscalls
{
#include "syscalls/ps2_syscalls_interrupt.inl"
#include "syscalls/ps2_syscalls_system.inl"
void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
if (dispatchSyscallOverride(syscallNumber, rdram, ctx, runtime))
{
return true;
}
switch (syscallNumber)
{
case 0x01:
@@ -77,6 +46,18 @@ namespace ps2_syscalls
case 0x17:
DisableDmac(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x1A):
iEnableIntc(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x1B):
iDisableIntc(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x1C):
iEnableDmac(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x1D):
iDisableDmac(rdram, ctx, runtime);
return true;
case 0x18:
case 0xFC:
SetAlarm(rdram, ctx, runtime);
@@ -113,13 +94,17 @@ namespace ps2_syscalls
TerminateThread(rdram, ctx, runtime);
return true;
case 0x29:
case static_cast<uint32_t>(-0x2A):
ChangeThreadPriority(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x2A):
iChangeThreadPriority(rdram, ctx, runtime);
return true;
case 0x2B:
case static_cast<uint32_t>(-0x2C):
RotateThreadReadyQueue(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x2C):
iRotateThreadReadyQueue(rdram, ctx, runtime);
return true;
case 0x2D:
ReleaseWaitThread(rdram, ctx, runtime);
return true;
@@ -131,9 +116,11 @@ namespace ps2_syscalls
GetThreadId(rdram, ctx, runtime);
return true;
case 0x30:
case static_cast<uint32_t>(-0x31):
ReferThreadStatus(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x31):
iReferThreadStatus(rdram, ctx, runtime);
return true;
case 0x32:
SleepThread(rdram, ctx, runtime);
return true;
@@ -211,7 +198,7 @@ namespace ps2_syscalls
case 0x52:
SetEventFlag(rdram, ctx, runtime);
return true;
case 0x53:
case static_cast<uint32_t>(-0x53):
iSetEventFlag(rdram, ctx, runtime);
return true;
case 0x54:
@@ -257,22 +244,32 @@ namespace ps2_syscalls
case static_cast<uint32_t>(-0x5F):
DisableDmacHandler(rdram, ctx, runtime);
return true;
case 0x61:
EnableCache(rdram, ctx, runtime);
return true;
case 0x62:
DisableCache(rdram, ctx, runtime);
return true;
case 0x64:
FlushCache(rdram, ctx, runtime);
return true;
case 0x70:
case static_cast<uint32_t>(-0x70):
GsGetIMR(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x70):
iGsGetIMR(rdram, ctx, runtime);
return true;
case 0x71:
case static_cast<uint32_t>(-0x71):
GsPutIMR(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x71):
iGsPutIMR(rdram, ctx, runtime);
return true;
case 0x73:
SetVSyncFlag(rdram, ctx, runtime);
return true;
case 0x74:
RegisterExitHandler(rdram, ctx, runtime);
SetSyscall(rdram, ctx, runtime);
return true;
case 0x76:
case static_cast<uint32_t>(-0x76):
@@ -286,6 +283,9 @@ namespace ps2_syscalls
case static_cast<uint32_t>(-0x78):
ps2_stubs::sceSifSetDChain(rdram, ctx, runtime);
return true;
case 0x83:
FindAddress(rdram, ctx, runtime);
return true;
case 0x85:
SetMemoryMode(rdram, ctx, runtime);
return true;
@@ -293,100 +293,4 @@ namespace ps2_syscalls
return false;
}
}
#include "syscalls/ps2_syscalls_thread.inl"
#include "syscalls/ps2_syscalls_flags.inl"
#include "syscalls/ps2_syscalls_rpc.inl"
#include "syscalls/ps2_syscalls_fileio.inl"
void notifyRuntimeStop()
{
stopInterruptWorker();
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
g_intcHandlers.clear();
g_dmacHandlers.clear();
g_nextIntcHandlerId = 1;
g_nextDmacHandlerId = 1;
g_enabled_intc_mask = 0xFFFFFFFFu;
g_enabled_dmac_mask = 0xFFFFFFFFu;
}
{
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
g_vsync_registration = {};
g_vsync_tick_counter = 0u;
}
std::vector<std::shared_ptr<ThreadInfo>> threads;
threads.reserve(32);
{
std::lock_guard<std::mutex> lock(g_thread_map_mutex);
for (const auto &entry : g_threads)
{
if (entry.second)
{
threads.push_back(entry.second);
}
}
g_threads.clear();
g_nextThreadId = 2; // Reserve id 1 for main thread.
}
g_currentThreadId = 1;
for (const auto &threadInfo : threads)
{
{
std::lock_guard<std::mutex> lock(threadInfo->m);
threadInfo->forceRelease = true;
threadInfo->terminated = true;
}
threadInfo->cv.notify_all();
}
joinAllHostThreads();
std::vector<std::shared_ptr<SemaInfo>> semas;
{
std::lock_guard<std::mutex> lock(g_sema_map_mutex);
semas.reserve(g_semas.size());
for (const auto &entry : g_semas)
{
if (entry.second)
{
semas.push_back(entry.second);
}
}
g_semas.clear();
g_nextSemaId = 1;
}
for (const auto &sema : semas)
{
sema->cv.notify_all();
}
std::vector<std::shared_ptr<EventFlagInfo>> eventFlags;
{
std::lock_guard<std::mutex> lock(g_event_flag_map_mutex);
eventFlags.reserve(g_eventFlags.size());
for (const auto &entry : g_eventFlags)
{
if (entry.second)
{
eventFlags.push_back(entry.second);
}
}
g_eventFlags.clear();
g_nextEventFlagId = 1;
}
for (const auto &eventFlag : eventFlags)
{
eventFlag->cv.notify_all();
}
{
std::lock_guard<std::mutex> lock(g_alarm_mutex);
g_alarms.clear();
}
g_alarm_cv.notify_all();
}
}
@@ -0,0 +1,8 @@
#pragma once
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,512 @@
#include "Common.h"
#include "FileIO.h"
namespace ps2_syscalls
{
static int allocatePs2Fd(FILE *file)
{
if (!file)
return -1;
std::lock_guard<std::mutex> lock(g_fd_mutex);
int fd = g_nextFd++;
g_fileDescriptors[fd] = file;
return fd;
}
static FILE *getHostFile(int ps2Fd)
{
std::lock_guard<std::mutex> lock(g_fd_mutex);
auto it = g_fileDescriptors.find(ps2Fd);
if (it != g_fileDescriptors.end())
{
return it->second;
}
return nullptr;
}
static void releasePs2Fd(int ps2Fd)
{
std::lock_guard<std::mutex> lock(g_fd_mutex);
g_fileDescriptors.erase(ps2Fd);
}
struct VagAccumEntry
{
std::vector<uint8_t> data;
uint32_t firstBufAddr = 0;
};
static std::unordered_map<int, VagAccumEntry> g_vagAccum;
static std::mutex g_vagAccumMutex;
static constexpr size_t kVagAccumMaxBytes = 16 * 1024 * 1024;
static const char *translateFioMode(int ps2Flags)
{
bool read = (ps2Flags & PS2_FIO_O_RDONLY) || (ps2Flags & PS2_FIO_O_RDWR);
bool write = (ps2Flags & PS2_FIO_O_WRONLY) || (ps2Flags & PS2_FIO_O_RDWR);
bool append = (ps2Flags & PS2_FIO_O_APPEND);
bool create = (ps2Flags & PS2_FIO_O_CREAT);
bool truncate = (ps2Flags & PS2_FIO_O_TRUNC);
if (read && write)
{
if (create && truncate)
return "w+b";
if (create)
return "a+b";
return "r+b";
}
else if (write)
{
if (append)
return "ab";
if (create && truncate)
return "wb";
if (create)
return "wx";
return "r+b";
}
else if (read)
{
return "rb";
}
return "rb";
}
void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
int flags = (int)getRegU32(ctx, 5); // $a1 (PS2 FIO flags)
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
if (!ps2Path)
{
std::cerr << "fioOpen error: Invalid path address" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::string hostPath = translatePs2Path(ps2Path);
if (hostPath.empty())
{
std::cerr << "fioOpen error: Failed to translate path '" << ps2Path << "'" << std::endl;
setReturnS32(ctx, -1);
return;
}
const char *mode = translateFioMode(flags);
RUNTIME_LOG("fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'");
FILE *fp = ::fopen(hostPath.c_str(), mode);
if (!fp)
{
std::cerr << "fioOpen error: fopen failed for '" << hostPath << "': " << strerror(errno) << std::endl;
setReturnS32(ctx, -1); // e.g., -ENOENT, -EACCES
return;
}
int ps2Fd = allocatePs2Fd(fp);
if (ps2Fd < 0)
{
std::cerr << "fioOpen error: Failed to allocate PS2 file descriptor" << std::endl;
::fclose(fp);
setReturnS32(ctx, -1); // e.g., -EMFILE
return;
}
// returns the PS2 file descriptor
setReturnS32(ctx, ps2Fd);
}
void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int ps2Fd = (int)getRegU32(ctx, 4);
FILE *fp = getHostFile(ps2Fd);
if (!fp)
{
std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl;
setReturnS32(ctx, -1);
return;
}
int ret = ::fclose(fp);
releasePs2Fd(ps2Fd);
{
std::lock_guard<std::mutex> lock(g_vagAccumMutex);
auto it = g_vagAccum.find(ps2Fd);
if (it != g_vagAccum.end())
{
VagAccumEntry &e = it->second;
if (e.data.size() >= 48)
{
const uint32_t magic = (static_cast<uint32_t>(e.data[0]) << 24) |
(static_cast<uint32_t>(e.data[1]) << 16) |
(static_cast<uint32_t>(e.data[2]) << 8) |
static_cast<uint32_t>(e.data[3]);
const uint32_t magicLE = (static_cast<uint32_t>(e.data[3]) << 24) |
(static_cast<uint32_t>(e.data[2]) << 16) |
(static_cast<uint32_t>(e.data[1]) << 8) |
static_cast<uint32_t>(e.data[0]);
if (magic == 0x56414770u || magicLE == 0x56414770u)
{
if (runtime)
runtime->audioBackend().onVagTransferFromBuffer(
e.data.data(), static_cast<uint32_t>(e.data.size()),
e.firstBufAddr ? e.firstBufAddr : 0u);
}
}
g_vagAccum.erase(it);
}
}
setReturnS32(ctx, ret == 0 ? 0 : -1);
}
void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int ps2Fd = (int)getRegU32(ctx, 4); // $a0
uint32_t bufAddr = getRegU32(ctx, 5); // $a1
size_t size = getRegU32(ctx, 6); // $a2
uint8_t *hostBuf = getMemPtr(rdram, bufAddr);
FILE *fp = getHostFile(ps2Fd);
if (!hostBuf)
{
std::cerr << "fioRead error: Invalid buffer address for fd " << ps2Fd << std::endl;
setReturnS32(ctx, -1); // -EFAULT
return;
}
if (!fp)
{
std::cerr << "fioRead error: Invalid file descriptor " << ps2Fd << std::endl;
setReturnS32(ctx, -1); // -EBADF
return;
}
if (size == 0)
{
setReturnS32(ctx, 0); // Read 0 bytes
return;
}
size_t bytesRead = 0;
{
std::lock_guard<std::mutex> lock(g_sys_fd_mutex);
bytesRead = fread(hostBuf, 1, size, fp);
}
if (bytesRead < size && ferror(fp))
{
std::cerr << "fioRead error: fread failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl;
clearerr(fp);
setReturnS32(ctx, -1);
return;
}
{
std::lock_guard<std::mutex> lock(g_vagAccumMutex);
auto it = g_vagAccum.find(ps2Fd);
if (it != g_vagAccum.end())
{
VagAccumEntry &e = it->second;
if (e.data.size() + bytesRead <= kVagAccumMaxBytes)
e.data.insert(e.data.end(), hostBuf, hostBuf + bytesRead);
}
else if (bytesRead >= 4)
{
const uint32_t magic = (static_cast<uint32_t>(hostBuf[0]) << 24) |
(static_cast<uint32_t>(hostBuf[1]) << 16) |
(static_cast<uint32_t>(hostBuf[2]) << 8) |
static_cast<uint32_t>(hostBuf[3]);
const uint32_t magicLE = (static_cast<uint32_t>(hostBuf[3]) << 24) |
(static_cast<uint32_t>(hostBuf[2]) << 16) |
(static_cast<uint32_t>(hostBuf[1]) << 8) |
static_cast<uint32_t>(hostBuf[0]);
if (magic == 0x56414770u || magicLE == 0x56414770u)
{
VagAccumEntry &e = g_vagAccum[ps2Fd];
e.firstBufAddr = bufAddr;
if (bytesRead <= kVagAccumMaxBytes)
e.data.assign(hostBuf, hostBuf + bytesRead);
}
}
}
setReturnS32(ctx, (int32_t)bytesRead);
}
void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int ps2Fd = (int)getRegU32(ctx, 4); // $a0
uint32_t bufAddr = getRegU32(ctx, 5); // $a1
size_t size = getRegU32(ctx, 6); // $a2
const uint8_t *hostBuf = getConstMemPtr(rdram, bufAddr);
if (!hostBuf)
{
setReturnS32(ctx, -1);
return;
}
FILE *fp = getHostFile(ps2Fd);
if (!fp)
{
setReturnS32(ctx, -1); // -EFAULT
return;
}
if (size == 0)
{
setReturnS32(ctx, 0); // Wrote 0 bytes
return;
}
size_t bytesWritten = 0;
{
std::lock_guard<std::mutex> lock(g_sys_fd_mutex);
bytesWritten = ::fwrite(hostBuf, 1, size, fp);
if (bytesWritten < size && ferror(fp))
{
clearerr(fp);
setReturnS32(ctx, -1); // -EIO, -ENOSPC etc.
return;
}
}
// returns number of bytes written
setReturnS32(ctx, (int32_t)bytesWritten);
}
void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int ps2Fd = (int)getRegU32(ctx, 4); // $a0
int32_t offset = getRegU32(ctx, 5); // $a1 (PS2 seems to use 32-bit offset here commonly)
int whence = (int)getRegU32(ctx, 6); // $a2 (PS2 FIO_SEEK constants)
FILE *fp = getHostFile(ps2Fd);
if (!fp)
{
std::cerr << "fioLseek error: Invalid file descriptor " << ps2Fd << std::endl;
setReturnS32(ctx, -1); // -EBADF
return;
}
int hostWhence;
switch (whence)
{
case PS2_FIO_SEEK_SET:
hostWhence = SEEK_SET;
break;
case PS2_FIO_SEEK_CUR:
hostWhence = SEEK_CUR;
break;
case PS2_FIO_SEEK_END:
hostWhence = SEEK_END;
break;
default:
std::cerr << "fioLseek error: Invalid whence value " << whence << " for fd " << ps2Fd << std::endl;
setReturnS32(ctx, -1); // -EINVAL
return;
}
if (::fseek(fp, static_cast<long>(offset), hostWhence) != 0)
{
std::cerr << "fioLseek error: fseek failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl;
setReturnS32(ctx, -1); // Return error code
return;
}
long newPos = ::ftell(fp);
if (newPos < 0)
{
std::cerr << "fioLseek error: ftell failed after fseek for fd " << ps2Fd << ": " << strerror(errno) << std::endl;
setReturnS32(ctx, -1);
}
else
{
if (newPos > 0xFFFFFFFFL)
{
std::cerr << "fioLseek warning: New position exceeds 32-bit for fd " << ps2Fd << std::endl;
setReturnS32(ctx, -1);
}
else
{
setReturnS32(ctx, (int32_t)newPos);
}
}
}
void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
// int mode = (int)getRegU32(ctx, 5); // $a1 - ignored on host
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
if (!ps2Path)
{
std::cerr << "fioMkdir error: Invalid path address" << std::endl;
setReturnS32(ctx, -1); // -EFAULT
return;
}
std::string hostPath = translatePs2Path(ps2Path);
if (hostPath.empty())
{
std::cerr << "fioMkdir error: Failed to translate path '" << ps2Path << "'" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::error_code ec;
bool success = std::filesystem::create_directory(hostPath, ec);
if (!success && ec)
{
std::cerr << "fioMkdir error: create_directory failed for '" << hostPath
<< "': " << ec.message() << std::endl;
setReturnS32(ctx, -1);
}
else
{
RUNTIME_LOG("fioMkdir: Created directory '" << hostPath << "'");
setReturnS32(ctx, 0); // Success
}
}
void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
if (!ps2Path)
{
std::cerr << "fioChdir error: Invalid path address" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::string hostPath = translatePs2Path(ps2Path);
if (hostPath.empty())
{
std::cerr << "fioChdir error: Failed to translate path '" << ps2Path << "'" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::error_code ec;
std::filesystem::current_path(hostPath, ec);
if (ec)
{
std::cerr << "fioChdir error: current_path failed for '" << hostPath
<< "': " << ec.message() << std::endl;
setReturnS32(ctx, -1);
}
else
{
RUNTIME_LOG("fioChdir: Changed directory to '" << hostPath << "'");
setReturnS32(ctx, 0); // Success
}
}
void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
if (!ps2Path)
{
std::cerr << "fioRmdir error: Invalid path address" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::string hostPath = translatePs2Path(ps2Path);
if (hostPath.empty())
{
std::cerr << "fioRmdir error: Failed to translate path '" << ps2Path << "'" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::error_code ec;
bool success = std::filesystem::remove(hostPath, ec);
if (!success || ec)
{
std::cerr << "fioRmdir error: remove failed for '" << hostPath
<< "': " << ec.message() << std::endl;
setReturnS32(ctx, -1);
}
else
{
RUNTIME_LOG("fioRmdir: Removed directory '" << hostPath << "'");
setReturnS32(ctx, 0); // Success
}
}
void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
// we wont implement this for now.
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
uint32_t statBufAddr = getRegU32(ctx, 5); // $a1
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
uint8_t *ps2StatBuf = getMemPtr(rdram, statBufAddr);
if (!ps2Path)
{
std::cerr << "fioGetstat error: Invalid path addr" << std::endl;
setReturnS32(ctx, -1);
return;
}
if (!ps2StatBuf)
{
std::cerr << "fioGetstat error: Invalid buffer addr" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::string hostPath = translatePs2Path(ps2Path);
if (hostPath.empty())
{
std::cerr << "fioGetstat error: Bad path translate" << std::endl;
setReturnS32(ctx, -1);
return;
}
setReturnS32(ctx, -1);
}
void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t pathAddr = getRegU32(ctx, 4); // $a0
const char *ps2Path = reinterpret_cast<const char *>(getConstMemPtr(rdram, pathAddr));
if (!ps2Path)
{
std::cerr << "fioRemove error: Invalid path" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::string hostPath = translatePs2Path(ps2Path);
if (hostPath.empty())
{
std::cerr << "fioRemove error: Path translate fail" << std::endl;
setReturnS32(ctx, -1);
return;
}
std::error_code ec;
bool success = std::filesystem::remove(hostPath, ec);
if (!success || ec)
{
std::cerr << "fioRemove error: remove failed for '" << hostPath
<< "': " << ec.message() << std::endl;
setReturnS32(ctx, -1);
}
else
{
RUNTIME_LOG("fioRemove: Removed file '" << hostPath << "'");
setReturnS32(ctx, 0); // Success
}
}
}
@@ -0,0 +1,17 @@
#pragma once
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -67,11 +67,11 @@ namespace
return;
}
std::cout << "[SIF module] " << op
RUNTIME_LOG("[SIF module] " << op
<< " id=" << moduleId
<< " ref=" << refCount
<< " path=\"" << path << "\""
<< std::endl;
<< std::endl);
++g_sif_module_log_count;
}
@@ -259,8 +259,8 @@ namespace
static uint32_t secFilterLogCount = 0;
if (!loadAll && secFilterLogCount < 8u)
{
std::cout << "[SifLoadElfPart] section filter \"" << sectionName
<< "\" requested; loading PT_LOAD segments only." << std::endl;
RUNTIME_LOG("[SifLoadElfPart] section filter \"" << sectionName
<< "\" requested; loading PT_LOAD segments only." << std::endl);
++secFilterLogCount;
}
@@ -412,8 +412,8 @@ namespace
static uint32_t successLogs = 0;
if (successLogs < 16u)
{
std::cout << "[SifLoadElfPart] loaded \"" << ps2Path << "\" epc=0x"
<< std::hex << execData.epc << " gp=0x" << execData.gp << std::dec << std::endl;
RUNTIME_LOG("[SifLoadElfPart] loaded \"" << ps2Path << "\" epc=0x"
<< std::hex << execData.epc << " gp=0x" << execData.gp << std::dec << std::endl);
++successLogs;
}
@@ -17,7 +17,7 @@ static void throwIfTerminated(const std::shared_ptr<ThreadInfo> &info)
}
}
static void waitWhileSuspended(const std::shared_ptr<ThreadInfo> &info)
static void waitWhileSuspended(const std::shared_ptr<ThreadInfo> &info, PS2Runtime *runtime = nullptr)
{
if (!info)
return;
@@ -28,8 +28,11 @@ static void waitWhileSuspended(const std::shared_ptr<ThreadInfo> &info)
info->status = THS_SUSPEND;
info->waitType = TSW_NONE;
info->waitId = 0;
info->cv.wait(lock, [&]()
{ return info->suspendCount == 0 || info->terminated.load(); });
{
PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime);
info->cv.wait(lock, [&]()
{ return info->suspendCount == 0 || info->terminated.load(); });
}
if (info->terminated.load())
{
throw ThreadExitException();
@@ -101,9 +104,9 @@ static std::shared_ptr<EventFlagInfo> lookupEventFlagInfo(int eid)
static void setRegU32(R5900Context *ctx, int reg, uint32_t value)
{
if (reg < 0 || reg > 31)
if (!ctx || reg < 0 || reg > 31)
return;
ctx->r[reg] = _mm_set_epi32(0, 0, 0, value);
SET_GPR_U32(ctx, reg, value);
}
static std::chrono::microseconds alarmTicksToDuration(uint16_t ticks)
@@ -165,9 +168,19 @@ static void ensureAlarmWorkerRunning()
try
{
constexpr uint32_t kAlarmCallbackStackSize = 0x4000u;
thread_local PS2Runtime *s_alarmStackRuntime = nullptr;
thread_local uint32_t s_alarmStackTop = 0u;
if (s_alarmStackRuntime != readyAlarm->runtime || s_alarmStackTop == 0u)
{
s_alarmStackRuntime = readyAlarm->runtime;
s_alarmStackTop = readyAlarm->runtime->reserveAsyncCallbackStack(kAlarmCallbackStackSize, 16u);
}
R5900Context callbackCtx{};
setRegU32(&callbackCtx, 28, readyAlarm->gp);
setRegU32(&callbackCtx, 29, readyAlarm->sp);
setRegU32(&callbackCtx, 29,
(s_alarmStackTop != 0u) ? s_alarmStackTop : (PS2_RAM_SIZE - 0x10u));
setRegU32(&callbackCtx, 31, 0);
setRegU32(&callbackCtx, 4, static_cast<uint32_t>(readyAlarm->id));
setRegU32(&callbackCtx, 5, static_cast<uint32_t>(readyAlarm->ticks));
@@ -268,6 +281,34 @@ static bool readStackU32(uint8_t *rdram, uint32_t sp, uint32_t offset, uint32_t
return true;
}
enum class RpcInvokeExitReason
{
Returned,
NullPc,
MissingFunction,
StepLimit,
SamePcLimit
};
static const char *rpcInvokeExitReasonName(RpcInvokeExitReason reason)
{
switch (reason)
{
case RpcInvokeExitReason::Returned:
return "returned";
case RpcInvokeExitReason::NullPc:
return "null-pc";
case RpcInvokeExitReason::MissingFunction:
return "missing-function";
case RpcInvokeExitReason::StepLimit:
return "step-limit";
case RpcInvokeExitReason::SamePcLimit:
return "same-pc-limit";
default:
return "unknown";
}
}
static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime,
uint32_t funcAddr, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t *outV0)
{
@@ -307,6 +348,7 @@ static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *run
uint32_t steps = 0u;
uint32_t lastPc = 0xFFFFFFFFu;
uint32_t samePcCount = 0u;
RpcInvokeExitReason exitReason = RpcInvokeExitReason::MissingFunction;
while (tmp.pc != 0u &&
tmp.pc != kRpcInvokeReturnSentinel &&
runtime->hasFunction(tmp.pc) &&
@@ -318,6 +360,7 @@ static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *run
++samePcCount;
if (samePcCount > 0x2000u)
{
exitReason = RpcInvokeExitReason::SamePcLimit;
break;
}
}
@@ -328,7 +371,10 @@ static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *run
}
PS2Runtime::RecompiledFunction func = runtime->lookupFunction(pc);
func(rdram, &tmp, runtime);
{
PS2Runtime::GuestExecutionScope guestExecution(runtime);
func(rdram, &tmp, runtime);
}
++steps;
}
@@ -336,7 +382,41 @@ static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *run
{
*outV0 = getRegU32(&tmp, 2);
}
return true;
if (tmp.pc == kRpcInvokeReturnSentinel)
{
return true;
}
if (tmp.pc == 0u)
{
exitReason = RpcInvokeExitReason::NullPc;
}
else if (steps >= kRpcInvokeMaxSteps)
{
exitReason = RpcInvokeExitReason::StepLimit;
}
else if (!runtime->hasFunction(tmp.pc))
{
exitReason = RpcInvokeExitReason::MissingFunction;
}
static std::atomic<uint32_t> s_rpcInvokeFailureLogs{0u};
constexpr uint32_t kMaxRpcInvokeFailureLogs = 64u;
const uint32_t logIndex = s_rpcInvokeFailureLogs.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < kMaxRpcInvokeFailureLogs)
{
std::cerr << "[SyscallOverride:invoke-failed]"
<< " func=0x" << std::hex << funcAddr
<< " exitPc=0x" << tmp.pc
<< " ra=0x" << getRegU32(&tmp, 31)
<< std::dec
<< " steps=" << steps
<< " reason=" << rpcInvokeExitReasonName(exitReason)
<< std::endl;
}
return false;
}
static uint32_t rpcAllocPacketAddr(uint8_t *rdram)
@@ -383,7 +463,7 @@ static int g_intc_tail_order = 1000;
static int g_dmac_head_order = 0;
static int g_dmac_tail_order = 1000;
std::string translatePs2Path(const char *ps2Path)
inline std::string translatePs2Path(const char *ps2Path)
{
if (!ps2Path || !*ps2Path)
{
@@ -638,3 +718,4 @@ static void ensureBootModeTable(uint8_t *rdram)
g_bootmode_initialized = true;
}
@@ -1,5 +1,5 @@
std::unordered_map<int, FILE *> g_fileDescriptors;
int g_nextFd = 3; // Start after stdin, stdout, stderr
inline std::unordered_map<int, FILE *> g_fileDescriptors;
inline int g_nextFd = 3; // Start after stdin, stdout, stderr
struct ThreadInfo
{
@@ -197,26 +197,26 @@ static constexpr uint32_t kFioSoIROth = 0x0004;
static constexpr uint32_t kFioSoIWOth = 0x0002;
static constexpr uint32_t kFioSoIXOth = 0x0001;
static std::unordered_map<int, std::shared_ptr<ThreadInfo>> g_threads;
static int g_nextThreadId = 2; // Reserve 1 for the main thread
static thread_local int g_currentThreadId = 1;
static std::mutex g_thread_map_mutex;
static std::unordered_map<int, std::thread> g_hostThreads;
static std::mutex g_host_thread_mutex;
inline std::unordered_map<int, std::shared_ptr<ThreadInfo>> g_threads;
inline int g_nextThreadId = 2; // Reserve 1 for the main thread
inline thread_local int g_currentThreadId = 1;
inline std::mutex g_thread_map_mutex;
inline std::unordered_map<int, std::thread> g_hostThreads;
inline std::mutex g_host_thread_mutex;
static std::unordered_map<int, std::shared_ptr<SemaInfo>> g_semas;
static int g_nextSemaId = 1;
static std::mutex g_sema_map_mutex;
static std::unordered_map<int, std::shared_ptr<EventFlagInfo>> g_eventFlags;
static int g_nextEventFlagId = 1;
static std::mutex g_event_flag_map_mutex;
static std::unordered_map<int, std::shared_ptr<AlarmInfo>> g_alarms;
static int g_nextAlarmId = 1;
static std::mutex g_alarm_mutex;
static std::condition_variable g_alarm_cv;
static std::once_flag g_alarm_worker_once;
std::atomic<int> g_activeThreads{0};
static std::mutex g_fd_mutex;
inline std::unordered_map<int, std::shared_ptr<SemaInfo>> g_semas;
inline int g_nextSemaId = 1;
inline std::mutex g_sema_map_mutex;
inline std::unordered_map<int, std::shared_ptr<EventFlagInfo>> g_eventFlags;
inline int g_nextEventFlagId = 1;
inline std::mutex g_event_flag_map_mutex;
inline std::unordered_map<int, std::shared_ptr<AlarmInfo>> g_alarms;
inline int g_nextAlarmId = 1;
inline std::mutex g_alarm_mutex;
inline std::condition_variable g_alarm_cv;
inline std::once_flag g_alarm_worker_once;
inline std::atomic<int> g_activeThreads{0};
inline std::mutex g_fd_mutex;
static void registerHostThread(int tid, std::thread worker)
{
@@ -304,6 +304,29 @@ static void joinAllHostThreads()
}
}
static void detachAllHostThreads()
{
std::vector<std::thread> workers;
{
std::lock_guard<std::mutex> lock(g_host_thread_mutex);
workers.reserve(g_hostThreads.size());
for (auto &entry : g_hostThreads)
{
workers.push_back(std::move(entry.second));
}
g_hostThreads.clear();
}
for (auto &worker : workers)
{
if (!worker.joinable())
{
continue;
}
worker.detach();
}
}
struct RpcServerState
{
uint32_t sid = 0;
@@ -317,23 +340,71 @@ struct RpcClientState
uint32_t sid = 0;
};
static std::unordered_map<uint32_t, RpcServerState> g_rpc_servers;
static std::unordered_map<uint32_t, RpcClientState> g_rpc_clients;
static std::mutex g_rpc_mutex;
static std::recursive_mutex g_sif_call_rpc_mutex;
static bool g_rpc_initialized = false;
static uint32_t g_rpc_next_id = 1;
static uint32_t g_rpc_packet_index = 0;
static uint32_t g_rpc_server_index = 0;
static uint32_t g_rpc_active_queue = 0;
static constexpr uint32_t kDtxRpcSid = 0x7D000000u;
static constexpr uint32_t kDtxUrpcObjBase = 0x01F18000u;
static constexpr uint32_t kDtxUrpcObjLimit = 0x01F1FF00u;
static constexpr uint32_t kDtxUrpcFnTableBase = 0x0034FED0u;
static constexpr uint32_t kDtxUrpcObjTableBase = 0x0034FFD0u;
static std::mutex g_dtx_rpc_mutex;
static std::unordered_map<uint32_t, uint32_t> g_dtx_remote_by_id;
static uint32_t g_dtx_next_urpc_obj = kDtxUrpcObjBase;
struct SoundDriverRpcState
{
uintptr_t ownerRuntime = 0;
bool initialized = false;
uint32_t storageBaseAddr = 0;
uint32_t storageSize = 0;
uint32_t statusAddr = 0;
uint32_t addrTableAddr = 0;
uint32_t hdBaseAddr = 0;
uint32_t sqBaseAddr = 0;
uint32_t dataBaseAddr = 0;
};
inline std::unordered_map<uint32_t, RpcServerState> g_rpc_servers;
inline std::unordered_map<uint32_t, RpcClientState> g_rpc_clients;
inline std::mutex g_rpc_mutex;
inline std::recursive_mutex g_sif_call_rpc_mutex;
inline bool g_rpc_initialized = false;
inline uint32_t g_rpc_next_id = 1;
inline uint32_t g_rpc_packet_index = 0;
inline uint32_t g_rpc_server_index = 0;
inline uint32_t g_rpc_active_queue = 0;
inline SoundDriverRpcState g_soundDriverRpcState;
inline PS2SoundDriverCompatLayout g_soundDriverCompatLayout;
inline PS2DtxCompatLayout g_dtxCompatLayout;
inline std::mutex g_dtx_rpc_mutex;
inline std::unordered_map<uint32_t, uint32_t> g_dtx_remote_by_id;
inline uint32_t g_dtx_next_urpc_obj = 0u;
struct DtxTransferState
{
uint32_t dtxId = 0;
uint32_t remoteHandle = 0;
uint32_t eeWorkAddr = 0;
uint32_t iopWorkAddr = 0;
uint32_t wkSize = 0;
};
inline std::unordered_map<uint32_t, DtxTransferState> g_dtx_transfer_by_id;
struct DtxSjxState
{
uint32_t handle = 0;
uint32_t srcSjHandle = 0;
uint32_t dstSjHandle = 0;
uint32_t line = 0;
uint32_t eeObjAddr = 0;
uint16_t xid = 0;
};
inline std::unordered_map<uint32_t, DtxSjxState> g_dtx_sjx_by_handle;
struct DtxPs2RnaState
{
uint32_t handle = 0;
uint32_t maxChannels = 0;
uint32_t sjHandle0 = 0;
uint32_t sjHandle1 = 0;
uint32_t channelCount = 0;
uint32_t sampleFreq = 0;
uint32_t volume = 0;
bool playEnabled = false;
};
inline std::unordered_map<uint32_t, DtxPs2RnaState> g_dtx_ps2rna_by_handle;
struct DtxSjrmtState
{
@@ -351,7 +422,7 @@ struct DtxSjrmtState
uint32_t uuid3 = 0;
};
static std::unordered_map<uint32_t, DtxSjrmtState> g_dtx_sjrmt_by_handle;
inline std::unordered_map<uint32_t, DtxSjrmtState> g_dtx_sjrmt_by_handle;
static uint32_t dtxNormalizeSjrmtCapacity(uint32_t requestedBytes)
{
@@ -364,16 +435,27 @@ static uint32_t dtxNormalizeSjrmtCapacity(uint32_t requestedBytes)
static uint32_t dtxAllocUrpcHandleLocked()
{
const PS2DtxCompatLayout &layout = g_dtxCompatLayout;
if (!layout.hasUrpcObjectRange())
{
return 0u;
}
if (g_dtx_next_urpc_obj < layout.urpcObjBase || g_dtx_next_urpc_obj >= layout.urpcObjLimit)
{
g_dtx_next_urpc_obj = layout.urpcObjBase;
}
for (uint32_t i = 0; i < 4096u; ++i)
{
uint32_t candidate = g_dtx_next_urpc_obj;
g_dtx_next_urpc_obj += 0x20u;
if (g_dtx_next_urpc_obj < kDtxUrpcObjBase || g_dtx_next_urpc_obj >= kDtxUrpcObjLimit)
g_dtx_next_urpc_obj += layout.urpcObjStride;
if (g_dtx_next_urpc_obj < layout.urpcObjBase || g_dtx_next_urpc_obj >= layout.urpcObjLimit)
{
g_dtx_next_urpc_obj = kDtxUrpcObjBase;
g_dtx_next_urpc_obj = layout.urpcObjBase;
}
if (candidate < kDtxUrpcObjBase || candidate >= kDtxUrpcObjLimit)
if (candidate < layout.urpcObjBase || candidate >= layout.urpcObjLimit)
{
continue;
}
@@ -383,6 +465,16 @@ static uint32_t dtxAllocUrpcHandleLocked()
continue;
}
if (g_dtx_sjx_by_handle.find(candidate) != g_dtx_sjx_by_handle.end())
{
continue;
}
if (g_dtx_ps2rna_by_handle.find(candidate) != g_dtx_ps2rna_by_handle.end())
{
continue;
}
bool inUseByDtxRemote = false;
for (const auto &entry : g_dtx_remote_by_id)
{
@@ -399,7 +491,7 @@ static uint32_t dtxAllocUrpcHandleLocked()
}
}
return kDtxUrpcObjBase;
return layout.urpcObjBase;
}
struct ExitHandlerEntry
@@ -408,28 +500,37 @@ struct ExitHandlerEntry
uint32_t arg = 0;
};
static std::mutex g_exit_handler_mutex;
static std::unordered_map<int, std::vector<ExitHandlerEntry>> g_exit_handlers;
inline std::mutex g_exit_handler_mutex;
inline std::unordered_map<int, std::vector<ExitHandlerEntry>> g_exit_handlers;
static std::mutex g_bootmode_mutex;
static bool g_bootmode_initialized = false;
static uint32_t g_bootmode_pool_offset = 0;
static std::unordered_map<uint8_t, uint32_t> g_bootmode_addresses;
inline std::mutex g_bootmode_mutex;
inline bool g_bootmode_initialized = false;
inline uint32_t g_bootmode_pool_offset = 0;
inline std::unordered_map<uint8_t, uint32_t> g_bootmode_addresses;
static std::mutex g_tls_mutex;
static uint32_t g_tls_index = 0;
inline std::mutex g_syscall_override_mutex;
inline std::unordered_map<uint32_t, uint32_t> g_syscall_overrides;
inline std::unordered_set<uint32_t> g_syscall_mirror_addrs;
static std::mutex g_osd_mutex;
static bool g_osd_config_initialized = false;
static uint32_t g_osd_config_raw = 0;
static constexpr uint32_t kGuestSyscallTableGuestBase = 0x80011F80u;
static constexpr uint32_t kGuestSyscallTablePhysBase = kGuestSyscallTableGuestBase & 0x1FFFFFFFu;
static constexpr uint32_t kGuestSyscallMirrorLimit = 0x00080000u;
static constexpr uint32_t kGuestSyscallTableProbeBase = 0x000002F0u;
static std::mutex g_ps2_path_mutex;
static bool g_ps2_paths_initialized = false;
static std::filesystem::path g_host_base;
static std::filesystem::path g_cdrom_base;
static std::filesystem::path g_host_cwd;
static std::filesystem::path g_cdrom_cwd;
static std::string g_ps2_cwd_device = "host0";
inline std::mutex g_tls_mutex;
inline uint32_t g_tls_index = 0;
inline std::mutex g_osd_mutex;
inline bool g_osd_config_initialized = false;
inline uint32_t g_osd_config_raw = 0;
inline std::mutex g_ps2_path_mutex;
inline bool g_ps2_paths_initialized = false;
inline std::filesystem::path g_host_base;
inline std::filesystem::path g_cdrom_base;
inline std::filesystem::path g_host_cwd;
inline std::filesystem::path g_cdrom_cwd;
inline std::string g_ps2_cwd_device = "host0";
static constexpr uint32_t kRpcPacketSize = 64;
static constexpr uint32_t kRpcPacketPoolBase = 0x01F00000;
@@ -536,8 +637,9 @@ struct SifModuleRecord
bool loaded = false;
};
static std::mutex g_sif_module_mutex;
static std::unordered_map<int32_t, SifModuleRecord> g_sif_modules_by_id;
static std::unordered_map<std::string, int32_t> g_sif_module_id_by_path;
static int32_t g_next_sif_module_id = 1;
static uint32_t g_sif_module_log_count = 0;
inline std::mutex g_sif_module_mutex;
inline std::unordered_map<int32_t, SifModuleRecord> g_sif_modules_by_id;
inline std::unordered_map<std::string, int32_t> g_sif_module_id_by_path;
inline int32_t g_next_sif_module_id = 1;
inline uint32_t g_sif_module_log_count = 0;
@@ -0,0 +1,675 @@
#include "Common.h"
#include "Interrupt.h"
#include "ps2_log.h"
#include "Stubs/GS.h"
namespace ps2_syscalls
{
namespace interrupt_state
{
constexpr uint32_t kIntcVblankStart = 2u;
constexpr uint32_t kIntcVblankEnd = 3u;
constexpr auto kVblankPeriod = std::chrono::microseconds(16667);
constexpr int kMaxCatchupTicks = 4;
std::mutex g_irq_handler_mutex;
std::mutex g_irq_worker_mutex;
std::condition_variable g_irq_worker_cv;
std::mutex g_vsync_flag_mutex;
std::condition_variable g_vsync_cv;
std::atomic<bool> g_irq_worker_stop{false};
std::atomic<bool> g_irq_worker_running{false};
uint32_t g_enabled_intc_mask = 0xFFFFFFFFu;
uint32_t g_enabled_dmac_mask = 0xFFFFFFFFu;
uint64_t g_vsync_tick_counter = 0u;
VSyncFlagRegistration g_vsync_registration{};
}
using namespace interrupt_state;
static void writeGuestU32NoThrow(uint8_t *rdram, uint32_t addr, uint32_t value)
{
if (addr == 0u)
{
return;
}
uint8_t *dst = getMemPtr(rdram, addr);
if (!dst)
{
return;
}
std::memcpy(dst, &value, sizeof(value));
}
static void writeGuestU64NoThrow(uint8_t *rdram, uint32_t addr, uint64_t value)
{
if (addr == 0u)
{
return;
}
uint8_t *dst = getMemPtr(rdram, addr);
if (!dst)
{
return;
}
std::memcpy(dst, &value, sizeof(value));
}
static uint32_t readGuestU32NoThrow(uint8_t *rdram, uint32_t addr)
{
if (addr == 0u)
{
return 0u;
}
uint8_t *src = getMemPtr(rdram, addr);
if (!src)
{
return 0u;
}
uint32_t value = 0u;
std::memcpy(&value, src, sizeof(value));
return value;
}
static uint32_t getAsyncHandlerStackTop(PS2Runtime *runtime)
{
constexpr uint32_t kAsyncHandlerStackSize = 0x4000u;
thread_local PS2Runtime *s_cachedRuntime = nullptr;
thread_local uint32_t s_cachedStackTop = 0u;
if (runtime == nullptr)
{
return PS2_RAM_SIZE - 0x10u;
}
if (s_cachedRuntime != runtime || s_cachedStackTop == 0u)
{
s_cachedRuntime = runtime;
s_cachedStackTop = runtime->reserveAsyncCallbackStack(kAsyncHandlerStackSize, 16u);
}
return (s_cachedStackTop != 0u) ? s_cachedStackTop : (PS2_RAM_SIZE - 0x10u);
}
static void dispatchIntcHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause)
{
if (!rdram || !runtime)
{
return;
}
std::vector<IrqHandlerInfo> handlers;
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
if (cause < 32u && (g_enabled_intc_mask & (1u << cause)) == 0u)
{
return;
}
handlers.reserve(g_intcHandlers.size());
for (const auto &[id, info] : g_intcHandlers)
{
(void)id;
if (!info.enabled)
{
continue;
}
if (info.cause != cause)
{
continue;
}
if (info.handler == 0u)
{
continue;
}
handlers.push_back(info);
}
std::sort(handlers.begin(), handlers.end(), [](const IrqHandlerInfo &a, const IrqHandlerInfo &b)
{ return a.order < b.order; });
}
for (const IrqHandlerInfo &info : handlers)
{
if (!runtime->hasFunction(info.handler))
{
if (cause == kIntcVblankStart)
{
PS2_IF_AGRESSIVE_LOGS({
static std::atomic<uint32_t> s_missingHandlerLogCount{0u};
const uint32_t logIndex = s_missingHandlerLogCount.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < 32u)
{
auto flags = std::cout.flags();
std::cout << "[INTC:missing] cause=" << cause
<< " handler=0x" << std::hex << info.handler
<< std::dec
<< " id=" << info.id
<< std::endl;
std::cout.flags(flags);
}
});
}
continue;
}
try
{
R5900Context irqCtx{};
SET_GPR_U32(&irqCtx, 28, info.gp);
SET_GPR_U32(&irqCtx, 29, getAsyncHandlerStackTop(runtime));
SET_GPR_U32(&irqCtx, 31, 0u);
SET_GPR_U32(&irqCtx, 4, cause);
SET_GPR_U32(&irqCtx, 5, info.arg);
SET_GPR_U32(&irqCtx, 6, 0u);
SET_GPR_U32(&irqCtx, 7, 0u);
irqCtx.pc = info.handler;
while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested())
{
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc);
if (!step)
{
break;
}
// Interrupt handlers must be able to preempt a guest thread that is
// spinning on interrupt-produced state, such as a vblank counter.
step(rdram, &irqCtx, runtime);
}
}
catch (const ThreadExitException &)
{
}
catch (const std::exception &e)
{
static uint32_t warnCount = 0;
if (warnCount < 8u)
{
std::cerr << "[INTC] handler 0x" << std::hex << info.handler
<< " threw exception: " << e.what() << std::dec << std::endl;
++warnCount;
}
}
}
}
void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause)
{
if (!rdram || !runtime)
{
return;
}
std::vector<IrqHandlerInfo> handlers;
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
if (cause < 32u && (g_enabled_dmac_mask & (1u << cause)) == 0u)
{
return;
}
handlers.reserve(g_dmacHandlers.size());
for (const auto &[id, info] : g_dmacHandlers)
{
(void)id;
if (!info.enabled)
{
continue;
}
if (info.cause != cause)
{
continue;
}
if (info.handler == 0u)
{
continue;
}
handlers.push_back(info);
}
std::sort(handlers.begin(), handlers.end(), [](const IrqHandlerInfo &a, const IrqHandlerInfo &b)
{ return a.order < b.order; });
}
for (const IrqHandlerInfo &info : handlers)
{
if (!runtime->hasFunction(info.handler))
{
continue;
}
try
{
R5900Context irqCtx{};
SET_GPR_U32(&irqCtx, 28, info.gp);
SET_GPR_U32(&irqCtx, 29, getAsyncHandlerStackTop(runtime));
SET_GPR_U32(&irqCtx, 31, 0u);
SET_GPR_U32(&irqCtx, 4, cause);
SET_GPR_U32(&irqCtx, 5, info.arg);
SET_GPR_U32(&irqCtx, 6, 0u);
SET_GPR_U32(&irqCtx, 7, 0u);
irqCtx.pc = info.handler;
while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested())
{
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc);
if (!step)
{
break;
}
step(rdram, &irqCtx, runtime);
}
}
catch (const ThreadExitException &)
{
}
catch (const std::exception &e)
{
static uint32_t warnCount = 0;
if (warnCount < 8u)
{
std::cerr << "[DMAC] handler 0x" << std::hex << info.handler
<< " threw exception: " << e.what() << std::dec << std::endl;
++warnCount;
}
}
}
}
static uint64_t signalVSyncFlag(uint8_t *rdram)
{
VSyncFlagRegistration reg{};
uint64_t tickValue = 0u;
{
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
reg = g_vsync_registration;
tickValue = ++g_vsync_tick_counter;
}
g_vsync_cv.notify_all();
if (reg.flagAddr != 0u)
{
writeGuestU32NoThrow(rdram, reg.flagAddr, 1u);
}
if (reg.tickAddr != 0u)
{
writeGuestU64NoThrow(rdram, reg.tickAddr, tickValue);
}
return tickValue;
}
static void interruptWorkerMain(uint8_t *rdram, PS2Runtime *runtime)
{
g_currentThreadId = -1;
using clock = std::chrono::steady_clock;
auto nextTick = clock::now() + kVblankPeriod;
while (runtime != nullptr && !runtime->isStopRequested())
{
{
std::unique_lock<std::mutex> lock(g_irq_worker_mutex);
if (g_irq_worker_cv.wait_until(lock, nextTick, []()
{ return g_irq_worker_stop.load(std::memory_order_acquire); }))
{
break;
}
}
const auto now = clock::now();
int ticksToProcess = 0;
while (now >= nextTick && ticksToProcess < kMaxCatchupTicks)
{
++ticksToProcess;
nextTick += kVblankPeriod;
}
if (ticksToProcess == 0)
{
continue;
}
for (int i = 0; i < ticksToProcess; ++i)
{
const uint64_t tickValue = signalVSyncFlag(rdram);
ps2_stubs::dispatchGsSyncVCallback(rdram, runtime, tickValue);
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankStart);
std::this_thread::sleep_for(std::chrono::microseconds(500));
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankEnd);
}
}
g_irq_worker_running.store(false, std::memory_order_release);
g_irq_worker_cv.notify_all();
}
static void ensureInterruptWorkerRunning(uint8_t *rdram, PS2Runtime *runtime)
{
if (!rdram || !runtime)
{
return;
}
std::lock_guard<std::mutex> lock(g_irq_worker_mutex);
if (g_irq_worker_running.load(std::memory_order_acquire))
{
return;
}
g_irq_worker_stop.store(false, std::memory_order_release);
g_irq_worker_running.store(true, std::memory_order_release);
try
{
std::thread(interruptWorkerMain, rdram, runtime).detach();
}
catch (...)
{
g_irq_worker_running.store(false, std::memory_order_release);
}
}
void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime)
{
ensureInterruptWorkerRunning(rdram, runtime);
}
uint64_t GetCurrentVSyncTick()
{
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
return g_vsync_tick_counter;
}
void stopInterruptWorker()
{
g_irq_worker_stop.store(true, std::memory_order_release);
g_irq_worker_cv.notify_all();
std::unique_lock<std::mutex> lock(g_irq_worker_mutex);
g_irq_worker_cv.wait_for(lock, std::chrono::milliseconds(500), []()
{ return !g_irq_worker_running.load(std::memory_order_acquire); });
g_vsync_cv.notify_all();
}
uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime)
{
ensureInterruptWorkerRunning(rdram, runtime);
std::unique_lock<std::mutex> lock(g_vsync_flag_mutex);
uint64_t current = g_vsync_tick_counter;
{
PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime);
g_vsync_cv.wait(lock, [current, runtime]()
{ return g_vsync_tick_counter > current || (runtime != nullptr && runtime->isStopRequested()); });
}
return g_vsync_tick_counter;
}
void WaitVSyncTick(uint8_t *rdram, PS2Runtime *runtime)
{
(void)WaitForNextVSyncTick(rdram, runtime);
}
void SetVSyncFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t flagAddr = getRegU32(ctx, 4);
const uint32_t tickAddr = getRegU32(ctx, 5);
{
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
g_vsync_registration.flagAddr = flagAddr;
g_vsync_registration.tickAddr = tickAddr;
}
writeGuestU32NoThrow(rdram, flagAddr, 0u);
writeGuestU64NoThrow(rdram, tickAddr, 0u);
ensureInterruptWorkerRunning(rdram, runtime);
setReturnS32(ctx, KE_OK);
}
void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cause = getRegU32(ctx, 4);
if (cause < 32u)
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
g_enabled_intc_mask |= (1u << cause);
}
if (cause == kIntcVblankStart || cause == kIntcVblankEnd)
{
PS2_IF_AGRESSIVE_LOGS({
static std::atomic<uint32_t> s_enableLogCount{0u};
const uint32_t logIndex = s_enableLogCount.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < 32u)
{
RUNTIME_LOG("[EnableIntc] cause=" << cause);
}
});
}
setReturnS32(ctx, KE_OK);
}
void iEnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
EnableIntc(rdram, ctx, runtime);
}
void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cause = getRegU32(ctx, 4);
if (cause < 32u)
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
g_enabled_intc_mask &= ~(1u << cause);
}
if (cause == kIntcVblankStart || cause == kIntcVblankEnd)
{
PS2_IF_AGRESSIVE_LOGS({
static std::atomic<uint32_t> s_disableLogCount{0u};
const uint32_t logIndex = s_disableLogCount.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < 32u)
{
RUNTIME_LOG("[DisableIntc] cause=" << cause);
}
});
}
setReturnS32(ctx, KE_OK);
}
void iDisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
DisableIntc(rdram, ctx, runtime);
}
void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
IrqHandlerInfo info{};
info.cause = getRegU32(ctx, 4);
info.handler = getRegU32(ctx, 5);
uint32_t next = getRegU32(ctx, 6);
info.arg = getRegU32(ctx, 7);
info.gp = getRegU32(ctx, 28);
info.sp = getRegU32(ctx, 29);
info.enabled = true;
int handlerId = 0;
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
info.order = (next == 0) ? --g_intc_head_order : ++g_intc_tail_order;
handlerId = g_nextIntcHandlerId++;
info.id = handlerId;
g_intcHandlers[handlerId] = info;
}
if (info.cause == kIntcVblankStart)
{
PS2_IF_AGRESSIVE_LOGS({
static std::atomic<uint32_t> s_addHandlerLogCount{0u};
const uint32_t logIndex = s_addHandlerLogCount.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < 32u)
{
auto flags = std::cout.flags();
std::cout << "[AddIntcHandler] cause=" << info.cause
<< " handler=0x" << std::hex << info.handler
<< " arg=0x" << info.arg
<< " gp=0x" << info.gp
<< " sp=0x" << info.sp
<< std::dec
<< " id=" << handlerId
<< std::endl;
std::cout.flags(flags);
}
});
}
ensureInterruptWorkerRunning(rdram, runtime);
setReturnS32(ctx, handlerId);
}
void AddIntcHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
AddIntcHandler(rdram, ctx, runtime);
}
void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cause = getRegU32(ctx, 4);
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
if (handlerId > 0)
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
auto it = g_intcHandlers.find(handlerId);
if (it != g_intcHandlers.end() && it->second.cause == cause)
{
g_intcHandlers.erase(it);
}
}
setReturnS32(ctx, KE_OK);
}
void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
IrqHandlerInfo info{};
info.cause = getRegU32(ctx, 4);
info.handler = getRegU32(ctx, 5);
uint32_t next = getRegU32(ctx, 6);
info.arg = getRegU32(ctx, 7);
info.gp = getRegU32(ctx, 28);
info.sp = getRegU32(ctx, 29);
info.enabled = true;
int handlerId = 0;
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
info.order = (next == 0) ? --g_dmac_head_order : ++g_dmac_tail_order;
handlerId = g_nextDmacHandlerId++;
info.id = handlerId;
g_dmacHandlers[handlerId] = info;
}
setReturnS32(ctx, handlerId);
}
void AddDmacHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
AddDmacHandler(rdram, ctx, runtime);
}
void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cause = getRegU32(ctx, 4);
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
if (handlerId > 0)
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
auto it = g_dmacHandlers.find(handlerId);
if (it != g_dmacHandlers.end() && it->second.cause == cause)
{
g_dmacHandlers.erase(it);
}
}
setReturnS32(ctx, KE_OK);
}
void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end())
{
it->second.enabled = true;
}
}
setReturnS32(ctx, KE_OK);
}
void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end())
{
it->second.enabled = false;
}
}
setReturnS32(ctx, KE_OK);
}
void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end())
{
it->second.enabled = true;
}
}
setReturnS32(ctx, KE_OK);
}
void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end())
{
it->second.enabled = false;
}
}
setReturnS32(ctx, KE_OK);
}
void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cause = getRegU32(ctx, 4);
if (cause < 32u)
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
g_enabled_dmac_mask |= (1u << cause);
}
setReturnS32(ctx, KE_OK);
}
void iEnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
EnableDmac(rdram, ctx, runtime);
}
void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t cause = getRegU32(ctx, 4);
if (cause < 32u)
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
g_enabled_dmac_mask &= ~(1u << cause);
}
setReturnS32(ctx, KE_OK);
}
void iDisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
DisableDmac(rdram, ctx, runtime);
}
}
@@ -0,0 +1,54 @@
#pragma once
#include <condition_variable>
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
namespace interrupt_state
{
struct VSyncFlagRegistration
{
uint32_t flagAddr;
uint32_t tickAddr;
};
extern std::mutex g_irq_handler_mutex;
extern std::mutex g_irq_worker_mutex;
extern std::condition_variable g_irq_worker_cv;
extern std::mutex g_vsync_flag_mutex;
extern std::condition_variable g_vsync_cv;
extern std::atomic<bool> g_irq_worker_stop;
extern std::atomic<bool> g_irq_worker_running;
extern uint32_t g_enabled_intc_mask;
extern uint32_t g_enabled_dmac_mask;
extern uint64_t g_vsync_tick_counter;
extern VSyncFlagRegistration g_vsync_registration;
}
void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause);
void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime);
uint64_t GetCurrentVSyncTick();
void stopInterruptWorker();
uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime);
void WaitVSyncTick(uint8_t *rdram, PS2Runtime *runtime);
void SetVSyncFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iEnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iDisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void AddIntcHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void AddDmacHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iEnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iDisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,116 @@
#include "Common.h"
#include "Interrupt.h"
#include "Lifecycle.h"
namespace ps2_syscalls
{
using namespace interrupt_state;
void notifyRuntimeStop()
{
stopInterruptWorker();
{
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
g_intcHandlers.clear();
g_dmacHandlers.clear();
g_nextIntcHandlerId = 1;
g_nextDmacHandlerId = 1;
g_enabled_intc_mask = 0xFFFFFFFFu;
g_enabled_dmac_mask = 0xFFFFFFFFu;
}
{
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
g_vsync_registration = {};
g_vsync_tick_counter = 0u;
}
std::vector<std::shared_ptr<ThreadInfo>> threads;
threads.reserve(32);
{
std::lock_guard<std::mutex> lock(g_thread_map_mutex);
for (const auto &entry : g_threads)
{
if (entry.second)
{
threads.push_back(entry.second);
}
}
g_threads.clear();
g_nextThreadId = 2; // Reserve id 1 for main thread.
}
g_currentThreadId = 1;
for (const auto &threadInfo : threads)
{
{
std::lock_guard<std::mutex> lock(threadInfo->m);
threadInfo->forceRelease = true;
threadInfo->terminated = true;
}
threadInfo->cv.notify_all();
}
std::vector<std::shared_ptr<SemaInfo>> semas;
{
std::lock_guard<std::mutex> lock(g_sema_map_mutex);
semas.reserve(g_semas.size());
for (const auto &entry : g_semas)
{
if (entry.second)
{
semas.push_back(entry.second);
}
}
g_semas.clear();
g_nextSemaId = 1;
}
for (const auto &sema : semas)
{
sema->cv.notify_all();
}
std::vector<std::shared_ptr<EventFlagInfo>> eventFlags;
{
std::lock_guard<std::mutex> lock(g_event_flag_map_mutex);
eventFlags.reserve(g_eventFlags.size());
for (const auto &entry : g_eventFlags)
{
if (entry.second)
{
eventFlags.push_back(entry.second);
}
}
g_eventFlags.clear();
g_nextEventFlagId = 1;
}
for (const auto &eventFlag : eventFlags)
{
eventFlag->cv.notify_all();
}
{
std::lock_guard<std::mutex> lock(g_alarm_mutex);
g_alarms.clear();
}
g_alarm_cv.notify_all();
{
std::lock_guard<std::mutex> lock(g_exit_handler_mutex);
g_exit_handlers.clear();
}
{
std::lock_guard<std::mutex> lock(g_syscall_override_mutex);
g_syscall_overrides.clear();
}
}
void joinAllGuestHostThreads()
{
joinAllHostThreads();
}
void detachAllGuestHostThreads()
{
detachAllHostThreads();
}
}
@@ -0,0 +1,10 @@
#pragma once
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
void notifyRuntimeStop();
void joinAllGuestHostThreads();
void detachAllGuestHostThreads();
}
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void noteDtxSifDmaTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes);
bool handleSoundDriverRpcService(uint8_t *rdram, PS2Runtime *runtime,
uint32_t sid, uint32_t rpcNum,
uint32_t sendBuf, uint32_t sendSize,
uint32_t recvBuf, uint32_t recvSize,
uint32_t &resultPtr,
bool &signalNowaitCompletion);
void prepareSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t size);
void finalizeSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t size);
void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,923 @@
#include "Common.h"
#include "Sync.h"
namespace ps2_syscalls
{
static bool looksLikeGuestPointerOrNull(uint32_t value)
{
if (value == 0u)
{
return true;
}
const uint32_t normalized = value & 0x1FFFFFFFu;
return normalized < PS2_RAM_SIZE;
}
static bool readGuestU32Safe(const uint8_t *rdram, uint32_t addr, uint32_t &out)
{
const uint8_t *b0 = getConstMemPtr(rdram, addr + 0u);
const uint8_t *b1 = getConstMemPtr(rdram, addr + 1u);
const uint8_t *b2 = getConstMemPtr(rdram, addr + 2u);
const uint8_t *b3 = getConstMemPtr(rdram, addr + 3u);
if (!b0 || !b1 || !b2 || !b3)
{
out = 0u;
return false;
}
out = static_cast<uint32_t>(*b0) |
(static_cast<uint32_t>(*b1) << 8) |
(static_cast<uint32_t>(*b2) << 16) |
(static_cast<uint32_t>(*b3) << 24);
return true;
}
struct DecodedSemaParams
{
int init = 0;
int max = 1;
uint32_t attr = 0;
uint32_t option = 0;
};
static DecodedSemaParams decodeCreateSemaParams(const uint32_t *param, uint32_t availableWords)
{
DecodedSemaParams out{};
if (!param || availableWords == 0u)
{
return out;
}
// EE layout (kernel.h):
// [0]=count [1]=max_count [2]=init_count [3]=wait_threads [4]=attr [5]=option
const bool hasEeLayout = availableWords >= 3u;
const int eeMax = hasEeLayout ? static_cast<int>(param[1]) : 1;
const int eeInit = hasEeLayout ? static_cast<int>(param[2]) : 0;
const uint32_t eeAttr = (availableWords >= 5u) ? param[4] : 0u;
const uint32_t eeOption = (availableWords >= 6u) ? param[5] : 0u;
// Legacy layout (IOP-style):
// [0]=attr [1]=option [2]=init [3]=max
const bool hasLegacyLayout = availableWords >= 4u;
const int legacyMax = hasLegacyLayout ? static_cast<int>(param[3]) : 1;
const int legacyInit = hasLegacyLayout ? static_cast<int>(param[2]) : 0;
const uint32_t legacyAttr = hasLegacyLayout ? param[0] : 0u;
const uint32_t legacyOption = hasLegacyLayout ? param[1] : 0u;
auto countLooksPlausible = [](int value) -> bool
{
return value > 0 && value <= 0x10000;
};
bool useLegacyLayout = hasLegacyLayout && !hasEeLayout;
if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && !countLooksPlausible(eeMax))
{
useLegacyLayout = true;
}
else if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && countLooksPlausible(eeMax))
{
// If both max values look valid, prefer the layout whose option field
// looks like a pointer/NULL payload.
const bool eeOptionLooksValid = looksLikeGuestPointerOrNull(eeOption);
const bool legacyOptionLooksValid = looksLikeGuestPointerOrNull(legacyOption);
if (!eeOptionLooksValid && legacyOptionLooksValid)
{
useLegacyLayout = true;
}
}
if (useLegacyLayout && hasLegacyLayout)
{
out.max = legacyMax;
out.init = legacyInit;
out.attr = legacyAttr;
out.option = legacyOption;
}
else
{
if (!hasEeLayout)
{
return out;
}
out.max = eeMax;
out.init = eeInit;
out.attr = eeAttr;
out.option = eeOption;
}
return out;
}
void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t paramAddr = getRegU32(ctx, 4); // $a0
if (paramAddr == 0u)
{
setReturnS32(ctx, KE_ERROR);
return;
}
uint32_t rawParams[6] = {};
uint32_t availableWords = 0u;
for (uint32_t i = 0; i < 6u; ++i)
{
if (!readGuestU32Safe(rdram, paramAddr + (i * 4u), rawParams[i]))
{
break;
}
availableWords = i + 1u;
}
if (availableWords < 3u)
{
setReturnS32(ctx, KE_ERROR);
return;
}
const DecodedSemaParams decoded = decodeCreateSemaParams(rawParams, availableWords);
int init = decoded.init;
int max = decoded.max;
uint32_t attr = decoded.attr;
uint32_t option = decoded.option;
if (max <= 0)
{
max = 1;
}
if (init < 0)
{
init = 0;
}
if (init > max)
{
init = max;
}
int id = 0;
auto info = std::make_shared<SemaInfo>();
info->count = init;
info->maxCount = max;
info->initCount = init;
info->attr = attr;
info->option = option;
{
std::lock_guard<std::mutex> lock(g_sema_map_mutex);
for (int attempts = 0; attempts < 0x7FFF; ++attempts)
{
if (g_nextSemaId <= 0)
{
g_nextSemaId = 1;
}
const int candidate = g_nextSemaId++;
if (candidate <= 0)
{
continue;
}
if (g_semas.find(candidate) == g_semas.end())
{
id = candidate;
break;
}
}
if (id <= 0)
{
setReturnS32(ctx, KE_ERROR);
return;
}
g_semas.emplace(id, info);
}
RUNTIME_LOG("[CreateSema] id=" << id << " init=" << init << " max=" << max);
setReturnS32(ctx, id);
}
void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int sid = static_cast<int>(getRegU32(ctx, 4));
std::shared_ptr<SemaInfo> sema;
{
std::lock_guard<std::mutex> lock(g_sema_map_mutex);
auto it = g_semas.find(sid);
if (it == g_semas.end())
{
setReturnS32(ctx, KE_UNKNOWN_SEMID);
return;
}
sema = it->second;
g_semas.erase(it);
}
{
std::lock_guard<std::mutex> lock(sema->m);
sema->deleted = true;
}
sema->cv.notify_all();
setReturnS32(ctx, KE_OK);
}
void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
DeleteSema(rdram, ctx, runtime);
}
void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int sid = static_cast<int>(getRegU32(ctx, 4));
auto sema = lookupSemaInfo(sid);
if (!sema)
{
setReturnS32(ctx, KE_UNKNOWN_SEMID);
return;
}
int ret = KE_OK;
int beforeCount = 0;
int afterCount = 0;
{
std::lock_guard<std::mutex> lock(sema->m);
beforeCount = sema->count;
if (sema->count >= sema->maxCount)
{
ret = KE_SEMA_OVF;
}
else
{
sema->count++;
sema->cv.notify_one();
}
afterCount = sema->count;
}
static std::atomic<uint32_t> s_signalSemaLogs{0};
const uint32_t sigLog = s_signalSemaLogs.fetch_add(1, std::memory_order_relaxed);
if (sigLog < 256u)
{
RUNTIME_LOG("[SignalSema] tid=" << g_currentThreadId
<< " sid=" << sid
<< " count=" << beforeCount << "->" << afterCount
<< " ret=" << ret
<< std::endl);
}
setReturnS32(ctx, ret);
}
void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
SignalSema(rdram, ctx, runtime);
}
void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int sid = static_cast<int>(getRegU32(ctx, 4));
auto sema = lookupSemaInfo(sid);
if (!sema)
{
setReturnS32(ctx, KE_UNKNOWN_SEMID);
return;
}
auto info = ensureCurrentThreadInfo(ctx);
throwIfTerminated(info);
std::unique_lock<std::mutex> lock(sema->m);
int ret = 0;
if (sema->count == 0)
{
static std::atomic<uint32_t> s_waitSemaBlockLogs{0};
const uint32_t blockLog = s_waitSemaBlockLogs.fetch_add(1, std::memory_order_relaxed);
if (blockLog < 256u)
{
RUNTIME_LOG("[WaitSema:block] tid=" << g_currentThreadId
<< " sid=" << sid
<< " pc=0x" << std::hex << ctx->pc
<< " ra=0x" << getRegU32(ctx, 31)
<< std::dec
<< std::endl);
}
if (info)
{
std::lock_guard<std::mutex> tLock(info->m);
info->status = (info->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT;
info->waitType = TSW_SEMA;
info->waitId = sid;
info->forceRelease = false;
}
sema->waiters++;
{
PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime);
sema->cv.wait(lock, [&]()
{
bool forced = info ? info->forceRelease.load() : false;
bool terminated = info ? info->terminated.load() : false;
return sema->count > 0 || sema->deleted || forced || terminated; //
});
}
sema->waiters--;
if (sema->deleted)
{
ret = KE_WAIT_DELETE;
}
if (info)
{
std::lock_guard<std::mutex> tLock(info->m);
info->status = (info->suspendCount > 0) ? THS_SUSPEND : THS_RUN;
info->waitType = TSW_NONE;
info->waitId = 0;
if (info->forceRelease)
{
info->forceRelease = false;
ret = KE_RELEASE_WAIT;
}
}
if (info && info->terminated.load())
{
throw ThreadExitException();
}
}
if (ret == 0 && sema->count > 0)
{
sema->count--;
}
static std::atomic<uint32_t> s_waitSemaWakeLogs{0};
const uint32_t wakeLog = s_waitSemaWakeLogs.fetch_add(1, std::memory_order_relaxed);
if (wakeLog < 256u)
{
RUNTIME_LOG("[WaitSema:wake] tid=" << g_currentThreadId
<< " sid=" << sid
<< " ret=" << ret
<< " count=" << sema->count
<< std::endl);
}
lock.unlock();
waitWhileSuspended(info, runtime);
setReturnS32(ctx, ret);
}
void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int sid = static_cast<int>(getRegU32(ctx, 4));
auto sema = lookupSemaInfo(sid);
if (!sema)
{
setReturnS32(ctx, KE_UNKNOWN_SEMID);
return;
}
std::lock_guard<std::mutex> lock(sema->m);
if (sema->count > 0)
{
sema->count--;
setReturnS32(ctx, KE_OK);
return;
}
setReturnS32(ctx, KE_SEMA_ZERO);
}
void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
PollSema(rdram, ctx, runtime);
}
void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int sid = static_cast<int>(getRegU32(ctx, 4));
uint32_t statusAddr = getRegU32(ctx, 5);
auto sema = lookupSemaInfo(sid);
if (!sema)
{
setReturnS32(ctx, KE_UNKNOWN_SEMID);
return;
}
ee_sema_t *status = reinterpret_cast<ee_sema_t *>(getMemPtr(rdram, statusAddr));
if (!status)
{
setReturnS32(ctx, KE_ERROR);
return;
}
std::lock_guard<std::mutex> lock(sema->m);
status->count = sema->count;
status->max_count = sema->maxCount;
status->init_count = sema->initCount;
status->wait_threads = sema->waiters;
status->attr = sema->attr;
status->option = sema->option;
setReturnS32(ctx, KE_OK);
}
void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ReferSemaStatus(rdram, ctx, runtime);
}
constexpr uint32_t WEF_OR = 1;
constexpr uint32_t WEF_CLEAR = 0x10;
constexpr uint32_t WEF_CLEAR_ALL = 0x20;
constexpr uint32_t WEF_MODE_MASK = WEF_OR | WEF_CLEAR | WEF_CLEAR_ALL;
constexpr uint32_t EA_MULTI = 0x2;
void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t paramAddr = getRegU32(ctx, 4); // $a0
const uint32_t *param = reinterpret_cast<const uint32_t *>(getConstMemPtr(rdram, paramAddr));
auto info = std::make_shared<EventFlagInfo>();
if (param)
{
info->attr = param[0];
info->option = param[1];
info->initBits = param[2];
info->bits = info->initBits;
}
int id = 0;
{
std::lock_guard<std::mutex> mapLock(g_event_flag_map_mutex);
id = g_nextEventFlagId++;
g_eventFlags[id] = info;
}
setReturnS32(ctx, id);
}
void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int eid = static_cast<int>(getRegU32(ctx, 4));
std::shared_ptr<EventFlagInfo> info;
{
std::lock_guard<std::mutex> mapLock(g_event_flag_map_mutex);
auto it = g_eventFlags.find(eid);
if (it == g_eventFlags.end())
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
info = it->second;
g_eventFlags.erase(it);
}
if (!info)
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
{
std::lock_guard<std::mutex> lock(info->m);
info->deleted = true;
}
info->cv.notify_all();
setReturnS32(ctx, 0);
}
void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int eid = static_cast<int>(getRegU32(ctx, 4));
uint32_t bits = getRegU32(ctx, 5);
auto info = lookupEventFlagInfo(eid);
if (!info)
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
if (bits == 0)
{
setReturnS32(ctx, KE_OK);
return;
}
uint32_t newBits = 0u;
{
std::lock_guard<std::mutex> lock(info->m);
info->bits |= bits;
newBits = info->bits;
}
static std::atomic<uint32_t> s_setEventFlagLogs{0};
const uint32_t setLog = s_setEventFlagLogs.fetch_add(1, std::memory_order_relaxed);
if (setLog < 256u)
{
RUNTIME_LOG("[SetEventFlag] tid=" << g_currentThreadId
<< " eid=" << eid
<< " bits=0x" << std::hex << bits
<< " newBits=0x" << newBits
<< std::dec << std::endl);
}
info->cv.notify_all();
setReturnS32(ctx, 0);
}
void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
SetEventFlag(rdram, ctx, runtime);
}
void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int eid = static_cast<int>(getRegU32(ctx, 4));
uint32_t bits = getRegU32(ctx, 5);
auto info = lookupEventFlagInfo(eid);
if (!info)
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
{
std::lock_guard<std::mutex> lock(info->m);
info->bits &= bits;
}
info->cv.notify_all();
setReturnS32(ctx, KE_OK);
}
void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ClearEventFlag(rdram, ctx, runtime);
}
void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int eid = static_cast<int>(getRegU32(ctx, 4));
uint32_t waitBits = getRegU32(ctx, 5);
uint32_t mode = getRegU32(ctx, 6);
uint32_t resBitsAddr = getRegU32(ctx, 7);
if ((mode & ~WEF_MODE_MASK) != 0)
{
setReturnS32(ctx, KE_ILLEGAL_MODE);
return;
}
if (waitBits == 0)
{
setReturnS32(ctx, KE_EVF_ILPAT);
return;
}
auto info = lookupEventFlagInfo(eid);
if (!info)
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast<uint32_t *>(getMemPtr(rdram, resBitsAddr)) : nullptr;
std::unique_lock<std::mutex> lock(info->m);
if ((info->attr & EA_MULTI) == 0 && info->waiters > 0)
{
setReturnS32(ctx, KE_EVF_MULTI);
return;
}
auto tInfo = ensureCurrentThreadInfo(ctx);
throwIfTerminated(tInfo);
int ret = KE_OK;
auto satisfied = [&]()
{
if (tInfo && tInfo->forceRelease.load())
return true;
if (tInfo && tInfo->terminated.load())
return true;
if (info->deleted)
{
return true;
}
if (mode & WEF_OR)
{
return (info->bits & waitBits) != 0;
}
return (info->bits & waitBits) == waitBits;
};
if (!satisfied())
{
static std::atomic<uint32_t> s_waitEventBlockLogs{0};
const uint32_t evBlockLog = s_waitEventBlockLogs.fetch_add(1, std::memory_order_relaxed);
if (evBlockLog < 256u)
{
RUNTIME_LOG("[WaitEventFlag:block] tid=" << g_currentThreadId
<< " eid=" << eid
<< " waitBits=0x" << std::hex << waitBits
<< " mode=0x" << mode
<< " bits=0x" << info->bits
<< " pc=0x" << ctx->pc
<< " ra=0x" << getRegU32(ctx, 31)
<< std::dec
<< std::endl);
}
if (tInfo)
{
std::lock_guard<std::mutex> tLock(tInfo->m);
tInfo->status = (tInfo->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT;
tInfo->waitType = TSW_EVENT;
tInfo->waitId = eid;
tInfo->forceRelease = false;
}
info->waiters++;
{
PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime);
info->cv.wait(lock, satisfied);
}
info->waiters--;
if (tInfo)
{
std::lock_guard<std::mutex> tLock(tInfo->m);
tInfo->status = (tInfo->suspendCount > 0) ? THS_SUSPEND : THS_RUN;
tInfo->waitType = TSW_NONE;
tInfo->waitId = 0;
if (tInfo->forceRelease)
{
tInfo->forceRelease = false;
ret = KE_RELEASE_WAIT;
}
}
if (tInfo && tInfo->terminated.load())
{
throw ThreadExitException();
}
}
if (ret == KE_OK && info->deleted)
{
ret = KE_WAIT_DELETE;
}
if (ret == KE_OK && resBitsPtr)
{
*resBitsPtr = info->bits;
}
if (ret == KE_OK)
{
if (resBitsPtr)
{
*resBitsPtr = info->bits;
}
if (mode & WEF_CLEAR_ALL)
{
info->bits = 0;
}
else if (mode & WEF_CLEAR)
{
info->bits &= ~waitBits;
}
}
static std::atomic<uint32_t> s_waitEventWakeLogs{0};
const uint32_t evWakeLog = s_waitEventWakeLogs.fetch_add(1, std::memory_order_relaxed);
if (evWakeLog < 256u)
{
RUNTIME_LOG("[WaitEventFlag:wake] tid=" << g_currentThreadId
<< " eid=" << eid
<< " ret=" << ret
<< " bits=0x" << std::hex << info->bits
<< std::dec
<< std::endl);
}
lock.unlock();
waitWhileSuspended(tInfo, runtime);
setReturnS32(ctx, ret);
}
void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int eid = static_cast<int>(getRegU32(ctx, 4));
uint32_t waitBits = getRegU32(ctx, 5);
uint32_t mode = getRegU32(ctx, 6);
uint32_t resBitsAddr = getRegU32(ctx, 7);
if ((mode & ~WEF_MODE_MASK) != 0)
{
setReturnS32(ctx, KE_ILLEGAL_MODE);
return;
}
if (waitBits == 0)
{
setReturnS32(ctx, KE_EVF_ILPAT);
return;
}
auto info = lookupEventFlagInfo(eid);
if (!info)
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast<uint32_t *>(getMemPtr(rdram, resBitsAddr)) : nullptr;
std::lock_guard<std::mutex> lock(info->m);
if ((info->attr & EA_MULTI) == 0 && info->waiters > 0)
{
setReturnS32(ctx, KE_EVF_MULTI);
return;
}
bool ok = false;
if (mode & WEF_OR)
{
ok = (info->bits & waitBits) != 0;
}
else
{
ok = (info->bits & waitBits) == waitBits;
}
if (!ok)
{
setReturnS32(ctx, KE_EVF_COND);
return;
}
if (resBitsPtr)
{
*resBitsPtr = info->bits;
}
if (mode & WEF_CLEAR_ALL)
{
info->bits = 0;
}
else if (mode & WEF_CLEAR)
{
info->bits &= ~waitBits;
}
setReturnS32(ctx, KE_OK);
}
void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
PollEventFlag(rdram, ctx, runtime);
}
void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int eid = static_cast<int>(getRegU32(ctx, 4));
uint32_t infoAddr = getRegU32(ctx, 5);
struct Ps2EventFlagInfo
{
uint32_t attr;
uint32_t option;
uint32_t initBits;
uint32_t currBits;
int32_t numThreads;
int32_t reserved1;
int32_t reserved2;
};
auto info = lookupEventFlagInfo(eid);
if (!info)
{
setReturnS32(ctx, KE_UNKNOWN_EVFID);
return;
}
Ps2EventFlagInfo *out = infoAddr ? reinterpret_cast<Ps2EventFlagInfo *>(getMemPtr(rdram, infoAddr)) : nullptr;
if (!out)
{
setReturnS32(ctx, -1);
return;
}
std::lock_guard<std::mutex> lock(info->m);
out->attr = info->attr;
out->option = info->option;
out->initBits = info->initBits;
out->currBits = info->bits;
out->numThreads = info->waiters;
out->reserved1 = 0;
out->reserved2 = 0;
setReturnS32(ctx, 0);
}
void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ReferEventFlagStatus(rdram, ctx, runtime);
}
void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint16_t ticks = static_cast<uint16_t>(getRegU32(ctx, 4) & 0xFFFFu);
uint32_t handler = getRegU32(ctx, 5);
uint32_t arg = getRegU32(ctx, 6);
static int logCount = 0;
if (logCount < 5)
{
RUNTIME_LOG("[SetAlarm] ticks=" << ticks
<< " handler=0x" << std::hex << handler
<< " arg=0x" << arg << std::dec << std::endl);
++logCount;
}
if (!runtime || !handler || !runtime->hasFunction(handler))
{
setReturnS32(ctx, KE_ERROR);
return;
}
auto info = std::make_shared<AlarmInfo>();
info->ticks = ticks;
info->handler = handler;
info->commonArg = arg;
info->gp = getRegU32(ctx, 28);
info->sp = getRegU32(ctx, 29);
info->rdram = rdram;
info->runtime = runtime;
info->dueAt = std::chrono::steady_clock::now() + alarmTicksToDuration(ticks);
int alarmId = 0;
{
std::lock_guard<std::mutex> lock(g_alarm_mutex);
alarmId = g_nextAlarmId++;
if (g_nextAlarmId <= 0)
{
g_nextAlarmId = 1;
}
info->id = alarmId;
g_alarms[alarmId] = info;
}
ensureAlarmWorkerRunning();
g_alarm_cv.notify_all();
setReturnS32(ctx, alarmId);
}
void InitAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
SetAlarm(rdram, ctx, runtime);
}
void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int alarmId = static_cast<int>(getRegU32(ctx, 4));
if (alarmId <= 0)
{
setReturnS32(ctx, KE_ERROR);
return;
}
bool removed = false;
{
std::lock_guard<std::mutex> lock(g_alarm_mutex);
removed = g_alarms.erase(alarmId) != 0;
}
if (removed)
{
g_alarm_cv.notify_all();
setReturnS32(ctx, KE_OK);
return;
}
setReturnS32(ctx, KE_ERROR);
}
void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
CancelAlarm(rdram, ctx, runtime);
}
void ReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
CancelAlarm(rdram, ctx, runtime);
}
void iReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
iCancelAlarm(rdram, ctx, runtime);
}
}
@@ -0,0 +1,35 @@
#pragma once
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void InitAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void ReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}
@@ -0,0 +1,959 @@
#include "Common.h"
#include "System.h"
namespace ps2_syscalls
{
void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int interlaced = getRegU32(ctx, 4); // $a0 - 0=non-interlaced, 1=interlaced
int videoMode = getRegU32(ctx, 5); // $a1 - 0=NTSC, 1=PAL, 2=VESA, 3=HiVision
int frameMode = getRegU32(ctx, 6); // $a2 - 0=field, 1=frame
if (runtime)
{
auto &gs = runtime->memory().gs();
const uint64_t smode2 =
(static_cast<uint64_t>(interlaced) & 0x1ull) |
((static_cast<uint64_t>(frameMode) & 0x1ull) << 1);
gs.smode2 = smode2;
// Keep CRT1 enabled after the BIOS syscall selects a display mode.
if ((gs.pmode & 0x3ull) == 0ull)
{
gs.pmode |= 0x1ull;
}
}
RUNTIME_LOG("PS2 GsSetCrt: interlaced=" << interlaced
<< ", videoMode=" << videoMode
<< ", frameMode=" << frameMode << std::endl);
setReturnS32(ctx, 0);
}
void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
GsSetCrt(rdram, ctx, runtime);
}
void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint64_t imr = 0;
if (runtime)
{
imr = runtime->memory().gs().imr;
}
RUNTIME_LOG("PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec);
setReturnU64(ctx, imr); // Return in $v0/$v1
}
void iGsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
GsGetIMR(rdram, ctx, runtime);
}
void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint64_t newImr = getRegU32(ctx, 4) | ((uint64_t)getRegU32(ctx, 5) << 32); // $a0 = lower 32 bits, $a1 = upper 32 bits
uint64_t oldImr = 0;
if (runtime)
{
oldImr = runtime->memory().gs().imr;
runtime->memory().gs().imr = newImr;
}
RUNTIME_LOG("PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec);
setReturnU64(ctx, oldImr);
}
void iGsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
GsPutIMR(rdram, ctx, runtime);
}
void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags)
RUNTIME_LOG("PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec);
// Do nothing for now.
}
void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure
if (!getMemPtr(rdram, paramAddr))
{
std::cerr << "PS2 GetOsdConfigParam error: Invalid parameter address: 0x"
<< std::hex << paramAddr << std::dec << std::endl;
setReturnS32(ctx, -1);
return;
}
uint32_t *param = reinterpret_cast<uint32_t *>(getMemPtr(rdram, paramAddr));
ensureOsdConfigInitialized();
uint32_t raw;
{
std::lock_guard<std::mutex> lock(g_osd_mutex);
raw = g_osd_config_raw;
}
*param = raw;
setReturnS32(ctx, 0);
}
void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure
if (!getConstMemPtr(rdram, paramAddr))
{
std::cerr << "PS2 SetOsdConfigParam error: Invalid parameter address: 0x"
<< std::hex << paramAddr << std::dec << std::endl;
setReturnS32(ctx, -1);
return;
}
const uint32_t *param = reinterpret_cast<const uint32_t *>(getConstMemPtr(rdram, paramAddr));
uint32_t raw = param ? *param : 0;
raw = sanitizeOsdConfigRaw(raw);
{
std::lock_guard<std::mutex> lock(g_osd_mutex);
g_osd_config_raw = raw;
g_osd_config_initialized = true;
}
setReturnS32(ctx, 0);
}
void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t bufAddr = getRegU32(ctx, 4); // $a0
size_t bufSize = getRegU32(ctx, 5); // $a1
char *hostBuf = reinterpret_cast<char *>(getMemPtr(rdram, bufAddr));
const char *romName = "ROMVER 0100";
if (!hostBuf)
{
std::cerr << "GetRomName error: Invalid buffer address" << std::endl;
setReturnS32(ctx, -1); // Error
return;
}
if (bufSize == 0)
{
setReturnS32(ctx, 0);
return;
}
strncpy(hostBuf, romName, bufSize - 1);
hostBuf[bufSize - 1] = '\0';
// returns the length of the string (excluding null?) or error
setReturnS32(ctx, (int32_t)strlen(hostBuf));
}
void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path
const uint32_t secNameAddr = getRegU32(ctx, 5); // $a1 - section name ("all" typically)
const uint32_t execDataAddr = getRegU32(ctx, 6); // $a2 - t_ExecData*
std::string secName = readGuestCStringBounded(rdram, secNameAddr, kLoadfileArgMaxBytes);
if (secName.empty())
{
secName = "all";
}
const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, secName, execDataAddr);
setReturnS32(ctx, ret);
}
void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path
const uint32_t execDataAddr = getRegU32(ctx, 5); // $a1 - t_ExecData*
const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, "all", execDataAddr);
setReturnS32(ctx, ret);
}
void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
SifLoadElfPart(rdram, ctx, runtime);
}
void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
// Use the same tracker as SifLoadModule so both APIs return the same module IDs.
SifLoadModule(rdram, ctx, runtime);
}
void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t bufferAddr = getRegU32(ctx, 4); // $a0
if (!rdram || bufferAddr == 0u)
{
setReturnS32(ctx, -1);
return;
}
// Match buffer-based module loads to stable synthetic tags so module ID lookup remains deterministic.
const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr);
const int32_t moduleId = trackSifModuleLoad(moduleTag);
if (moduleId <= 0)
{
setReturnS32(ctx, -1);
return;
}
uint32_t refs = 0;
{
std::lock_guard<std::mutex> lock(g_sif_module_mutex);
auto it = g_sif_modules_by_id.find(moduleId);
if (it != g_sif_modules_by_id.end())
{
refs = it->second.refCount;
}
}
logSifModuleAction("load-buffer", moduleId, moduleTag, refs);
setReturnS32(ctx, moduleId);
}
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId)
{
// a bit more detail mayber reomve old logic, lets get it more raw
std::cerr << "[Syscall TODO]"
<< " encoded=0x" << std::hex << encodedSyscallId
<< " v1=0x" << getRegU32(ctx, 3)
<< " v0=0x" << getRegU32(ctx, 2)
<< " a0=0x" << getRegU32(ctx, 4)
<< " a1=0x" << getRegU32(ctx, 5)
<< " a2=0x" << getRegU32(ctx, 6)
<< " a3=0x" << getRegU32(ctx, 7)
<< " pc=0x" << ctx->pc
<< std::dec << std::endl;
const uint32_t v0 = getRegU32(ctx, 2);
const uint32_t v1 = getRegU32(ctx, 3);
const uint32_t caller_ra = getRegU32(ctx, 31);
uint32_t syscallId = encodedSyscallId;
if (syscallId == 0u)
{
syscallId = (v0 != 0u) ? v0 : v1;
}
std::cerr << "Warning: Unimplemented PS2 syscall called. PC=0x" << std::hex << ctx->pc
<< ", RA=0x" << caller_ra
<< ", Encoded=0x" << encodedSyscallId
<< ", v0=0x" << v0
<< ", v1=0x" << v1
<< ", Chosen=0x" << syscallId
<< std::dec << std::endl;
std::cerr << " Args: $a0=0x" << std::hex << getRegU32(ctx, 4)
<< ", $a1=0x" << getRegU32(ctx, 5)
<< ", $a2=0x" << getRegU32(ctx, 6)
<< ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl;
// Common syscalls:
// 0x04: Exit
// 0x06: LoadExecPS2
// 0x07: ExecPS2
if (syscallId == 0x04u)
{
std::cerr << " -> Syscall is Exit(), calling ExitThread stub." << std::endl;
ExitThread(rdram, ctx, runtime);
return;
}
static std::mutex s_unknownMutex;
static std::unordered_map<uint32_t, uint64_t> s_unknownCounts;
{
std::lock_guard<std::mutex> lock(s_unknownMutex);
const uint64_t count = ++s_unknownCounts[syscallId];
if (count == 1 || (count % 5000u) == 0u)
{
std::cerr << " -> Unknown syscallId=0x" << std::hex << syscallId
<< " hits=" << std::dec << count << std::endl;
}
}
// Bootstrap default: avoid hard-failing loops that probe syscall availability.
setReturnS32(ctx, 0);
}
static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram,
uint32_t originalStart,
uint32_t originalEnd,
uint32_t target);
bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t handler = 0u;
{
std::lock_guard<std::mutex> lock(g_syscall_override_mutex);
auto it = g_syscall_overrides.find(syscallNumber);
if (it == g_syscall_overrides.end())
{
return false;
}
handler = it->second;
}
if (!runtime || !ctx || handler == 0u)
{
return false;
}
const uint32_t overrideA0 = getRegU32(ctx, 4);
const uint32_t overrideA1 = getRegU32(ctx, 5);
const uint32_t overrideA2 = getRegU32(ctx, 6);
const uint32_t overrideA3 = getRegU32(ctx, 7);
const uint32_t overridePc = ctx->pc;
const uint32_t overrideRa = getRegU32(ctx, 31);
thread_local std::vector<uint32_t> s_activeSyscallOverrides;
if (std::find(s_activeSyscallOverrides.begin(), s_activeSyscallOverrides.end(), syscallNumber) != s_activeSyscallOverrides.end())
{
static std::atomic<uint32_t> s_reentrantLogs{0u};
constexpr uint32_t kMaxReentrantLogs = 32u;
const uint32_t logIndex = s_reentrantLogs.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < kMaxReentrantLogs)
{
std::cerr << "[SyscallOverride:reentrant]"
<< " syscall=0x" << std::hex << syscallNumber
<< " handler=0x" << handler
<< " pc=0x" << ctx->pc
<< " ra=0x" << getRegU32(ctx, 31)
<< std::dec << std::endl;
}
return false;
}
s_activeSyscallOverrides.push_back(syscallNumber);
struct ScopedActiveOverride
{
std::vector<uint32_t> &active;
~ScopedActiveOverride()
{
if (!active.empty())
{
active.pop_back();
}
}
} scopedActiveOverride{s_activeSyscallOverrides};
uint32_t retV0 = 0u;
const bool invoked = rpcInvokeFunction(rdram,
ctx,
runtime,
handler,
getRegU32(ctx, 4),
getRegU32(ctx, 5),
getRegU32(ctx, 6),
getRegU32(ctx, 7),
&retV0);
if (syscallNumber == 0x83u)
{
const uint32_t builtinRet = computeBuiltinFindAddressResult(rdram, overrideA0, overrideA1, overrideA2);
const bool mismatch = (retV0 != builtinRet);
static std::atomic<uint32_t> s_findAddressOverrideLogs{0u};
static std::atomic<uint32_t> s_findAddressOverrideMismatchLogs{0u};
constexpr uint32_t kMaxFindAddressOverrideLogs = 64u;
constexpr uint32_t kMaxFindAddressOverrideMismatchLogs = 128u;
const uint32_t logIndex = s_findAddressOverrideLogs.fetch_add(1u, std::memory_order_relaxed);
const uint32_t mismatchIndex = mismatch
? s_findAddressOverrideMismatchLogs.fetch_add(1u, std::memory_order_relaxed)
: 0u;
if (logIndex < kMaxFindAddressOverrideLogs ||
(mismatch && mismatchIndex < kMaxFindAddressOverrideMismatchLogs))
{
const uint32_t guestMinus20c = (retV0 != 0u) ? (retV0 - 0x20Cu) : 0u;
const uint32_t guestMinus168 = (retV0 != 0u) ? (retV0 - 0x168u) : 0u;
const uint32_t builtinMinus20c = (builtinRet != 0u) ? (builtinRet - 0x20Cu) : 0u;
const uint32_t builtinMinus168 = (builtinRet != 0u) ? (builtinRet - 0x168u) : 0u;
std::cerr << "[Syscall83:override]"
<< " handler=0x" << std::hex << handler
<< " invoked=" << (invoked ? "true" : "false")
<< " pc=0x" << overridePc
<< " ra=0x" << overrideRa
<< " a0=0x" << overrideA0
<< " a1=0x" << overrideA1
<< " a2=0x" << overrideA2
<< " a3=0x" << overrideA3
<< " guestRet=0x" << retV0
<< " builtinRet=0x" << builtinRet
<< " guest-20c=0x" << guestMinus20c
<< " builtin-20c=0x" << builtinMinus20c
<< " guest-168=0x" << guestMinus168
<< " builtin-168=0x" << builtinMinus168
<< " match=" << (mismatch ? "false" : "true")
<< std::dec << std::endl;
}
}
if (!invoked)
{
static std::atomic<uint32_t> s_fallbackLogs{0u};
constexpr uint32_t kMaxFallbackLogs = 64u;
const uint32_t logIndex = s_fallbackLogs.fetch_add(1u, std::memory_order_relaxed);
if (logIndex < kMaxFallbackLogs)
{
std::cerr << "[SyscallOverride:fallback]"
<< " syscall=0x" << std::hex << syscallNumber
<< " handler=0x" << handler
<< " pc=0x" << ctx->pc
<< " ra=0x" << getRegU32(ctx, 31)
<< std::dec << std::endl;
}
return false;
}
setReturnU32(ctx, retV0);
return true;
}
static bool tryResolveGuestSyscallMirrorAddr(uint32_t syscallIndex, uint32_t &guestAddr)
{
const int64_t offsetBytes =
static_cast<int64_t>(static_cast<int32_t>(syscallIndex)) * static_cast<int64_t>(sizeof(uint32_t));
const int64_t guestAddr64 = static_cast<int64_t>(kGuestSyscallTablePhysBase) + offsetBytes;
if (guestAddr64 < 0 || (guestAddr64 + static_cast<int64_t>(sizeof(uint32_t))) > static_cast<int64_t>(kGuestSyscallMirrorLimit))
{
return false;
}
guestAddr = static_cast<uint32_t>(guestAddr64);
return true;
}
static void writeGuestKernelWord(uint8_t *rdram, uint32_t guestAddr, uint32_t value)
{
if (!rdram)
{
return;
}
if (uint8_t *ptr = getMemPtr(rdram, guestAddr))
{
std::memcpy(ptr, &value, sizeof(value));
}
}
static void seedGuestSyscallTableProbeLocked(uint8_t *rdram)
{
writeGuestKernelWord(rdram, kGuestSyscallTableProbeBase + 0u, kGuestSyscallTableGuestBase >> 16);
writeGuestKernelWord(rdram, kGuestSyscallTableProbeBase + 8u, kGuestSyscallTableGuestBase & 0xFFFFu);
g_syscall_mirror_addrs.insert(kGuestSyscallTableProbeBase + 0u);
g_syscall_mirror_addrs.insert(kGuestSyscallTableProbeBase + 8u);
}
static void mirrorGuestSyscallEntryLocked(uint8_t *rdram, uint32_t syscallIndex, uint32_t handler)
{
uint32_t guestAddr = 0u;
if (!tryResolveGuestSyscallMirrorAddr(syscallIndex, guestAddr))
{
return;
}
writeGuestKernelWord(rdram, guestAddr, handler);
if (handler == 0u)
{
g_syscall_mirror_addrs.erase(guestAddr);
return;
}
g_syscall_mirror_addrs.insert(guestAddr);
}
void initializeGuestKernelState(uint8_t *rdram)
{
if (!rdram)
{
return;
}
std::lock_guard<std::mutex> lock(g_syscall_override_mutex);
for (uint32_t guestAddr : g_syscall_mirror_addrs)
{
writeGuestKernelWord(rdram, guestAddr, 0u);
}
g_syscall_mirror_addrs.clear();
seedGuestSyscallTableProbeLocked(rdram);
for (const auto &entry : g_syscall_overrides)
{
mirrorGuestSyscallEntryLocked(rdram, entry.first, entry.second);
}
}
void SetSyscall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
const uint32_t syscallIndex = getRegU32(ctx, 4);
const uint32_t handler = getRegU32(ctx, 5);
{
std::lock_guard<std::mutex> lock(g_syscall_override_mutex);
if (handler == 0u)
{
g_syscall_overrides.erase(syscallIndex);
}
else
{
g_syscall_overrides[syscallIndex] = handler;
}
mirrorGuestSyscallEntryLocked(rdram, syscallIndex, handler);
}
setReturnS32(ctx, 0);
}
// 0x3C SetupThread
// args: $a0 = gp, $a1 = stack, $a2 = stack_size, $a3 = args, $t0 = root_func
void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t gp = getRegU32(ctx, 4);
const uint32_t stack = getRegU32(ctx, 5);
const int32_t stackSizeSigned = static_cast<int32_t>(getRegU32(ctx, 6));
const uint32_t currentSp = getRegU32(ctx, 29);
if (gp != 0u)
{
setRegU32(ctx, 28, gp);
}
uint32_t sp = currentSp;
if (stack == 0xFFFFFFFFu)
{
if (stackSizeSigned > 0)
{
const uint32_t requestedSize = static_cast<uint32_t>(stackSizeSigned);
if (requestedSize < PS2_RAM_SIZE)
{
sp = PS2_RAM_SIZE - requestedSize;
}
else
{
sp = PS2_RAM_SIZE;
}
}
else
{
sp = PS2_RAM_SIZE;
}
}
else if (stack != 0u)
{
if (stackSizeSigned > 0)
{
sp = stack + static_cast<uint32_t>(stackSizeSigned);
}
else
{
sp = stack;
}
}
sp &= ~0xFu;
setReturnU32(ctx, sp);
}
// 0x3D SetupHeap: returns heap base/start pointer
void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t heapBase = getRegU32(ctx, 4); // $a0
const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size)
if (runtime)
{
uint32_t heapLimit = PS2_RAM_SIZE;
if (heapSize != 0u && heapBase < PS2_RAM_SIZE)
{
const uint64_t candidateLimit = static_cast<uint64_t>(heapBase) + static_cast<uint64_t>(heapSize);
heapLimit = static_cast<uint32_t>(std::min<uint64_t>(candidateLimit, PS2_RAM_SIZE));
}
runtime->configureGuestHeap(heapBase, heapLimit);
setReturnU32(ctx, runtime->guestHeapBase());
return;
}
setReturnU32(ctx, heapBase);
}
// 0x3E EndOfHeap: commonly returns current heap end; keep it stable for now.
void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
if (runtime)
{
setReturnU32(ctx, runtime->guestHeapEnd());
return;
}
setReturnU32(ctx, getRegU32(ctx, 4));
}
void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnU32(ctx, PS2_RAM_SIZE);
}
static inline uint32_t normalizeKernelAlias(uint32_t addr)
{
if (addr >= 0x80000000u && addr < 0xC0000000u)
{
return addr & 0x1FFFFFFFu;
}
return addr;
}
static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram,
uint32_t originalStart,
uint32_t originalEnd,
uint32_t target)
{
uint32_t start = (originalStart + 3u) & ~0x3u;
uint32_t end = originalEnd & ~0x3u;
if (start >= end)
{
return 0u;
}
const uint32_t targetNorm = normalizeKernelAlias(target);
for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t))
{
const uint8_t *entryPtr = getConstMemPtr(rdram, addr);
if (!entryPtr)
{
break;
}
uint32_t entry = 0u;
std::memcpy(&entry, entryPtr, sizeof(entry));
if (entry == target || normalizeKernelAlias(entry) == targetNorm)
{
return addr;
}
}
return 0u;
}
struct FindAddressWordSample
{
uint32_t addr = 0u;
uint32_t value = 0u;
};
struct FindAddressMatchSample
{
uint32_t addr = 0u;
uint32_t value = 0u;
bool aliasOnly = false;
};
static void logFindAddressDiagnostics(uint32_t callerPc,
uint32_t originalStart,
uint32_t originalEnd,
uint32_t alignedStart,
uint32_t alignedEnd,
uint32_t target,
uint32_t targetNorm,
bool found,
uint32_t resultAddr,
uint32_t scannedWords,
bool allZero,
bool aborted,
uint32_t abortedAddr,
const FindAddressWordSample *firstWords,
uint32_t firstWordCount,
const FindAddressWordSample *nonZeroWords,
uint32_t nonZeroWordCount,
const FindAddressMatchSample *matches,
uint32_t matchCount)
{
static std::atomic<uint32_t> s_findAddressHitLogs{0u};
static std::atomic<uint32_t> s_findAddressMissLogs{0u};
constexpr uint32_t kMaxFindAddressHitLogs = 16u;
constexpr uint32_t kMaxFindAddressMissLogs = 128u;
std::atomic<uint32_t> &counter = found ? s_findAddressHitLogs : s_findAddressMissLogs;
const uint32_t logIndex = counter.fetch_add(1u, std::memory_order_relaxed);
const uint32_t logLimit = found ? kMaxFindAddressHitLogs : kMaxFindAddressMissLogs;
if (logIndex >= logLimit)
{
return;
}
std::cerr << "[FindAddress:" << (found ? "hit" : "miss") << "]"
<< " pc=0x" << std::hex << callerPc
<< " start=0x" << originalStart
<< " end=0x" << originalEnd
<< " alignedStart=0x" << alignedStart
<< " alignedEnd=0x" << alignedEnd
<< " target=0x" << target
<< " targetNorm=0x" << targetNorm
<< " result=0x" << resultAddr
<< std::dec
<< " scannedWords=" << scannedWords
<< " allZero=" << (allZero ? "true" : "false")
<< " aborted=" << (aborted ? "true" : "false");
if (aborted)
{
std::cerr << " abortedAddr=0x" << std::hex << abortedAddr << std::dec;
}
std::cerr << std::endl;
std::cerr << " firstWords:";
if (firstWordCount == 0u)
{
std::cerr << " none";
}
else
{
for (uint32_t i = 0; i < firstWordCount; ++i)
{
std::cerr << " [0x" << std::hex << firstWords[i].addr
<< "]=0x" << firstWords[i].value;
}
std::cerr << std::dec;
}
std::cerr << std::endl;
std::cerr << " nonZeroSample:";
if (nonZeroWordCount == 0u)
{
std::cerr << " none";
}
else
{
for (uint32_t i = 0; i < nonZeroWordCount; ++i)
{
std::cerr << " [0x" << std::hex << nonZeroWords[i].addr
<< "]=0x" << nonZeroWords[i].value;
}
std::cerr << std::dec;
}
std::cerr << std::endl;
std::cerr << " matches:";
if (matchCount == 0u)
{
std::cerr << " none";
}
else
{
for (uint32_t i = 0; i < matchCount; ++i)
{
std::cerr << " [0x" << std::hex << matches[i].addr
<< "]=0x" << matches[i].value
<< (matches[i].aliasOnly ? "(alias)" : "(exact)");
}
std::cerr << std::dec;
}
std::cerr << std::endl;
}
// 0x83 FindAddress:
// - a0: table start (inclusive)
// - a1: table end (exclusive)
// - a2: target address to locate inside the table (word entries)
// Returns the guest address of the matching word entry, or 0 if not found.
void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)runtime;
constexpr uint32_t kFindAddressWordSamples = 8u;
constexpr uint32_t kFindAddressMatchSamples = 4u;
const uint32_t originalStart = getRegU32(ctx, 4);
const uint32_t originalEnd = getRegU32(ctx, 5);
const uint32_t target = getRegU32(ctx, 6);
const uint32_t targetNorm = normalizeKernelAlias(target);
const uint32_t callerPc = ctx->pc;
uint32_t start = originalStart;
uint32_t end = originalEnd;
// Word-scan semantics: align the search window to uint32 boundaries.
start = (start + 3u) & ~0x3u;
end &= ~0x3u;
if (start >= end)
{
logFindAddressDiagnostics(callerPc,
originalStart,
originalEnd,
start,
end,
target,
targetNorm,
false,
0u,
0u,
true,
false,
0u,
nullptr,
0u,
nullptr,
0u,
nullptr,
0u);
setReturnU32(ctx, 0u);
return;
}
FindAddressWordSample firstWords[kFindAddressWordSamples]{};
FindAddressWordSample nonZeroWords[kFindAddressWordSamples]{};
FindAddressMatchSample matches[kFindAddressMatchSamples]{};
uint32_t firstWordCount = 0u;
uint32_t nonZeroWordCount = 0u;
uint32_t matchCount = 0u;
uint32_t scannedWords = 0u;
uint32_t resultAddr = 0u;
uint32_t abortedAddr = 0u;
bool aborted = false;
bool allZero = true;
bool foundMatch = false;
for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t))
{
const uint8_t *entryPtr = getConstMemPtr(rdram, addr);
if (!entryPtr)
{
aborted = true;
abortedAddr = addr;
break;
}
uint32_t entry = 0;
std::memcpy(&entry, entryPtr, sizeof(entry));
++scannedWords;
if (firstWordCount < kFindAddressWordSamples)
{
firstWords[firstWordCount++] = {addr, entry};
}
if (entry != 0u)
{
allZero = false;
if (nonZeroWordCount < kFindAddressWordSamples)
{
nonZeroWords[nonZeroWordCount++] = {addr, entry};
}
}
const bool exactMatch = (entry == target);
const bool aliasMatch = !exactMatch && (normalizeKernelAlias(entry) == targetNorm);
if (exactMatch || aliasMatch)
{
if (!foundMatch)
{
resultAddr = addr;
foundMatch = true;
}
if (matchCount < kFindAddressMatchSamples)
{
matches[matchCount++] = {addr, entry, aliasMatch};
}
}
}
logFindAddressDiagnostics(callerPc,
originalStart,
originalEnd,
start,
end,
target,
targetNorm,
foundMatch,
resultAddr,
scannedWords,
allZero,
aborted,
abortedAddr,
firstWords,
firstWordCount,
nonZeroWords,
nonZeroWordCount,
matches,
matchCount);
setReturnU32(ctx, resultAddr);
}
void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)runtime;
setReturnS32(ctx, KE_OK);
}
// 0x5A QueryBootMode (stub): return 0 for now
void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t mode = getRegU32(ctx, 4);
ensureBootModeTable(rdram);
uint32_t addr = 0;
{
std::lock_guard<std::mutex> lock(g_bootmode_mutex);
auto it = g_bootmode_addresses.find(static_cast<uint8_t>(mode));
if (it != g_bootmode_addresses.end())
addr = it->second;
}
setReturnU32(ctx, addr);
}
// 0x5B GetThreadTLS (stub): return 0
void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
auto info = ensureCurrentThreadInfo(ctx);
if (!info)
{
setReturnU32(ctx, 0);
return;
}
if (info->tlsBase == 0)
{
info->tlsBase = allocTlsAddr(rdram);
}
setReturnU32(ctx, info->tlsBase);
}
// 0x74 RegisterExitHandler (stub): return 0
void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t func = getRegU32(ctx, 4);
uint32_t arg = getRegU32(ctx, 5);
if (func == 0)
{
setReturnS32(ctx, -1);
return;
}
int tid = g_currentThreadId;
{
std::lock_guard<std::mutex> lock(g_exit_handler_mutex);
g_exit_handlers[tid].push_back({func, arg});
}
setReturnS32(ctx, 0);
}
}
@@ -0,0 +1,35 @@
#pragma once
#include "ps2_syscalls.h"
namespace ps2_syscalls
{
bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iGsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void iGsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId);
void initializeGuestKernelState(uint8_t *rdram);
void SetSyscall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
}

Some files were not shown because too many files have changed in this diff Show More