Skip to content
Open
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
52 changes: 26 additions & 26 deletions ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Based on Blackline Interactive implementation
#include "runtime/ps2_memory.h"
#include <algorithm>
#include <cstring>

enum VIFCmd : uint8_t
Expand Down Expand Up @@ -79,6 +80,15 @@ namespace

return 0u;
}

void uploadMicroMemory(uint8_t *micro, uint32_t microSize, uint32_t address,
const uint8_t *data, uint32_t size)
{
const uint32_t firstSize = std::min(size, microSize - address);
std::memcpy(micro + address, data, firstSize);
if (firstSize < size)
std::memcpy(micro, data + firstSize, size - firstSize);
}
}

void PS2Memory::processVIF0Data(uint32_t srcPhys, uint32_t sizeBytes)
Expand Down Expand Up @@ -170,25 +180,19 @@ void PS2Memory::processVIF0Data(const uint8_t *data, uint32_t sizeBytes)
}
else if (opcode == VIF_MPG)
{
const uint32_t destAddr = static_cast<uint32_t>(imm & 0x1FFu) * 8u;
const uint32_t destAddr =
(static_cast<uint32_t>(imm) * 8u) & (PS2_VU0_CODE_SIZE - 1u);
const uint32_t instructionCount = (num == 0u) ? 256u : static_cast<uint32_t>(num);
const uint32_t mpgBytes = instructionCount * 8u;
uint32_t copyBytes = 0u;
if (m_vu0Code && destAddr < PS2_VU0_CODE_SIZE && mpgBytes > 0u)
if (sizeBytes - pos < mpgBytes)
break;

if (m_vu0Code)
{
copyBytes = mpgBytes;
if (destAddr + copyBytes > PS2_VU0_CODE_SIZE)
copyBytes = PS2_VU0_CODE_SIZE - destAddr;
if (pos + copyBytes <= sizeBytes)
{
std::memcpy(m_vu0Code + destAddr, data + pos, copyBytes);
markVU0CodeModified();
}
uploadMicroMemory(m_vu0Code, PS2_VU0_CODE_SIZE, destAddr, data + pos, mpgBytes);
markVU0CodeModified();
}

pos += mpgBytes;
if (pos > sizeBytes)
break;
continue;
}
else if ((opcode & 0x60u) == 0x60u)
Expand Down Expand Up @@ -466,23 +470,19 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes)
}
else if (opcode == VIF_MPG)
{
uint32_t destAddr = (uint32_t)imm * 8u;
const uint32_t destAddr =
(static_cast<uint32_t>(imm) * 8u) & (PS2_VU1_CODE_SIZE - 1u);
const uint32_t instructionCount = (num == 0u) ? 256u : static_cast<uint32_t>(num);
const uint32_t mpgBytes = instructionCount * 8u;
if (m_vu1Code && destAddr < PS2_VU1_CODE_SIZE && mpgBytes > 0)
if (sizeBytes - pos < mpgBytes)
break;

if (m_vu1Code)
{
uint32_t copyBytes = mpgBytes;
if (destAddr + copyBytes > PS2_VU1_CODE_SIZE)
copyBytes = PS2_VU1_CODE_SIZE - destAddr;
if (pos + copyBytes <= sizeBytes)
{
std::memcpy(m_vu1Code + destAddr, data + pos, copyBytes);
markVU1CodeModified();
}
uploadMicroMemory(m_vu1Code, PS2_VU1_CODE_SIZE, destAddr, data + pos, mpgBytes);
markVU1CodeModified();
}
pos += mpgBytes;
if (pos > sizeBytes)
break;
continue;
}
else if (opcode == VIF_DIRECT || opcode == VIF_DIRECTHL)
Expand Down
88 changes: 88 additions & 0 deletions ps2xTest/src/ps2_memory_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,94 @@ void register_ps2_memory_tests()
t.IsTrue(matches, "MPG num=0 should copy 2048 bytes into VU1 code memory");
});

