mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 16:59:35 -04:00
f49ca4edbc
* feat: implement fix and changes based on dark cloud report fix: fix GS AFAIL for RGB/alpha/Z, ZMSK fix: fix VU1 flags mask and pipeline fix: small VU1 cache fix feat: __ct__, __sinit_ are not sillent stubs anymore * feat: fix song JP pulling * feat: sound update for lotR * feat: prevent guest execution to be very slow * fix: small gs size bug * feat: refactor VU fix: fix cliping and other issues on gs fix: fix wrong vu0 register on recompiler * fix fix ACC scheduler stall feat: remove unused test fix: .fix overflow e underflow on FMAC * feat: small setting for windows test
300 lines
9.4 KiB
C++
300 lines
9.4 KiB
C++
#ifndef PS2_VU1_H
|
|
#define PS2_VU1_H
|
|
|
|
#include <array>
|
|
#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 r;
|
|
uint32_t pc;
|
|
uint32_t mac;
|
|
uint32_t clip;
|
|
uint32_t status;
|
|
uint64_t cycles;
|
|
bool ebit;
|
|
bool haltAfterDelaySlot;
|
|
bool dBitEnabled;
|
|
bool tBitEnabled;
|
|
bool stoppedByD;
|
|
bool stoppedByT;
|
|
uint32_t top; // VIF TOP visible to XTOP
|
|
uint32_t itop; // VIF ITOP visible to XITOP
|
|
|
|
bool branchPending;
|
|
uint32_t branchTarget;
|
|
uint32_t branchDelay;
|
|
};
|
|
|
|
class VU1Interpreter
|
|
{
|
|
public:
|
|
enum class Unit : uint8_t
|
|
{
|
|
VU0,
|
|
VU1
|
|
};
|
|
|
|
explicit VU1Interpreter(Unit unit = Unit::VU1);
|
|
|
|
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 top = 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 top = 0, uint32_t itop = 0, uint32_t maxCycles = 65536);
|
|
|
|
VU1State &state() { return m_state; }
|
|
const VU1State &state() const { return m_state; }
|
|
|
|
private:
|
|
enum Pipeline : uint8_t
|
|
{
|
|
PipelineNone = 0,
|
|
PipelineFmac,
|
|
PipelineLsu,
|
|
PipelineFdiv,
|
|
PipelineEfu,
|
|
PipelineIalu,
|
|
PipelineBranch,
|
|
PipelineXgkick
|
|
};
|
|
|
|
struct VfAccess
|
|
{
|
|
uint8_t reg = 0;
|
|
uint8_t lanes = 0;
|
|
};
|
|
|
|
struct InstructionUsage
|
|
{
|
|
std::array<VfAccess, 2> vfRead{};
|
|
VfAccess vfWrite{};
|
|
uint8_t vfReadCount = 0;
|
|
uint16_t viRead = 0;
|
|
uint16_t viWrite = 0;
|
|
uint8_t accRead = 0;
|
|
uint8_t accWrite = 0;
|
|
uint8_t latency = 0;
|
|
uint8_t vfLatency = 0;
|
|
uint8_t viLatency = 0;
|
|
Pipeline pipeline = PipelineNone;
|
|
bool waitQ = false;
|
|
bool waitP = false;
|
|
bool readsClip = false;
|
|
bool writesClip = false;
|
|
bool delaysNextBranchRead = false;
|
|
bool reserved = false;
|
|
};
|
|
|
|
struct DecodedInstructionPair
|
|
{
|
|
uint32_t lower = 0;
|
|
uint32_t upper = 0;
|
|
InstructionUsage lowerUsage{};
|
|
InstructionUsage upperUsage{};
|
|
bool iBit = false;
|
|
bool eBit = false;
|
|
bool mBit = false;
|
|
bool dBit = false;
|
|
bool tBit = false;
|
|
uint8_t upperVfShadowReg = 0;
|
|
uint8_t suppressedLowerVf = 0;
|
|
};
|
|
|
|
struct FlagPipelineEntry
|
|
{
|
|
uint64_t readyCycle = 0;
|
|
uint64_t issueCycle = 0;
|
|
uint32_t mac = 0;
|
|
uint32_t status = 0;
|
|
uint32_t extraSticky = 0;
|
|
uint32_t clip = 0;
|
|
bool valid = false;
|
|
bool writesMac = false;
|
|
bool writesStatus = false;
|
|
bool writesSticky = false;
|
|
bool writesClip = false;
|
|
};
|
|
|
|
struct ScalarPipelineEntry
|
|
{
|
|
uint64_t readyCycle = 0;
|
|
float value = 0.0f;
|
|
uint32_t statusDi = 0;
|
|
bool valid = false;
|
|
};
|
|
|
|
struct PendingStore
|
|
{
|
|
uint64_t readyCycle = 0;
|
|
uint32_t address = 0;
|
|
std::array<uint32_t, 4> words{};
|
|
uint8_t laneMask = 0;
|
|
bool valid = false;
|
|
};
|
|
|
|
struct PendingVfWrite
|
|
{
|
|
uint64_t readyCycle = 0;
|
|
uint64_t sequence = 0;
|
|
std::array<float, 4> value{};
|
|
uint8_t reg = 0;
|
|
uint8_t laneMask = 0;
|
|
bool valid = false;
|
|
};
|
|
|
|
struct PendingViWrite
|
|
{
|
|
uint64_t readyCycle = 0;
|
|
uint64_t sequence = 0;
|
|
int32_t value = 0;
|
|
uint8_t reg = 0;
|
|
bool valid = false;
|
|
};
|
|
|
|
struct PendingAccWrite
|
|
{
|
|
uint64_t readyCycle = 0;
|
|
uint64_t sequence = 0;
|
|
std::array<float, 4> value{};
|
|
uint8_t laneMask = 0;
|
|
bool valid = false;
|
|
};
|
|
|
|
struct XgkickPipeline
|
|
{
|
|
static constexpr uint32_t kBufferSize = 0x10000u;
|
|
std::array<uint8_t, kBufferSize> packet{};
|
|
uint32_t sourceAddress = 0;
|
|
uint32_t totalBytes = 0;
|
|
uint32_t copiedBytes = 0;
|
|
uint32_t currentTagEnd = 0;
|
|
uint32_t cycleCredit = 0;
|
|
uint64_t issueCycle = 0;
|
|
bool active = false;
|
|
bool currentTagEop = false;
|
|
};
|
|
|
|
static constexpr uint32_t kFmacLatency = 4u;
|
|
static constexpr uint32_t kAccForwardLatency = 1u;
|
|
static constexpr uint32_t kMaxFlagEntries = 8u;
|
|
static constexpr uint32_t kMaxPendingStores = 8u;
|
|
static constexpr uint32_t kMaxPendingVfWrites = 16u;
|
|
static constexpr uint32_t kMaxPendingViWrites = 8u;
|
|
static constexpr uint32_t kMaxPendingAccWrites = 8u;
|
|
static constexpr uint32_t kMaxDecodedPairs = 0x4000u / 8u;
|
|
|
|
Unit m_unit;
|
|
VU1State m_state;
|
|
std::array<DecodedInstructionPair, kMaxDecodedPairs> m_decodedCodeCache{};
|
|
const uint8_t *m_cachedVuCode = nullptr;
|
|
const PS2Memory *m_cachedMemory = nullptr;
|
|
uint32_t m_cachedCodeSize = 0;
|
|
uint64_t m_cachedCodeGeneration = 0;
|
|
bool m_decodedCodeCacheValid = false;
|
|
|
|
std::array<FlagPipelineEntry, kMaxFlagEntries> m_flagPipeline{};
|
|
ScalarPipelineEntry m_fdiv{};
|
|
std::array<ScalarPipelineEntry, 2> m_efu{};
|
|
std::array<PendingStore, kMaxPendingStores> m_storePipeline{};
|
|
std::array<PendingVfWrite, kMaxPendingVfWrites> m_vfWritePipeline{};
|
|
std::array<PendingViWrite, kMaxPendingViWrites> m_viWritePipeline{};
|
|
std::array<PendingAccWrite, kMaxPendingAccWrites> m_accWritePipeline{};
|
|
XgkickPipeline m_xgkick{};
|
|
std::array<std::array<uint64_t, 4>, 32> m_vfReady{};
|
|
std::array<uint64_t, 16> m_viReady{};
|
|
std::array<uint64_t, 4> m_accReady{};
|
|
std::array<std::array<uint64_t, 4>, 32> m_vfLatestWrite{};
|
|
std::array<uint64_t, 16> m_viLatestWrite{};
|
|
std::array<uint64_t, 4> m_accLatestWrite{};
|
|
|
|
uint64_t m_cycle = 0;
|
|
uint64_t m_nextWriteSequence = 0;
|
|
uint64_t m_efuResourceReady = 0;
|
|
uint32_t m_workingClip = 0;
|
|
uint32_t m_currentUpperInstruction = 0;
|
|
int32_t m_viBranchBackupValue = 0;
|
|
uint8_t m_viBranchBackupReg = 0;
|
|
bool m_viBranchBackupValid = false;
|
|
uint8_t *m_activeVuData = nullptr;
|
|
uint32_t m_activeVuDataSize = 0;
|
|
GS *m_activeGs = nullptr;
|
|
PS2Memory *m_activeMemory = nullptr;
|
|
bool m_stopRequested = false;
|
|
bool m_pendingHaltD = false;
|
|
bool m_pendingHaltT = false;
|
|
|
|
void run(uint8_t *vuCode, uint32_t codeSize,
|
|
uint8_t *vuData, uint32_t dataSize,
|
|
GS &gs, PS2Memory *memory, uint32_t maxCycles);
|
|
|
|
InstructionUsage decodeUpperUsage(uint32_t upper) const;
|
|
InstructionUsage decodeLowerUsage(uint32_t lower) const;
|
|
static void addVfRead(InstructionUsage &usage, uint8_t reg, uint8_t lanes);
|
|
static void addVfWrite(InstructionUsage &usage, uint8_t reg, uint8_t lanes);
|
|
static uint8_t vfReadLanes(const InstructionUsage &usage, uint8_t reg);
|
|
DecodedInstructionPair decodeInstructionPair(const uint8_t *vuCode, uint32_t pc) const;
|
|
DecodedInstructionPair getDecodedInstructionPairForPc(const uint8_t *vuCode, uint32_t codeSize, PS2Memory *memory, uint32_t pc);
|
|
void rebuildDecodedCodeCache(const uint8_t *vuCode, uint32_t codeSize, const PS2Memory *memory, uint64_t generation);
|
|
|
|
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);
|
|
void applyFmacDest(float *dst, float *result, uint8_t dest);
|
|
void applyFmacDestAcc(float *result, uint8_t dest);
|
|
void normalizeFmacResult(float *result, uint8_t dest, uint8_t laneFlags[4]);
|
|
bool calculateFmacExactResult(uint32_t component, long double &result) const;
|
|
uint8_t normalizeFmacExactResult(float &value, long double exactResult) const;
|
|
uint32_t calculateFmacProductSticky(uint8_t dest) const;
|
|
void updateFmacFlags(const uint8_t laneFlags[4], uint8_t dest, uint32_t extraSticky);
|
|
void queueFsset(uint16_t immediate);
|
|
void queueClip(uint32_t clip);
|
|
void queueFcset(uint32_t clip);
|
|
void queueQ(float value, uint32_t latency, uint32_t statusDi);
|
|
void queueP(float value, uint32_t latency);
|
|
void queueStore(uint32_t address, const uint32_t words[4], uint8_t laneMask);
|
|
void queueVfWrite(uint8_t reg, uint8_t laneMask, const float value[4], uint32_t latency);
|
|
void queueViWrite(uint8_t reg, int32_t value, uint32_t latency);
|
|
void queueAccWrite(uint8_t laneMask, const float value[4], uint32_t latency);
|
|
void startXgkick(uint32_t qwordAddress);
|
|
|
|
void resetScheduler();
|
|
void commitReadyPipelines();
|
|
void advanceOneCycle();
|
|
void advanceTo(uint64_t targetCycle);
|
|
void flushPipelines();
|
|
void progressXgkick();
|
|
void finishXgkick();
|
|
uint64_t calculatePairReadyCycle(const DecodedInstructionPair &decoded) const;
|
|
void markPairWrites(const DecodedInstructionPair &decoded);
|
|
bool pipelinesPending() const;
|
|
|
|
float normalizeOperand(float value) const;
|
|
float normalizeResult(float value, uint32_t &laneFlags) const;
|
|
uint32_t microAddressMask() const;
|
|
int32_t readBranchVi(uint8_t reg) const;
|
|
void recordViWriteForBranch(uint8_t reg, int32_t oldValue);
|
|
void reportReservedInstruction(bool upper, uint32_t instruction);
|
|
float broadcast(const float *vf, uint8_t bc);
|
|
};
|
|
|
|
#endif
|