Files
PS2Recomp/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp
T
2026-08-13 09:33:49 -03:00

1542 lines
57 KiB
C++

#include "Common.h"
#include "GS.h"
#include "ps2_log.h"
#include "runtime/gs/ps2_gs_common.h"
#include "runtime/gs/ps2_gs_psmct16.h"
#include "runtime/ee_scheduler.h"
namespace ps2_stubs
{
namespace
{
uint64_t makeClearPrim(bool useContext2)
{
return static_cast<uint64_t>(GS_PRIM_SPRITE) |
(static_cast<uint64_t>(useContext2 ? 1u : 0u) << 9);
}
uint64_t makeClearRgbaq(uint32_t rgba)
{
return static_cast<uint64_t>(rgba);
}
uint64_t makeClearXyz(int32_t x, int32_t y)
{
return static_cast<uint64_t>(static_cast<uint16_t>(x << 4)) |
(static_cast<uint64_t>(static_cast<uint16_t>(y << 4)) << 16);
}
void seedGsClearPacket(GsClearMem &clear,
int32_t width,
int32_t height,
uint32_t rgba,
uint32_t ztest,
bool useContext2)
{
const int32_t offX = 0x800 - (width >> 1);
const int32_t offY = 0x800 - (height >> 1);
const uint64_t clearTest = makeTest(0u);
const uint64_t restoreTest = makeTest(ztest);
const uint64_t prim = makeClearPrim(useContext2);
const uint64_t rgbaq = makeClearRgbaq(rgba);
const uint64_t xyz0 = makeClearXyz(offX, offY);
const uint64_t xyz1 = makeClearXyz(offX + width, offY + height);
const uint64_t testReg = useContext2 ? GS_REG_TEST_2 : GS_REG_TEST_1;
clear.testa = {clearTest, testReg};
clear.prim = {prim, GS_REG_PRIM};
clear.rgbaq = {rgbaq, GS_REG_RGBAQ};
clear.xyz2a = {xyz0, GS_REG_XYZ2};
clear.xyz2b = {xyz1, GS_REG_XYZ2};
clear.testb = {restoreTest, testReg};
}
bool hasSeededGsClearPacket(const GsClearMem &clear)
{
return clear.rgbaq.reg == GS_REG_RGBAQ &&
clear.xyz2a.reg == GS_REG_XYZ2 &&
clear.xyz2b.reg == GS_REG_XYZ2;
}
struct GsTrailingArgs2
{
uint32_t arg0 = 0u;
uint32_t arg1 = 0u;
};
struct GsTrailingArgs3
{
uint32_t arg0 = 0u;
uint32_t arg1 = 0u;
uint32_t arg2 = 0u;
};
GsTrailingArgs2 decodeGsTrailingArgs2(uint8_t *rdram, R5900Context *ctx)
{
const uint32_t reg8 = getRegU32(ctx, 8);
const uint32_t reg9 = getRegU32(ctx, 9);
const uint32_t stack0 = readStackU32(rdram, ctx, 16);
const uint32_t stack1 = readStackU32(rdram, ctx, 20);
const bool hasRegArgs = (reg8 != 0u || reg9 != 0u);
const bool hasStackArgs = (stack0 != 0u || stack1 != 0u);
if (hasRegArgs || !hasStackArgs)
{
return {reg8, reg9};
}
return {stack0, stack1};
}
GsTrailingArgs3 decodeGsTrailingArgs3(uint8_t *rdram, R5900Context *ctx)
{
const uint32_t reg8 = getRegU32(ctx, 8);
const uint32_t reg9 = getRegU32(ctx, 9);
const uint32_t reg10 = getRegU32(ctx, 10);
const uint32_t stack0 = readStackU32(rdram, ctx, 16);
const uint32_t stack1 = readStackU32(rdram, ctx, 20);
const uint32_t stack2 = readStackU32(rdram, ctx, 24);
const bool hasRegArgs = (reg8 != 0u || reg9 != 0u || reg10 != 0u);
const bool hasStackArgs = (stack0 != 0u || stack1 != 0u || stack2 != 0u);
if (hasRegArgs || !hasStackArgs)
{
return {reg8, reg9, reg10};
}
return {stack0, stack1, stack2};
}
void applyGsClearPacket(PS2Runtime *runtime, const GsClearMem &clear)
{
if (!runtime->syncCoreSubsystems() || !hasSeededGsClearPacket(clear))
{
return;
}
runtime->gs().writeRegister(static_cast<uint8_t>(clear.testa.reg & 0xFFu), clear.testa.value);
runtime->gs().writeRegister(static_cast<uint8_t>(clear.prim.reg & 0xFFu), clear.prim.value);
runtime->gs().writeRegister(static_cast<uint8_t>(clear.rgbaq.reg & 0xFFu), clear.rgbaq.value);
runtime->gs().writeRegister(static_cast<uint8_t>(clear.xyz2a.reg & 0xFFu), clear.xyz2a.value);
runtime->gs().writeRegister(static_cast<uint8_t>(clear.xyz2b.reg & 0xFFu), clear.xyz2b.value);
runtime->gs().writeRegister(static_cast<uint8_t>(clear.testb.reg & 0xFFu), clear.testb.value);
}
void refreshPacketBuilderPendingCount(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr);
void writePacketBuilderCurrent(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t currentAddr);
void initPacketBuilderState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t baseAddr = getRegU32(ctx, 5);
const uint32_t words[4] = {baseAddr, baseAddr, 0u, 0u};
writeGuestBytes(rdram,
runtime,
stateAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
}
uint32_t terminatePacketBuilderState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return 0u;
}
const uint32_t zero = 0u;
while ((currentAddr & 0xCu) != 0u)
{
writeGuestBytes(rdram,
runtime,
currentAddr,
reinterpret_cast<const uint8_t *>(&zero),
sizeof(zero));
currentAddr += 4u;
}
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr);
writeGuestBytes(rdram,
runtime,
stateAddr + 8u,
reinterpret_cast<const uint8_t *>(&zero),
sizeof(zero));
return currentAddr;
}
void resetPacketBuilderState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
uint32_t baseAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr + 4u, baseAddr))
{
setReturnU32(ctx, 0u);
return;
}
const uint32_t words[4] = {baseAddr, baseAddr, 0u, 0u};
writeGuestBytes(rdram,
runtime,
stateAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
setReturnU32(ctx, baseAddr);
}
bool tryReadQwordFromGuest(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint64_t &outQword)
{
uint32_t low = 0u;
uint32_t high = 0u;
if (!tryReadWordFromGuest(rdram, runtime, addr, low) ||
!tryReadWordFromGuest(rdram, runtime, addr + 4u, high))
{
return false;
}
outQword = static_cast<uint64_t>(low) | (static_cast<uint64_t>(high) << 32u);
return true;
}
void writeGuestU32(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint32_t value)
{
writeGuestBytes(rdram,
runtime,
addr,
reinterpret_cast<const uint8_t *>(&value),
sizeof(value));
}
void writeGuestU64(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint64_t value)
{
writeGuestBytes(rdram,
runtime,
addr,
reinterpret_cast<const uint8_t *>(&value),
sizeof(value));
}
void writeGuestVec128(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, __m128i value)
{
alignas(16) __m128i temp = value;
writeGuestBytes(rdram,
runtime,
addr,
reinterpret_cast<const uint8_t *>(&temp),
sizeof(temp));
}
void refreshPacketBuilderPendingCount(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr)
{
uint32_t currentAddr = 0u;
uint32_t pendingCountAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr) ||
!tryReadWordFromGuest(rdram, runtime, stateAddr + 8u, pendingCountAddr) ||
pendingCountAddr == 0u ||
currentAddr <= pendingCountAddr)
{
return;
}
uint32_t countWord = 0u;
if (!tryReadWordFromGuest(rdram, runtime, pendingCountAddr, countWord))
{
return;
}
const uint32_t deltaBytes = currentAddr - pendingCountAddr;
uint32_t deltaQwords = 0u;
if (deltaBytes >= 16u)
{
deltaQwords = (deltaBytes >> 4u) - 1u;
}
countWord = (countWord & 0xFFFF0000u) | (deltaQwords & 0xFFFFu);
writeGuestU32(rdram, runtime, pendingCountAddr, countWord);
}
void writePacketBuilderCurrent(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t currentAddr)
{
writeGuestU32(rdram, runtime, stateAddr, currentAddr);
refreshPacketBuilderPendingCount(rdram, runtime, stateAddr);
}
uint32_t reservePacketBuilderWords(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t wordCount)
{
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return 0u;
}
const uint32_t reservedAddr = currentAddr;
currentAddr += wordCount * 4u;
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr);
return reservedAddr;
}
void alignPacketBuilderState(uint8_t *rdram,
PS2Runtime *runtime,
uint32_t stateAddr,
uint32_t alignMode,
uint32_t reserveWords)
{
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return;
}
const uint32_t adjusted = (alignMode + 2u) & 31u;
const uint32_t shift = (32u - adjusted) & 31u;
const uint32_t lowMask = 0xFFFFFFFFu >> shift;
const uint32_t alignedBase = currentAddr & ~lowMask;
uint32_t targetAddr = alignedBase + (reserveWords << 2u);
if (targetAddr < currentAddr)
{
targetAddr = (targetAddr + 1u) + lowMask;
}
const uint32_t zero = 0u;
while (currentAddr < targetAddr)
{
writeGuestU32(rdram, runtime, currentAddr, zero);
currentAddr += 4u;
}
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr);
}
void openPacketGifTag(uint8_t *rdram,
R5900Context *ctx,
PS2Runtime *runtime,
uint32_t stateAddr,
uint32_t openAddrOffset)
{
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return;
}
writeGuestVec128(rdram, runtime, currentAddr, GPR_VEC(ctx, 5));
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u);
writeGuestU32(rdram, runtime, stateAddr + openAddrOffset, currentAddr);
}
void closePacketGifTag(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t openAddrOffset)
{
uint32_t openAddr = 0u;
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr + openAddrOffset, openAddr) ||
!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr) ||
openAddr == 0u)
{
return;
}
uint64_t tagValue = 0u;
if (!tryReadQwordFromGuest(rdram, runtime, openAddr, tagValue))
{
return;
}
uint32_t packetQwords = ((currentAddr - openAddr) >> 3u) - 2u;
const uint32_t flag = static_cast<uint32_t>((tagValue >> 58u) & 0x3u);
if (flag != 1u)
{
packetQwords >>= 1u;
}
if (flag != 2u)
{
uint32_t nreg = static_cast<uint32_t>((tagValue >> 60u) & 0xFu);
if (nreg == 0u)
{
nreg = 16u;
}
packetQwords = (packetQwords + nreg - 1u) / nreg;
}
tagValue += static_cast<uint64_t>(packetQwords);
writeGuestU32(rdram, runtime, stateAddr + openAddrOffset, 0u);
writeGuestU64(rdram, runtime, openAddr, tagValue);
while ((currentAddr & 0xCu) != 0u)
{
writeGuestU32(rdram, runtime, currentAddr, 0u);
currentAddr += 4u;
}
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr);
}
}
void sceGifPkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return;
}
const uint64_t dataValue = GPR_U64(ctx, 6);
const uint64_t regValue = static_cast<uint64_t>(getRegU32(ctx, 5));
writeGuestU64(rdram, runtime, currentAddr, dataValue);
writeGuestU64(rdram, runtime, currentAddr + 8u, regValue);
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u);
}
void sceGifPkAddGsData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return;
}
writeGuestU64(rdram, runtime, currentAddr, GPR_U64(ctx, 5));
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 8u);
}
void sceGifPkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)ctx;
closePacketGifTag(rdram, runtime, getRegU32(ctx, 4), 12u);
}
void sceGifPkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t countValue = getRegU32(ctx, 5);
const uint32_t extraValue = getRegU32(ctx, 6);
const uint32_t tagWord = getRegU32(ctx, 7) | 0x10000000u;
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[4] = {tagWord, 0u, countValue, extraValue};
const uint32_t nextAddr = packetAddr + 16u;
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, nextAddr);
}
void sceGifPkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t countValue = getRegU32(ctx, 5);
const uint32_t extraValue = getRegU32(ctx, 6);
const uint32_t tagWord = getRegU32(ctx, 7) | 0x70000000u;
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[4] = {tagWord, countValue, extraValue, 0u};
const uint32_t nextAddr = packetAddr + 16u;
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, nextAddr);
}
void sceGifPkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
initPacketBuilderState(rdram, ctx, runtime);
}
void sceGifPkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
openPacketGifTag(rdram, ctx, runtime, getRegU32(ctx, 4), 12u);
}
void sceGifPkRef(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t refAddr = getRegU32(ctx, 5) & 0x9FFFFFFFu;
const uint32_t tagWord = getRegU32(ctx, 9) | getRegU32(ctx, 6) | 0x30000000u;
const uint32_t extra0 = getRegU32(ctx, 7);
const uint32_t extra1 = getRegU32(ctx, 8);
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[4] = {tagWord, refAddr, extra0, extra1};
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 16u);
}
void sceGifPkRefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t dbp = getRegU32(ctx, 5) & 0xFFFFu;
const uint32_t dpsm = getRegU32(ctx, 6) & 0xFFu;
const uint32_t dbw = getRegU32(ctx, 7) & 0xFFFFu;
uint32_t dataAddr = getRegU32(ctx, 8);
uint32_t qwcRemaining = getRegU32(ctx, 9);
const uint32_t dsax = getRegU32(ctx, 10);
const uint32_t dsay = getRegU32(ctx, 11);
const uint32_t width = readStackU32(rdram, ctx, 0);
const uint32_t height = readStackU32(rdram, ctx, 8);
// Open a 4-register A+D GIF tag and emit the GS load-image setup.
{
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[4] = {0x10000000u, 0u, 0u, 0u};
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 16u);
// Seed an open A+D tag (nloop=0, EOP clear): closePacketGifTag adds the true
// appended qword count, so a pre-set nloop would double-count. Open variant
// (not makeGiftagAplusD) because that always sets EOP on this chained tag.
const uint64_t giftag[2] = {makeGiftagAplusDOpen(0u), 0xEULL};
uint32_t currentAddr = packetAddr + 16u;
writeGuestBytes(rdram, runtime, currentAddr, reinterpret_cast<const uint8_t *>(giftag), sizeof(giftag));
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u);
writeGuestU32(rdram, runtime, stateAddr + 12u, currentAddr);
const uint64_t bitbltbuf =
(static_cast<uint64_t>(dbp) << 32u) |
(static_cast<uint64_t>(dbw & 0xFFu) << 48u) |
(static_cast<uint64_t>(dpsm) << 56u);
const uint64_t trxpos =
(static_cast<uint64_t>(dsax) << 32u) |
(static_cast<uint64_t>(dsay) << 48u);
const uint64_t trxreg =
static_cast<uint64_t>(width) |
(static_cast<uint64_t>(height) << 32u);
{
uint32_t addr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, addr))
{
return;
}
writeGuestU64(rdram, runtime, addr, bitbltbuf);
writeGuestU64(rdram, runtime, addr + 8u, static_cast<uint64_t>(GS_REG_BITBLTBUF));
addr += 16u;
writeGuestU64(rdram, runtime, addr, trxpos);
writeGuestU64(rdram, runtime, addr + 8u, static_cast<uint64_t>(GS_REG_TRXPOS));
addr += 16u;
writeGuestU64(rdram, runtime, addr, trxreg);
writeGuestU64(rdram, runtime, addr + 8u, static_cast<uint64_t>(GS_REG_TRXREG));
addr += 16u;
writeGuestU64(rdram, runtime, addr, 0u);
writeGuestU64(rdram, runtime, addr + 8u, static_cast<uint64_t>(GS_REG_TRXDIR));
addr += 16u;
writePacketBuilderCurrent(rdram, runtime, stateAddr, addr);
closePacketGifTag(rdram, runtime, stateAddr, 12u);
}
}
while (qwcRemaining != 0u)
{
const uint32_t chunkQwc = std::min<uint32_t>(qwcRemaining, 32767u);
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[4] = {0x10000000u, 0u, 0u, 0u};
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 16u);
const uint32_t reservedAddr = reservePacketBuilderWords(rdram, runtime, stateAddr, 4u);
const bool isLastChunk = (chunkQwc == qwcRemaining);
const uint64_t gifTag =
static_cast<uint64_t>(chunkQwc) |
(isLastChunk ? 0x0800000000008000ULL : 0x0800000000000000ULL);
writeGuestU64(rdram, runtime, reservedAddr, gifTag);
writeGuestU64(rdram, runtime, reservedAddr + 8u, 0u);
const uint32_t refPacketAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t refWords[4] = {0x30000000u | chunkQwc, dataAddr & 0x9FFFFFFFu, 0u, 0u};
writeGuestBytes(rdram,
runtime,
refPacketAddr,
reinterpret_cast<const uint8_t *>(refWords),
sizeof(refWords));
writePacketBuilderCurrent(rdram, runtime, stateAddr, refPacketAddr + 16u);
qwcRemaining -= chunkQwc;
dataAddr += chunkQwc * 16u;
}
}
void sceGifPkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
resetPacketBuilderState(rdram, ctx, runtime);
}
void sceGifPkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, reservePacketBuilderWords(rdram, runtime, getRegU32(ctx, 4), getRegU32(ctx, 5)));
}
void sceGifPkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, terminatePacketBuilderState(rdram, ctx, runtime));
}
void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t imgAddr = getRegU32(ctx, 4);
uint32_t srcAddr = getRegU32(ctx, 5);
GsImageMem img{};
if (!runtime || !runtime->syncCoreSubsystems() || !readGsImage(rdram, imgAddr, img))
{
setReturnS32(ctx, -1);
return;
}
const uint32_t rowBytes = bytesForPixels(img.psm, static_cast<uint32_t>(img.width));
if (rowBytes == 0)
{
setReturnS32(ctx, -1);
return;
}
uint32_t fbw = img.vram_width ? img.vram_width : std::max<uint32_t>(1, (img.width + 63) / 64);
const uint32_t totalImageBytes = rowBytes * static_cast<uint32_t>(img.height);
const uint32_t headerQwc = 6u;
const uint32_t imageQwc = (totalImageBytes + 15u) / 16u;
const uint32_t totalQwc = headerQwc + imageQwc;
uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u);
if (pktAddr == 0)
{
setReturnS32(ctx, -1);
return;
}
uint8_t *pkt = getMemPtr(rdram, pktAddr);
const uint8_t *src = getConstMemPtr(rdram, srcAddr);
if (!pkt || !src)
{
runtime->guestFree(pktAddr);
setReturnS32(ctx, -1);
return;
}
uint32_t dbp = (static_cast<uint32_t>(img.vram_addr) * 2048u) / 256u;
uint32_t dsax = static_cast<uint32_t>(img.x);
uint32_t dsay = static_cast<uint32_t>(img.y);
// Full messy
uint64_t *q = reinterpret_cast<uint64_t *>(pkt);
q[0] = makeGiftagAplusD(4u);
q[1] = 0xEULL;
q[2] = (static_cast<uint64_t>(img.psm & 0x3Fu) << 24) | (static_cast<uint64_t>(1u) << 16) |
(static_cast<uint64_t>(dbp & 0x3FFFu) << 32) | (static_cast<uint64_t>(fbw & 0x3Fu) << 48) |
(static_cast<uint64_t>(img.psm & 0x3Fu) << 56);
q[3] = 0x50ULL;
q[4] = (static_cast<uint64_t>(dsay & 0x7FFu) << 48) | (static_cast<uint64_t>(dsax & 0x7FFu) << 32);
q[5] = 0x51ULL;
q[6] = (static_cast<uint64_t>(img.height) << 32) | static_cast<uint64_t>(img.width);
q[7] = 0x52ULL;
q[8] = 0ULL;
q[9] = 0x53ULL;
q[10] = (static_cast<uint64_t>(2) << 58) | (static_cast<uint64_t>(imageQwc) & 0x7FFF) |
(1ULL << 15);
q[11] = 0ULL;
std::memcpy(pkt + headerQwc * 16u, src, totalImageBytes);
constexpr uint32_t GIF_CHANNEL = 0x1000A000;
constexpr uint32_t CHCR_STR_MODE0 = 0x101u;
auto &mem = runtime->memory();
mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr);
mem.writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu);
mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0);
mem.processPendingTransfers();
runtime->guestFree(pktAddr);
setReturnS32(ctx, 0);
}
void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t imgAddr = getRegU32(ctx, 4);
uint32_t dstAddr = getRegU32(ctx, 5);
GsImageMem img{};
if (!runtime || !runtime->syncCoreSubsystems() || !readGsImage(rdram, imgAddr, img))
{
setReturnS32(ctx, -1);
return;
}
const uint32_t rowBytes = bytesForPixels(img.psm, static_cast<uint32_t>(img.width));
if (rowBytes == 0)
{
setReturnS32(ctx, -1);
return;
}
uint32_t fbw = img.vram_width ? img.vram_width : std::max<uint32_t>(1, (img.width + 63) / 64);
const uint32_t totalImageBytes = rowBytes * static_cast<uint32_t>(img.height);
uint8_t *dst = getMemPtr(rdram, dstAddr);
if (!dst)
{
setReturnS32(ctx, -1);
return;
}
uint32_t sbp = (static_cast<uint32_t>(img.vram_addr) * 2048u) / 256u;
uint64_t bitbltbuf = (static_cast<uint64_t>(sbp & 0x3FFFu) << 0) |
(static_cast<uint64_t>(fbw & 0x3Fu) << 16) |
(static_cast<uint64_t>(img.psm & 0x3Fu) << 24) |
(static_cast<uint64_t>(0u) << 32) |
(static_cast<uint64_t>(1u) << 48) |
(static_cast<uint64_t>(0u) << 56);
uint64_t trxpos = (static_cast<uint64_t>(img.x & 0x7FFu) << 0) |
(static_cast<uint64_t>(img.y & 0x7FFu) << 16) |
(static_cast<uint64_t>(0u) << 32) |
(static_cast<uint64_t>(0u) << 48);
uint64_t trxreg = static_cast<uint64_t>(img.height) << 32 | static_cast<uint64_t>(img.width);
uint32_t pktAddr = runtime->guestMalloc(80u, 16u);
if (pktAddr == 0)
{
setReturnS32(ctx, -1);
return;
}
uint8_t *pkt = getMemPtr(rdram, pktAddr);
if (!pkt)
{
runtime->guestFree(pktAddr);
setReturnS32(ctx, -1);
return;
}
uint64_t *q = reinterpret_cast<uint64_t *>(pkt);
q[0] = makeGiftagAplusD(4u);
q[1] = 0xEULL;
q[2] = bitbltbuf;
q[3] = 0x50ULL;
q[4] = trxpos;
q[5] = 0x51ULL;
q[6] = trxreg;
q[7] = 0x52ULL;
q[8] = 1ULL;
q[9] = 0x53ULL;
constexpr uint32_t GIF_CHANNEL = 0x1000A000;
constexpr uint32_t CHCR_STR_MODE0 = 0x101u;
auto &mem = runtime->memory();
mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr);
mem.writeIORegister(GIF_CHANNEL + 0x20u, 5u);
mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0);
mem.processPendingTransfers();
ps2TraceGuestRangeWrite(rdram, dstAddr, totalImageBytes, "sceGsExecStoreImage", ctx);
runtime->gs().consumeLocalToHostBytes(dst, totalImageBytes);
runtime->guestFree(pktAddr);
setReturnS32(ctx, 0);
}
void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t addr = writeGsGParamToScratch(runtime);
setReturnU32(ctx, addr);
}
void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t envAddr = getRegU32(ctx, 4);
GsDispEnvMem env{};
if (!readGsDispEnv(rdram, envAddr, env))
{
setReturnS32(ctx, -1);
return;
}
applyGsDispEnv(runtime, env);
setReturnS32(ctx, 0);
}
void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t envAddr = getRegU32(ctx, 4);
GsRegPairMem pairs[8]{};
if (!readGsRegPairs(rdram, envAddr, pairs, 8u))
{
setReturnS32(ctx, -1);
return;
}
applyGsRegPairs(runtime, pairs, 8u);
setReturnS32(ctx, 0);
}
void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t mode = getRegU32(ctx, 4);
uint32_t interlace = getRegU32(ctx, 5);
uint32_t omode = getRegU32(ctx, 6);
uint32_t ffmode = getRegU32(ctx, 7);
if (mode == 0)
{
if (runtime && !runtime->syncCoreSubsystems())
{
setReturnS32(ctx, -1);
return;
}
g_gparam.interlace = static_cast<uint8_t>(interlace & 0x1);
g_gparam.omode = static_cast<uint8_t>(omode & 0xFF);
g_gparam.ffmode = static_cast<uint8_t>(ffmode & 0x1);
writeGsGParamToScratch(runtime);
uint64_t pmode = makePmode(1, 0, 0, 0, 0, 0x80);
uint64_t smode2 = (interlace & 0x1) | ((ffmode & 0x1) << 1);
uint64_t dispfb = makeDispFb(0, 10, 0, 0, 0);
uint64_t display = makeDisplay(0, 0, 0, 0, 639, 447);
uint64_t bgcolor = 0ULL;
if (runtime)
{
uint32_t pktAddr = runtime->guestMalloc(128u, 16u);
if (pktAddr != 0u)
{
uint8_t *pkt = getMemPtr(rdram, pktAddr);
if (pkt)
{
uint64_t *q = reinterpret_cast<uint64_t *>(pkt);
q[0] = makeGiftagAplusD(7u);
q[1] = 0xEULL;
q[2] = pmode;
q[3] = 0x41ULL;
q[4] = smode2;
q[5] = 0x42ULL;
q[6] = dispfb;
q[7] = 0x59ULL;
q[8] = display;
q[9] = 0x5aULL;
q[10] = dispfb;
q[11] = 0x5bULL;
q[12] = display;
q[13] = 0x5cULL;
q[14] = bgcolor;
q[15] = 0x5fULL;
constexpr uint32_t GIF_CHANNEL = 0x1000A000;
constexpr uint32_t CHCR_STR_MODE0 = 0x101u;
auto &mem = runtime->memory();
mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr);
mem.writeIORegister(GIF_CHANNEL + 0x20u, 8u);
mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0);
mem.processPendingTransfers();
runtime->guestFree(pktAddr);
}
else
{
runtime->guestFree(pktAddr);
}
}
}
}
setReturnS32(ctx, 0);
}
void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 0);
}
void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
(void)ctx;
(void)runtime;
setReturnS32(ctx, 0);
}
void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t envAddr = getRegU32(ctx, 4);
uint32_t psm = getRegU32(ctx, 5);
uint32_t w = getRegU32(ctx, 6);
uint32_t h = getRegU32(ctx, 7);
const GsTrailingArgs3 trailing = decodeGsTrailingArgs3(rdram, ctx);
const uint32_t ztest = trailing.arg0;
const uint32_t zpsm = trailing.arg1;
const uint32_t clear = trailing.arg2;
if (w == 0u)
{
w = 640u;
}
if (h == 0u)
{
h = 448u;
}
const uint32_t fbw = std::max<uint32_t>(1u, (w + 63u) / 64u);
const uint64_t pmode = makePmode(1u, 1u, 0u, 0u, 0u, 0x80u);
const uint64_t smode2 =
(static_cast<uint64_t>(g_gparam.interlace & 0x1u) << 0) |
(static_cast<uint64_t>(g_gparam.ffmode & 0x1u) << 1);
const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u);
const int32_t drawWidth = static_cast<int32_t>(w);
const int32_t drawHeight = static_cast<int32_t>(h);
uint32_t zbufAddr = 0u;
{
R5900Context temp = *ctx;
sceGszbufaddr(rdram, &temp, runtime);
zbufAddr = getRegU32(&temp, 2);
}
const uint32_t fbp1 = zbufAddr;
const uint64_t dispfb0 = makeDispFb(fbp1, fbw, psm, 0u, 0u);
const uint64_t dispfb1 = makeDispFb(0u, fbw, psm, 0u, 0u);
GsDBuffDcMem db{};
db.disp[0].pmode = pmode;
db.disp[0].smode2 = smode2;
db.disp[0].dispfb = dispfb0;
db.disp[0].display = display;
db.disp[0].bgcolor = 0u;
db.disp[1] = db.disp[0];
db.disp[1].dispfb = dispfb1;
const bool seedClear = clear != 0u;
db.giftag0 = {makeGiftagAplusD(seedClear ? 22u : 16u), 0xEULL};
seedGsDrawEnv1(db.draw01, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false);
seedGsDrawEnv2(db.draw02, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false);
db.giftag1 = db.giftag0;
seedGsDrawEnv1(db.draw11, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false);
seedGsDrawEnv2(db.draw12, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false);
if (seedClear)
{
seedGsClearPacket(db.clear0, drawWidth, drawHeight, 0u, ztest, false);
seedGsClearPacket(db.clear1, drawWidth, drawHeight, 0u, ztest, true);
}
if (!writeGsDBuffDc(rdram, envAddr, db))
{
setReturnS32(ctx, -1);
return;
}
setReturnS32(ctx, 0);
}
void sceGsSetDefDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t envAddr = getRegU32(ctx, 4);
uint32_t psm = getRegU32(ctx, 5);
uint32_t w = getRegU32(ctx, 6);
uint32_t h = getRegU32(ctx, 7);
const uint32_t ztest = readStackU32(rdram, ctx, 16);
const uint32_t zpsm = readStackU32(rdram, ctx, 20);
const uint32_t clear = readStackU32(rdram, ctx, 24);
(void)clear;
if (w == 0u)
{
w = 640u;
}
if (h == 0u)
{
h = 448u;
}
const uint32_t fbw = std::max<uint32_t>(1u, (w + 63u) / 64u);
const uint64_t pmode = makePmode(1u, 1u, 0u, 0u, 0u, 0x80u);
const uint64_t smode2 =
(static_cast<uint64_t>(g_gparam.interlace & 0x1u) << 0) |
(static_cast<uint64_t>(g_gparam.ffmode & 0x1u) << 1);
const uint64_t dispfb = makeDispFb(0u, fbw, psm, 0u, 0u);
const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u);
const int32_t drawWidth = static_cast<int32_t>(w);
const int32_t drawHeight = static_cast<int32_t>(h);
uint32_t zbufAddr = 0u;
{
R5900Context temp = *ctx;
sceGszbufaddr(rdram, &temp, runtime);
zbufAddr = getRegU32(&temp, 2);
}
GsDBuffMem db{};
db.disp[0].pmode = pmode;
db.disp[0].smode2 = smode2;
db.disp[0].dispfb = dispfb;
db.disp[0].display = display;
db.disp[0].bgcolor = 0u;
db.disp[1] = db.disp[0];
db.giftag0 = {makeGiftagAplusD(14u), 0x0E0E0E0E0E0E0E0EULL};
seedGsDrawEnv1(db.draw0, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false);
db.giftag1 = db.giftag0;
seedGsDrawEnv1(db.draw1, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false);
if (!writeGsDBuff(rdram, envAddr, db))
{
setReturnS32(ctx, -1);
return;
}
setReturnS32(ctx, 0);
}
void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t envAddr = getRegU32(ctx, 4);
uint32_t psm = getRegU32(ctx, 5);
uint32_t w = getRegU32(ctx, 6);
uint32_t h = getRegU32(ctx, 7);
const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx);
uint32_t dx = trailing.arg0;
uint32_t dy = trailing.arg1;
if (w == 0)
w = 640;
if (h == 0)
h = 448;
uint32_t fbw = (w + 63) / 64;
uint64_t dispfb = makeDispFb(0, fbw, psm, 0, 0);
uint64_t display = makeDisplay(dx, dy, 0, 0, w - 1, h - 1);
writeGsDispEnv(rdram, envAddr, display, dispfb);
setReturnS32(ctx, 0);
}
void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t envAddr = getRegU32(ctx, 4);
uint32_t param_2 = getRegU32(ctx, 5);
int32_t w = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 6) & 0xFFFF));
int32_t h = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 7) & 0xFFFF));
const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx);
uint32_t param_5 = trailing.arg0;
uint32_t param_6 = trailing.arg1;
if (w <= 0)
w = 640;
if (h <= 0)
h = 448;
uint32_t psm = param_2 & 0xFU;
uint32_t fbw = ((static_cast<uint32_t>(w) + 63u) >> 6) & 0x3FU;
sceGszbufaddr(rdram, ctx, runtime);
int32_t zbuf = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 2) & 0xFFFF));
GsDrawEnv1Mem env{};
seedGsDrawEnv1(env,
w,
h,
0u,
fbw,
psm,
static_cast<uint32_t>(zbuf),
param_6 & 0xFu,
param_5 & 0x3u,
(param_2 & 2u) != 0u);
uint8_t *const ptr = getMemPtr(rdram, envAddr);
if (!ptr)
{
setReturnS32(ctx, 8);
return;
}
std::memcpy(ptr, &env, sizeof(env));
setReturnS32(ctx, 8);
}
void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t envAddr = getRegU32(ctx, 4);
uint32_t param_2 = getRegU32(ctx, 5);
int32_t w = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 6) & 0xFFFF));
int32_t h = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 7) & 0xFFFF));
const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx);
uint32_t param_5 = trailing.arg0;
uint32_t param_6 = trailing.arg1;
if (w <= 0)
w = 640;
if (h <= 0)
h = 448;
uint32_t psm = param_2 & 0xFU;
uint32_t fbw = ((static_cast<uint32_t>(w) + 63u) >> 6) & 0x3FU;
sceGszbufaddr(rdram, ctx, runtime);
int32_t zbuf = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 2) & 0xFFFF));
GsDrawEnv2Mem env{};
seedGsDrawEnv2(env,
w,
h,
0u,
fbw,
psm,
static_cast<uint32_t>(zbuf),
param_6 & 0xFu,
param_5 & 0x3u,
(param_2 & 2u) != 0u);
uint8_t *const ptr = getMemPtr(rdram, envAddr);
if (!ptr)
{
setReturnS32(ctx, 8);
return;
}
std::memcpy(ptr, &env, sizeof(env));
setReturnS32(ctx, 8);
}
void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t imgAddr = getRegU32(ctx, 4);
const GsSetDefImageArgs args = decodeGsSetDefImageArgs(rdram, ctx);
GsImageMem img{};
img.x = static_cast<uint16_t>(args.x);
img.y = static_cast<uint16_t>(args.y);
img.width = static_cast<uint16_t>(args.width);
img.height = static_cast<uint16_t>(args.height);
img.vram_addr = static_cast<uint16_t>(args.vramAddr);
img.vram_width = static_cast<uint8_t>(args.vramWidth);
img.psm = static_cast<uint8_t>(args.psm);
writeGsImage(rdram, imgAddr, img);
setReturnS32(ctx, 0);
}
void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
sceGsSetDefLoadImage(rdram, ctx, runtime);
}
void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t envAddr = getRegU32(ctx, 4);
const uint32_t which = getRegU32(ctx, 5) & 1u;
GsDBuffDcMem db{};
if (!runtime || !readGsDBuffDc(rdram, envAddr, db))
{
setReturnS32(ctx, -1);
return;
}
applyGsDispEnv(runtime, db.disp[which]);
static uint32_t s_swapDbuffLogCount = 0u;
if (s_swapDbuffLogCount < 32u)
{
const uint32_t dispFbp = static_cast<uint32_t>(db.disp[which].dispfb & 0x1FFu);
const uint32_t clearContext = (which == 0u)
? static_cast<uint32_t>((db.clear0.prim.value >> 9) & 0x1u)
: static_cast<uint32_t>((db.clear1.prim.value >> 9) & 0x1u);
PS2_IF_AGRESSIVE_LOGS({
RUNTIME_LOG("[gs:swapdbuff] which=" << which
<< " env=0x" << std::hex << envAddr
<< " dispfb=0x" << db.disp[which].dispfb
<< " display=0x" << db.disp[which].display
<< " pmode=0x" << db.disp[which].pmode
<< " dispFbp=" << dispFbp
<< " clearCtxt=" << clearContext
<< std::dec << std::endl);
});
++s_swapDbuffLogCount;
}
if (which == 0u)
{
applyGsRegPairs(runtime, reinterpret_cast<const GsRegPairMem *>(&db.draw01), 8u);
applyGsRegPairs(runtime, reinterpret_cast<const GsRegPairMem *>(&db.draw02), 8u);
if (hasSeededGsClearPacket(db.clear0))
{
const uint32_t clearContext = static_cast<uint32_t>((db.clear0.prim.value >> 9) & 0x1u);
runtime->gs().clearFramebufferContext(clearContext, static_cast<uint32_t>(db.clear0.rgbaq.value));
}
applyGsClearPacket(runtime, db.clear0);
}
else
{
applyGsRegPairs(runtime, reinterpret_cast<const GsRegPairMem *>(&db.draw11), 8u);
applyGsRegPairs(runtime, reinterpret_cast<const GsRegPairMem *>(&db.draw12), 8u);
if (hasSeededGsClearPacket(db.clear1))
{
const uint32_t clearContext = static_cast<uint32_t>((db.clear1.prim.value >> 9) & 0x1u);
runtime->gs().clearFramebufferContext(clearContext, static_cast<uint32_t>(db.clear1.rgbaq.value));
}
applyGsClearPacket(runtime, db.clear1);
}
setReturnS32(ctx, static_cast<int32_t>(which ^ 1u));
}
void sceGsSwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t envAddr = getRegU32(ctx, 4);
const uint32_t which = getRegU32(ctx, 5) & 1u;
GsDBuffMem db{};
if (!runtime || !readGsDBuff(rdram, envAddr, db))
{
setReturnS32(ctx, -1);
return;
}
applyGsDispEnv(runtime, db.disp[which]);
if (which == 0u)
{
applyGsRegPairs(runtime, reinterpret_cast<const GsRegPairMem *>(&db.draw0), 8u);
}
else
{
applyGsRegPairs(runtime, reinterpret_cast<const GsRegPairMem *>(&db.draw1), 8u);
}
setReturnS32(ctx, static_cast<int32_t>(which ^ 1u));
}
void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
int32_t mode = static_cast<int32_t>(getRegU32(ctx, 4));
auto &mem = runtime->memory();
if (mode == 0)
{
mem.processPendingTransfers();
uint32_t count = 0;
constexpr uint32_t kTimeout = 0x1000000;
while ((mem.readIORegister(0x10009000) & 0x100) != 0)
{
if (++count > kTimeout)
{
setReturnS32(ctx, -1);
return;
}
}
while ((mem.readIORegister(0x1000A000) & 0x100) != 0)
{
if (++count > kTimeout)
{
setReturnS32(ctx, -1);
return;
}
}
while ((mem.readIORegister(0x10003C00) & 0x1F000003) != 0)
{
if (++count > kTimeout)
{
setReturnS32(ctx, -1);
return;
}
}
while ((mem.readIORegister(0x10003020) & 0xC00) != 0)
{
if (++count > kTimeout)
{
setReturnS32(ctx, -1);
return;
}
}
setReturnS32(ctx, 0);
}
else
{
uint32_t result = 0;
if ((mem.readIORegister(0x10009000) & 0x100) != 0)
result |= 1;
if ((mem.readIORegister(0x1000A000) & 0x100) != 0)
result |= 2;
if ((mem.readIORegister(0x10003C00) & 0x1F000003) != 0)
result |= 4;
if ((mem.readIORegister(0x10003020) & 0xC00) != 0)
result |= 0x10;
setReturnS32(ctx, result);
}
}
void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
ps2_syscalls::WaitVSyncTick(rdram,
ctx,
runtime,
g_gparam.interlace != 0u ? -1 : 1);
}
void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t newCallback = getRegU32(ctx, 4);
const uint32_t callerPc = ctx ? ctx->pc : 0u;
const uint32_t callerRa = ctx ? getRegU32(ctx, 31) : 0u;
const uint32_t gp = getRegU32(ctx, 28);
const uint32_t sp = getRegU32(ctx, 29);
EeScheduler &ee = runtime->eeScheduler();
ee.bindMainContextForSyscall(*ctx, rdram);
const uint32_t oldCallback = ee.setGsVSyncCallback(newCallback, gp, sp);
static uint32_t s_syncVCallbackLogCount = 0u;
if (s_syncVCallbackLogCount < 128u)
{
PS2_IF_AGRESSIVE_LOGS({
RUNTIME_LOG("[sceGsSyncVCallback:set] new=0x" << std::hex << newCallback
<< " old=0x" << oldCallback
<< " callerPc=0x" << callerPc
<< " callerRa=0x" << callerRa
<< " gp=0x" << gp
<< " sp=0x" << sp
<< std::dec << std::endl);
});
++s_syncVCallbackLogCount;
}
setReturnU32(ctx, oldCallback);
}
void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
uint32_t param_1 = getRegU32(ctx, 4);
int32_t w = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 6) & 0xFFFF));
int32_t h = static_cast<int32_t>(static_cast<int16_t>(getRegU32(ctx, 7) & 0xFFFF));
int32_t width_blocks = (w + 63) >> 6;
if (w + 63 < 0)
width_blocks = (w + 126) >> 6;
int32_t height_blocks;
if ((param_1 & 2) != 0)
{
int32_t v = (h + 63) >> 6;
if (h + 63 < 0)
v = (h + 126) >> 6;
height_blocks = v;
}
else
{
int32_t v = (h + 31) >> 5;
if (h + 31 < 0)
v = (h + 62) >> 5;
height_blocks = v;
}
int32_t product = width_blocks * height_blocks;
uint64_t gparam_val = 0;
if (runtime)
{
uint8_t *scratch = runtime->memory().getScratchpad();
if (scratch)
{
std::memcpy(&gparam_val, scratch + 0x100, sizeof(gparam_val));
}
}
if ((gparam_val & 0xFFFF0000FFFFULL) == 1ULL)
product = (product * 0x10000) >> 16;
else
product = (product * 0x20000) >> 16;
setReturnS32(ctx, product);
}
void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
static int logCount = 0;
if (logCount < 8)
{
RUNTIME_LOG("ps2_stub Ps2SwapDBuff");
++logCount;
}
setReturnS32(ctx, 0);
}
void sceVif1PkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return;
}
const uint64_t dataValue = GPR_U64(ctx, 6);
const uint32_t words[4] = {
static_cast<uint32_t>(dataValue & 0xFFFFFFFFu),
static_cast<uint32_t>(dataValue >> 32u),
getRegU32(ctx, 5),
0u,
};
writeGuestBytes(rdram,
runtime,
currentAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u);
}
void sceVif1PkAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
alignPacketBuilderState(rdram,
runtime,
getRegU32(ctx, 4),
getRegU32(ctx, 5),
getRegU32(ctx, 6));
}
void sceVif1PkCall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t refAddr = getRegU32(ctx, 5) & 0x9FFFFFFFu;
const uint32_t tagWord = getRegU32(ctx, 6) | 0x50000000u;
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[2] = {tagWord, refAddr};
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 8u);
writeGuestU32(rdram, runtime, stateAddr + 12u, 0u);
}
void sceVif1PkCloseDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
uint32_t currentAddr = 0u;
uint32_t openAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr) ||
!tryReadWordFromGuest(rdram, runtime, stateAddr + 12u, openAddr) ||
openAddr == 0u)
{
return;
}
const uint32_t currentMinusTag = currentAddr - 4u;
const uint32_t wordCount = (currentMinusTag - openAddr) >> 2u;
const uint32_t qwordCount = wordCount >> 2u;
uint32_t tagWord = 0u;
if (!tryReadWordFromGuest(rdram, runtime, openAddr, tagWord))
{
return;
}
tagWord += qwordCount;
writeGuestU32(rdram, runtime, stateAddr + 12u, 0u);
writeGuestU32(rdram, runtime, openAddr, tagWord);
}
void sceVif1PkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)ctx;
closePacketGifTag(rdram, runtime, getRegU32(ctx, 4), 20u);
}
void sceVif1PkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t tagWord = getRegU32(ctx, 5) | 0x10000000u;
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[2] = {tagWord, 0u};
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writeGuestU32(rdram, runtime, stateAddr + 12u, 0u);
writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 8u);
}
void sceVif1PkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t tagWord = getRegU32(ctx, 5) | 0x70000000u;
const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime);
const uint32_t words[2] = {tagWord, 0u};
writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr);
writeGuestBytes(rdram,
runtime,
packetAddr,
reinterpret_cast<const uint8_t *>(words),
sizeof(words));
writeGuestU32(rdram, runtime, stateAddr + 12u, 0u);
writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 8u);
}
void sceVif1PkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
initPacketBuilderState(rdram, ctx, runtime);
writeGuestU32(rdram, runtime, getRegU32(ctx, 4) + 20u, 0u);
}
void sceVif1PkOpenDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
alignPacketBuilderState(rdram, runtime, stateAddr, 2u, 3u);
uint32_t currentAddr = 0u;
if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr))
{
return;
}
const uint32_t tagWord = (getRegU32(ctx, 5) != 0u) ? 0xD0000000u : 0x50000000u;
writeGuestU32(rdram, runtime, currentAddr, tagWord);
writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 4u);
writeGuestU32(rdram, runtime, stateAddr + 12u, currentAddr);
}
void sceVif1PkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
openPacketGifTag(rdram, ctx, runtime, getRegU32(ctx, 4), 20u);
}
void sceVif1PkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
resetPacketBuilderState(rdram, ctx, runtime);
writeGuestU32(rdram, runtime, getRegU32(ctx, 4) + 20u, 0u);
}
void sceVif1PkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t stateAddr = getRegU32(ctx, 4);
const uint32_t wordCount = getRegU32(ctx, 5);
uint32_t currentAddr = 0u;
tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr);
const uint32_t reservedAddr = reservePacketBuilderWords(rdram, runtime, stateAddr, wordCount);
setReturnU32(ctx, reservedAddr);
}
void sceVif1PkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnU32(ctx, terminatePacketBuilderState(rdram, ctx, runtime));
}
}