mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-27 09:05:28 -04:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93e221feaa | |||
| 553a9027d8 | |||
| cad1ca0bb5 | |||
| 7ca6a866c9 | |||
| 9e4fd794c1 | |||
| 493522a769 | |||
| da10073cb9 | |||
| ccd17e5244 | |||
| 765cbcca0f | |||
| b3be2650b0 |
+51
-10
@@ -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()
|
||||
|
||||
@@ -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`).
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 §ion : 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)
|
||||
{
|
||||
|
||||
@@ -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> §ions)
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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> §ions,
|
||||
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> §ions)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "raylib.h"
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
+3
-2
@@ -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);
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
+455
-282
@@ -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 ®s = 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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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{};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+4
-32
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
+46
-142
@@ -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);
|
||||
}
|
||||
+6
-6
@@ -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;
|
||||
}
|
||||
|
||||
+90
-9
@@ -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;
|
||||
}
|
||||
|
||||
+169
-67
@@ -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
@@ -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
Reference in New Issue
Block a user