Feature/runtime ecosystem refactor (#107)

* feat: remove memory and pad from stub section

* feat: add support for resume entry targets in CodeGenerator (this allow jumps in address outside function)
feat: refactor entry point discovery one more try to reduce big generated file

* feat: optmizations for release build

* feat: remove unused  file

* feat: added some test cases for code gen

* feat: added log macro and remove win specific code

* feat: refactor runtime folder structure
feat: added reset sound driver RPC state and compatibility layout
feat: rename and added new test
feat: update RPC calls to use defined constants
feat: added more PSMC(16, 32)
feat: change cd read to try find the asset ignoring case sensitive
fix: fix some render problems
feat: add logs on pad
feat: added more RPC handles

* feat: added game override for code veronica

* feat: apply vita patch

* feat: flags to disable build

* feat: fix merges
feat: break a lot of tests

* feat: better throw error on empty cd path
feat: remove recompiler unusde function
feat: apply missing patch

* feat: gamedp is now part of lib
feat: missing file

* feat: small cleanup

* feat: missing vita changes

* feat: fix more merge

* feat: fix tests

* feat: last missing feature

* feat: added missing import

* feat: rename test local functions

* feat: init syscall on ps2 list

* feat: added DMA helpers

* feat: faster builds
feat: more implement for darkcloud

* feat: back missing file

* feat: added missing includes

* feat: remove test

* feat: missing include

* feat: read register funtion

* feat: build  fix

* feat: force  exit on detach thread
This commit is contained in:
Ranieri
2026-04-04 23:09:56 -03:00
committed by GitHub
parent 553a9027d8
commit 93e221feaa
137 changed files with 26594 additions and 15314 deletions
+49
View File
@@ -0,0 +1,49 @@
#ifndef PS2_AUDIO_H
#define PS2_AUDIO_H
#include <cstdint>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
class PS2AudioBackend
{
public:
PS2AudioBackend();
~PS2AudioBackend();
void onVagTransfer(const uint8_t *rdram, uint32_t srcAddr, uint32_t sizeBytes);
void onVagTransferFromBuffer(const uint8_t *data, uint32_t sizeBytes, uint32_t keyAddr);
void onSoundCommand(uint32_t sid, uint32_t rpcNum,
const uint8_t *sendBuf, uint32_t sendSize,
uint8_t *recvBuf, uint32_t recvSize);
void play(uint32_t sampleAddr, float pitch = 1.0f, float volume = 1.0f,
uint32_t voiceIndex = 0xFFFFFFFFu);
void stop(uint32_t voiceId);
void stopAll();
void setAudioReady(bool ready) { m_audioReady = ready; }
private:
struct DecodedSample
{
std::vector<int16_t> pcm;
uint32_t sampleRate = 44100;
};
struct Impl;
std::unique_ptr<Impl> m_impl;
bool m_audioReady = false;
uint32_t m_mostRecentSampleKey = 0;
std::vector<DecodedSample> m_loadOrderSamples;
std::vector<uint32_t> m_loadOrderSampleKeys;
std::unordered_map<uint32_t, DecodedSample> m_sampleBank;
std::mutex m_mutex;
void playDecodedSample(uint32_t sampleKey, DecodedSample &sample, float pitch, float volume,
bool isBgm = false);
void pruneFinishedSounds();
};
#endif
@@ -0,0 +1,45 @@
#ifndef PS2_GIF_ARBITER_H
#define PS2_GIF_ARBITER_H
#include <cstdint>
#include <functional>
#include <vector>
enum class GifPathId : uint8_t
{
Path1 = 1,
Path2 = 2,
Path3 = 3,
};
struct GifArbiterPacket
{
GifPathId pathId;
bool path2DirectHl = false;
bool path3Image = false;
std::vector<uint8_t> data;
};
class GifArbiter
{
public:
using ProcessPacketFn = std::function<void(const uint8_t *, uint32_t)>;
GifArbiter() = default;
explicit GifArbiter(ProcessPacketFn processFn);
void setProcessPacketFn(ProcessPacketFn fn) { m_processFn = std::move(fn); }
void submit(GifPathId pathId, const uint8_t *data, uint32_t sizeBytes, bool path2DirectHl = false);
void drain();
private:
ProcessPacketFn m_processFn;
std::vector<GifArbiterPacket> m_queue;
static bool isImagePacket(const uint8_t *data, uint32_t sizeBytes);
static uint8_t pathPriority(GifPathId id);
};
#endif
@@ -0,0 +1,62 @@
#ifndef PS2_GS_COMMON_H
#define PS2_GS_COMMON_H
#include "ps2_gs_gpu.h"
#include <cstdint>
namespace GSInternal
{
static inline uint32_t bitsPerPixel(uint8_t psm)
{
switch (psm)
{
case GS_PSM_CT32:
case GS_PSM_Z32:
return 32;
case GS_PSM_CT24:
case GS_PSM_Z24:
return 32;
case GS_PSM_CT16:
case GS_PSM_CT16S:
case GS_PSM_Z16:
case GS_PSM_Z16S:
return 16;
case GS_PSM_T8:
case GS_PSM_T8H:
return 8;
case GS_PSM_T4:
case GS_PSM_T4HL:
case GS_PSM_T4HH:
return 4;
default:
return 32;
}
}
static inline uint32_t fbStride(uint32_t fbw, uint8_t psm)
{
uint32_t pixelsPerRow = fbw * 64u;
return pixelsPerRow * (bitsPerPixel(psm) / 8u);
}
static inline uint32_t framePageBaseToBlock(uint32_t fbp)
{
return fbp << 5u;
}
static inline int clampInt(int v, int lo, int hi)
{
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}
static inline uint8_t clampU8(int v)
{
if (v < 0) return 0;
if (v > 255) return 255;
return static_cast<uint8_t>(v);
}
}
#endif
+323
View File
@@ -0,0 +1,323 @@
#ifndef PS2_GS_GPU_H
#define PS2_GS_GPU_H
#include "ps2_gs_rasterizer.h"
#include <cstdint>
#include <cstring>
#include <mutex>
#include <vector>
enum GSPrimType : uint8_t
{
GS_PRIM_POINT = 0,
GS_PRIM_LINE = 1,
GS_PRIM_LINESTRIP = 2,
GS_PRIM_TRIANGLE = 3,
GS_PRIM_TRISTRIP = 4,
GS_PRIM_TRIFAN = 5,
GS_PRIM_SPRITE = 6,
};
enum GSPsm : uint8_t
{
GS_PSM_CT32 = 0,
GS_PSM_CT24 = 1,
GS_PSM_CT16 = 2,
GS_PSM_CT16S = 10,
GS_PSM_T8 = 19,
GS_PSM_T4 = 20,
GS_PSM_T8H = 27,
GS_PSM_T4HL = 36,
GS_PSM_T4HH = 44,
GS_PSM_Z32 = 48,
GS_PSM_Z24 = 49,
GS_PSM_Z16 = 50,
GS_PSM_Z16S = 58,
};
enum GSGifFormat : uint8_t
{
GIF_FMT_PACKED = 0,
GIF_FMT_REGLIST = 1,
GIF_FMT_IMAGE = 2,
GIF_FMT_DISABLED = 3,
};
enum GSRegId : uint8_t
{
GS_REG_PRIM = 0x00,
GS_REG_RGBAQ = 0x01,
GS_REG_ST = 0x02,
GS_REG_UV = 0x03,
GS_REG_XYZF2 = 0x04,
GS_REG_XYZ2 = 0x05,
GS_REG_TEX0_1 = 0x06,
GS_REG_TEX0_2 = 0x07,
GS_REG_CLAMP_1 = 0x08,
GS_REG_CLAMP_2 = 0x09,
GS_REG_FOG = 0x0A,
GS_REG_XYZF3 = 0x0C,
GS_REG_XYZ3 = 0x0D,
GS_REG_AD = 0x0F,
GS_REG_TEX1_1 = 0x14,
GS_REG_TEX1_2 = 0x15,
GS_REG_TEX2_1 = 0x16,
GS_REG_TEX2_2 = 0x17,
GS_REG_XYOFFSET_1 = 0x18,
GS_REG_XYOFFSET_2 = 0x19,
GS_REG_PRMODECONT = 0x1A,
GS_REG_PRMODE = 0x1B,
GS_REG_TEXCLUT = 0x1C,
GS_REG_SCANMSK = 0x22,
GS_REG_MIPTBP1_1 = 0x34,
GS_REG_MIPTBP1_2 = 0x35,
GS_REG_MIPTBP2_1 = 0x36,
GS_REG_MIPTBP2_2 = 0x37,
GS_REG_TEXA = 0x3B,
GS_REG_FOGCOL = 0x3D,
GS_REG_TEXFLUSH = 0x3F,
GS_REG_SCISSOR_1 = 0x40,
GS_REG_SCISSOR_2 = 0x41,
GS_REG_ALPHA_1 = 0x42,
GS_REG_ALPHA_2 = 0x43,
GS_REG_DIMX = 0x44,
GS_REG_DTHE = 0x45,
GS_REG_COLCLAMP = 0x46,
GS_REG_TEST_1 = 0x47,
GS_REG_TEST_2 = 0x48,
GS_REG_PABE = 0x49,
GS_REG_FBA_1 = 0x4A,
GS_REG_FBA_2 = 0x4B,
GS_REG_FRAME_1 = 0x4C,
GS_REG_FRAME_2 = 0x4D,
GS_REG_ZBUF_1 = 0x4E,
GS_REG_ZBUF_2 = 0x4F,
GS_REG_BITBLTBUF = 0x50,
GS_REG_TRXPOS = 0x51,
GS_REG_TRXREG = 0x52,
GS_REG_TRXDIR = 0x53,
GS_REG_HWREG = 0x54,
GS_REG_SIGNAL = 0x60,
GS_REG_FINISH = 0x61,
GS_REG_LABEL = 0x62,
};
struct GSVertex
{
float x, y, z;
uint8_t r, g, b, a;
float q;
float s, t;
uint16_t u, v;
uint8_t fog;
};
struct GSFrameReg
{
uint32_t fbp;
uint32_t fbw;
uint8_t psm;
uint32_t fbmsk;
};
struct GSScissorReg
{
uint16_t x0, x1, y0, y1;
};
struct GSTex0Reg
{
uint32_t tbp0;
uint8_t tbw;
uint8_t psm;
uint8_t tw;
uint8_t th;
uint8_t tcc;
uint8_t tfx;
uint32_t cbp;
uint8_t cpsm;
uint8_t csm;
uint8_t csa;
uint8_t cld;
};
struct GSXYOffsetReg
{
uint16_t ofx;
uint16_t ofy;
};
struct GSTexaReg
{
uint8_t ta0;
bool aem;
uint8_t ta1;
};
struct GSTexClutReg
{
uint8_t cbw;
uint8_t cou;
uint16_t cov;
};
struct GSContext
{
GSFrameReg frame;
GSScissorReg scissor;
GSTex0Reg tex0;
GSXYOffsetReg xyoffset;
uint64_t zbuf;
uint64_t tex1;
uint64_t clamp;
uint64_t alpha;
uint64_t test;
uint64_t fba;
};
struct GSPrimReg
{
GSPrimType type;
bool iip;
bool tme;
bool fge;
bool abe;
bool aa1;
bool fst;
bool ctxt;
bool fix;
};
struct GSBitBltBuf
{
uint32_t sbp;
uint8_t sbw;
uint8_t spsm;
uint32_t dbp;
uint8_t dbw;
uint8_t dpsm;
};
struct GSTrxPos
{
uint16_t ssax, ssay;
uint16_t dsax, dsay;
uint8_t dir;
};
struct GSTrxReg
{
uint16_t rrw, rrh;
};
class GSRasterizer;
class GS
{
friend class GSRasterizer;
public:
GS();
~GS() = default;
void init(uint8_t *vram, uint32_t vramSize, struct GSRegisters *privRegs = nullptr);
void reset();
void processGIFPacket(const uint8_t *data, uint32_t sizeBytes);
void writeRegister(uint8_t regAddr, uint64_t value);
const uint8_t *lockDisplaySnapshot(uint32_t &outSize);
void unlockDisplaySnapshot();
uint32_t getLastDisplayBaseBytes() const;
const GSFrameReg &getContextFrame(int index) const
{
return m_ctx[(index != 0) ? 1 : 0].frame;
}
bool getPreferredDisplaySource(GSFrameReg &outSource, uint32_t &outDestFbp) const;
void latchHostPresentationFrame();
bool copyLatchedHostPresentationFrame(std::vector<uint8_t> &outPixels,
uint32_t &outWidth,
uint32_t &outHeight,
uint32_t *outDisplayFbp = nullptr,
uint32_t *outSourceFbp = nullptr,
bool *outUsedPreferred = nullptr) const;
bool clearFramebufferContext(uint32_t contextIndex, uint32_t rgba);
bool clearActiveFramebuffer(uint32_t rgba);
uint32_t consumeLocalToHostBytes(uint8_t *dst, uint32_t maxBytes);
void refreshDisplaySnapshot();
private:
void snapshotVRAM();
void writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi);
void vertexKick(bool drawing);
void processImageData(const uint8_t *data, uint32_t sizeBytes);
void performLocalToLocalTransfer();
void performLocalToHostToBuffer();
bool copyFrameToHostRgbaUnlocked(const GSFrameReg &frame,
uint32_t width,
uint32_t height,
std::vector<uint8_t> &outPixels,
bool preserveAlpha = false,
bool useLocalMemoryLayout = false,
bool frameBaseIsPages = true,
uint32_t sourceOriginX = 0u,
uint32_t sourceOriginY = 0u) const;
GSContext &activeContext();
uint8_t *m_vram = nullptr;
uint32_t m_vramSize = 0;
struct GSRegisters *m_privRegs = nullptr;
mutable std::recursive_mutex m_stateMutex;
GSContext m_ctx[2];
GSPrimReg m_prim{};
uint8_t m_curR = 0x80, m_curG = 0x80, m_curB = 0x80, m_curA = 0x80;
float m_curQ = 1.0f;
float m_curS = 0.0f, m_curT = 0.0f;
uint16_t m_curU = 0, m_curV = 0;
uint8_t m_curFog = 0;
bool m_prmodecont = true;
bool m_pabe = false;
GSTexaReg m_texa{0u, false, 0u};
GSTexClutReg m_texclut{0u, 0u, 0u};
GSBitBltBuf m_bitbltbuf{};
GSTrxPos m_trxpos{};
GSTrxReg m_trxreg{};
uint32_t m_trxdir = 3;
uint32_t m_hwregX = 0;
uint32_t m_hwregY = 0;
static constexpr int kMaxVerts = 6;
GSVertex m_vtxQueue[kMaxVerts];
int m_vtxCount = 0;
int m_vtxIndex = 0;
std::vector<uint8_t> m_displaySnapshot;
std::mutex m_snapshotMutex;
uint32_t m_lastDisplayBaseBytes = 0;
GSFrameReg m_preferredDisplaySourceFrame{};
uint32_t m_preferredDisplayDestFbp = 0;
bool m_hasPreferredDisplaySource = false;
std::vector<uint8_t> m_hostPresentationFrame;
uint32_t m_hostPresentationWidth = 0;
uint32_t m_hostPresentationHeight = 0;
uint32_t m_hostPresentationDisplayFbp = 0;
uint32_t m_hostPresentationSourceFbp = 0;
bool m_hostPresentationUsedPreferred = false;
bool m_hasHostPresentationFrame = false;
std::vector<uint8_t> m_localToHostBuffer;
size_t m_localToHostReadPos = 0;
GSRasterizer m_rasterizer;
};
#endif
@@ -0,0 +1,96 @@
#ifndef PS2_GS_PSMCT16_H
#define PS2_GS_PSMCT16_H
#include <cstdint>
namespace GSPSMCT16
{
static constexpr uint8_t blockTable16[8][4] = {
{0, 2, 8, 10},
{1, 3, 9, 11},
{4, 6, 12, 14},
{5, 7, 13, 15},
{16, 18, 24, 26},
{17, 19, 25, 27},
{20, 22, 28, 30},
{21, 23, 29, 31},
};
static constexpr uint8_t blockTable16S[8][4] = {
{0, 2, 16, 18},
{1, 3, 17, 19},
{8, 10, 24, 26},
{9, 11, 25, 27},
{4, 6, 20, 22},
{5, 7, 21, 23},
{12, 14, 28, 30},
{13, 15, 29, 31},
};
static constexpr uint8_t blockTableZ16[8][4] = {
{24, 26, 16, 18},
{25, 27, 17, 19},
{28, 30, 20, 22},
{29, 31, 21, 23},
{8, 10, 0, 2},
{9, 11, 1, 3},
{12, 14, 4, 6},
{13, 15, 5, 7},
};
static constexpr uint8_t blockTableZ16S[8][4] = {
{24, 26, 8, 10},
{25, 27, 9, 11},
{16, 18, 0, 2},
{17, 19, 1, 3},
{28, 30, 12, 14},
{29, 31, 13, 15},
{20, 22, 4, 6},
{21, 23, 5, 7},
};
static constexpr uint8_t columnTable16[2][16] = {
{0, 2, 8, 10, 16, 18, 24, 26, 1, 3, 9, 11, 17, 19, 25, 27},
{4, 6, 12, 14, 20, 22, 28, 30, 5, 7, 13, 15, 21, 23, 29, 31},
};
inline uint32_t addrPSMCT16Like(uint32_t block,
uint32_t width,
uint32_t x,
uint32_t y,
const uint8_t (&blockTable)[8][4])
{
const uint32_t pagesPerRow = (width != 0u) ? width : 1u;
const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 6u);
const uint32_t blockId = (block & 0x1Fu) + blockTable[(y >> 3u) & 0x7u][(x >> 4u) & 0x3u];
const uint32_t pageOffset = (blockId >> 5u) << 13u;
const uint32_t localBlock = blockId & 0x1Fu;
const uint32_t columnOffset = ((y >> 1u) & 0x3u) * 64u;
return (page << 13u) + pageOffset + localBlock * 256u + columnOffset +
static_cast<uint32_t>(columnTable16[y & 0x1u][x & 0x0Fu]) * 2u;
}
inline uint32_t addrPSMCT16(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTable16);
}
inline uint32_t addrPSMCT16S(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTable16S);
}
inline uint32_t addrPSMZ16(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTableZ16);
}
inline uint32_t addrPSMZ16S(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
return addrPSMCT16Like(block, width, x, y, blockTableZ16S);
}
}
#endif
@@ -0,0 +1,40 @@
#ifndef PS2_GS_PSMCT32_H
#define PS2_GS_PSMCT32_H
#include <cstdint>
namespace GSPSMCT32
{
static constexpr uint8_t blockTable32[4][8] = {
{0, 1, 4, 5, 16, 17, 20, 21},
{2, 3, 6, 7, 18, 19, 22, 23},
{8, 9, 12, 13, 24, 25, 28, 29},
{10, 11, 14, 15, 26, 27, 30, 31},
};
static constexpr uint8_t columnTable32[8][8] = {
{0, 1, 4, 5, 8, 9, 12, 13},
{2, 3, 6, 7, 10, 11, 14, 15},
{16, 17, 20, 21, 24, 25, 28, 29},
{18, 19, 22, 23, 26, 27, 30, 31},
{32, 33, 36, 37, 40, 41, 44, 45},
{34, 35, 38, 39, 42, 43, 46, 47},
{48, 49, 52, 53, 56, 57, 60, 61},
{50, 51, 54, 55, 58, 59, 62, 63},
};
inline uint32_t addrPSMCT32(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
const uint32_t pagesPerRow = (width != 0u) ? width : 1u;
const uint32_t page = (block >> 5u) + (y >> 5u) * pagesPerRow + (x >> 6u);
const uint32_t blockId = (block & 0x1Fu) + blockTable32[(y >> 3u) & 3u][(x >> 3u) & 7u];
const uint32_t pageOffset = (blockId >> 5u) << 13u;
const uint32_t localBlock = blockId & 0x1Fu;
return (page << 13u) + pageOffset + localBlock * 256u +
static_cast<uint32_t>(columnTable32[y & 0x7u][x & 0x7u]) * 4u;
}
}
#endif
@@ -0,0 +1,51 @@
#ifndef PS2_GS_PSMT4_H
#define PS2_GS_PSMT4_H
#include <cstdint>
namespace GSPSMT4
{
static constexpr uint8_t blockTable4[8][4] = {
{0, 2, 8, 10},
{1, 3, 9, 11},
{4, 6, 12, 14},
{5, 7, 13, 15},
{16, 18, 24, 26},
{17, 19, 25, 27},
{20, 22, 28, 30},
{21, 23, 29, 31},
};
static const uint16_t columnTable4[16][32] = {
{0, 8, 32, 40, 64, 72, 96, 104, 2, 10, 34, 42, 66, 74, 98, 106, 4, 12, 36, 44, 68, 76, 100, 108, 6, 14, 38, 46, 70, 78, 102, 110},
{16, 24, 48, 56, 80, 88, 112, 120, 18, 26, 50, 58, 82, 90, 114, 122, 20, 28, 52, 60, 84, 92, 116, 124, 22, 30, 54, 62, 86, 94, 118, 126},
{65, 73, 97, 105, 1, 9, 33, 41, 67, 75, 99, 107, 3, 11, 35, 43, 69, 77, 101, 109, 5, 13, 37, 45, 71, 79, 103, 111, 7, 15, 39, 47},
{81, 89, 113, 121, 17, 25, 49, 57, 83, 91, 115, 123, 19, 27, 51, 59, 85, 93, 117, 125, 21, 29, 53, 61, 87, 95, 119, 127, 23, 31, 55, 63},
{192, 200, 224, 232, 128, 136, 160, 168, 194, 202, 226, 234, 130, 138, 162, 170, 196, 204, 228, 236, 132, 140, 164, 172, 198, 206, 230, 238, 134, 142, 166, 174},
{208, 216, 240, 248, 144, 152, 176, 184, 210, 218, 242, 250, 146, 154, 178, 186, 212, 220, 244, 252, 148, 156, 180, 188, 214, 222, 246, 254, 150, 158, 182, 190},
{129, 137, 161, 169, 193, 201, 225, 233, 131, 139, 163, 171, 195, 203, 227, 235, 133, 141, 165, 173, 197, 205, 229, 237, 135, 143, 167, 175, 199, 207, 231, 239},
{145, 153, 177, 185, 209, 217, 241, 249, 147, 155, 179, 187, 211, 219, 243, 251, 149, 157, 181, 189, 213, 221, 245, 253, 151, 159, 183, 191, 215, 223, 247, 255},
{256, 264, 288, 296, 320, 328, 352, 360, 258, 266, 290, 298, 322, 330, 354, 362, 260, 268, 292, 300, 324, 332, 356, 364, 262, 270, 294, 302, 326, 334, 358, 366},
{272, 280, 304, 312, 336, 344, 368, 376, 274, 282, 306, 314, 338, 346, 370, 378, 276, 284, 308, 316, 340, 348, 372, 380, 278, 286, 310, 318, 342, 350, 374, 382},
{321, 329, 353, 361, 257, 265, 289, 297, 323, 331, 355, 363, 259, 267, 291, 299, 325, 333, 357, 365, 261, 269, 293, 301, 327, 335, 359, 367, 263, 271, 295, 303},
{337, 345, 369, 377, 273, 281, 305, 313, 339, 347, 371, 379, 275, 283, 307, 315, 341, 349, 373, 381, 277, 285, 309, 317, 343, 351, 375, 383, 279, 287, 311, 319},
{448, 456, 480, 488, 384, 392, 416, 424, 450, 458, 482, 490, 386, 394, 418, 426, 452, 460, 484, 492, 388, 396, 420, 428, 454, 462, 486, 494, 390, 398, 422, 430},
{464, 472, 496, 504, 400, 408, 432, 440, 466, 474, 498, 506, 402, 410, 434, 442, 468, 476, 500, 508, 404, 412, 436, 444, 470, 478, 502, 510, 406, 414, 438, 446},
{385, 393, 417, 425, 449, 457, 481, 489, 387, 395, 419, 427, 451, 459, 483, 491, 389, 397, 421, 429, 453, 461, 485, 493, 391, 399, 423, 431, 455, 463, 487, 495},
{401, 409, 433, 441, 465, 473, 497, 505, 403, 411, 435, 443, 467, 475, 499, 507, 405, 413, 437, 445, 469, 477, 501, 509, 407, 415, 439, 447, 471, 479, 503, 511},
};
inline uint32_t addrPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u;
const uint32_t page = (block >> 5u) + (y >> 7u) * pagesPerRow + (x >> 7u);
const uint32_t blockId = (block & 0x1Fu) + blockTable4[(y >> 4u) & 7u][(x >> 5u) & 3u];
const uint32_t pageOffset = (blockId >> 5u) << 14u;
const uint32_t localBlock = blockId & 0x1Fu;
return (page << 14u) + pageOffset + localBlock * 512u + columnTable4[y & 0x0Fu][x & 0x1Fu];
}
}
#endif
@@ -0,0 +1,47 @@
#ifndef PS2_GS_PSMT8_H
#define PS2_GS_PSMT8_H
#include <cstdint>
namespace GSPSMT8
{
static constexpr uint8_t blockTable8[4][8] = {
{0, 1, 4, 5, 16, 17, 20, 21},
{2, 3, 6, 7, 18, 19, 22, 23},
{8, 9, 12, 13, 24, 25, 28, 29},
{10, 11, 14, 15, 26, 27, 30, 31},
};
static constexpr uint8_t columnTable8[16][16] = {
{0, 4, 16, 20, 32, 36, 48, 52, 2, 6, 18, 22, 34, 38, 50, 54},
{8, 12, 24, 28, 40, 44, 56, 60, 10, 14, 26, 30, 42, 46, 58, 62},
{33, 37, 49, 53, 1, 5, 17, 21, 35, 39, 51, 55, 3, 7, 19, 23},
{41, 45, 57, 61, 9, 13, 25, 29, 43, 47, 59, 63, 11, 15, 27, 31},
{96, 100, 112, 116, 64, 68, 80, 84, 98, 102, 114, 118, 66, 70, 82, 86},
{104, 108, 120, 124, 72, 76, 88, 92, 106, 110, 122, 126, 74, 78, 90, 94},
{65, 69, 81, 85, 97, 101, 113, 117, 67, 71, 83, 87, 99, 103, 115, 119},
{73, 77, 89, 93, 105, 109, 121, 125, 75, 79, 91, 95, 107, 111, 123, 127},
{128, 132, 144, 148, 160, 164, 176, 180, 130, 134, 146, 150, 162, 166, 178, 182},
{136, 140, 152, 156, 168, 172, 184, 188, 138, 142, 154, 158, 170, 174, 186, 190},
{161, 165, 177, 181, 129, 133, 145, 149, 163, 167, 179, 183, 131, 135, 147, 151},
{169, 173, 185, 189, 137, 141, 153, 157, 171, 175, 187, 191, 139, 143, 155, 159},
{224, 228, 240, 244, 192, 196, 208, 212, 226, 230, 242, 246, 194, 198, 210, 214},
{232, 236, 248, 252, 200, 204, 216, 220, 234, 238, 250, 254, 202, 206, 218, 222},
{193, 197, 209, 213, 225, 229, 241, 245, 195, 199, 211, 215, 227, 231, 243, 247},
{201, 205, 217, 221, 233, 237, 249, 253, 203, 207, 219, 223, 235, 239, 251, 255},
};
inline uint32_t addrPSMT8(uint32_t block, uint32_t width, uint32_t x, uint32_t y)
{
const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u;
const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 7u);
const uint32_t blockId = (block & 0x1Fu) + blockTable8[(y >> 4) & 3u][(x >> 4) & 7u];
const uint32_t pageOffset = (blockId >> 5u) << 13u;
const uint32_t localBlock = blockId & 0x1Fu;
return (page << 13u) + pageOffset + localBlock * 256u + columnTable8[y & 0x0Fu][x & 0x0Fu];
}
}
#endif
@@ -0,0 +1,25 @@
#ifndef PS2_GS_RASTERIZER_H
#define PS2_GS_RASTERIZER_H
#include <cstdint>
class GS;
class GSRasterizer
{
public:
void drawPrimitive(GS *gs);
void writePixel(GS *gs, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
uint32_t sampleTexture(GS *gs, float s, float t, float q, uint16_t u, uint16_t v);
uint32_t readTexelPSMCT32(GS *gs, uint32_t tbp0, uint32_t tbw, int texU, int texV);
uint32_t readTexelPSMCT16(GS *gs, uint32_t tbp0, uint32_t tbw, int texU, int texV);
uint32_t readTexelPSMT4(GS *gs, uint32_t tbp0, uint32_t tbw, int texU, int texV);
uint32_t lookupCLUT(GS *gs, uint8_t index, uint32_t cbp, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm);
private:
void drawSprite(GS *gs);
void drawTriangle(GS *gs);
void drawLine(GS *gs);
};
#endif
+36
View File
@@ -0,0 +1,36 @@
#ifndef PS2_IOP_H
#define PS2_IOP_H
#include <cstdint>
class PS2Runtime;
constexpr uint32_t IOP_SID_SNDDRV_COMMAND = 0x00000000u;
constexpr uint32_t IOP_SID_SNDDRV_STATE = 0x00000001u;
constexpr uint32_t IOP_SID_LIBSD = 0x80000701u;
constexpr uint32_t IOP_RPC_SNDDRV_SUBMIT = 0x00000000u;
constexpr uint32_t IOP_RPC_SNDDRV_GET_STATUS_ADDR = 0x00000012u;
constexpr uint32_t IOP_RPC_SNDDRV_GET_ADDR_TABLE = 0x00000013u;
class ps2_iop
{
public:
ps2_iop();
~ps2_iop() = default;
void init(uint8_t *rdram);
void reset();
bool handleRPC(PS2Runtime *runtime,
uint32_t sid, uint32_t rpcNum,
uint32_t sendBufAddr, uint32_t sendSize,
uint32_t recvBufAddr, uint32_t recvSize,
uint32_t &resultPtr,
bool &signalNowaitCompletion);
private:
uint8_t *m_rdram = nullptr;
};
#endif
@@ -0,0 +1,15 @@
#ifndef PS2_IOP_AUDIO_H
#define PS2_IOP_AUDIO_H
#include <cstdint>
class PS2Runtime;
namespace ps2_iop_audio
{
void handleLibSdRpc(PS2Runtime *runtime, uint32_t sid, uint32_t rpcNum,
const uint8_t *sendBuf, uint32_t sendSize,
uint8_t *recvBuf, uint32_t recvSize);
}
#endif
+410
View File
@@ -0,0 +1,410 @@
#ifndef PS2_MEMORY_H
#define PS2_MEMORY_H
#include <cstddef>
#include <cstdint>
#include <functional>
#include <vector>
#include <unordered_map>
#include <atomic>
#include <iostream>
#include "ps2_gif_arbiter.h"
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(USE_SSE2NEON)
#include "sse2neon.h"
#else
#include <immintrin.h> // For SSE/AVX instructions
#include <smmintrin.h> // For SSE4.1 instructions
#endif
constexpr uint32_t PS2_RAM_SIZE = 32u * 1024u * 1024u; // 32MB
constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1u; // Mask for 32MB alignment
constexpr uint32_t PS2_RAM_BASE = 0x00000000; // Physical base of RDRAM
constexpr uint32_t PS2_SCRATCHPAD_BASE = 0x70000000;
constexpr uint32_t PS2_SCRATCHPAD_ALIAS_BASE = 0xF0000000;
constexpr uint32_t PS2_SCRATCHPAD_SIZE = 16u * 1024u; // 16KB
constexpr uint32_t PS2_IO_BASE = 0x10000000; // Base for many I/O regs (Timers, DMAC, INTC)
constexpr uint32_t PS2_IO_SIZE = 0x10000; // 64KB
constexpr uint32_t PS2_BIOS_BASE = 0x1FC00000; // Or BFC00000 depending on KSEG
constexpr uint32_t PS2_BIOS_SIZE = 4u * 1024u * 1024u; // 4MB
constexpr uint32_t PS2_VU0_CODE_BASE = 0x11000000; // Base address as seen from EE
constexpr uint32_t PS2_VU0_DATA_BASE = 0x11004000;
constexpr uint32_t PS2_VU0_CODE_SIZE = 4u * 1024u; // 4KB Micro Memory
constexpr uint32_t PS2_VU0_DATA_SIZE = 4u * 1024u; // 4KB Data Memory (VU Mem)
constexpr uint32_t PS2_VU1_CODE_BASE = 0x11008000;
constexpr uint32_t PS2_VU1_DATA_BASE = 0x1100C000;
constexpr uint32_t PS2_VU1_MEM_BASE = PS2_VU1_CODE_BASE; // Alias used by older code paths
constexpr uint32_t PS2_VU1_CODE_SIZE = 16u * 1024u; // 16KB Micro Memory
constexpr uint32_t PS2_VU1_DATA_SIZE = 16u * 1024u; // 16KB Data Memory (VU Mem)
constexpr uint32_t PS2_GS_BASE = 0x12000000;
constexpr uint32_t PS2_GS_PRIV_REG_BASE = PS2_GS_BASE; // GS Privileged Registers
constexpr uint32_t PS2_GS_PRIV_REG_SIZE = 0x2000;
constexpr size_t PS2_GS_VRAM_SIZE = 4u * 1024u * 1024u; // 4MB GS VRAM
inline constexpr uint32_t PS2_FIO_O_RDONLY = 0x0001;
inline constexpr uint32_t PS2_FIO_O_WRONLY = 0x0002;
inline constexpr uint32_t PS2_FIO_O_RDWR = 0x0003;
inline constexpr uint32_t PS2_FIO_O_NBLOCK = 0x0010;
inline constexpr uint32_t PS2_FIO_O_APPEND = 0x0100;
inline constexpr uint32_t PS2_FIO_O_CREAT = 0x0200;
inline constexpr uint32_t PS2_FIO_O_TRUNC = 0x0400;
inline constexpr uint32_t PS2_FIO_O_EXCL = 0x0800;
inline constexpr uint32_t PS2_FIO_O_NOWAIT = 0x8000;
inline constexpr uint32_t PS2_FIO_SEEK_SET = 0;
inline constexpr uint32_t PS2_FIO_SEEK_CUR = 1;
inline constexpr uint32_t PS2_FIO_SEEK_END = 2;
inline constexpr uint32_t PS2_FIO_S_IFDIR = 0x1000;
inline constexpr uint32_t PS2_FIO_S_IFREG = 0x2000;
static_assert((PS2_RAM_SIZE & (PS2_RAM_SIZE - 1u)) == 0u, "PS2_RAM_SIZE must be a power of two");
static_assert(PS2_RAM_MASK == (PS2_RAM_SIZE - 1u), "PS2_RAM_MASK must match PS2_RAM_SIZE");
inline std::atomic<uint8_t *> &ps2ScratchpadHostPtrStorage()
{
static std::atomic<uint8_t *> ptr{nullptr};
return ptr;
}
inline void ps2SetScratchpadHostPtr(uint8_t *ptr)
{
ps2ScratchpadHostPtrStorage().store(ptr, std::memory_order_relaxed);
}
inline uint8_t *ps2GetScratchpadHostPtr()
{
return ps2ScratchpadHostPtrStorage().load(std::memory_order_relaxed);
}
inline bool ps2IsScratchpadAddress(uint32_t addr)
{
if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
{
return true;
}
if ((addr & 0x80000000u) != 0u)
{
const uint32_t lower = addr & 0x7FFFFFFFu;
return lower >= PS2_SCRATCHPAD_BASE &&
lower < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE);
}
return false;
}
inline uint32_t ps2ScratchpadOffset(uint32_t addr)
{
if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
{
return addr - PS2_SCRATCHPAD_BASE;
}
const uint32_t lower = addr & 0x7FFFFFFFu;
return lower - PS2_SCRATCHPAD_BASE;
}
inline bool ps2ResolveGuestPointer(uint32_t addr, uint32_t &offset, bool &scratch)
{
if (ps2IsScratchpadAddress(addr))
{
scratch = true;
offset = ps2ScratchpadOffset(addr);
return true;
}
uint32_t phys = 0;
if (addr < 0x20000000u)
{
phys = addr;
}
else if ((addr >= 0x20000000u && addr < 0x40000000u) ||
(addr >= 0x80000000u && addr < 0xC0000000u))
{
phys = addr & 0x1FFFFFFFu;
}
if (phys >= PS2_RAM_SIZE)
{
phys &= PS2_RAM_MASK;
}
scratch = false;
offset = phys;
return true;
}
inline uint8_t *getMemPtr(uint8_t *rdram, uint32_t addr)
{
if (rdram == nullptr)
{
return nullptr;
}
uint32_t offset = 0;
bool scratch = false;
if (!ps2ResolveGuestPointer(addr, offset, scratch))
{
return nullptr;
}
if (scratch)
{
uint8_t *scratchpad = ps2GetScratchpadHostPtr();
return scratchpad ? (scratchpad + offset) : nullptr;
}
return rdram + offset;
}
inline const uint8_t *getConstMemPtr(const uint8_t *rdram, uint32_t addr)
{
if (rdram == nullptr)
{
return nullptr;
}
uint32_t offset = 0;
bool scratch = false;
if (!ps2ResolveGuestPointer(addr, offset, scratch))
{
return nullptr;
}
if (scratch)
{
const uint8_t *scratchpad = ps2GetScratchpadHostPtr();
return scratchpad ? (scratchpad + offset) : nullptr;
}
return rdram + offset;
}
// PS2 GS (Graphics Synthesizer) registers
struct GSRegisters
{
uint64_t pmode; // Pixel mode
uint64_t smode1; // Sync mode 1
uint64_t smode2; // Sync mode 2
uint64_t srfsh; // Refresh control
uint64_t synch1; // Synchronization control 1
uint64_t synch2; // Synchronization control 2
uint64_t syncv; // Synchronization control V
uint64_t dispfb1; // Display buffer 1
uint64_t display1; // Display area 1
uint64_t dispfb2; // Display buffer 2
uint64_t display2; // Display area 2
uint64_t extbuf; // External buffer
uint64_t extdata; // External data
uint64_t extwrite; // External write
uint64_t bgcolor; // Background color
uint64_t csr; // Status
uint64_t imr; // Interrupt mask
uint64_t busdir; // Bus direction
uint64_t siglblid; // Signal label ID
};
static_assert(sizeof(GSRegisters) == (19u * sizeof(uint64_t)), "GSRegisters layout changed unexpectedly");
static_assert(alignof(GSRegisters) == alignof(uint64_t), "GSRegisters alignment must remain 64-bit");
// PS2 VIF (VPU Interface) registers
struct VIFRegisters
{
uint32_t stat; // Status
uint32_t fbrst; // VIF Force Break
uint32_t err; // Error status
uint32_t mark; // Interrupt control
uint32_t cycle; // Transfer mode
uint32_t mode; // Mode control
uint32_t num; // Data amount counter
uint32_t mask; // Data mask
uint32_t code; // VIFcode
uint32_t itops; // ITOP save
uint32_t base; // Base address
uint32_t ofst; // Offset
uint32_t tops; // TOPS
uint32_t itop; // ITOP
uint32_t top; // TOP
uint32_t row[4]; // Transfer row data
uint32_t col[4]; // Transfer column data
};
static_assert(sizeof(VIFRegisters) == (23u * sizeof(uint32_t)), "VIFRegisters layout changed unexpectedly");
// PS2 DMA registers
struct DMARegisters
{
uint32_t chcr; // Channel control
uint32_t madr; // Memory address
uint32_t qwc; // Quadword count
uint32_t tadr; // Tag address
uint32_t asr0; // Address stack 0
uint32_t asr1; // Address stack 1
uint32_t sadr; // Source address
};
static_assert(sizeof(DMARegisters) == (7u * sizeof(uint32_t)), "DMARegisters layout changed unexpectedly");
struct JumpTable
{
uint32_t address = 0; // Base address of the jump table
uint32_t baseRegister = 0; // Register used for index
std::vector<uint32_t> targets; // Jump targets
};
class PS2Memory
{
public:
PS2Memory();
~PS2Memory();
PS2Memory(const PS2Memory &) = delete;
PS2Memory &operator=(const PS2Memory &) = delete;
PS2Memory(PS2Memory &&) = delete;
PS2Memory &operator=(PS2Memory &&) = delete;
// Initialize memory
bool initialize(size_t ramSize = PS2_RAM_SIZE);
// Memory access methods
uint8_t *getRDRAM() { return m_rdram; }
uint8_t *getScratchpad() { return m_scratchpad; }
uint8_t *getIOPRAM() { return iop_ram; }
uint64_t dmaStartCount() const { return m_dmaStartCount.load(std::memory_order_relaxed); }
uint64_t gifCopyCount() const { return m_gifCopyCount.load(std::memory_order_relaxed); }
uint64_t gsWriteCount() const { return m_gsWriteCount.load(std::memory_order_relaxed); }
uint64_t vifWriteCount() const { return m_vifWriteCount.load(std::memory_order_relaxed); }
// Read/write memory
uint8_t read8(uint32_t address);
uint16_t read16(uint32_t address);
uint32_t read32(uint32_t address);
uint64_t read64(uint32_t address);
__m128i read128(uint32_t address);
void write8(uint32_t address, uint8_t value);
void write16(uint32_t address, uint16_t value);
void write32(uint32_t address, uint32_t value);
void write64(uint32_t address, uint64_t value);
void write128(uint32_t address, __m128i value);
// TLB handling
uint32_t translateAddress(uint32_t virtualAddress);
bool tlbRead(uint32_t index, uint32_t &vpn, uint32_t &pfn, uint32_t &mask, bool &valid) const;
bool tlbWrite(uint32_t index, uint32_t vpn, uint32_t pfn, uint32_t mask, bool valid);
int32_t tlbProbe(uint32_t vpn) const;
size_t tlbEntryCount() const { return m_tlbEntries.size(); }
// Hardware register interface
bool writeIORegister(uint32_t address, uint32_t value);
uint32_t readIORegister(uint32_t address);
using GifPacketCallback = std::function<void(const uint8_t *, uint32_t)>;
void setGifPacketCallback(GifPacketCallback cb) { m_gifPacketCallback = std::move(cb); }
void setGifArbiter(GifArbiter *arbiter) { m_gifArbiter = arbiter; }
using Vu1MscalCallback = std::function<void(uint32_t startPC, uint32_t itop)>;
void setVu1MscalCallback(Vu1MscalCallback cb) { m_vu1MscalCallback = std::move(cb); }
using Vu1MscntCallback = std::function<void(uint32_t itop)>;
void setVu1MscntCallback(Vu1MscntCallback cb) { m_vu1MscntCallback = std::move(cb); }
uint8_t *getVU1Code() { return m_vu1Code; }
const uint8_t *getVU1Code() const { return m_vu1Code; }
uint8_t *getVU1Data() { return m_vu1Data; }
const uint8_t *getVU1Data() const { return m_vu1Data; }
bool isPath3Masked() const { return m_path3Masked; }
void flushMaskedPath3Packets(bool drainImmediately = true);
void submitGifPacket(GifPathId pathId, const uint8_t *data, uint32_t sizeBytes, bool drainImmediately = true, bool path2DirectHl = false);
void processGIFPacket(uint32_t srcPhysAddr, uint32_t qwCount);
void processGIFPacket(const uint8_t *data, uint32_t sizeBytes);
void processVIF1Data(uint32_t srcPhysAddr, uint32_t sizeBytes);
void processVIF1Data(const uint8_t *data, uint32_t sizeBytes);
void processPendingTransfers();
int pollDmaRegisters();
// Track code modifications for self-modifying code
void registerCodeRegion(uint32_t start, uint32_t end);
bool isCodeAddress(uint32_t address) const;
bool isCodeModified(uint32_t address, uint32_t size);
void clearModifiedFlag(uint32_t address, uint32_t size);
// GS register accessors
GSRegisters &gs() { return gs_regs; }
const GSRegisters &gs() const { return gs_regs; }
uint8_t *getGSVRAM() { return m_gsVRAM; }
const uint8_t *getGSVRAM() const { return m_gsVRAM; }
bool hasSeenGifCopy() const { return m_seenGifCopy; }
// Main RAM (32MB)
uint8_t *m_rdram;
// Scratchpad memory (16KB)
uint8_t *m_scratchpad;
// IOP RAM (2MB)
uint8_t *iop_ram;
bool m_seenGifCopy;
std::atomic<uint64_t> m_dmaStartCount{0};
std::atomic<uint64_t> m_gifCopyCount{0};
std::atomic<uint64_t> m_gsWriteCount{0};
std::atomic<uint64_t> m_vifWriteCount{0};
// I/O registers
std::unordered_map<uint32_t, uint32_t> m_ioRegisters;
// Registers
GSRegisters gs_regs;
uint8_t *m_gsVRAM;
VIFRegisters vif0_regs;
VIFRegisters vif1_regs;
DMARegisters dma_regs[10]; // 10 DMA channels
// TLB entries
struct TLBEntry
{
uint32_t vpn;
uint32_t pfn;
uint32_t mask;
bool valid;
};
std::vector<TLBEntry> m_tlbEntries;
GifPacketCallback m_gifPacketCallback;
GifArbiter *m_gifArbiter = nullptr;
Vu1MscalCallback m_vu1MscalCallback;
Vu1MscntCallback m_vu1MscntCallback;
uint8_t *m_vu1Code = nullptr;
uint8_t *m_vu1Data = nullptr;
bool m_path3Masked = false;
uint32_t m_vif1PendingPath2ImageQwc = 0u;
bool m_vif1PendingPath2DirectHl = false;
std::vector<std::vector<uint8_t>> m_path3MaskedFifo;
struct PendingTransfer
{
bool fromScratchpad = false;
uint32_t srcAddr = 0;
uint32_t qwc = 0;
std::vector<uint8_t> chainData;
};
std::vector<PendingTransfer> m_pendingGifTransfers;
std::vector<PendingTransfer> m_pendingVif1Transfers;
struct CodeRegion
{
uint32_t start;
uint32_t end;
std::vector<bool> modified; // Bitmap of modified 4-byte blocks
};
std::vector<CodeRegion> m_codeRegions;
bool isAddressInRegion(uint32_t address, const CodeRegion &region);
void markModified(uint32_t address, uint32_t size);
bool isScratchpad(uint32_t address) const;
};
#endif // PS2_MEMORY_H
+16
View File
@@ -0,0 +1,16 @@
#ifndef PS2_PAD_H
#define PS2_PAD_H
#include <cstddef>
#include <cstdint>
class PSPadBackend
{
public:
PSPadBackend() = default;
~PSPadBackend() = default;
bool readState(int port, int slot, uint8_t *data, size_t size);
};
#endif
+62
View File
@@ -0,0 +1,62 @@
#ifndef PS2_VU1_H
#define PS2_VU1_H
#include <cstdint>
class GS;
class PS2Memory;
struct VU1State
{
float vf[32][4];
int32_t vi[16];
float acc[4];
float q;
float p;
float i;
uint32_t pc;
uint32_t mac;
uint32_t clip;
uint32_t status;
bool ebit;
uint32_t itop;
uint32_t xitop;
};
class VU1Interpreter
{
public:
VU1Interpreter();
void reset();
void execute(uint8_t *vuCode, uint32_t codeSize,
uint8_t *vuData, uint32_t dataSize,
GS &gs, PS2Memory *memory = nullptr,
uint32_t startPC = 0, uint32_t itop = 0,
uint32_t maxCycles = 65536);
void resume(uint8_t *vuCode, uint32_t codeSize,
uint8_t *vuData, uint32_t dataSize,
GS &gs, PS2Memory *memory = nullptr,
uint32_t itop = 0, uint32_t maxCycles = 65536);
VU1State &state() { return m_state; }
const VU1State &state() const { return m_state; }
private:
VU1State m_state;
void run(uint8_t *vuCode, uint32_t codeSize,
uint8_t *vuData, uint32_t dataSize,
GS &gs, PS2Memory *memory, uint32_t maxCycles);
void execUpper(uint32_t instr);
void execLower(uint32_t instr, uint8_t *vuData, uint32_t dataSize, GS &gs, PS2Memory *memory, uint32_t upperInstr);
void applyDest(float *dst, const float *result, uint8_t dest);
void applyDestAcc(const float *result, uint8_t dest);
float broadcast(const float *vf, uint8_t bc);
};
#endif