Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 35 additions & 29 deletions ps2xRuntime/src/lib/ps2_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,7 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
uint32_t asr1 = m_ioRegisters[channelBase + 0x50];
uint32_t asp = (chcr >> 4) & 0x3u;
const bool tieEnabled = (chcr & (1u << 7)) != 0u;
const int kMaxChainTags = 4096;
const int kMaxChainTags = 65536;
std::vector<uint8_t> chainBuf;

auto appendData = [&](uint32_t srcAddr, uint32_t qwCount)
Expand Down Expand Up @@ -1216,27 +1216,18 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
}
};

auto appendCompactVif1TagData = [&](uint32_t localTagAddr, uint32_t qwCount)
{
uint32_t tagPhys = 0u;
const bool tagScratch = isScratchpad(localTagAddr);
tagPhys = translateAddress(localTagAddr);

const uint8_t *localBase = tagScratch ? m_scratchpad : m_rdram;
const uint32_t localMax = tagScratch ? PS2_SCRATCHPAD_SIZE : PS2_RAM_SIZE;
if (tagPhys + 16u > localMax)
return;

// VIF packet helpers embed 8 bytes of VIF stream in the DMAtag's upper half.
chainBuf.insert(chainBuf.end(), localBase + tagPhys + 8u, localBase + tagPhys + 16u);
appendData(localTagAddr + 16u, qwCount);
};

int tagsProcessed = 0;
bool chainTagCapHit = false;
uint32_t lastTagUpper = (chcr >> 16) & 0xFFFFu;
const bool vifChannel = (channelBase == 0x10009000u || channelBase == 0x10008000u);

while (tagsProcessed < kMaxChainTags)
while (true)
{
if (tagsProcessed >= kMaxChainTags)
{
chainTagCapHit = true;
break;
}
const uint32_t currentTagAddr = tagAddr;
const bool tagInSPR = isScratchpad(tagAddr);
uint32_t physTag = 0;
Expand Down Expand Up @@ -1340,25 +1331,40 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
break;
}

const bool compactVifLocalTag =
(channelBase == 0x10009000u || channelBase == 0x10008000u) &&
(id == 1u || id == 2u || id == 5u || id == 6u || id == 7u);
if (compactVifLocalTag)
appendCompactVif1TagData(currentTagAddr, 0u);

if (hasPayload)
if (vifChannel)
{
if (compactVifLocalTag)
appendData(currentTagAddr + 16u, tagQwc);
else
appendData(dataAddr, tagQwc);
// A VIF chain tag's upper quadword holds two embedded vifcodes (for
// example STCYCL+UNPACK, or a DIRECT header). Hardware transfers it
// for every tag id when CHCR.TTE is set, regardless of where the
// tag's own payload lives, so append it unconditionally. physTag and
// tagBase were translated and the +16 bound validated at the top of
// the loop, so no re-translation is needed here.
chainBuf.insert(chainBuf.end(),
tagBase + physTag + 8u, tagBase + physTag + 16u);
}

if (hasPayload)
appendData(dataAddr, tagQwc);

if (irq && tieEnabled)
endChain = true;
if (endChain)
break;
}

if (chainTagCapHit)
{
static bool s_vifChainTagCapWarned = false;
if (!s_vifChainTagCapWarned)
{
s_vifChainTagCapWarned = true;
std::cerr << "PS2Memory: DMA chain walker hit the " << kMaxChainTags
<< "-tag cap; truncating the chain to guard against a "
"self-referential NEXT tag hanging the walker."
<< std::endl;
}
}

m_ioRegisters[channelBase + 0x30] = tagAddr;
m_ioRegisters[channelBase + 0x40] = asr0;
m_ioRegisters[channelBase + 0x50] = asr1;
Expand Down
80 changes: 42 additions & 38 deletions ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,37 +265,6 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes)

