feat: misc runtime changes

This commit is contained in:
Ran-j
2025-11-29 00:18:25 -03:00
parent 377786cf43
commit c423e2f1ae
5 changed files with 140 additions and 83 deletions
+6 -1
View File
@@ -38,6 +38,11 @@ target_link_libraries(ps2_runtime PRIVATE raylib)
target_link_libraries(ps2EntryRunner PRIVATE raylib)
target_link_libraries(ps2EntryRunner PRIVATE ps2_runtime)
# Work around WinAPI vs raylib symbol clash for CloseWindow on x64
if (MSVC)
target_link_options(ps2EntryRunner PRIVATE "/FORCE:MULTIPLE")
endif()
install(TARGETS ps2_runtime
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
@@ -45,4 +50,4 @@ install(TARGETS ps2_runtime
install(DIRECTORY include/
DESTINATION include
)
)
+9 -1
View File
@@ -216,6 +216,13 @@ inline void setReturnS32(R5900Context *ctx, int32_t value)
ctx->r[2] = _mm_set_epi32(0, 0, 0, value); // $v0 Sign extension handled by cast? TODO Check MIPS ABI.
}
inline void setReturnU64(R5900Context *ctx, uint64_t value)
{
// 64-bit returns use $v0/$v1 (r2/r3)
ctx->r[2] = _mm_set_epi32(0, 0, 0, static_cast<uint32_t>(value));
ctx->r[3] = _mm_set_epi32(0, 0, 0, static_cast<uint32_t>(value >> 32));
}
inline uint8_t *getMemPtr(uint8_t *rdram, uint32_t addr)
{
constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1;
@@ -372,6 +379,7 @@ private:
bool isAddressInRegion(uint32_t address, const CodeRegion &region);
void markModified(uint32_t address, uint32_t size);
bool isScratchpad(uint32_t address) const;
};
class PS2Runtime
@@ -435,4 +443,4 @@ private:
std::vector<LoadedModule> m_loadedModules;
};
#endif // PS2_RUNTIME_H
#endif // PS2_RUNTIME_H
+64 -48
View File
@@ -86,10 +86,16 @@ bool PS2Memory::initialize(size_t ramSize)
}
}
bool PS2Memory::isScratchpad(uint32_t address) const
{
return address >= PS2_SCRATCHPAD_BASE &&
address < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE;
}
uint32_t PS2Memory::translateAddress(uint32_t virtualAddress)
{
// Handle special memory regions
if (virtualAddress >= PS2_SCRATCHPAD_BASE && virtualAddress < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
if (isScratchpad(virtualAddress))
{
// Scratchpad is directly mapped
return virtualAddress - PS2_SCRATCHPAD_BASE;
@@ -132,16 +138,17 @@ uint32_t PS2Memory::translateAddress(uint32_t virtualAddress)
uint8_t PS2Memory::read8(uint32_t address)
{
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (scratch)
{
return m_scratchpad[physAddr];
}
if (physAddr < PS2_RAM_SIZE)
{
return m_rdram[physAddr];
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
return m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE];
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - often not handled byte by byte
@@ -167,16 +174,17 @@ uint16_t PS2Memory::read16(uint32_t address)
throw std::runtime_error("Unaligned 16-bit read at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (scratch)
{
return *reinterpret_cast<uint16_t *>(&m_scratchpad[physAddr]);
}
if (physAddr < PS2_RAM_SIZE)
{
return *reinterpret_cast<uint16_t *>(&m_rdram[physAddr]);
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
return *reinterpret_cast<uint16_t *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]);
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - align to word boundary and extract relevant bits
@@ -201,16 +209,17 @@ uint32_t PS2Memory::read32(uint32_t address)
throw std::runtime_error("Unaligned 32-bit read at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (scratch)
{
return *reinterpret_cast<uint32_t *>(&m_scratchpad[physAddr]);
}
if (physAddr < PS2_RAM_SIZE)
{
return *reinterpret_cast<uint32_t *>(&m_rdram[physAddr]);
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
return *reinterpret_cast<uint32_t *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]);
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers
@@ -232,16 +241,17 @@ uint64_t PS2Memory::read64(uint32_t address)
throw std::runtime_error("Unaligned 64-bit read at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (scratch)
{
return *reinterpret_cast<uint64_t *>(&m_scratchpad[physAddr]);
}
if (physAddr < PS2_RAM_SIZE)
{
return *reinterpret_cast<uint64_t *>(&m_rdram[physAddr]);
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
return *reinterpret_cast<uint64_t *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]);
}
// 64-bit IO operations are not common, but who knows
return (uint64_t)read32(address) | ((uint64_t)read32(address + 4) << 32);
@@ -255,16 +265,17 @@ __m128i PS2Memory::read128(uint32_t address)
throw std::runtime_error("Unaligned 128-bit read at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (scratch)
{
return _mm_loadu_si128(reinterpret_cast<__m128i *>(&m_scratchpad[physAddr]));
}
if (physAddr < PS2_RAM_SIZE)
{
return _mm_loadu_si128(reinterpret_cast<__m128i *>(&m_rdram[physAddr]));
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
return _mm_loadu_si128(reinterpret_cast<__m128i *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]));
}
// 128-bit reads are primarily for quad-word loads in the EE, which are only valid for RAM areas
// Return zeroes for unsupported areas
@@ -273,16 +284,17 @@ __m128i PS2Memory::read128(uint32_t address)
void PS2Memory::write8(uint32_t address, uint8_t value)
{
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (physAddr < PS2_RAM_SIZE)
if (scratch)
{
m_scratchpad[physAddr] = value;
}
else if (physAddr < PS2_RAM_SIZE)
{
m_rdram[physAddr] = value;
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE] = value;
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - handle byte writes by modifying the appropriate byte in the word
@@ -304,16 +316,17 @@ void PS2Memory::write16(uint32_t address, uint16_t value)
throw std::runtime_error("Unaligned 16-bit write at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (physAddr < PS2_RAM_SIZE)
if (scratch)
{
*reinterpret_cast<uint16_t *>(&m_scratchpad[physAddr]) = value;
}
else if (physAddr < PS2_RAM_SIZE)
{
*reinterpret_cast<uint16_t *>(&m_rdram[physAddr]) = value;
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
*reinterpret_cast<uint16_t *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]) = value;
}
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// IO registers - handle halfword writes
@@ -335,20 +348,21 @@ void PS2Memory::write32(uint32_t address, uint32_t value)
throw std::runtime_error("Unaligned 32-bit write at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (physAddr < PS2_RAM_SIZE)
if (scratch)
{
*reinterpret_cast<uint32_t *>(&m_scratchpad[physAddr]) = value;
}
else if (physAddr < PS2_RAM_SIZE)
{
// Check if this might be code modification
markModified(address, 4);
*reinterpret_cast<uint32_t *>(&m_rdram[physAddr]) = value;
}
else if (physAddr >= 0x70000000 && physAddr < 0x70004000)
{ // Scratchpad
*reinterpret_cast<uint32_t *>(&m_scratchpad[physAddr - 0x70000000]) = value;
}
else if (physAddr >= 0x10000000 && physAddr < 0x10010000)
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
{
// Handle IO register writes with potential side effects
writeIORegister(physAddr, value);
@@ -363,16 +377,17 @@ void PS2Memory::write64(uint32_t address, uint64_t value)
throw std::runtime_error("Unaligned 64-bit write at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (physAddr < PS2_RAM_SIZE)
if (scratch)
{
*reinterpret_cast<uint64_t *>(&m_scratchpad[physAddr]) = value;
}
else if (physAddr < PS2_RAM_SIZE)
{
*reinterpret_cast<uint64_t *>(&m_rdram[physAddr]) = value;
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
*reinterpret_cast<uint64_t *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]) = value;
}
else
{
// Split into two 32-bit writes for other memory regions
@@ -389,16 +404,17 @@ void PS2Memory::write128(uint32_t address, __m128i value)
throw std::runtime_error("Unaligned 128-bit write at address: 0x" + std::to_string(address));
}
const bool scratch = isScratchpad(address);
uint32_t physAddr = translateAddress(address);
if (physAddr < PS2_RAM_SIZE)
if (scratch)
{
_mm_storeu_si128(reinterpret_cast<__m128i *>(&m_scratchpad[physAddr]), value);
}
else if (physAddr < PS2_RAM_SIZE)
{
_mm_storeu_si128(reinterpret_cast<__m128i *>(&m_rdram[physAddr]), value);
}
else if (physAddr >= PS2_SCRATCHPAD_BASE && physAddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
_mm_storeu_si128(reinterpret_cast<__m128i *>(&m_scratchpad[physAddr - PS2_SCRATCHPAD_BASE]), value);
}
else
{
// Split into smaller writes for other memory regions
@@ -607,4 +623,4 @@ void PS2Memory::clearModifiedFlag(uint32_t address, uint32_t size)
}
}
}
}
}
+59 -30
View File
@@ -3,6 +3,8 @@
#include <fstream>
#include <algorithm>
#include <cstring>
#include <atomic>
#include <thread>
#include "raylib.h"
#define ELF_MAGIC 0x464C457F // "\x7FELF" in little endian
@@ -141,13 +143,27 @@ bool PS2Runtime::loadELF(const std::string &elfPath)
// Copy to memory
uint32_t physAddr = m_memory.translateAddress(ph.vaddr);
uint8_t *dest = m_memory.getRDRAM() + physAddr;
uint8_t *dest = nullptr;
if (ph.vaddr >= PS2_SCRATCHPAD_BASE && ph.vaddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
{
dest = m_memory.getScratchpad() + physAddr;
}
else
{
dest = m_memory.getRDRAM() + physAddr;
}
std::memcpy(dest, buffer.data(), ph.filesz);
if (ph.memsz > ph.filesz)
{
std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz);
}
// Track executable regions for self-modifying code invalidation
if (ph.flags & 0x1) // PF_X
{
m_memory.registerCodeRegion(ph.vaddr, ph.vaddr + ph.memsz);
}
}
}
@@ -277,39 +293,52 @@ void PS2Runtime::run()
std::cout << "Starting execution at address 0x" << std::hex << m_cpuContext.pc << std::dec << std::endl;
try
{
// Call the entry point function
entryPoint(m_memory.getRDRAM(), &m_cpuContext, this);
// A blank image to use as a framebuffer
Image blank = GenImageColor(FB_WIDTH, FB_HEIGHT, BLANK);
Texture2D frameTex = LoadTextureFromImage(blank);
UnloadImage(blank);
std::cout << "Program execution completed successfully" << std::endl;
std::atomic<bool> running{true};
// A blank image to use as a framebuffer
Image blank = GenImageColor(FB_WIDTH, FB_HEIGHT, BLANK);
Texture2D frameTex = LoadTextureFromImage(blank);
UnloadImage(blank);
while (!WindowShouldClose())
std::thread gameThread([&, entryPoint]() {
try
{
// TODO step only a small slice each host
auto self = this;
lookupFunction(memory().read32(0))(
memory().getRDRAM(),
&cpu(),
self);
UploadFrame(frameTex, self);
BeginDrawing();
ClearBackground(BLACK);
DrawTexture(frameTex, 0, 0, WHITE);
EndDrawing();
entryPoint(m_memory.getRDRAM(), &m_cpuContext, this);
}
catch (const std::exception &e)
{
std::cerr << "Error during program execution: " << e.what() << std::endl;
}
running = false;
});
CloseWindow();
}
catch (const std::exception &e)
while (running && !WindowShouldClose())
{
std::cerr << "Error during program execution: " << e.what() << std::endl;
UploadFrame(frameTex, this);
BeginDrawing();
ClearBackground(BLACK);
DrawTexture(frameTex, 0, 0, WHITE);
EndDrawing();
}
}
if (!running)
{
// Game thread finished on its own
if (gameThread.joinable())
{
gameThread.join();
}
}
else
{
// Window was closed while the game thread is still running
if (gameThread.joinable())
{
gameThread.detach();
}
}
UnloadTexture(frameTex);
CloseWindow();
}
+2 -3
View File
@@ -811,8 +811,7 @@ namespace ps2_syscalls
std::cout << "PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec << std::endl;
setReturnS32(ctx, (int32_t)(imr & 0xFFFFFFFF)); // Return lower 32 bits
setReturnS32(ctx, (int32_t)(imr >> 32)); // Return upper 32 bits
setReturnU64(ctx, imr); // Return in $v0/$v1
}
void GsPutIMR(uint8_t *rdram, R5900Context *ctx)
@@ -947,4 +946,4 @@ namespace ps2_syscalls
// Return generic error for unimplemented ones
setReturnS32(ctx, -1); // Return -ENOSYS or similar? Use -1 for simplicity.
}
}
}