2021-10-08 17:00:19 -04:00
|
|
|
#include "Vpu.h"
|
2015-05-09 23:26:01 -04:00
|
|
|
#include "make_unique.h"
|
2021-10-08 17:00:19 -04:00
|
|
|
#include "string_format.h"
|
2025-03-11 12:48:26 -04:00
|
|
|
#include "Log.h"
|
2019-02-06 13:34:51 -05:00
|
|
|
#include "../states/RegisterStateFile.h"
|
2015-05-07 01:52:45 -04:00
|
|
|
#include "../Ps2Const.h"
|
|
|
|
#include "../FrameDump.h"
|
|
|
|
#include "Vif.h"
|
|
|
|
#include "Vif1.h"
|
|
|
|
#include "GIF.h"
|
|
|
|
|
2018-05-25 12:38:51 -04:00
|
|
|
#define LOG_NAME ("ee_vpu")
|
2015-05-07 01:52:45 -04:00
|
|
|
|
2021-10-08 17:00:19 -04:00
|
|
|
#define STATE_PATH_REGS_FORMAT ("vpu/vpu_%d.xml")
|
|
|
|
|
2024-07-31 11:44:30 -04:00
|
|
|
#define STATE_REGS_VUSTATE ("vuState")
|
2024-08-12 18:06:45 -04:00
|
|
|
#define STATE_REGS_FBRST ("fbrst")
|
2021-10-08 17:00:19 -04:00
|
|
|
|
2017-08-20 18:03:24 -04:00
|
|
|
CVpu::CVpu(unsigned int number, const VPUINIT& vpuInit, CGIF& gif, CINTC& intc, uint8* ram, uint8* spr)
|
2018-04-30 21:01:23 +01:00
|
|
|
: m_number(number)
|
|
|
|
, m_vif((number == 0) ? std::make_unique<CVif>(0, *this, intc, ram, spr) : std::make_unique<CVif1>(1, *this, gif, intc, ram, spr))
|
|
|
|
, m_microMem(vpuInit.microMem)
|
2020-07-10 16:31:09 -04:00
|
|
|
, m_microMemSize((number == 0) ? PS2::MICROMEM0SIZE : PS2::MICROMEM1SIZE)
|
2018-04-30 21:01:23 +01:00
|
|
|
, 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"))
|
2015-05-07 01:52:45 -04:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
2018-04-30 21:01:23 +01:00
|
|
|
, 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)
|
2015-05-07 01:52:45 -04:00
|
|
|
#endif
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CVpu::~CVpu()
|
|
|
|
{
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
2018-04-30 21:01:23 +01:00
|
|
|
delete[] m_microMemMiniState;
|
|
|
|
delete[] m_vuMemMiniState;
|
2015-05-07 01:52:45 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-09-27 21:54:42 -04:00
|
|
|
void CVpu::Execute(int32 quota)
|
2015-05-07 01:52:45 -04:00
|
|
|
{
|
2024-07-31 11:44:30 -04:00
|
|
|
if(m_vuState != VU_STATE_RUNNING) return;
|
2015-05-07 01:52:45 -04:00
|
|
|
|
|
|
|
#ifdef PROFILE
|
|
|
|
CProfilerZone profilerZone(m_vuProfilerZone);
|
|
|
|
#endif
|
|
|
|
|
2018-07-20 12:51:04 -04:00
|
|
|
m_ctx->m_executor->Execute(quota);
|
2024-07-31 11:51:44 -04:00
|
|
|
switch(m_ctx->m_State.nHasException)
|
2015-05-07 01:52:45 -04:00
|
|
|
{
|
2024-08-12 18:06:45 -04:00
|
|
|
case MIPS_EXCEPTION_VU_EBIT:
|
2015-05-07 01:52:45 -04:00
|
|
|
//E bit encountered
|
2024-07-31 11:49:41 -04:00
|
|
|
m_vuState = VU_STATE_READY;
|
|
|
|
VuStateChanged(m_vuState);
|
2024-07-31 11:51:44 -04:00
|
|
|
break;
|
2024-08-12 18:06:45 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2024-07-31 11:51:44 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2015-05-07 01:52:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#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);
|
2018-08-30 13:25:46 -04:00
|
|
|
memcpy(&m_vuMiniState, &m_ctx->m_State, sizeof(MIPSSTATE));
|
2015-05-07 01:52:45 -04:00
|
|
|
m_topMiniState = (m_number == 0) ? 0 : m_vif->GetTOP();
|
|
|
|
m_itopMiniState = m_vif->GetITOP();
|
|
|
|
}
|
|
|
|
|
|
|
|
const MIPSSTATE& CVpu::GetVuMiniState() const
|
|
|
|
{
|
2018-08-30 13:25:46 -04:00
|
|
|
return m_vuMiniState;
|
2015-05-07 01:52:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2024-07-31 11:44:30 -04:00
|
|
|
m_vuState = VU_STATE_READY;
|
2018-07-20 12:51:04 -04:00
|
|
|
m_ctx->m_executor->Reset();
|
2015-05-07 01:52:45 -04:00
|
|
|
m_vif->Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CVpu::SaveState(Framework::CZipArchiveWriter& archive)
|
|
|
|
{
|
2021-10-08 17:00:19 -04:00
|
|
|
{
|
|
|
|
auto path = string_format(STATE_PATH_REGS_FORMAT, m_number);
|
2023-07-23 17:22:18 -04:00
|
|
|
auto registerFile = std::make_unique<CRegisterStateFile>(path.c_str());
|
2024-07-31 11:44:30 -04:00
|
|
|
registerFile->SetRegister32(STATE_REGS_VUSTATE, m_vuState);
|
2024-08-12 18:06:45 -04:00
|
|
|
registerFile->SetRegister32(STATE_REGS_FBRST, m_fbrst);
|
2023-07-23 17:22:18 -04:00
|
|
|
archive.InsertFile(std::move(registerFile));
|
2021-10-08 17:00:19 -04:00
|
|
|
}
|
|
|
|
|
2015-05-07 01:52:45 -04:00
|
|
|
m_vif->SaveState(archive);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CVpu::LoadState(Framework::CZipArchiveReader& archive)
|
|
|
|
{
|
2021-10-08 17:00:19 -04:00
|
|
|
{
|
|
|
|
auto path = string_format(STATE_PATH_REGS_FORMAT, m_number);
|
|
|
|
CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
|
2024-07-31 11:44:30 -04:00
|
|
|
m_vuState = static_cast<VU_STATE>(registerFile.GetRegister32(STATE_REGS_VUSTATE));
|
2024-08-12 18:06:45 -04:00
|
|
|
m_fbrst = registerFile.GetRegister32(STATE_REGS_FBRST);
|
2021-10-08 17:00:19 -04:00
|
|
|
}
|
|
|
|
|
2015-05-07 01:52:45 -04:00
|
|
|
m_vif->LoadState(archive);
|
|
|
|
}
|
|
|
|
|
|
|
|
CMIPS& CVpu::GetContext() const
|
|
|
|
{
|
|
|
|
return *m_ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8* CVpu::GetMicroMemory() const
|
|
|
|
{
|
|
|
|
return m_microMem;
|
|
|
|
}
|
|
|
|
|
2020-07-10 16:31:09 -04:00
|
|
|
uint32 CVpu::GetMicroMemorySize() const
|
|
|
|
{
|
|
|
|
return m_microMemSize;
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:52:45 -04:00
|
|
|
uint8* CVpu::GetVuMemory() const
|
|
|
|
{
|
|
|
|
return m_vuMem;
|
|
|
|
}
|
|
|
|
|
2015-12-13 18:08:22 -05:00
|
|
|
uint32 CVpu::GetVuMemorySize() const
|
|
|
|
{
|
|
|
|
return m_vuMemSize;
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:52:45 -04:00
|
|
|
CVif& CVpu::GetVif()
|
|
|
|
{
|
|
|
|
return *m_vif.get();
|
|
|
|
}
|
|
|
|
|
2024-08-12 18:06:45 -04:00
|
|
|
void CVpu::SetFbrst(uint32 fbrst)
|
|
|
|
{
|
|
|
|
//Only keep DE and TE bits
|
|
|
|
m_fbrst = (fbrst & (FBRST_DE | FBRST_TE));
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:52:45 -04:00
|
|
|
void CVpu::ExecuteMicroProgram(uint32 nAddress)
|
|
|
|
{
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOG_NAME, "Starting microprogram execution at 0x%08X.\r\n", nAddress);
|
2015-05-07 01:52:45 -04:00
|
|
|
|
|
|
|
m_ctx->m_State.nPC = nAddress;
|
|
|
|
m_ctx->m_State.pipeTime = 0;
|
2023-10-19 11:27:00 -04:00
|
|
|
m_ctx->m_State.pipeFmacWrite[0] = {};
|
|
|
|
m_ctx->m_State.pipeFmacWrite[1] = {};
|
|
|
|
m_ctx->m_State.pipeFmacWrite[2] = {};
|
2024-05-24 12:27:25 -04:00
|
|
|
m_ctx->m_State.savedNextBlockIntRegIdx = 0;
|
2015-05-07 01:52:45 -04:00
|
|
|
m_ctx->m_State.nHasException = 0;
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
SaveMiniState();
|
|
|
|
#endif
|
|
|
|
|
2024-07-31 11:44:30 -04:00
|
|
|
assert(m_vuState != VU_STATE_RUNNING);
|
|
|
|
m_vuState = VU_STATE_RUNNING;
|
|
|
|
VuStateChanged(m_vuState);
|
2015-05-07 01:52:45 -04:00
|
|
|
for(unsigned int i = 0; i < 100; i++)
|
|
|
|
{
|
2016-09-27 21:54:42 -04:00
|
|
|
Execute(5000);
|
2024-07-31 11:44:30 -04:00
|
|
|
if(m_vuState != VU_STATE_RUNNING) break;
|
2015-05-07 01:52:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CVpu::InvalidateMicroProgram()
|
|
|
|
{
|
2018-07-21 20:49:58 -04:00
|
|
|
m_ctx->m_executor->ClearActiveBlocksInRange(0, (m_number == 0) ? PS2::MICROMEM0SIZE : PS2::MICROMEM1SIZE, false);
|
2015-05-07 01:52:45 -04:00
|
|
|
}
|
|
|
|
|
2019-10-18 13:04:26 -04:00
|
|
|
void CVpu::InvalidateMicroProgram(uint32 start, uint32 end)
|
|
|
|
{
|
|
|
|
m_ctx->m_executor->ClearActiveBlocksInRange(start, end, false);
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:52:45 -04:00
|
|
|
void CVpu::ProcessXgKick(uint32 address)
|
|
|
|
{
|
|
|
|
address &= 0x3FF;
|
|
|
|
address *= 0x10;
|
|
|
|
|
|
|
|
CGsPacketMetadata metadata;
|
2018-04-30 21:01:23 +01:00
|
|
|
metadata.pathIndex = 1;
|
2019-08-12 20:35:57 -04:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
2018-04-30 21:01:23 +01:00
|
|
|
metadata.vuMemPacketAddress = address;
|
|
|
|
metadata.vpu1Top = GetVuTopMiniState();
|
|
|
|
metadata.vpu1Itop = GetVuItopMiniState();
|
2015-05-07 01:52:45 -04:00
|
|
|
memcpy(&metadata.vu1State, &GetVuMiniState(), sizeof(MIPSSTATE));
|
|
|
|
memcpy(metadata.vuMem1, GetVuMemoryMiniState(), PS2::VUMEM1SIZE);
|
|
|
|
memcpy(metadata.microMem1, GetMicroMemoryMiniState(), PS2::MICROMEM1SIZE);
|
|
|
|
#endif
|
|
|
|
|
2022-10-27 17:29:21 -04:00
|
|
|
address += m_gif.ProcessSinglePacket(GetVuMemory(), PS2::VUMEM1SIZE, address, PS2::VUMEM1SIZE, metadata);
|
|
|
|
if((address == PS2::VUMEM1SIZE) && (m_gif.GetActivePath() == 1))
|
|
|
|
{
|
2022-10-27 17:32:18 -04:00
|
|
|
address = 0;
|
|
|
|
address += m_gif.ProcessSinglePacket(GetVuMemory(), PS2::VUMEM1SIZE, address, PS2::VUMEM1SIZE, metadata);
|
2022-10-27 17:29:21 -04:00
|
|
|
}
|
2022-10-27 17:32:18 -04:00
|
|
|
assert(m_gif.GetActivePath() == 0);
|
2015-05-07 01:52:45 -04:00
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
SaveMiniState();
|
|
|
|
#endif
|
|
|
|
}
|