Play-/Source/ee/Vif.cpp

1073 lines
26 KiB
C++
Raw Normal View History

#include <cassert>
2019-08-17 13:51:31 -04:00
#include <cstring>
#include <stdexcept>
#include "static_loop.h"
#include "string_format.h"
#include "../Log.h"
#include "../Ps2Const.h"
#include "../states/RegisterStateFile.h"
#include "../states/MemoryStateFile.h"
#include "Vpu.h"
#include "Vif.h"
#include "INTC.h"
2018-05-25 12:38:51 -04:00
#define LOG_NAME ("ee_vif")
2018-04-30 21:01:23 +01:00
#define STATE_PATH_REGS_FORMAT ("vpu/vif_%d.xml")
#define STATE_PATH_FIFO_FORMAT ("vpu/vif_%d_fifo")
#define STATE_REGS_STAT ("STAT")
2022-05-19 15:41:23 -04:00
#define STATE_REGS_ERR ("ERR")
2018-04-30 21:01:23 +01:00
#define STATE_REGS_CODE ("CODE")
#define STATE_REGS_CYCLE ("CYCLE")
#define STATE_REGS_NUM ("NUM")
#define STATE_REGS_MASK ("MASK")
#define STATE_REGS_MODE ("MODE")
#define STATE_REGS_ROW0 ("ROW0")
#define STATE_REGS_ROW1 ("ROW1")
#define STATE_REGS_ROW2 ("ROW2")
#define STATE_REGS_ROW3 ("ROW3")
#define STATE_REGS_COL0 ("COL0")
#define STATE_REGS_COL1 ("COL1")
#define STATE_REGS_COL2 ("COL2")
#define STATE_REGS_COL3 ("COL3")
#define STATE_REGS_MARK ("MARK")
#define STATE_REGS_ITOP ("ITOP")
#define STATE_REGS_ITOPS ("ITOPS")
#define STATE_REGS_READTICK ("readTick")
#define STATE_REGS_WRITETICK ("writeTick")
2020-08-24 21:12:46 -04:00
#define STATE_REGS_PENDINGMICROPROGRAM ("pendingMicroProgram")
2018-04-30 21:01:23 +01:00
#define STATE_REGS_FIFOINDEX ("fifoIndex")
2021-01-06 12:14:17 -05:00
#define STATE_REGS_INCOMINGFIFODELAY ("incomingFifoDelay")
2022-05-19 15:41:23 -04:00
#define STATE_REGS_INTERRUPTDELAYTICKS ("interruptDelayTicks")
#define STATE_REGS_STREAM_BUFFER ("streamBuffer")
#define STATE_REGS_STREAM_BUFFERPOSITION ("streamBufferPosition")
CVif::CVif(unsigned int number, CVpu& vpu, CINTC& intc, uint8* ram, uint8* spr)
2018-04-30 21:01:23 +01:00
: m_number(number)
2023-05-02 08:56:25 -04:00
, m_vpu(vpu)
, m_intc(intc)
2018-04-30 21:01:23 +01:00
, m_ram(ram)
, m_spr(spr)
, m_stream(ram, spr)
, m_vifProfilerZone(CProfiler::GetInstance().RegisterZone(string_format("VIF%d", number).c_str()))
{
static_loop<int, MAX_UNPACKERS>(
[this](auto i) {
constexpr uint8 dataType = (i & 0x0F);
constexpr bool clGreaterEqualWl = (i & 0x10) ? true : false;
constexpr bool useMask = (i & 0x20) ? true : false;
constexpr uint8 mode = (i & 0xC0) >> 6;
constexpr uint8 usn = (i & 0x100) >> 8;
m_unpacker[i] = &CVif::Unpack<dataType, clGreaterEqualWl, useMask, mode, usn>;
});
}
void CVif::Reset()
{
memset(&m_STAT, 0, sizeof(STAT));
memset(&m_CODE, 0, sizeof(CODE));
memset(&m_CYCLE, 0, sizeof(CYCLE));
memset(&m_R, 0, sizeof(m_R));
memset(&m_C, 0, sizeof(m_C));
2015-08-18 23:04:18 -04:00
memset(&m_fifoBuffer, 0, sizeof(m_fifoBuffer));
m_CYCLE.nCL = 1;
m_CYCLE.nWL = 1;
2015-08-18 23:04:18 -04:00
m_fifoIndex = 0;
m_MODE = 0;
m_NUM = 0;
m_MASK = 0;
m_MARK = 0;
m_ITOP = 0;
m_ITOPS = 0;
m_readTick = 0;
m_writeTick = 0;
m_stream.Reset();
m_pendingMicroProgram = -1;
m_incomingFifoDelay = 0;
2022-03-28 17:55:02 -04:00
m_interruptDelayTicks = 0;
}
uint32 CVif::GetRegister(uint32 address)
{
uint32 result = 0;
2015-06-29 23:03:53 -04:00
switch(address)
{
2016-09-04 00:14:06 -04:00
case VIF0_STAT:
case VIF1_STAT:
if((m_STAT.nVEW != 0) && (m_CODE.nCMD == CODE_CMD_FLUSHE))
{
//We have a pending FLUSHE command that's waiting for the microprogram's end.
//Check if it's finished and act as if the command was executed properly.
//Some games will keep reading this register in a loop to verify the state of the VEW bit.
//- Red Faction
//- RPG Maker 3
2024-07-31 11:44:30 -04:00
if(m_vpu.IsVuReady())
{
m_STAT.nVEW = 0;
}
}
2016-09-04 00:14:06 -04:00
result = m_STAT;
if(m_STAT.nFDR != 0)
{
//When FDR is set, it usually means the game is trying to
//read data from GS and that FIFO has some data in it
//Set FQC (amount of quadwords in FIFO) to something to
//let the game know the transfer is being executed
//Games sensitive to this behavior:
//- Serious Sam: Takes screen shots at checkpoints, waits for FQC to become 0
//- PS2PSXe: Takes a screen shot of what is currently on screen, waits for FQC to become 0
//- There are games that will check that it's not zero once FDR is set.
result |= (m_incomingFifoDelay << 24);
if(m_incomingFifoDelay != 0)
{
m_incomingFifoDelay--;
}
}
if(m_STAT.nVIS)
{
//If we're not idle here, something might be wrong
assert(m_STAT.nVPS == STAT_VPS_IDLE);
//If we're stalled because of an interrupt, report that VIF is decoding
result &= ~STAT_VPS_MASK;
result |= STAT_VPS_DECODING;
}
2016-09-04 00:14:06 -04:00
break;
2020-07-13 16:57:55 +02:00
case VIF0_ERR:
case VIF1_ERR:
result = m_ERR;
break;
2015-06-29 23:03:53 -04:00
case VIF0_MARK:
case VIF1_MARK:
result = m_MARK;
break;
2015-12-12 21:38:07 -05:00
case VIF0_CYCLE:
case VIF1_CYCLE:
result = m_CYCLE;
break;
2015-12-13 21:04:38 -05:00
case VIF0_MODE:
case VIF1_MODE:
result = m_MODE;
break;
case VIF0_NUM:
case VIF1_NUM:
result = m_NUM;
break;
2019-06-01 22:28:51 -04:00
case VIF0_MASK:
case VIF1_MASK:
result = m_MASK;
break;
case VIF0_CODE:
case VIF1_CODE:
result = m_CODE;
break;
2015-11-21 21:34:55 -05:00
case VIF0_R0:
case VIF1_R0:
result = m_R[0];
break;
case VIF0_R1:
case VIF1_R1:
result = m_R[1];
break;
case VIF0_R2:
case VIF1_R2:
result = m_R[2];
break;
case VIF0_R3:
case VIF1_R3:
result = m_R[3];
break;
2020-07-13 16:56:44 +02:00
default:
CLog::GetInstance().Warn(LOG_NAME, "Reading unknown register 0x%08X.\r\n", address);
break;
2015-06-29 23:03:53 -04:00
}
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
DisassembleGet(address);
#endif
return result;
}
void CVif::SetRegister(uint32 address, uint32 value)
{
2015-08-18 23:04:18 -04:00
if(
2018-04-30 21:01:23 +01:00
(address >= VIF0_FIFO_START && address < VIF0_FIFO_END) ||
(address >= VIF1_FIFO_START && address < VIF1_FIFO_END))
2015-05-07 23:13:30 -04:00
{
2015-08-18 23:04:18 -04:00
ProcessFifoWrite(address, value);
}
else
{
switch(address)
2015-05-07 23:13:30 -04:00
{
case VIF1_STAT:
m_STAT.nFDR = ((value & STAT_FDR) != 0) ? 1 : 0;
if(m_STAT.nFDR)
{
m_incomingFifoDelay = 0x1F;
}
break;
case VIF0_FBRST:
2015-08-18 23:04:18 -04:00
case VIF1_FBRST:
if(value & FBRST_RST)
{
//TODO: Reset FIFO
m_CODE <<= 0;
m_STAT <<= 0;
m_NUM = 0;
}
if(value & FBRST_FBK || value & FBRST_STP)
{
// TODO: We need to properly handle this!
// But I lack games which leverage it.
assert(0);
}
2015-08-18 23:04:18 -04:00
if(value & FBRST_STC)
{
m_STAT.nVSS = 0;
m_STAT.nVFS = 0;
2015-08-18 23:04:18 -04:00
m_STAT.nVIS = 0;
m_STAT.nINT = 0;
m_STAT.nER0 = 0;
m_STAT.nER1 = 0;
2015-08-18 23:04:18 -04:00
}
break;
2020-07-13 16:57:55 +02:00
case VIF0_ERR:
case VIF1_ERR:
m_ERR <<= value;
break;
2015-08-18 23:04:18 -04:00
case VIF0_MARK:
case VIF1_MARK:
m_MARK = value;
m_STAT.nMRK = 0;
2015-08-18 23:04:18 -04:00
break;
2020-07-13 16:56:44 +02:00
default:
CLog::GetInstance().Warn(LOG_NAME, "Writing unknown register 0x%08X, 0x%08X.\r\n", address, value);
break;
2015-05-07 23:13:30 -04:00
}
}
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
DisassembleSet(address, value);
#endif
}
2022-03-28 17:55:02 -04:00
void CVif::CountTicks(uint32 ticks)
{
if(m_interruptDelayTicks != 0)
{
m_interruptDelayTicks -= ticks;
if(m_interruptDelayTicks <= 0)
{
assert(m_STAT.nINT);
m_intc.AssertLine(CINTC::INTC_LINE_VIF0 + m_number);
m_interruptDelayTicks = 0;
}
}
}
void CVif::SaveState(Framework::CZipArchiveWriter& archive)
{
2015-08-18 23:04:18 -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());
2018-04-30 21:01:23 +01:00
registerFile->SetRegister32(STATE_REGS_STAT, m_STAT);
2022-05-19 15:41:23 -04:00
registerFile->SetRegister32(STATE_REGS_ERR, m_ERR);
2018-04-30 21:01:23 +01:00
registerFile->SetRegister32(STATE_REGS_CODE, m_CODE);
registerFile->SetRegister32(STATE_REGS_CYCLE, m_CYCLE);
registerFile->SetRegister32(STATE_REGS_NUM, m_NUM);
registerFile->SetRegister32(STATE_REGS_MODE, m_MODE);
registerFile->SetRegister32(STATE_REGS_MASK, m_MASK);
registerFile->SetRegister32(STATE_REGS_MARK, m_MARK);
registerFile->SetRegister32(STATE_REGS_ROW0, m_R[0]);
registerFile->SetRegister32(STATE_REGS_ROW1, m_R[1]);
registerFile->SetRegister32(STATE_REGS_ROW2, m_R[2]);
registerFile->SetRegister32(STATE_REGS_ROW3, m_R[3]);
registerFile->SetRegister32(STATE_REGS_COL0, m_C[0]);
registerFile->SetRegister32(STATE_REGS_COL1, m_C[1]);
registerFile->SetRegister32(STATE_REGS_COL2, m_C[2]);
registerFile->SetRegister32(STATE_REGS_COL3, m_C[3]);
registerFile->SetRegister32(STATE_REGS_ITOP, m_ITOP);
registerFile->SetRegister32(STATE_REGS_ITOPS, m_ITOPS);
registerFile->SetRegister32(STATE_REGS_READTICK, m_readTick);
registerFile->SetRegister32(STATE_REGS_WRITETICK, m_writeTick);
2020-08-24 21:12:46 -04:00
registerFile->SetRegister32(STATE_REGS_PENDINGMICROPROGRAM, m_pendingMicroProgram);
2018-04-30 21:01:23 +01:00
registerFile->SetRegister32(STATE_REGS_FIFOINDEX, m_fifoIndex);
2021-01-06 12:14:17 -05:00
registerFile->SetRegister32(STATE_REGS_INCOMINGFIFODELAY, m_incomingFifoDelay);
2022-05-19 15:41:23 -04:00
registerFile->SetRegister32(STATE_REGS_INTERRUPTDELAYTICKS, m_interruptDelayTicks);
registerFile->SetRegister128(STATE_REGS_STREAM_BUFFER, m_stream.GetBuffer());
registerFile->SetRegister32(STATE_REGS_STREAM_BUFFERPOSITION, m_stream.GetBufferPosition());
2023-07-23 17:22:18 -04:00
archive.InsertFile(std::move(registerFile));
2015-08-18 23:04:18 -04:00
}
{
auto path = string_format(STATE_PATH_FIFO_FORMAT, m_number);
2023-07-23 17:22:18 -04:00
archive.InsertFile(std::make_unique<CMemoryStateFile>(path.c_str(), &m_fifoBuffer, sizeof(m_fifoBuffer)));
2015-08-18 23:04:18 -04:00
}
}
void CVif::LoadState(Framework::CZipArchiveReader& archive)
{
2015-08-18 23:04:18 -04:00
{
auto path = string_format(STATE_PATH_REGS_FORMAT, m_number);
CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
2018-04-30 21:01:23 +01:00
m_STAT <<= registerFile.GetRegister32(STATE_REGS_STAT);
2022-05-19 15:41:23 -04:00
m_ERR <<= registerFile.GetRegister32(STATE_REGS_ERR);
2018-04-30 21:01:23 +01:00
m_CODE <<= registerFile.GetRegister32(STATE_REGS_CODE);
m_CYCLE <<= registerFile.GetRegister32(STATE_REGS_CYCLE);
m_NUM = static_cast<uint8>(registerFile.GetRegister32(STATE_REGS_NUM));
m_MODE = registerFile.GetRegister32(STATE_REGS_MODE);
m_MASK = registerFile.GetRegister32(STATE_REGS_MASK);
m_MARK = registerFile.GetRegister32(STATE_REGS_MARK);
m_R[0] = registerFile.GetRegister32(STATE_REGS_ROW0);
m_R[1] = registerFile.GetRegister32(STATE_REGS_ROW1);
m_R[2] = registerFile.GetRegister32(STATE_REGS_ROW2);
m_R[3] = registerFile.GetRegister32(STATE_REGS_ROW3);
m_C[0] = registerFile.GetRegister32(STATE_REGS_COL0);
m_C[1] = registerFile.GetRegister32(STATE_REGS_COL1);
m_C[2] = registerFile.GetRegister32(STATE_REGS_COL2);
m_C[3] = registerFile.GetRegister32(STATE_REGS_COL3);
m_ITOP = registerFile.GetRegister32(STATE_REGS_ITOP);
m_ITOPS = registerFile.GetRegister32(STATE_REGS_ITOPS);
m_readTick = registerFile.GetRegister32(STATE_REGS_READTICK);
m_writeTick = registerFile.GetRegister32(STATE_REGS_WRITETICK);
2020-08-24 21:12:46 -04:00
m_pendingMicroProgram = registerFile.GetRegister32(STATE_REGS_PENDINGMICROPROGRAM);
2018-04-30 21:01:23 +01:00
m_fifoIndex = registerFile.GetRegister32(STATE_REGS_FIFOINDEX);
2021-01-06 12:14:17 -05:00
m_incomingFifoDelay = registerFile.GetRegister32(STATE_REGS_INCOMINGFIFODELAY);
2022-05-19 15:41:23 -04:00
m_interruptDelayTicks = registerFile.GetRegister32(STATE_REGS_INTERRUPTDELAYTICKS);
m_stream.SetBuffer(registerFile.GetRegister128(STATE_REGS_STREAM_BUFFER));
m_stream.SetBufferPosition(registerFile.GetRegister32(STATE_REGS_STREAM_BUFFERPOSITION));
2015-08-18 23:04:18 -04:00
}
{
auto path = string_format(STATE_PATH_FIFO_FORMAT, m_number);
archive.BeginReadFile(path.c_str())->Read(&m_fifoBuffer, sizeof(m_fifoBuffer));
}
}
uint32 CVif::GetTOP() const
{
throw std::exception();
}
uint32 CVif::GetITOP() const
{
return m_ITOP;
}
uint32 CVif::ReceiveDMA(uint32 address, uint32 qwc, uint32 unused, bool tagIncluded)
{
2024-07-31 11:44:30 -04:00
if(m_STAT.nVEW && !m_vpu.IsVuReady())
{
//Is waiting for program end, don't bother
return 0;
}
#ifdef PROFILE
CProfilerZone profilerZone(m_vifProfilerZone);
#endif
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
CLog::GetInstance().Print(LOG_NAME, "vif%i : Processing packet @ 0x%08X, qwc = 0x%X, tagIncluded = %i\r\n",
2018-04-30 21:01:23 +01:00
m_number, address, qwc, static_cast<int>(tagIncluded));
#endif
m_stream.SetDmaParams(address, qwc * 0x10, tagIncluded);
ProcessPacket(m_stream);
uint32 remainingSize = m_stream.GetRemainingDmaTransferSize();
assert((remainingSize & 0x0F) == 0);
remainingSize /= 0x10;
return qwc - remainingSize;
}
bool CVif::IsWaitingForProgramEnd() const
{
return (m_STAT.nVEW != 0);
}
2015-08-18 23:04:18 -04:00
void CVif::ProcessFifoWrite(uint32 address, uint32 value)
{
assert(m_fifoIndex != FIFO_SIZE);
if(m_fifoIndex == FIFO_SIZE)
{
return;
}
uint32 index = (address & 0xF) / 4;
*reinterpret_cast<uint32*>(m_fifoBuffer + m_fifoIndex + index * 4) = value;
if(index == 3)
{
m_fifoIndex += 0x10;
m_stream.SetFifoParams(m_fifoBuffer, m_fifoIndex);
ProcessPacket(m_stream);
uint32 newIndex = m_stream.GetRemainingDmaTransferSize();
uint32 discardSize = m_fifoIndex - newIndex;
memmove(m_fifoBuffer, m_fifoBuffer + discardSize, newIndex);
m_fifoIndex = newIndex;
}
}
void CVif::ProcessPacket(StreamType& stream)
{
while(stream.GetAvailableReadBytes())
{
if(m_STAT.nVPS == 1)
{
//Command is waiting for more data...
ExecuteCommand(stream, m_CODE);
if((m_STAT.nVPS == 1) && (stream.GetAvailableReadBytes() != 0))
{
//We have data in our FIFO but we still need more than what's available
break;
}
else
{
continue;
}
}
if(m_STAT.nVEW == 1)
{
2024-07-31 11:44:30 -04:00
if(!m_vpu.IsVuReady()) break;
m_STAT.nVEW = 0;
//Command is waiting for micro-program to end.
ExecuteCommand(stream, m_CODE);
continue;
}
2015-05-07 23:13:30 -04:00
if(m_STAT.nVIS)
{
break;
}
stream.ReadValue<4>(&m_CODE);
2015-05-07 23:13:30 -04:00
if(m_CODE.nI != 0)
{
//Next command will be stalled (if not MARK)
if(m_CODE.nCMD != CODE_CMD_MARK)
{
m_STAT.nVIS = 1;
}
2015-05-07 23:13:30 -04:00
m_STAT.nINT = 1;
2022-03-28 17:55:02 -04:00
m_interruptDelayTicks = 0x1000;
2015-05-07 23:13:30 -04:00
}
switch(m_CODE.nCMD)
{
case CODE_CMD_STMASK:
m_NUM = 1;
break;
case CODE_CMD_STROW:
case CODE_CMD_STCOL:
m_NUM = 4;
break;
default:
m_NUM = m_CODE.nNUM;
break;
}
ExecuteCommand(stream, m_CODE);
}
if(stream.GetAvailableReadBytes() == 0)
{
ResumeDelayedMicroProgram();
}
}
void CVif::ExecuteCommand(StreamType& stream, CODE nCommand)
{
2025-02-12 14:12:37 -05:00
#if LOGGING_ENABLED
if(m_number == 0)
{
DisassembleCommand(nCommand);
}
#endif
if(nCommand.nCMD >= 0x60)
{
Cmd_UNPACK(stream, nCommand, (nCommand.nIMM & 0x03FF));
return;
}
switch(nCommand.nCMD)
{
2023-03-21 21:31:10 -04:00
case CODE_CMD_NOP:
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_STCYCL:
m_CYCLE <<= nCommand.nIMM;
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_ITOP:
if(ResumeDelayedMicroProgram())
{
m_STAT.nVEW = 1;
return;
}
m_ITOPS = nCommand.nIMM & 0x3FF;
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_STMOD:
m_MODE = nCommand.nIMM & 0x03;
break;
case CODE_CMD_MARK:
m_MARK = nCommand.nIMM;
m_STAT.nMRK = 1;
break;
case CODE_CMD_FLUSHE:
2024-07-31 11:44:30 -04:00
if(!m_vpu.IsVuReady())
{
m_STAT.nVEW = 1;
}
else
{
m_STAT.nVEW = 0;
}
if(ResumeDelayedMicroProgram())
{
m_STAT.nVEW = 1;
return;
}
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_MSCAL:
if(ResumeDelayedMicroProgram())
{
m_STAT.nVEW = 1;
return;
}
StartDelayedMicroProgram(nCommand.nIMM * 8);
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_MSCALF:
2018-04-30 21:01:23 +01:00
//TODO: Wait for GIF PATH 1 and 2 transfers to be over
if(ResumeDelayedMicroProgram())
{
m_STAT.nVEW = 1;
return;
}
StartMicroProgram(nCommand.nIMM * 8);
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_MSCNT:
if(ResumeDelayedMicroProgram())
{
m_STAT.nVEW = 1;
return;
}
StartMicroProgram(m_vpu.GetContext().m_State.nPC);
break;
case CODE_CMD_STMASK:
Cmd_STMASK(stream, nCommand);
break;
case CODE_CMD_STROW:
Cmd_STROW(stream, nCommand);
break;
case CODE_CMD_STCOL:
Cmd_STCOL(stream, nCommand);
break;
2023-03-21 21:31:10 -04:00
case CODE_CMD_MPG:
Cmd_MPG(stream, nCommand);
break;
default:
2020-11-04 17:45:00 +01:00
CLog::GetInstance().Warn(LOG_NAME, "Executed invalid command %d.\r\n", nCommand.nCMD);
m_STAT.nER1 = 1;
break;
}
}
void CVif::Cmd_MPG(StreamType& stream, CODE nCommand)
{
uint32 nSize = stream.GetAvailableReadBytes();
2018-04-30 21:01:23 +01:00
uint32 nNum = (m_NUM == 0) ? (256) : (m_NUM);
uint32 nCodeNum = (m_CODE.nNUM == 0) ? (256) : (m_CODE.nNUM);
uint32 nTransfered = (nCodeNum - nNum) * 8;
nCodeNum *= 8;
nNum *= 8;
nSize = std::min<uint32>(nNum, nSize);
uint32 nDstAddr = (m_CODE.nIMM * 8) + nTransfered;
nDstAddr &= (m_vpu.GetMicroMemorySize() - 1);
//Check if microprogram is running
2024-07-31 11:44:30 -04:00
if(!m_vpu.IsVuReady())
{
m_STAT.nVEW = 1;
return;
}
if(nSize != 0)
{
auto microMem = m_vpu.GetMicroMemory();
auto copyToMicroMem =
[&](const uint8* microProgramPtr, uint32 start, uint32 size) {
//Check if there's a change
if(memcmp(microMem + start, microProgramPtr, size) != 0)
{
m_vpu.InvalidateMicroProgram(start, start + size);
memcpy(microMem + start, microProgramPtr, size);
}
};
uint8* microProgram = reinterpret_cast<uint8*>(alloca(nSize));
stream.Read(microProgram, nSize);
assert(nSize <= m_vpu.GetMicroMemorySize());
//Check if the copy's destination address will wrap around
if((nDstAddr + nSize) > m_vpu.GetMicroMemorySize())
{
uint32 start1 = nDstAddr;
uint32 size1 = m_vpu.GetMicroMemorySize() - nDstAddr;
uint32 start2 = 0;
uint32 size2 = nSize - size1;
copyToMicroMem(microProgram, start1, size1);
copyToMicroMem(microProgram + size1, start2, size2);
}
else
{
copyToMicroMem(microProgram, nDstAddr, nSize);
}
}
m_NUM -= static_cast<uint8>(nSize / 8);
if((m_NUM == 0) && (nSize != 0))
{
m_STAT.nVPS = 0;
}
else
{
m_STAT.nVPS = 1;
}
}
void CVif::Cmd_STROW(StreamType& stream, CODE nCommand)
{
while(m_NUM != 0 && stream.GetAvailableReadBytes())
{
assert(m_NUM <= 4);
stream.ReadValue<4>(&m_R[4 - m_NUM]);
m_NUM--;
}
if(m_NUM == 0)
{
m_STAT.nVPS = 0;
}
else
{
m_STAT.nVPS = 1;
}
}
void CVif::Cmd_STCOL(StreamType& stream, CODE nCommand)
{
while(m_NUM != 0 && stream.GetAvailableReadBytes())
{
assert(m_NUM <= 4);
stream.ReadValue<4>(&m_C[4 - m_NUM]);
m_NUM--;
}
if(m_NUM == 0)
{
m_STAT.nVPS = 0;
}
else
{
m_STAT.nVPS = 1;
}
}
void CVif::Cmd_STMASK(StreamType& stream, CODE command)
{
while(m_NUM != 0 && stream.GetAvailableReadBytes())
{
stream.ReadValue<4>(&m_MASK);
m_NUM--;
}
if(m_NUM == 0)
{
m_STAT.nVPS = 0;
}
else
{
m_STAT.nVPS = 1;
}
}
void CVif::Cmd_UNPACK(StreamType& stream, CODE nCommand, uint32 nDstAddr)
{
uint32 cl = m_CYCLE.nCL;
uint32 wl = m_CYCLE.nWL;
2018-04-30 21:01:23 +01:00
if(wl == 0)
{
wl = UINT_MAX;
cl = 0;
}
bool clGreaterEqualWl = (cl >= wl);
bool useMask = (nCommand.nCMD & 0x10) != 0;
bool usn = (m_CODE.nIMM & 0x4000) != 0;
uint8 mode = m_MODE & 0x3;
auto unpackFct = m_unpacker[(nCommand.nCMD & 0x0F) | ((clGreaterEqualWl ? 1 : 0) << 4) | ((useMask ? 1 : 0) << 5) | (mode << 6) | (usn << 8)];
((*this).*(unpackFct))(stream, nCommand, nDstAddr);
}
uint32 CVif::GetColMaskOp(unsigned int col) const
{
assert(col < 4);
return (m_MASK >> (col * 8)) & 0xFF;
}
void CVif::PrepareMicroProgram()
{
m_ITOP = m_ITOPS;
}
void CVif::StartMicroProgram(uint32 address)
{
2024-07-31 11:44:30 -04:00
if(!m_vpu.IsVuReady())
{
m_STAT.nVEW = 1;
return;
}
assert(!m_STAT.nVEW);
PrepareMicroProgram();
m_vpu.ExecuteMicroProgram(address);
}
void CVif::StartDelayedMicroProgram(uint32 address)
{
//Snowblind Studio games start a VU microprogram and issues an UNPACK command
//which has data needed by the microprogram. We simulate the microprogram
//starting a bit later to let the UNPACK command execute
2024-07-31 11:44:30 -04:00
if(!m_vpu.IsVuReady())
{
m_STAT.nVEW = 1;
return;
}
assert(!m_STAT.nVEW);
PrepareMicroProgram();
m_pendingMicroProgram = address;
}
bool CVif::ResumeDelayedMicroProgram()
{
if(m_pendingMicroProgram != -1)
{
assert(!IsWaitingForProgramEnd());
2024-07-31 11:44:30 -04:00
assert(m_vpu.IsVuReady());
2015-05-09 22:10:26 -04:00
m_vpu.ExecuteMicroProgram(m_pendingMicroProgram);
m_pendingMicroProgram = -1;
return true;
}
else
{
return false;
}
}
void CVif::DisassembleGet(uint32 address)
{
2018-04-30 21:01:23 +01:00
#define LOG_GET(registerId) \
case registerId: \
CLog::GetInstance().Print(LOG_NAME, "= " #registerId ".\r\n"); \
break;
switch(address)
{
LOG_GET(VIF0_STAT)
2020-07-13 16:57:55 +02:00
LOG_GET(VIF0_ERR)
LOG_GET(VIF0_MARK)
LOG_GET(VIF0_CYCLE)
LOG_GET(VIF0_MODE)
LOG_GET(VIF0_NUM)
2019-06-01 22:28:51 -04:00
LOG_GET(VIF0_MASK)
LOG_GET(VIF0_CODE)
LOG_GET(VIF0_R0)
LOG_GET(VIF0_R1)
LOG_GET(VIF0_R2)
LOG_GET(VIF0_R3)
LOG_GET(VIF1_STAT)
2020-07-13 16:57:55 +02:00
LOG_GET(VIF1_ERR)
LOG_GET(VIF1_MARK)
LOG_GET(VIF1_CYCLE)
LOG_GET(VIF1_MODE)
LOG_GET(VIF1_NUM)
2019-06-01 22:28:51 -04:00
LOG_GET(VIF1_MASK)
LOG_GET(VIF1_CODE)
LOG_GET(VIF1_R0)
LOG_GET(VIF1_R1)
LOG_GET(VIF1_R2)
LOG_GET(VIF1_R3)
default:
CLog::GetInstance().Print(LOG_NAME, "Reading unknown register 0x%08X.\r\n", address);
break;
}
#undef LOG_GET
}
void CVif::DisassembleSet(uint32 address, uint32 value)
{
2015-08-18 23:04:18 -04:00
if((address >= VIF0_FIFO_START) && (address < VIF0_FIFO_END))
{
CLog::GetInstance().Print(LOG_NAME, "VIF0_FIFO(0x%03X) = 0x%08X.\r\n", address & 0xFFF, value);
2015-08-18 23:04:18 -04:00
}
else if((address >= VIF1_FIFO_START) && (address < VIF1_FIFO_END))
{
CLog::GetInstance().Print(LOG_NAME, "VIF1_FIFO(0x%03X) = 0x%08X.\r\n", address & 0xFFF, value);
2015-08-18 23:04:18 -04:00
}
else
{
2018-04-30 21:01:23 +01:00
#define LOG_SET(registerId) \
case registerId: \
CLog::GetInstance().Print(LOG_NAME, #registerId " = 0x%08X.\r\n", value); \
break;
2015-08-18 23:04:18 -04:00
switch(address)
{
LOG_SET(VIF0_FBRST)
LOG_SET(VIF0_MARK)
2020-07-13 16:57:55 +02:00
LOG_SET(VIF0_ERR)
LOG_SET(VIF1_FBRST)
LOG_SET(VIF1_MARK)
2020-07-13 16:57:55 +02:00
LOG_SET(VIF1_ERR)
2015-08-18 23:04:18 -04:00
default:
CLog::GetInstance().Print(LOG_NAME, "Writing unknown register 0x%08X, 0x%08X.\r\n", address, value);
2015-08-18 23:04:18 -04:00
break;
}
#undef LOG_SET
}
}
void CVif::DisassembleCommand(CODE code)
{
if(m_STAT.nVPS != 0) return;
CLog::GetInstance().Print(LOG_NAME, "vif%i : ", m_number);
if(code.nI)
{
CLog::GetInstance().Print(LOG_NAME, "(I) ");
}
if(code.nCMD >= 0x60)
{
2018-04-30 21:01:23 +01:00
static const char* packFormats[16] =
{
"S-32",
"S-16",
"S-8",
"(Unknown)",
"V2-32",
"V2-16",
"V2-8",
"(Unknown)",
"V3-32",
"V3-16",
"V3-8",
"(Unknown)",
"V4-32",
"V4-16",
"V4-8",
"V4-5"};
CLog::GetInstance().Print(LOG_NAME, "UNPACK(format = %s, imm = 0x%x, num = 0x%x);\r\n",
2018-04-30 21:01:23 +01:00
packFormats[code.nCMD & 0x0F], code.nIMM, code.nNUM);
}
else
{
switch(code.nCMD)
{
2024-03-01 15:12:53 -05:00
case CODE_CMD_NOP:
CLog::GetInstance().Print(LOG_NAME, "NOP\r\n");
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_STCYCL:
CLog::GetInstance().Print(LOG_NAME, "STCYCL(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_OFFSET:
CLog::GetInstance().Print(LOG_NAME, "OFFSET(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_BASE:
CLog::GetInstance().Print(LOG_NAME, "BASE(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_ITOP:
CLog::GetInstance().Print(LOG_NAME, "ITOP(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_STMOD:
CLog::GetInstance().Print(LOG_NAME, "STMOD(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_MSKPATH3:
2017-03-06 09:41:07 -05:00
CLog::GetInstance().Print(LOG_NAME, "MSKPATH3(mask = %d);\r\n", (code.nIMM & 0x8000) ? 1 : 0);
break;
case CODE_CMD_MARK:
CLog::GetInstance().Print(LOG_NAME, "MARK(imm = 0x%x);\r\n", code.nIMM);
break;
case CODE_CMD_FLUSHE:
CLog::GetInstance().Print(LOG_NAME, "FLUSHE();\r\n");
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_FLUSH:
CLog::GetInstance().Print(LOG_NAME, "FLUSH();\r\n");
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_FLUSHA:
CLog::GetInstance().Print(LOG_NAME, "FLUSHA();\r\n");
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_MSCAL:
CLog::GetInstance().Print(LOG_NAME, "MSCAL(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_MSCALF:
CLog::GetInstance().Print(LOG_NAME, "MSCALF(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_MSCNT:
CLog::GetInstance().Print(LOG_NAME, "MSCNT();\r\n");
break;
case CODE_CMD_STMASK:
CLog::GetInstance().Print(LOG_NAME, "STMASK();\r\n");
break;
case CODE_CMD_STROW:
CLog::GetInstance().Print(LOG_NAME, "STROW();\r\n");
break;
case CODE_CMD_STCOL:
CLog::GetInstance().Print(LOG_NAME, "STCOL();\r\n");
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_MPG:
CLog::GetInstance().Print(LOG_NAME, "MPG(imm = 0x%x, num = 0x%x);\r\n", code.nIMM, code.nNUM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_DIRECT:
CLog::GetInstance().Print(LOG_NAME, "DIRECT(imm = 0x%x);\r\n", code.nIMM);
break;
2024-03-01 15:12:53 -05:00
case CODE_CMD_DIRECTHL:
CLog::GetInstance().Print(LOG_NAME, "DIRECTHL(imm = 0x%x);\r\n", code.nIMM);
break;
default:
CLog::GetInstance().Print(LOG_NAME, "Unknown command (0x%x).\r\n", code.nCMD);
break;
}
}
}
//CFifoStream
//--------------------------------------------------
2018-04-30 21:01:23 +01:00
CVif::CFifoStream::CFifoStream(uint8* ram, uint8* spr)
: m_ram(ram)
, m_spr(spr)
{
}
void CVif::CFifoStream::Reset()
{
m_buffer = {};
m_bufferPosition = BUFFERSIZE;
m_startAddress = 0;
m_nextAddress = 0;
m_endAddress = 0;
m_tagIncluded = false;
m_source = nullptr;
}
void CVif::CFifoStream::Flush()
{
m_bufferPosition = BUFFERSIZE;
}
void CVif::CFifoStream::SetDmaParams(uint32 address, uint32 size, bool tagIncluded)
{
if(address & 0x80000000)
{
m_source = m_spr;
address &= (PS2::EE_SPR_SIZE - 1);
assert((address + size) <= PS2::EE_SPR_SIZE);
}
else
{
m_source = m_ram;
address &= (PS2::EE_RAM_SIZE - 1);
assert((address + size) <= PS2::EE_RAM_SIZE);
}
m_startAddress = address;
m_nextAddress = address;
m_endAddress = address + size;
m_tagIncluded = tagIncluded;
SyncBuffer();
}
2015-08-18 23:04:18 -04:00
void CVif::CFifoStream::SetFifoParams(uint8* source, uint32 size)
{
m_source = source;
m_startAddress = 0;
2015-08-18 23:04:18 -04:00
m_nextAddress = 0;
m_endAddress = size;
m_tagIncluded = false;
SyncBuffer();
2015-08-18 23:04:18 -04:00
}
void CVif::CFifoStream::Align32()
{
unsigned int remainBytes = m_bufferPosition & 0x03;
if(remainBytes == 0) return;
uint32 dummy = 0;
Read(&dummy, 4 - remainBytes);
assert((m_bufferPosition & 0x03) == 0);
}
uint8* CVif::CFifoStream::GetDirectPointer() const
{
assert(!m_tagIncluded);
if(m_bufferPosition == BUFFERSIZE)
{
return m_source + m_nextAddress;
}
else
{
assert((m_nextAddress - m_startAddress) >= 0x10);
return m_source + m_nextAddress + m_bufferPosition - 0x10;
}
}
void CVif::CFifoStream::Advance(uint32 size)
{
assert((size & 0x0F) == 0);
assert(!m_tagIncluded);
//If buffer was untouched, we can do as if we read from it directly
if(m_bufferPosition == 0)
{
assert(size >= 0x10);
size -= 0x10;
m_bufferPosition = BUFFERSIZE;
}
2017-02-19 15:49:45 -05:00
assert((m_nextAddress + size) <= m_endAddress);
m_nextAddress += size;
if(m_bufferPosition != BUFFERSIZE)
{
//Update buffer
assert((m_nextAddress - m_startAddress) >= 0x10);
m_buffer = *reinterpret_cast<uint128*>(&m_source[m_nextAddress - 0x10]);
}
}
uint128 CVif::CFifoStream::GetBuffer() const
{
return m_buffer;
}
void CVif::CFifoStream::SetBuffer(uint128 buffer)
{
m_buffer = buffer;
}
uint32 CVif::CFifoStream::GetBufferPosition() const
{
return m_bufferPosition;
}
void CVif::CFifoStream::SetBufferPosition(uint32 bufferPosition)
{
m_bufferPosition = bufferPosition;
}