tc.Run("VIF0 MPG masks and wraps micro memory addresses", [](TestCase &t)
{
PS2Memory mem;
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");

std::memset(mem.getVU0Code(), 0xCD, PS2_VU0_CODE_SIZE);
std::vector<uint8_t> packet;
appendU32(packet, makeVifCmd(0x4Au, 2u, 0x01FFu));
for (uint32_t i = 0; i < 16u; ++i)
packet.push_back(static_cast<uint8_t>(0x10u + i));

mem.processVIF0Data(packet.data(), static_cast<uint32_t>(packet.size()));

const uint8_t *code = mem.getVU0Code();
bool tailMatches = true;
bool wrappedStartMatches = true;
for (uint32_t i = 0; i < 8u; ++i)
{
tailMatches &= code[PS2_VU0_CODE_SIZE - 8u + i] == static_cast<uint8_t>(0x10u + i);
wrappedStartMatches &= code[i] == static_cast<uint8_t>(0x18u + i);
}
t.IsTrue(tailMatches, "MPG should write through the end of VU0 micro memory");
t.IsTrue(wrappedStartMatches, "MPG payload should wrap to the start of VU0 micro memory");

packet.clear();
appendU32(packet, makeVifCmd(0x4Au, 1u, 0x0200u));
for (uint32_t i = 0; i < 8u; ++i)
packet.push_back(static_cast<uint8_t>(0x80u + i));
mem.processVIF0Data(packet.data(), static_cast<uint32_t>(packet.size()));

bool maskedStartMatches = true;
for (uint32_t i = 0; i < 8u; ++i)
maskedStartMatches &= code[i] == static_cast<uint8_t>(0x80u + i);
t.IsTrue(maskedStartMatches, "MPG immediate 0x200 should wrap to VU0 micro address zero");

std::memset(mem.getVU0Code(), 0xCD, PS2_VU0_CODE_SIZE);
packet.clear();
appendU32(packet, makeVifCmd(0x4Au, 2u, 0x01FFu));
packet.resize(packet.size() + 8u, 0x55u);
mem.processVIF0Data(packet.data(), static_cast<uint32_t>(packet.size()));
t.Equals(static_cast<uint32_t>(code[PS2_VU0_CODE_SIZE - 1u]), 0xCDu,
"truncated MPG payload should not partially modify VU0 micro memory");
});

tc.Run("VIF1 MPG masks and wraps micro memory addresses", [](TestCase &t)
{
PS2Memory mem;
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");

std::memset(mem.getVU1Code(), 0xCD, PS2_VU1_CODE_SIZE);
std::vector<uint8_t> packet;
appendU32(packet, makeVifCmd(0x4Au, 2u, 0x07FFu));
for (uint32_t i = 0; i < 16u; ++i)
packet.push_back(static_cast<uint8_t>(0x10u + i));

mem.processVIF1Data(packet.data(), static_cast<uint32_t>(packet.size()));

const uint8_t *code = mem.getVU1Code();
bool tailMatches = true;
bool wrappedStartMatches = true;
for (uint32_t i = 0; i < 8u; ++i)
{
tailMatches &= code[PS2_VU1_CODE_SIZE - 8u + i] == static_cast<uint8_t>(0x10u + i);
wrappedStartMatches &= code[i] == static_cast<uint8_t>(0x18u + i);
}
t.IsTrue(tailMatches, "MPG should write through the end of VU1 micro memory");
t.IsTrue(wrappedStartMatches, "MPG payload should wrap to the start of VU1 micro memory");

packet.clear();
appendU32(packet, makeVifCmd(0x4Au, 1u, 0x0800u));
for (uint32_t i = 0; i < 8u; ++i)
packet.push_back(static_cast<uint8_t>(0x80u + i));
mem.processVIF1Data(packet.data(), static_cast<uint32_t>(packet.size()));

bool maskedStartMatches = true;
for (uint32_t i = 0; i < 8u; ++i)
maskedStartMatches &= code[i] == static_cast<uint8_t>(0x80u + i);
t.IsTrue(maskedStartMatches, "MPG immediate 0x800 should wrap to VU1 micro address zero");

std::memset(mem.getVU1Code(), 0xCD, PS2_VU1_CODE_SIZE);
packet.clear();
appendU32(packet, makeVifCmd(0x4Au, 2u, 0x07FFu));
packet.resize(packet.size() + 8u, 0x55u);
mem.processVIF1Data(packet.data(), static_cast<uint32_t>(packet.size()));
t.Equals(static_cast<uint32_t>(code[PS2_VU1_CODE_SIZE - 1u]), 0xCDu,
"truncated MPG payload should not partially modify VU1 micro memory");
});

tc.Run("VIF UNPACK num zero uploads 256 vectors", [](TestCase &t)
{
PS2Memory mem;
Expand Down