mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-10-02 02:31:51 -04:00
Feature/agressive recompiler (#146)
* feat: added guestBranchKind enum to categorize branch types feat: added missingFunctionPolicy enum to define behaviors for missing function scenarios refactor: added handle guest branches and report missing functions feat lookupFunction to utilize new dispatch logic and improve error handling for unregistered functions * fix: fix test conflict * feat: added debug sound driver logs * feat: emmiter for return * feat: added recompiler reporter feat: added strict diagnostics flag for heavy debug calls * feat: staticc table insted of hashmap for runtime * feat: back file to ignore * feat: explode code across helpers and classes * feat: update codegen test feat: better guest nop check * feat: fix link problem on linux * feat: fix Segmentation fault * feat: added recompile replace for DMA and MMIO feat: added a clean memory helpers feat: use memory helpers across the project feat: fix ucrt on msvc * feat: undo messup merge
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "Common.h"
|
||||
#include "SIF.h"
|
||||
#include "../Syscalls/RPC.h"
|
||||
#include "runtime/ps2_address.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -186,22 +187,22 @@ namespace ps2_stubs
|
||||
|
||||
bool isCopyableGuestAddress(uint32_t addr)
|
||||
{
|
||||
if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
|
||||
if (Ps2AddressInRange(addr, PS2_SCRATCHPAD_BASE, PS2_SCRATCHPAD_SIZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (addr < 0x20000000u)
|
||||
if (addr < PS2_EE_UNCACHED_RAM_MIRROR_BASE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (addr >= 0x20000000u && addr < 0x40000000u)
|
||||
if (Ps2IsUncachedRamMirrorAddress(addr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (addr >= 0x80000000u && addr < 0xC0000000u)
|
||||
if (Ps2IsKseg01Address(addr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,63 @@ namespace
|
||||
return v;
|
||||
}
|
||||
|
||||
struct PackedGifPacketTag
|
||||
{
|
||||
uint64_t lo = 0u;
|
||||
uint64_t hi = 0u;
|
||||
uint32_t payloadOffset = 0u;
|
||||
uint32_t nloop = 0u;
|
||||
uint32_t nreg = 0u;
|
||||
uint8_t regs[16]{};
|
||||
};
|
||||
|
||||
template <typename Visitor>
|
||||
bool visitPackedGifPacket(const uint8_t *data, uint32_t sizeBytes, Visitor &&visitor)
|
||||
{
|
||||
uint32_t offset = 0u;
|
||||
while (offset + 16u <= sizeBytes)
|
||||
{
|
||||
PackedGifPacketTag tag{};
|
||||
tag.lo = loadLE64(data + offset);
|
||||
tag.hi = loadLE64(data + offset + 8u);
|
||||
|
||||
const uint8_t flg = static_cast<uint8_t>((tag.lo >> 58u) & 0x3u);
|
||||
if (flg != GIF_FMT_PACKED)
|
||||
return false;
|
||||
|
||||
tag.nloop = static_cast<uint32_t>(tag.lo & 0x7FFFu);
|
||||
tag.nreg = static_cast<uint32_t>((tag.lo >> 60u) & 0xFu);
|
||||
if (tag.nreg == 0u)
|
||||
tag.nreg = 16u;
|
||||
|
||||
const uint64_t payloadBytes64 =
|
||||
static_cast<uint64_t>(tag.nloop) * static_cast<uint64_t>(tag.nreg) * 16ull;
|
||||
if (payloadBytes64 > 0xFFFFFFFFull)
|
||||
return false;
|
||||
|
||||
offset += 16u;
|
||||
const uint32_t payloadBytes = static_cast<uint32_t>(payloadBytes64);
|
||||
if (payloadBytes > sizeBytes - offset)
|
||||
return false;
|
||||
|
||||
tag.payloadOffset = offset;
|
||||
for (uint32_t i = 0u; i < tag.nreg; ++i)
|
||||
tag.regs[i] = static_cast<uint8_t>((tag.hi >> (i * 4u)) & 0xFu);
|
||||
|
||||
if (!visitor(tag))
|
||||
return false;
|
||||
|
||||
offset += payloadBytes;
|
||||
}
|
||||
|
||||
return offset == sizeBytes;
|
||||
}
|
||||
|
||||
bool validatePackedGifPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
{
|
||||
return visitPackedGifPacket(data, sizeBytes, [](const PackedGifPacketTag &) { return true; });
|
||||
}
|
||||
|
||||
void decodeDisplaySize(uint64_t display64, uint32_t &outWidth, uint32_t &outHeight)
|
||||
{
|
||||
const uint32_t dx = static_cast<uint32_t>((display64 >> 0) & 0x0FFFu);
|
||||
@@ -1221,6 +1278,9 @@ void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
if (!data || sizeBytes < 16 || !m_vram)
|
||||
return;
|
||||
|
||||
if (tryProcessNativeImageUploadPacket(data, sizeBytes))
|
||||
return;
|
||||
|
||||
PS2_IF_AGRESSIVE_LOGS({
|
||||
const uint32_t packetIndex = s_debugGifPacketCount.fetch_add(1, std::memory_order_relaxed);
|
||||
if (packetIndex < 48u)
|
||||
@@ -1311,6 +1371,136 @@ void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
}
|
||||
}
|
||||
|
||||
bool GS::processNativePackedGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!data || sizeBytes < 16u || !m_vram)
|
||||
return false;
|
||||
|
||||
if (!validatePackedGifPacket(data, sizeBytes))
|
||||
return false;
|
||||
|
||||
const bool processed = visitPackedGifPacket(data, sizeBytes, [&](const PackedGifPacketTag &tag)
|
||||
{
|
||||
m_curQ = 1.0f;
|
||||
|
||||
recordGifTagDebugEventUnlocked(sizeBytes, tag.nloop, GIF_FMT_PACKED, tag.nreg);
|
||||
|
||||
const bool pre = ((tag.lo >> 46u) & 1u) != 0u;
|
||||
if (pre)
|
||||
writeRegister(GS_REG_PRIM, (tag.lo >> 47u) & 0x7FFu);
|
||||
|
||||
uint32_t offset = tag.payloadOffset;
|
||||
for (uint32_t loop = 0u; loop < tag.nloop; ++loop)
|
||||
{
|
||||
for (uint32_t r = 0u; r < tag.nreg; ++r)
|
||||
{
|
||||
const uint64_t lo = loadLE64(data + offset);
|
||||
const uint64_t hi = loadLE64(data + offset + 8u);
|
||||
offset += 16u;
|
||||
writeRegisterPacked(tag.regs[r], lo, hi);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!processed)
|
||||
return false;
|
||||
|
||||
++m_nativePackedGIFPacketCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GS::uploadImageNative(uint64_t bitbltbuf,
|
||||
uint64_t trxpos,
|
||||
uint64_t trxreg,
|
||||
uint64_t trxdir,
|
||||
const uint8_t *data,
|
||||
uint32_t sizeBytes)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!data || sizeBytes == 0 || !m_vram)
|
||||
return;
|
||||
|
||||
writeRegister(GS_REG_BITBLTBUF, bitbltbuf);
|
||||
writeRegister(GS_REG_TRXPOS, trxpos);
|
||||
writeRegister(GS_REG_TRXREG, trxreg);
|
||||
writeRegister(GS_REG_TRXDIR, trxdir);
|
||||
processImageData(data, sizeBytes);
|
||||
++m_nativeImageUploadCount;
|
||||
}
|
||||
|
||||
bool GS::tryProcessNativeImageUploadPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
{
|
||||
constexpr uint32_t kSetupRegisters = 4u;
|
||||
constexpr uint32_t kPackedAdPayloadBytes = kSetupRegisters * 16u;
|
||||
constexpr uint64_t kPackedAdDescriptor = 0x0Eull;
|
||||
|
||||
if (!data || sizeBytes < 16u + kPackedAdPayloadBytes + 16u)
|
||||
return false;
|
||||
|
||||
const uint64_t setupTagLo = loadLE64(data);
|
||||
const uint64_t setupTagHi = loadLE64(data + 8u);
|
||||
const uint32_t setupNloop = static_cast<uint32_t>(setupTagLo & 0x7FFFu);
|
||||
const uint8_t setupFlg = static_cast<uint8_t>((setupTagLo >> 58u) & 0x3u);
|
||||
uint32_t setupNreg = static_cast<uint32_t>((setupTagLo >> 60u) & 0xFu);
|
||||
if (setupNreg == 0u)
|
||||
setupNreg = 16u;
|
||||
|
||||
if (setupNloop != kSetupRegisters ||
|
||||
setupFlg != GIF_FMT_PACKED ||
|
||||
setupNreg != 1u ||
|
||||
(setupTagHi & 0xFull) != kPackedAdDescriptor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t regs[kSetupRegisters] = {};
|
||||
uint32_t offset = 16u;
|
||||
constexpr uint8_t expectedRegs[kSetupRegisters] = {
|
||||
GS_REG_BITBLTBUF,
|
||||
GS_REG_TRXPOS,
|
||||
GS_REG_TRXREG,
|
||||
GS_REG_TRXDIR,
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < kSetupRegisters; ++i)
|
||||
{
|
||||
regs[i] = loadLE64(data + offset);
|
||||
const uint64_t reg = loadLE64(data + offset + 8u);
|
||||
if ((reg & 0xFFu) != expectedRegs[i])
|
||||
return false;
|
||||
offset += 16u;
|
||||
}
|
||||
|
||||
const uint32_t trxdirMode = static_cast<uint32_t>(regs[3] & 0x3ull);
|
||||
const uint32_t rrw = static_cast<uint32_t>(regs[2] & 0xFFFull);
|
||||
const uint32_t rrh = static_cast<uint32_t>((regs[2] >> 32u) & 0xFFFull);
|
||||
if (trxdirMode != 0u || rrw == 0u || rrh == 0u)
|
||||
return false;
|
||||
|
||||
if (offset + 16u > sizeBytes)
|
||||
return false;
|
||||
|
||||
const uint64_t imageTagLo = loadLE64(data + offset);
|
||||
const uint8_t imageFlg = static_cast<uint8_t>((imageTagLo >> 58u) & 0x3u);
|
||||
const uint32_t imageNloop = static_cast<uint32_t>(imageTagLo & 0x7FFFu);
|
||||
if (imageFlg != GIF_FMT_IMAGE || imageNloop == 0u)
|
||||
return false;
|
||||
|
||||
offset += 16u;
|
||||
const uint64_t imageBytes64 = static_cast<uint64_t>(imageNloop) * 16ull;
|
||||
if (imageBytes64 > 0xFFFFFFFFull)
|
||||
return false;
|
||||
const uint32_t imageBytes = static_cast<uint32_t>(imageBytes64);
|
||||
if (offset + imageBytes != sizeBytes)
|
||||
return false;
|
||||
|
||||
uploadImageNative(regs[0], regs[1], regs[2], regs[3], data + offset, imageBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi)
|
||||
{
|
||||
switch (regDesc)
|
||||
|
||||
@@ -791,8 +791,10 @@ void GSRasterizer::drawSprite(GS *gs)
|
||||
uint32_t texel = 0xFFFF00FFu;
|
||||
if (gs->m_prim.fst)
|
||||
{
|
||||
const uint16_t sampleU = static_cast<uint16_t>(clampInt(static_cast<int>(std::lround(texUf * 16.0f)), 0, 0xFFFF));
|
||||
const uint16_t sampleV = static_cast<uint16_t>(clampInt(static_cast<int>(std::lround(texVf * 16.0f)), 0, 0xFFFF));
|
||||
const int fixedU = static_cast<int>((texUf * 16.0f) + 0.5f);
|
||||
const int fixedV = static_cast<int>((texVf * 16.0f) + 0.5f);
|
||||
const uint16_t sampleU = static_cast<uint16_t>(clampInt(fixedU, 0, 0xFFFF));
|
||||
const uint16_t sampleV = static_cast<uint16_t>(clampInt(fixedV, 0, 0xFFFF));
|
||||
texel = sampleTexture(gs, 0.0f, 0.0f, 1.0f, sampleU, sampleV);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "runtime/ps2_memory.h"
|
||||
#include "runtime/ps2_address.h"
|
||||
#include "runtime/ps2_gs_gpu.h"
|
||||
#include "ps2_log.h"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
@@ -36,7 +38,12 @@ namespace
|
||||
|
||||
inline bool isGsPrivReg(uint32_t addr)
|
||||
{
|
||||
return addr >= PS2_GS_PRIV_REG_BASE && addr < PS2_GS_PRIV_REG_BASE + PS2_GS_PRIV_REG_SIZE;
|
||||
return Ps2AddressInRange(addr, PS2_GS_PRIV_REG_BASE, PS2_GS_PRIV_REG_SIZE);
|
||||
}
|
||||
|
||||
inline bool isIoRegister(uint32_t addr)
|
||||
{
|
||||
return Ps2AddressInRange(addr, PS2_IO_BASE, PS2_IO_SIZE);
|
||||
}
|
||||
|
||||
inline uint64_t *gsRegPtr(GSRegisters &gs, uint32_t addr)
|
||||
@@ -156,6 +163,42 @@ namespace
|
||||
return static_cast<uint64_t>(duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
struct DmaTagView
|
||||
{
|
||||
uint16_t qwc = 0;
|
||||
uint8_t id = 0;
|
||||
bool irq = false;
|
||||
uint32_t addr = 0;
|
||||
uint32_t upper = 0;
|
||||
};
|
||||
|
||||
inline DmaTagView decodeDmaTag(uint64_t tag)
|
||||
{
|
||||
DmaTagView out{};
|
||||
out.qwc = static_cast<uint16_t>(tag & 0xFFFFu);
|
||||
out.id = static_cast<uint8_t>((tag >> 28u) & 0x7u);
|
||||
out.irq = ((tag >> 31u) & 0x1ull) != 0ull;
|
||||
out.addr = static_cast<uint32_t>((tag >> 32u) & 0x7FFFFFFFu);
|
||||
out.upper = static_cast<uint32_t>((tag >> 16u) & 0xFFFFu);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline uint32_t gifTagNloop(uint64_t tagLo)
|
||||
{
|
||||
return static_cast<uint32_t>(tagLo & 0x7FFFu);
|
||||
}
|
||||
|
||||
inline uint8_t gifTagFlg(uint64_t tagLo)
|
||||
{
|
||||
return static_cast<uint8_t>((tagLo >> 58u) & 0x3u);
|
||||
}
|
||||
|
||||
inline uint32_t gifTagNreg(uint64_t tagLo)
|
||||
{
|
||||
uint32_t nreg = static_cast<uint32_t>((tagLo >> 60u) & 0xFu);
|
||||
return nreg == 0u ? 16u : nreg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Helpers for GS VRAM addressing (PSMCT32 path).
|
||||
@@ -412,15 +455,15 @@ uint32_t PS2Memory::translateAddress(uint32_t virtualAddress)
|
||||
// EE uncached aliases of main RAM (per PS2 memory map):
|
||||
// 0x20000000-0x3FFFFFFF -> 32MB mirror of RDRAM
|
||||
// This includes the accelerated window rooted at 0x30100000.
|
||||
if (virtualAddress >= 0x20000000u && virtualAddress < 0x40000000u)
|
||||
if (Ps2IsUncachedRamMirrorAddress(virtualAddress))
|
||||
{
|
||||
return virtualAddress & PS2_RAM_MASK;
|
||||
}
|
||||
|
||||
// KSEG0/KSEG1 direct-mapped window.
|
||||
if (virtualAddress >= 0x80000000 && virtualAddress < 0xC0000000)
|
||||
if (Ps2IsKseg01Address(virtualAddress))
|
||||
{
|
||||
return virtualAddress & 0x1FFFFFFF;
|
||||
return Ps2DirectMappedPhysicalAddress(virtualAddress);
|
||||
}
|
||||
|
||||
// In this runtime, low segments are treated as physical-style addresses already.
|
||||
@@ -430,7 +473,7 @@ uint32_t PS2Memory::translateAddress(uint32_t virtualAddress)
|
||||
}
|
||||
|
||||
// KSEG2/KSEG3 are TLB mapped.
|
||||
if (virtualAddress >= 0xC0000000)
|
||||
if (Ps2IsKseg23Address(virtualAddress))
|
||||
{
|
||||
for (const auto &entry : m_tlbEntries)
|
||||
{
|
||||
@@ -526,7 +569,7 @@ uint8_t PS2Memory::read8(uint32_t address)
|
||||
(void)vuLimit;
|
||||
return vuMem[vuOffset];
|
||||
}
|
||||
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
else if (isIoRegister(physAddr))
|
||||
{
|
||||
uint32_t regAddr = physAddr & ~0x3;
|
||||
uint32_t value = readIORegister(regAddr);
|
||||
@@ -561,7 +604,7 @@ uint16_t PS2Memory::read16(uint32_t address)
|
||||
{
|
||||
return loadScalar<uint16_t>(vuMem, vuOffset, vuLimit, "read16 vu", address);
|
||||
}
|
||||
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
else if (isIoRegister(physAddr))
|
||||
{
|
||||
uint32_t regAddr = physAddr & ~0x3;
|
||||
uint32_t value = readIORegister(regAddr);
|
||||
@@ -612,7 +655,7 @@ uint32_t PS2Memory::read32(uint32_t address)
|
||||
{
|
||||
return loadScalar<uint32_t>(vuMem, vuOffset, vuLimit, "read32 vu", address);
|
||||
}
|
||||
else if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
else if (isIoRegister(physAddr))
|
||||
{
|
||||
return readIORegister(physAddr);
|
||||
}
|
||||
@@ -658,7 +701,7 @@ uint64_t PS2Memory::read64(uint32_t address)
|
||||
|
||||
// 64-bit IO read: compose from the two adjacent 32-bit IO register slots
|
||||
// to avoid any side-effects from read32 handlers.
|
||||
if (address >= PS2_IO_BASE && address < (PS2_IO_BASE + PS2_IO_SIZE))
|
||||
if (isIoRegister(address))
|
||||
{
|
||||
uint32_t lo = m_ioRegisters.count(address) ? m_ioRegisters[address] : 0u;
|
||||
uint32_t hi = m_ioRegisters.count(address + 4) ? m_ioRegisters[address + 4] : 0u;
|
||||
@@ -724,7 +767,7 @@ void PS2Memory::write8(uint32_t address, uint8_t value)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
if (isIoRegister(physAddr))
|
||||
{
|
||||
// IO registers - handle byte writes by modifying the appropriate byte in the word
|
||||
uint32_t regAddr = physAddr & ~0x3;
|
||||
@@ -763,7 +806,7 @@ void PS2Memory::write16(uint32_t address, uint16_t value)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
if (isIoRegister(physAddr))
|
||||
{
|
||||
uint32_t regAddr = physAddr & ~0x3;
|
||||
uint32_t shift = (physAddr & 2) * 8;
|
||||
@@ -823,7 +866,7 @@ void PS2Memory::write32(uint32_t address, uint32_t value)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
if (isIoRegister(physAddr))
|
||||
{
|
||||
writeIORegister(physAddr, value);
|
||||
}
|
||||
@@ -874,7 +917,7 @@ void PS2Memory::write64(uint32_t address, uint64_t value)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
if (isIoRegister(physAddr))
|
||||
{
|
||||
write32(address, (uint32_t)value);
|
||||
write32(address + 4, (uint32_t)(value >> 32));
|
||||
@@ -913,7 +956,7 @@ void PS2Memory::write128(uint32_t address, __m128i value)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (physAddr >= PS2_IO_BASE && physAddr < PS2_IO_BASE + PS2_IO_SIZE)
|
||||
if (isIoRegister(physAddr))
|
||||
{
|
||||
// Non-RAM 128-bit stores are modeled as two 64-bit stores.
|
||||
uint64_t lo = _mm_extract_epi64(value, 0);
|
||||
@@ -1693,6 +1736,278 @@ void PS2Memory::processGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
m_gifPacketCallback(data, sizeBytes);
|
||||
}
|
||||
|
||||
bool PS2Memory::tryProcessNativeGifImageUploadChain(GS &gs, uint32_t tadr, uint32_t chcr)
|
||||
{
|
||||
static constexpr uint32_t GIF_CHANNEL = 0x1000A000u;
|
||||
static constexpr uint32_t D_STAT = 0x1000E010u;
|
||||
static constexpr uint32_t D_CTRL = 0x1000E000u;
|
||||
|
||||
if (!m_rdram || !m_gsVRAM || m_path3Masked)
|
||||
return false;
|
||||
if (m_gifArbiter && !m_gifArbiter->empty())
|
||||
return false;
|
||||
if ((chcr & 0x100u) == 0u || ((chcr >> 2u) & 0x3u) != 1u)
|
||||
return false;
|
||||
if ((chcr & (1u << 7u)) != 0u || ((chcr >> 4u) & 0x3u) != 0u)
|
||||
return false;
|
||||
|
||||
const auto dctrlIt = m_ioRegisters.find(D_CTRL);
|
||||
if (dctrlIt != m_ioRegisters.end() && ((dctrlIt->second & 0x1u) == 0u))
|
||||
return false;
|
||||
|
||||
auto resolveContiguous = [&](uint32_t guestAddr, uint32_t bytes, const uint8_t *&out) -> bool
|
||||
{
|
||||
try
|
||||
{
|
||||
const bool scratch = isScratchpad(guestAddr);
|
||||
const uint32_t phys = translateAddress(guestAddr);
|
||||
const uint8_t *base = scratch ? m_scratchpad : m_rdram;
|
||||
const uint32_t limit = scratch ? PS2_SCRATCHPAD_SIZE : PS2_RAM_SIZE;
|
||||
if (!base || phys > limit || bytes > limit - phys)
|
||||
return false;
|
||||
out = base + phys;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto loadDmaTagAt = [&](uint32_t guestAddr, DmaTagView &out) -> bool
|
||||
{
|
||||
const uint8_t *ptr = nullptr;
|
||||
if (!resolveContiguous(guestAddr, 16u, ptr))
|
||||
return false;
|
||||
out = decodeDmaTag(loadScalar<uint64_t>(ptr, 0u, 16u, "native gif dma tag", guestAddr));
|
||||
return true;
|
||||
};
|
||||
|
||||
auto decodeSetupPayload = [&](const uint8_t *payload, uint64_t (®s)[4]) -> bool
|
||||
{
|
||||
const uint64_t tagLo = loadScalar<uint64_t>(payload, 0u, 80u, "native gif setup tag", 0u);
|
||||
const uint64_t tagHi = loadScalar<uint64_t>(payload, 8u, 80u, "native gif setup regs", 0u);
|
||||
if (gifTagNloop(tagLo) != 4u ||
|
||||
gifTagFlg(tagLo) != GIF_FMT_PACKED ||
|
||||
gifTagNreg(tagLo) != 1u ||
|
||||
(tagHi & 0xFull) != 0x0Eull)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr uint8_t kExpectedRegs[4] = {
|
||||
GS_REG_BITBLTBUF,
|
||||
GS_REG_TRXPOS,
|
||||
GS_REG_TRXREG,
|
||||
GS_REG_TRXDIR,
|
||||
};
|
||||
|
||||
uint32_t offset = 16u;
|
||||
for (uint32_t i = 0; i < 4u; ++i)
|
||||
{
|
||||
regs[i] = loadScalar<uint64_t>(payload, offset, 80u, "native gif setup value", 0u);
|
||||
const uint64_t reg = loadScalar<uint64_t>(payload, offset + 8u, 80u, "native gif setup register", 0u);
|
||||
if ((reg & 0xFFu) != kExpectedRegs[i])
|
||||
return false;
|
||||
offset += 16u;
|
||||
}
|
||||
|
||||
const uint32_t trxdirMode = static_cast<uint32_t>(regs[3] & 0x3ull);
|
||||
const uint32_t rrw = static_cast<uint32_t>(regs[2] & 0xFFFull);
|
||||
const uint32_t rrh = static_cast<uint32_t>((regs[2] >> 32u) & 0xFFFull);
|
||||
return trxdirMode == 0u && rrw != 0u && rrh != 0u;
|
||||
};
|
||||
|
||||
DmaTagView setupTag{};
|
||||
if (!loadDmaTagAt(tadr, setupTag) ||
|
||||
setupTag.id != 1u ||
|
||||
setupTag.qwc != 5u ||
|
||||
setupTag.irq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *setupPayload = nullptr;
|
||||
const uint32_t setupPayloadAddr = tadr + 16u;
|
||||
if (!resolveContiguous(setupPayloadAddr, 5u * 16u, setupPayload))
|
||||
return false;
|
||||
|
||||
uint64_t setupRegs[4] = {};
|
||||
if (!decodeSetupPayload(setupPayload, setupRegs))
|
||||
return false;
|
||||
|
||||
uint32_t imageTagDmaAddr = setupPayloadAddr + 5u * 16u;
|
||||
DmaTagView imageTagDma{};
|
||||
if (!loadDmaTagAt(imageTagDmaAddr, imageTagDma) ||
|
||||
imageTagDma.id != 1u ||
|
||||
imageTagDma.qwc != 1u ||
|
||||
imageTagDma.irq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *imageGifTag = nullptr;
|
||||
if (!resolveContiguous(imageTagDmaAddr + 16u, 16u, imageGifTag))
|
||||
return false;
|
||||
|
||||
const uint64_t imageTagLo = loadScalar<uint64_t>(imageGifTag, 0u, 16u, "native gif image tag", imageTagDmaAddr + 16u);
|
||||
if (gifTagFlg(imageTagLo) != GIF_FMT_IMAGE)
|
||||
return false;
|
||||
|
||||
const uint32_t imageQwc = gifTagNloop(imageTagLo);
|
||||
if (imageQwc == 0u)
|
||||
return false;
|
||||
|
||||
const uint64_t imageBytes64 = static_cast<uint64_t>(imageQwc) * 16ull;
|
||||
if (imageBytes64 > 0xFFFFFFFFull)
|
||||
return false;
|
||||
const uint32_t imageBytes = static_cast<uint32_t>(imageBytes64);
|
||||
|
||||
const uint32_t payloadTagAddr = imageTagDmaAddr + 32u;
|
||||
DmaTagView payloadTag{};
|
||||
if (!loadDmaTagAt(payloadTagAddr, payloadTag) ||
|
||||
payloadTag.qwc != imageQwc ||
|
||||
payloadTag.irq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t imageDataAddr = 0u;
|
||||
uint32_t finalTadr = payloadTagAddr;
|
||||
uint32_t lastTagUpper = payloadTag.upper;
|
||||
if (payloadTag.id == 3u || payloadTag.id == 4u)
|
||||
{
|
||||
imageDataAddr = payloadTag.addr;
|
||||
const uint32_t terminalTagAddr = payloadTagAddr + 16u;
|
||||
DmaTagView terminalTag{};
|
||||
if (!loadDmaTagAt(terminalTagAddr, terminalTag) ||
|
||||
terminalTag.qwc != 0u ||
|
||||
terminalTag.irq ||
|
||||
(terminalTag.id != 0u && terminalTag.id != 7u))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finalTadr = (terminalTag.id == 0u) ? (terminalTagAddr + 16u) : terminalTagAddr;
|
||||
lastTagUpper = terminalTag.upper;
|
||||
}
|
||||
else if (payloadTag.id == 7u)
|
||||
{
|
||||
imageDataAddr = payloadTagAddr + 16u;
|
||||
finalTadr = payloadTagAddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *imageData = nullptr;
|
||||
if (!resolveContiguous(imageDataAddr, imageBytes, imageData))
|
||||
return false;
|
||||
|
||||
m_dmaStartCount.fetch_add(1, std::memory_order_relaxed);
|
||||
m_seenGifCopy = true;
|
||||
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
|
||||
gs.uploadImageNative(setupRegs[0], setupRegs[1], setupRegs[2], setupRegs[3], imageData, imageBytes);
|
||||
|
||||
m_ioRegisters[GIF_CHANNEL + 0x30u] = finalTadr;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x40u] = 0u;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x50u] = 0u;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x00u] = ((chcr & 0x0000FFFFu) | (lastTagUpper << 16u)) & ~0x100u;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x20u] = 0u;
|
||||
|
||||
uint32_t dstat = m_ioRegisters.count(D_STAT) ? m_ioRegisters[D_STAT] : 0u;
|
||||
dstat |= (1u << 2u);
|
||||
const uint32_t status = dstat & 0x3FFu;
|
||||
const uint32_t mask = (dstat >> 16u) & 0x3FFu;
|
||||
if ((status & mask) != 0u)
|
||||
dstat |= (1u << 31u);
|
||||
else
|
||||
dstat &= ~(1u << 31u);
|
||||
m_ioRegisters[D_STAT] = dstat;
|
||||
queueCompletedDmacCause(2u);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PS2Memory::tryProcessNativeGifPackedChain(GS &gs, uint32_t tadr, uint32_t chcr)
|
||||
{
|
||||
static constexpr uint32_t GIF_CHANNEL = 0x1000A000u;
|
||||
static constexpr uint32_t D_STAT = 0x1000E010u;
|
||||
static constexpr uint32_t D_CTRL = 0x1000E000u;
|
||||
|
||||
if (!m_rdram || !m_gsVRAM || m_path3Masked)
|
||||
return false;
|
||||
if (m_gifArbiter && !m_gifArbiter->empty())
|
||||
return false;
|
||||
if ((chcr & 0x100u) == 0u || ((chcr >> 2u) & 0x3u) != 1u)
|
||||
return false;
|
||||
if ((chcr & (1u << 7u)) != 0u || ((chcr >> 4u) & 0x3u) != 0u)
|
||||
return false;
|
||||
|
||||
const auto dctrlIt = m_ioRegisters.find(D_CTRL);
|
||||
if (dctrlIt != m_ioRegisters.end() && ((dctrlIt->second & 0x1u) == 0u))
|
||||
return false;
|
||||
|
||||
auto resolveContiguous = [&](uint32_t guestAddr, uint32_t bytes, const uint8_t *&out) -> bool
|
||||
{
|
||||
try
|
||||
{
|
||||
const bool scratch = isScratchpad(guestAddr);
|
||||
const uint32_t phys = translateAddress(guestAddr);
|
||||
const uint8_t *base = scratch ? m_scratchpad : m_rdram;
|
||||
const uint32_t limit = scratch ? PS2_SCRATCHPAD_SIZE : PS2_RAM_SIZE;
|
||||
if (!base || phys > limit || bytes > limit - phys)
|
||||
return false;
|
||||
out = base + phys;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const uint8_t *tagPtr = nullptr;
|
||||
if (!resolveContiguous(tadr, 16u, tagPtr))
|
||||
return false;
|
||||
|
||||
const DmaTagView tag = decodeDmaTag(loadScalar<uint64_t>(tagPtr, 0u, 16u, "native packed gif dma tag", tadr));
|
||||
if (tag.id != 7u || tag.qwc == 0u || tag.irq)
|
||||
return false;
|
||||
|
||||
const uint64_t payloadBytes64 = static_cast<uint64_t>(tag.qwc) * 16ull;
|
||||
if (payloadBytes64 > 0xFFFFFFFFull)
|
||||
return false;
|
||||
const uint32_t payloadBytes = static_cast<uint32_t>(payloadBytes64);
|
||||
|
||||
const uint8_t *payload = nullptr;
|
||||
if (!resolveContiguous(tadr + 16u, payloadBytes, payload))
|
||||
return false;
|
||||
if (!gs.processNativePackedGIFPacket(payload, payloadBytes))
|
||||
return false;
|
||||
|
||||
m_dmaStartCount.fetch_add(1, std::memory_order_relaxed);
|
||||
m_seenGifCopy = true;
|
||||
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
m_ioRegisters[GIF_CHANNEL + 0x30u] = tadr;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x40u] = 0u;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x50u] = 0u;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x00u] = ((chcr & 0x0000FFFFu) | (tag.upper << 16u)) & ~0x100u;
|
||||
m_ioRegisters[GIF_CHANNEL + 0x20u] = 0u;
|
||||
|
||||
uint32_t dstat = m_ioRegisters.count(D_STAT) ? m_ioRegisters[D_STAT] : 0u;
|
||||
dstat |= (1u << 2u);
|
||||
const uint32_t status = dstat & 0x3FFu;
|
||||
const uint32_t mask = (dstat >> 16u) & 0x3FFu;
|
||||
if ((status & mask) != 0u)
|
||||
dstat |= (1u << 31u);
|
||||
else
|
||||
dstat &= ~(1u << 31u);
|
||||
m_ioRegisters[D_STAT] = dstat;
|
||||
queueCompletedDmacCause(2u);
|
||||
return true;
|
||||
}
|
||||
|
||||
int PS2Memory::pollDmaRegisters()
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -2114,6 +2114,40 @@ void PS2Runtime::Store128(uint8_t *rdram, R5900Context *ctx, uint32_t vaddr, __m
|
||||
}
|
||||
}
|
||||
|
||||
void PS2Runtime::kickGifDmaChainFromMMIO(uint8_t *rdram,
|
||||
R5900Context *ctx,
|
||||
uint32_t dPcrValue,
|
||||
uint32_t dStatValue,
|
||||
uint32_t tadr,
|
||||
uint32_t chcr)
|
||||
{
|
||||
constexpr uint32_t D_PCR = 0x1000E020u;
|
||||
constexpr uint32_t D_STAT = 0x1000E010u;
|
||||
constexpr uint32_t GIF_TADR = 0x1000A030u;
|
||||
constexpr uint32_t GIF_CHCR = 0x1000A000u;
|
||||
|
||||
ps2TraceGuestWrite(rdram, D_PCR, 4u, dPcrValue, 0u, "WRITE32", ctx);
|
||||
m_memory.writeIORegister(D_PCR, dPcrValue);
|
||||
ps2TraceGuestWrite(rdram, D_STAT, 4u, dStatValue, 0u, "WRITE32", ctx);
|
||||
m_memory.writeIORegister(D_STAT, dStatValue);
|
||||
ps2TraceGuestWrite(rdram, GIF_TADR, 4u, tadr, 0u, "WRITE32", ctx);
|
||||
m_memory.writeIORegister(GIF_TADR, tadr);
|
||||
ps2TraceGuestWrite(rdram, GIF_CHCR, 4u, chcr, 0u, "WRITE32", ctx);
|
||||
if (m_memory.tryProcessNativeGifImageUploadChain(m_gs, tadr, chcr))
|
||||
{
|
||||
drainCompletedDmacHandlers(rdram);
|
||||
return;
|
||||
}
|
||||
if (m_memory.tryProcessNativeGifPackedChain(m_gs, tadr, chcr))
|
||||
{
|
||||
drainCompletedDmacHandlers(rdram);
|
||||
return;
|
||||
}
|
||||
m_memory.writeIORegister(GIF_CHCR, chcr);
|
||||
m_memory.processPendingTransfers();
|
||||
drainCompletedDmacHandlers(rdram);
|
||||
}
|
||||
|
||||
void PS2Runtime::requestStop()
|
||||
{
|
||||
m_stopRequested.store(true, std::memory_order_relaxed);
|
||||
|
||||
Reference in New Issue
Block a user