while (pos + 4 <= sizeBytes)
{
if (m_vif1PendingPath2ImageQwc != 0u)
{
const uint32_t availableQw = (sizeBytes - pos) / 16u;
if (availableQw == 0u)
{
break;
}

const uint32_t chunkQw = std::min<uint32_t>(m_vif1PendingPath2ImageQwc, availableQw);
std::vector<uint8_t> imagePacket(16u + static_cast<size_t>(chunkQw) * 16u, 0u);
const uint64_t imageTag =
static_cast<uint64_t>(chunkQw & 0x7FFFu) |
((m_vif1PendingPath2ImageQwc == chunkQw) ? (1ull << 15) : 0ull) |
(static_cast<uint64_t>(kGifFmtImage) << 58);
std::memcpy(imagePacket.data(), &imageTag, sizeof(imageTag));
std::memcpy(imagePacket.data() + 16u, data + pos, static_cast<size_t>(chunkQw) * 16u);
submitGifPacket(GifPathId::Path2,
imagePacket.data(),
static_cast<uint32_t>(imagePacket.size()),
true,
m_vif1PendingPath2DirectHl);

pos += chunkQw * 16u;
m_vif1PendingPath2ImageQwc -= chunkQw;
if (m_vif1PendingPath2ImageQwc == 0u)
{
m_vif1PendingPath2DirectHl = false;
}
continue;
}

uint32_t cmd;
memcpy(&cmd, data + pos, 4);
pos += 4;
Expand Down Expand Up @@ -468,16 +437,51 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes)
if (qwCount > 0)
{
const bool directHl = (opcode == VIF_DIRECTHL);
submitGifPacket(GifPathId::Path2, data + pos, qwCount * 16, true, directHl);
uint32_t chunkPos = pos;
uint32_t remainingQw = qwCount;

// A pending PATH2 image continuation from an earlier DIRECT/DIRECTHL is
// satisfied out of the FRONT of this packet's own payload, before any of
// it is reinterpreted as a fresh GIF tag. This keeps any vifcodes between
// the two DIRECTs (NOP padding, etc.) from being misread as image data.
if (m_vif1PendingPath2ImageQwc != 0u)
{
const uint32_t chunkQw = std::min<uint32_t>(m_vif1PendingPath2ImageQwc, remainingQw);
std::vector<uint8_t> imagePacket(16u + static_cast<size_t>(chunkQw) * 16u, 0u);
const uint64_t imageTag =
static_cast<uint64_t>(chunkQw & 0x7FFFu) |
((m_vif1PendingPath2ImageQwc == chunkQw) ? (1ull << 15) : 0ull) |
(static_cast<uint64_t>(kGifFmtImage) << 58);
std::memcpy(imagePacket.data(), &imageTag, sizeof(imageTag));
std::memcpy(imagePacket.data() + 16u, data + chunkPos, static_cast<size_t>(chunkQw) * 16u);
submitGifPacket(GifPathId::Path2,
imagePacket.data(),
static_cast<uint32_t>(imagePacket.size()),
true,
m_vif1PendingPath2DirectHl);

chunkPos += chunkQw * 16u;
remainingQw -= chunkQw;
m_vif1PendingPath2ImageQwc -= chunkQw;
if (m_vif1PendingPath2ImageQwc == 0u)
{
m_vif1PendingPath2DirectHl = false;
}
}

const uint32_t imageQw = gifImageQwcFromTag(data + pos, qwCount * 16u);
if (imageQw != 0u)
if (remainingQw > 0u)
{
const uint32_t inlineImageQw = (qwCount > 0u) ? (qwCount - 1u) : 0u;
if (imageQw > inlineImageQw)
submitGifPacket(GifPathId::Path2, data + chunkPos, remainingQw * 16, true, directHl);

const uint32_t imageQw = gifImageQwcFromTag(data + chunkPos, remainingQw * 16u);
if (imageQw != 0u)
{
m_vif1PendingPath2ImageQwc = imageQw - inlineImageQw;
m_vif1PendingPath2DirectHl = directHl;
const uint32_t inlineImageQw = remainingQw - 1u;
if (imageQw > inlineImageQw)
{
m_vif1PendingPath2ImageQwc = imageQw - inlineImageQw;
m_vif1PendingPath2DirectHl = directHl;
}
}
}
}
Expand Down
Loading