#ifndef PS2_VU1_H #define PS2_VU1_H #include #include 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 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 words{}; uint8_t laneMask = 0; bool valid = false; }; struct PendingVfWrite { uint64_t readyCycle = 0; uint64_t sequence = 0; std::array 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 value{}; uint8_t laneMask = 0; bool valid = false; }; struct XgkickPipeline { static constexpr uint32_t kBufferSize = 0x10000u; std::array 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 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 m_flagPipeline{}; ScalarPipelineEntry m_fdiv{}; std::array m_efu{}; std::array m_storePipeline{}; std::array m_vfWritePipeline{}; std::array m_viWritePipeline{}; std::array m_accWritePipeline{}; XgkickPipeline m_xgkick{}; std::array, 32> m_vfReady{}; std::array m_viReady{}; std::array m_accReady{}; std::array, 32> m_vfLatestWrite{}; std::array m_viLatestWrite{}; std::array 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