mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-10-06 19:52:36 -04:00
Feature/iop emulator (#244)
* refactor: from guest threads to EE scheduler * feat: bad wip mpeg fix for code veronica * feat: cheap copy from host feat: small perf o vsync tick * feat: added EE clock Hz fix: fix MPEG out of sync with new EE refactor * fix: fix lotr tests * fix: fix cri dtx loading fix: fix wrong mmi instruction translation fix: fix thread info params feat: added EE timers decoder and consumer feat: split SFI and IOP memory to prevent collision and overrides * feat: revert wrong changes * refactor: change GS architecture * feat: IOP emulator refactor: codegen to catch callbacks on mips code feat: added a lot of entries or IOP emulator * feat: analyzer resolve the complete constant-producing sequence with five-instruction backward scan stopped at LUI and therefore * feat: remove recompiled version of GetRomName refactor: split IOP emulator logic feat: added more HLE IOP modules feat: added ps2_path * eat: enhance ELF parser with improved callable entry detection and control flow analysis * feat: update memory hint handling and enhance entry point discovery logic * feat: add SET_GPR_ZE32 macro for zero-extending loads with unsigned semantics * refactor: Refactor PS2 IOP Host Adapter and Memory Management feat: Added PS2Vfs for virtual file system operations, including file opening, reading, writing, and path resolution. feat: Improve VIF1 data processing to handle GIF image packets more efficiently. * feat: added a lot of tests * fix: fix texture caching feat: wip multi version on dbcman * feat: remove LLE IOPs
This commit is contained in:
@@ -90,6 +90,7 @@ enum class GuestInvocationKind : uint8_t
|
||||
SyscallOverride,
|
||||
ExitHandler,
|
||||
HleCall,
|
||||
SifCommand,
|
||||
};
|
||||
|
||||
struct GuestInvocation
|
||||
@@ -181,6 +182,9 @@ struct EeThreadSnapshot
|
||||
{
|
||||
int id = 0;
|
||||
uint32_t pc = 0;
|
||||
uint32_t ra = 0;
|
||||
uint32_t sp = 0;
|
||||
uint32_t contextGp = 0;
|
||||
uint32_t entry = 0;
|
||||
uint32_t stack = 0;
|
||||
uint32_t stackSize = 0;
|
||||
@@ -192,6 +196,7 @@ struct EeThreadSnapshot
|
||||
int waitId = 0;
|
||||
int suspendCount = 0;
|
||||
uint32_t wakeupCount = 0;
|
||||
uint32_t invocationDepth = 0;
|
||||
};
|
||||
|
||||
struct EeSemaphoreSnapshot
|
||||
@@ -390,6 +395,8 @@ private:
|
||||
[[nodiscard]] bool hasReadyAtOrAbovePriority(int priority) const;
|
||||
void renewTimeSlice();
|
||||
void copyMainContextToRuntime();
|
||||
void publishDebugContext(const R5900Context &context);
|
||||
void publishIdleDebugContext();
|
||||
|
||||
PS2Runtime &m_runtime;
|
||||
uint8_t *m_rdram = nullptr;
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
virtual void Reset() = 0;
|
||||
|
||||
virtual void Submit(const GSPrimitiveBatch &batch) = 0;
|
||||
virtual void LoadClut(const GSTex0Reg &tex0, const GSTexClutReg &texclut) = 0;
|
||||
|
||||
virtual void BeginTransfer(const GSTransferCommand &command) = 0;
|
||||
virtual void UploadImage(const uint8_t *data, uint32_t sizeBytes) = 0;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "runtime/gs/gs_backend.h"
|
||||
#include "runtime/gs/gs_texture_page_cache.h"
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
void Reset() override;
|
||||
|
||||
void Submit(const GSPrimitiveBatch &batch) override;
|
||||
void LoadClut(const GSTex0Reg &tex0, const GSTexClutReg &texclut) override;
|
||||
void BeginTransfer(const GSTransferCommand &command) override;
|
||||
void UploadImage(const uint8_t *data, uint32_t sizeBytes) override;
|
||||
|
||||
@@ -34,7 +35,9 @@ public:
|
||||
|
||||
private:
|
||||
void ResetUnlocked();
|
||||
void LoadClutUnlocked(const GSTex0Reg &tex0, const GSTexClutReg &texclut);
|
||||
uint32_t ReadVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y) const;
|
||||
uint32_t ReadTextureVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y);
|
||||
void WriteVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y, uint32_t value);
|
||||
|
||||
void DrawPrimitive(const GSPrimitiveBatch &batch);
|
||||
@@ -43,7 +46,7 @@ private:
|
||||
void DrawLine(const GSPrimitiveBatch &batch);
|
||||
void WritePixel(const GSDrawState &state, int x, int y, int z, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t fog);
|
||||
uint32_t SampleTexture(const GSDrawState &state, float s, float t, float q, uint16_t u, uint16_t v);
|
||||
uint32_t LookupCLUT(const GSDrawState &state, uint8_t index, uint32_t cbp, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm);
|
||||
uint32_t LookupCLUT(const GSDrawState &state, uint8_t index, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm);
|
||||
|
||||
void PerformLocalToLocalTransfer();
|
||||
void PerformLocalToHostTransfer();
|
||||
@@ -58,8 +61,8 @@ private:
|
||||
uint32_t sourceOriginX,
|
||||
uint32_t sourceOriginY) const;
|
||||
|
||||
using WriteVramFunc = std::function<void(uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)>;
|
||||
using ReadVramFunc = std::function<uint32_t(uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t)>;
|
||||
using WriteVramFunc = void (*)(uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
||||
using ReadVramFunc = uint32_t (*)(uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t);
|
||||
|
||||
static constexpr size_t kPsmHandlerCount = 1u << 6u;
|
||||
mutable std::mutex m_mutex;
|
||||
@@ -67,6 +70,9 @@ private:
|
||||
uint32_t m_vramSize = 0;
|
||||
std::array<ReadVramFunc, kPsmHandlerCount> m_readVramFuncs{};
|
||||
std::array<WriteVramFunc, kPsmHandlerCount> m_writeVramFuncs{};
|
||||
std::array<uint16_t, 512> m_clut{};
|
||||
std::array<uint32_t, 2> m_clutCbp{};
|
||||
GSMem::TexturePageCache m_texturePageCache;
|
||||
|
||||
GSTransferCommand m_transfer{};
|
||||
GSTransferSnapshot m_transferState{};
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace GSMem
|
||||
{
|
||||
class TexturePageCache
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t kPageSize = 8192u;
|
||||
|
||||
void Invalidate() noexcept
|
||||
{
|
||||
m_pageBase = UINT32_MAX;
|
||||
}
|
||||
|
||||
// byteAddress is the wrapped, swizzled VRAM address. The returned
|
||||
// pointer is valid only until the next miss or invalidation.
|
||||
const uint8_t* Resolve(const uint8_t* vram, uint32_t byteAddress) noexcept
|
||||
{
|
||||
const uint32_t pageBase = byteAddress & ~(kPageSize - 1u);
|
||||
if (m_pageBase != pageBase)
|
||||
{
|
||||
std::memcpy(m_bytes.data(), vram + pageBase, kPageSize);
|
||||
m_pageBase = pageBase;
|
||||
}
|
||||
return m_bytes.data() + (byteAddress & (kPageSize - 1u));
|
||||
}
|
||||
|
||||
private:
|
||||
alignas(64) std::array<uint8_t, kPageSize> m_bytes{};
|
||||
uint32_t m_pageBase = UINT32_MAX;
|
||||
};
|
||||
}
|
||||
@@ -7,11 +7,12 @@
|
||||
#include <span>
|
||||
|
||||
#include "types.h"
|
||||
#include "runtime/gs/gs_texture_page_cache.h"
|
||||
|
||||
namespace GSMem
|
||||
{
|
||||
constexpr usz MEMORY_SIZE = 4_mb;
|
||||
constexpr usz GS_PAGE_SIZE = 8_kb;
|
||||
constexpr usz GS_PAGE_SIZE = TexturePageCache::kPageSize;
|
||||
|
||||
// these are all the same regardless of storage mode
|
||||
constexpr usz BLOCKS_PER_PAGE = 32;
|
||||
@@ -261,7 +262,7 @@ namespace GSMem
|
||||
static constexpr void Write(const PageLookupTableT& table, u8* data, u32 block, u32 bw, u32 x, u32 y, PackedT value);
|
||||
|
||||
// reads the pixel
|
||||
static constexpr auto Read(const PageLookupTableT& table, u8* data, u32 block, u32 bw, u32 x, u32 y) -> PackedT;
|
||||
static constexpr auto Read(const PageLookupTableT& table, const u8* data, u32 block, u32 bw, u32 x, u32 y, TexturePageCache* cache = nullptr) -> PackedT;
|
||||
|
||||
static_assert(BlocksPerPage() == BLOCKS_PER_PAGE);
|
||||
static_assert(IsValidPsm(psm));
|
||||
@@ -501,15 +502,16 @@ namespace GSMem
|
||||
}
|
||||
|
||||
template<PixelStorageMode psm>
|
||||
constexpr auto PixelStorageTraits<psm>::Read(const PageLookupTableT& table, u8* data, u32 block, u32 bw, u32 x, u32 y) -> PackedT
|
||||
constexpr auto PixelStorageTraits<psm>::Read(const PageLookupTableT& table, const u8* data, u32 block, u32 bw, u32 x, u32 y, TexturePageCache* cache) -> PackedT
|
||||
{
|
||||
const u32 pixel_addr = Address(table, block, bw, x, y);
|
||||
const u32 bits = pixel_addr * UnpackedBitWidth(psm) + BitOffset();
|
||||
const u32 byte_addr = (bits / 8) & (MEMORY_SIZE - sizeof(PackedT));
|
||||
const u32 shift = bits % 8;
|
||||
|
||||
const u8* source = cache ? cache->Resolve(data, byte_addr) : data + byte_addr;
|
||||
PackedT v;
|
||||
std::memcpy(&v, &data[byte_addr], sizeof(PackedT));
|
||||
std::memcpy(&v, source, sizeof(PackedT));
|
||||
|
||||
switch (psm)
|
||||
{
|
||||
@@ -533,11 +535,14 @@ namespace GSMem
|
||||
break;
|
||||
}
|
||||
|
||||
return 0xFFFF00FFu;
|
||||
return static_cast<PackedT>(0xFFFF00FFu);
|
||||
}
|
||||
|
||||
void InitLookupTables();
|
||||
|
||||
// Shares swizzle, VRAM wrapping, and lane extraction with the direct reads.
|
||||
u32 ReadTexture(TexturePageCache& cache, const u8* data, u32 psm, u32 bp, u32 bw, u32 x, u32 y);
|
||||
|
||||
void WriteCT32(u8* data, u32 bp, u32 bw, u32 x, u32 y, u32 value);
|
||||
void WriteZ32(u8* data, u32 bp, u32 bw, u32 x, u32 y, u32 value);
|
||||
|
||||
|
||||
@@ -449,6 +449,8 @@ public:
|
||||
};
|
||||
|
||||
std::array<EeTimer, 4> m_eeTimers{};
|
||||
bool tryProcessScratchpadDma(uint32_t channelBase, uint32_t chcr);
|
||||
void completeDmacChannel(uint32_t channelBase, uint32_t cause);
|
||||
void queueCompletedDmacCause(uint32_t cause);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "ps2x/iop/iop_types.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct PS2RomProfile
|
||||
{
|
||||
std::string id;
|
||||
std::string provider = "application";
|
||||
ps2x::iop::GameMatcher matcher;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> files;
|
||||
};
|
||||
|
||||
class PS2RomDevice
|
||||
{
|
||||
public:
|
||||
PS2RomDevice();
|
||||
|
||||
static void registerProfile(PS2RomProfile profile);
|
||||
|
||||
bool configure(const ps2x::iop::GameIdentity &identity, std::string *error = nullptr);
|
||||
[[nodiscard]] bool readFile(std::string_view ps2Path, std::vector<uint8_t> &bytes) const;
|
||||
[[nodiscard]] bool fileSize(std::string_view ps2Path, uint64_t &size) const;
|
||||
[[nodiscard]] bool contains(std::string_view ps2Path) const;
|
||||
[[nodiscard]] std::string_view activeProfile() const noexcept { return m_activeProfile; }
|
||||
[[nodiscard]] std::string_view activeProvider() const noexcept { return m_activeProvider; }
|
||||
|
||||
private:
|
||||
static std::string normalizePath(std::string_view path);
|
||||
void mountBaseProfile();
|
||||
void mountFiles(const std::unordered_map<std::string, std::vector<uint8_t>> &files);
|
||||
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> m_files;
|
||||
std::string m_activeProfile;
|
||||
std::string m_activeProvider;
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "ps2x/iop/ps2_path.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class PS2RomDevice;
|
||||
|
||||
struct PS2VfsMounts
|
||||
{
|
||||
std::filesystem::path hostRoot;
|
||||
std::filesystem::path cdRoot;
|
||||
std::filesystem::path memoryCard0Root;
|
||||
};
|
||||
|
||||
struct PS2VfsStat
|
||||
{
|
||||
bool directory = false;
|
||||
bool readOnly = false;
|
||||
uint64_t size = 0u;
|
||||
std::time_t created = 0;
|
||||
std::time_t accessed = 0;
|
||||
std::time_t modified = 0;
|
||||
};
|
||||
|
||||
struct PS2VfsDescriptorInfo
|
||||
{
|
||||
int32_t descriptor = -1;
|
||||
std::string device;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class IPS2OpenFile
|
||||
{
|
||||
public:
|
||||
virtual ~IPS2OpenFile() = default;
|
||||
|
||||
[[nodiscard]] virtual int64_t read(void *destination, size_t size) = 0;
|
||||
[[nodiscard]] virtual int64_t write(const void *source, size_t size) = 0;
|
||||
[[nodiscard]] virtual int64_t seek(int64_t offset, int whence) = 0;
|
||||
};
|
||||
|
||||
class PS2Vfs
|
||||
{
|
||||
public:
|
||||
PS2Vfs() = default;
|
||||
~PS2Vfs();
|
||||
|
||||
PS2Vfs(const PS2Vfs &) = delete;
|
||||
PS2Vfs &operator=(const PS2Vfs &) = delete;
|
||||
|
||||
[[nodiscard]] int32_t open(std::string_view path, uint32_t flags, const PS2VfsMounts &mounts, const PS2RomDevice &rom);
|
||||
[[nodiscard]] int32_t close(int32_t descriptor);
|
||||
[[nodiscard]] int64_t read(int32_t descriptor, void *destination, size_t size);
|
||||
[[nodiscard]] int64_t write(int32_t descriptor, const void *source, size_t size);
|
||||
[[nodiscard]] int64_t seek(int32_t descriptor, int64_t offset, int whence);
|
||||
|
||||
[[nodiscard]] bool stat(std::string_view path, const PS2VfsMounts &mounts, const PS2RomDevice &rom, PS2VfsStat &result) const;
|
||||
[[nodiscard]] bool resolveHostPath(std::string_view path, const PS2VfsMounts &mounts, std::filesystem::path &result) const;
|
||||
[[nodiscard]] std::vector<PS2VfsDescriptorInfo> descriptors() const;
|
||||
|
||||
private:
|
||||
struct OpenDescriptor
|
||||
{
|
||||
std::unique_ptr<IPS2OpenFile> file;
|
||||
std::string device;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
std::unordered_map<int32_t, OpenDescriptor> m_descriptors;
|
||||
int32_t m_nextDescriptor = 3;
|
||||
};
|
||||
Reference in New Issue
Block a user