#include "Vpu.h" #include "make_unique.h" #include "string_format.h" #include "Log.h" #include "../states/RegisterStateFile.h" #include "../Ps2Const.h" #include "../FrameDump.h" #include "Vif.h" #include "Vif1.h" #include "GIF.h" #define LOG_NAME ("ee_vpu") #define STATE_PATH_REGS_FORMAT ("vpu/vpu_%d.xml") #define STATE_REGS_VUSTATE ("vuState") #define STATE_REGS_FBRST ("fbrst") CVpu::CVpu(unsigned int number, const VPUINIT& vpuInit, CGIF& gif, CINTC& intc, uint8* ram, uint8* spr) : m_number(number) , m_vif((number == 0) ? std::make_unique(0, *this, intc, ram, spr) : std::make_unique(1, *this, gif, intc, ram, spr)) , m_microMem(vpuInit.microMem) , m_microMemSize((number == 0) ? PS2::MICROMEM0SIZE : PS2::MICROMEM1SIZE) , m_vuMem(vpuInit.vuMem) , m_vuMemSize((number == 0) ? PS2::VUMEM0SIZE : PS2::VUMEM1SIZE) , m_ctx(vpuInit.context) , m_gif(gif) , m_vuProfilerZone(CProfiler::GetInstance().RegisterZone("VU")) #ifdef DEBUGGER_INCLUDED , m_microMemMiniState(new uint8[(number == 0) ? PS2::MICROMEM0SIZE : PS2::MICROMEM1SIZE]) , m_vuMemMiniState(new uint8[(number == 0) ? PS2::VUMEM0SIZE : PS2::VUMEM1SIZE]) , m_topMiniState(0) , m_itopMiniState(0) #endif { } CVpu::~CVpu() { #ifdef DEBUGGER_INCLUDED delete[] m_microMemMiniState; delete[] m_vuMemMiniState; #endif } void CVpu::Execute(int32 quota) { if(m_vuState != VU_STATE_RUNNING) return; #ifdef PROFILE CProfilerZone profilerZone(m_vuProfilerZone); #endif m_ctx->m_executor->Execute(quota); switch(m_ctx->m_State.nHasException) { case MIPS_EXCEPTION_VU_EBIT: //E bit encountered m_vuState = VU_STATE_READY; VuStateChanged(m_vuState); break; case MIPS_EXCEPTION_VU_TBIT: case MIPS_EXCEPTION_VU_DBIT: //T/D bit encountered { bool mustBreak = false; mustBreak |= (m_ctx->m_State.nHasException == MIPS_EXCEPTION_VU_TBIT) && (m_fbrst & FBRST_TE); mustBreak |= (m_ctx->m_State.nHasException == MIPS_EXCEPTION_VU_DBIT) && (m_fbrst & FBRST_DE); if(mustBreak) { m_vuState = VU_STATE_STOPPED; VuStateChanged(m_vuState); VuInterruptTriggered(); } else { m_ctx->m_State.nHasException = 0; } } break; default: break; } } #ifdef DEBUGGER_INCLUDED void CVpu::SaveMiniState() { memcpy(m_microMemMiniState, m_microMem, (m_number == 0) ? PS2::MICROMEM0SIZE : PS2::MICROMEM1SIZE); memcpy(m_vuMemMiniState, m_vuMem, (m_number == 0) ? PS2::VUMEM0SIZE : PS2::VUMEM1SIZE); memcpy(&m_vuMiniState, &m_ctx->m_State, sizeof(MIPSSTATE)); m_topMiniState = (m_number == 0) ? 0 : m_vif->GetTOP(); m_itopMiniState = m_vif->GetITOP(); } const MIPSSTATE& CVpu::GetVuMiniState() const { return m_vuMiniState; } uint8* CVpu::GetVuMemoryMiniState() const { return m_vuMemMiniState; } uint8* CVpu::GetMicroMemoryMiniState() const { return m_microMemMiniState; } uint32 CVpu::GetVuTopMiniState() const { return m_topMiniState; } uint32 CVpu::GetVuItopMiniState() const { return m_itopMiniState; } #endif void CVpu::Reset() { m_vuState = VU_STATE_READY; m_ctx->m_executor->Reset(); m_vif->Reset(); } void CVpu::SaveState(Framework::CZipArchiveWriter& archive) { { auto path = string_format(STATE_PATH_REGS_FORMAT, m_number); auto registerFile = std::make_unique(path.c_str()); registerFile->SetRegister32(STATE_REGS_VUSTATE, m_vuState); registerFile->SetRegister32(STATE_REGS_FBRST, m_fbrst); archive.InsertFile(std::move(registerFile)); } m_vif->SaveState(archive); } void CVpu::LoadState(Framework::CZipArchiveReader& archive) { { auto path = string_format(STATE_PATH_REGS_FORMAT, m_number); CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str())); m_vuState = static_cast(registerFile.GetRegister32(STATE_REGS_VUSTATE)); m_fbrst = registerFile.GetRegister32(STATE_REGS_FBRST); } m_vif->LoadState(archive); } CMIPS& CVpu::GetContext() const { return *m_ctx; } uint8* CVpu::GetMicroMemory() const { return m_microMem; } uint32 CVpu::GetMicroMemorySize() const { return m_microMemSize; } uint8* CVpu::GetVuMemory() const { return m_vuMem; } uint32 CVpu::GetVuMemorySize() const { return m_vuMemSize; } CVif& CVpu::GetVif() { return *m_vif.get(); } void CVpu::SetFbrst(uint32 fbrst) { //Only keep DE and TE bits m_fbrst = (fbrst & (FBRST_DE | FBRST_TE)); } void CVpu::ExecuteMicroProgram(uint32 nAddress) { CLog::GetInstance().Print(LOG_NAME, "Starting microprogram execution at 0x%08X.\r\n", nAddress); m_ctx->m_State.nPC = nAddress; m_ctx->m_State.pipeTime = 0; m_ctx->m_State.pipeFmacWrite[0] = {}; m_ctx->m_State.pipeFmacWrite[1] = {}; m_ctx->m_State.pipeFmacWrite[2] = {}; m_ctx->m_State.savedNextBlockIntRegIdx = 0; m_ctx->m_State.nHasException = 0; #ifdef DEBUGGER_INCLUDED SaveMiniState(); #endif assert(m_vuState != VU_STATE_RUNNING); m_vuState = VU_STATE_RUNNING; VuStateChanged(m_vuState); for(unsigned int i = 0; i < 100; i++) { Execute(5000); if(m_vuState != VU_STATE_RUNNING) break; } } void CVpu::InvalidateMicroProgram() { m_ctx->m_executor->ClearActiveBlocksInRange(0, (m_number == 0) ? PS2::MICROMEM0SIZE : PS2::MICROMEM1SIZE, false); } void CVpu::InvalidateMicroProgram(uint32 start, uint32 end) { m_ctx->m_executor->ClearActiveBlocksInRange(start, end, false); } void CVpu::ProcessXgKick(uint32 address) { address &= 0x3FF; address *= 0x10; CGsPacketMetadata metadata; metadata.pathIndex = 1; #ifdef DEBUGGER_INCLUDED metadata.vuMemPacketAddress = address; metadata.vpu1Top = GetVuTopMiniState(); metadata.vpu1Itop = GetVuItopMiniState(); memcpy(&metadata.vu1State, &GetVuMiniState(), sizeof(MIPSSTATE)); memcpy(metadata.vuMem1, GetVuMemoryMiniState(), PS2::VUMEM1SIZE); memcpy(metadata.microMem1, GetMicroMemoryMiniState(), PS2::MICROMEM1SIZE); #endif address += m_gif.ProcessSinglePacket(GetVuMemory(), PS2::VUMEM1SIZE, address, PS2::VUMEM1SIZE, metadata); if((address == PS2::VUMEM1SIZE) && (m_gif.GetActivePath() == 1)) { address = 0; address += m_gif.ProcessSinglePacket(GetVuMemory(), PS2::VUMEM1SIZE, address, PS2::VUMEM1SIZE, metadata); } assert(m_gif.GetActivePath() == 0); #ifdef DEBUGGER_INCLUDED SaveMiniState(); #endif }