1 #include <cassert>
2 #include <algorithm>
3 #include "string_format.h"
4 #include "../states/RegisterStateFile.h"
5 #include "../FrameDump.h"
6 #include "GIF.h"
7 #include "Dmac_Channel.h"
8 #include "Vpu.h"
9 #include "Vif1.h"
10 
11 #define STATE_PATH_FORMAT ("vpu/vif1_%d.xml")
12 #define STATE_REGS_BASE ("BASE")
13 #define STATE_REGS_TOP ("TOP")
14 #define STATE_REGS_TOPS ("TOPS")
15 #define STATE_REGS_OFST ("OFST")
16 #define STATE_REGS_DIRECTQWORDBUFFER ("directQwordBuffer")
17 #define STATE_REGS_DIRECTQWORDBUFFER_INDEX ("directQwordBufferIndex")
18 
CVif1(unsigned int number,CVpu & vpu,CGIF & gif,CINTC & intc,uint8 * ram,uint8 * spr)19 CVif1::CVif1(unsigned int number, CVpu& vpu, CGIF& gif, CINTC& intc, uint8* ram, uint8* spr)
20     : CVif(1, vpu, intc, ram, spr)
21     , m_gif(gif)
22 {
23 }
24 
Reset()25 void CVif1::Reset()
26 {
27 	CVif::Reset();
28 	m_BASE = 0;
29 	m_TOP = 0;
30 	m_TOPS = 0;
31 	m_OFST = 0;
32 	m_directQwordBufferIndex = 0;
33 }
34 
SaveState(Framework::CZipArchiveWriter & archive)35 void CVif1::SaveState(Framework::CZipArchiveWriter& archive)
36 {
37 	CVif::SaveState(archive);
38 
39 	auto path = string_format(STATE_PATH_FORMAT, m_number);
40 	CRegisterStateFile* registerFile = new CRegisterStateFile(path.c_str());
41 	registerFile->SetRegister32(STATE_REGS_BASE, m_BASE);
42 	registerFile->SetRegister32(STATE_REGS_TOP, m_TOP);
43 	registerFile->SetRegister32(STATE_REGS_TOPS, m_TOPS);
44 	registerFile->SetRegister32(STATE_REGS_OFST, m_OFST);
45 	registerFile->SetRegister128(STATE_REGS_DIRECTQWORDBUFFER, *reinterpret_cast<uint128*>(&m_directQwordBuffer));
46 	registerFile->SetRegister32(STATE_REGS_DIRECTQWORDBUFFER_INDEX, m_directQwordBufferIndex);
47 	archive.InsertFile(registerFile);
48 }
49 
LoadState(Framework::CZipArchiveReader & archive)50 void CVif1::LoadState(Framework::CZipArchiveReader& archive)
51 {
52 	CVif::LoadState(archive);
53 
54 	auto path = string_format(STATE_PATH_FORMAT, m_number);
55 	CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
56 	m_BASE = registerFile.GetRegister32(STATE_REGS_BASE);
57 	m_TOP = registerFile.GetRegister32(STATE_REGS_TOP);
58 	m_TOPS = registerFile.GetRegister32(STATE_REGS_TOPS);
59 	m_OFST = registerFile.GetRegister32(STATE_REGS_OFST);
60 	*reinterpret_cast<uint128*>(&m_directQwordBuffer) = registerFile.GetRegister128(STATE_REGS_DIRECTQWORDBUFFER);
61 	m_directQwordBufferIndex = registerFile.GetRegister32(STATE_REGS_DIRECTQWORDBUFFER_INDEX);
62 }
63 
GetTOP() const64 uint32 CVif1::GetTOP() const
65 {
66 	return m_TOP;
67 }
68 
ReceiveDMA(uint32 address,uint32 qwc,uint32 direction,bool tagIncluded)69 uint32 CVif1::ReceiveDMA(uint32 address, uint32 qwc, uint32 direction, bool tagIncluded)
70 {
71 	if(direction == Dmac::CChannel::CHCR_DIR_TO)
72 	{
73 		uint8* source = nullptr;
74 		uint32 size = qwc * 0x10;
75 		if(address & 0x80000000)
76 		{
77 			source = m_spr;
78 			address &= (PS2::EE_SPR_SIZE - 1);
79 			assert((address + size) <= PS2::EE_SPR_SIZE);
80 		}
81 		else
82 		{
83 			source = m_ram;
84 			address &= (PS2::EE_RAM_SIZE - 1);
85 			assert((address + size) <= PS2::EE_RAM_SIZE);
86 		}
87 		auto gs = m_gif.GetGsHandler();
88 		gs->ReadImageData(source + address, size);
89 		return qwc;
90 	}
91 	else
92 	{
93 		return CVif::ReceiveDMA(address, qwc, direction, tagIncluded);
94 	}
95 }
96 
ExecuteCommand(StreamType & stream,CODE nCommand)97 void CVif1::ExecuteCommand(StreamType& stream, CODE nCommand)
98 {
99 #ifdef _DEBUG
100 	DisassembleCommand(nCommand);
101 #endif
102 	switch(nCommand.nCMD)
103 	{
104 	case 0x02:
105 		//OFFSET
106 		m_OFST = nCommand.nIMM;
107 		m_STAT.nDBF = 0;
108 		m_TOPS = m_BASE;
109 		break;
110 	case 0x03:
111 		//BASE
112 		m_BASE = nCommand.nIMM;
113 		break;
114 	case 0x06:
115 		//MSKPATH3
116 		m_gif.SetPath3Masked((nCommand.nIMM & 0x8000) != 0);
117 		break;
118 	case 0x11:
119 		//FLUSH
120 		if(m_vpu.IsVuRunning())
121 		{
122 			m_STAT.nVEW = 1;
123 		}
124 		else
125 		{
126 			m_STAT.nVEW = 0;
127 		}
128 		if(ResumeDelayedMicroProgram())
129 		{
130 			m_STAT.nVEW = 1;
131 			return;
132 		}
133 		break;
134 	case 0x13:
135 		//FLUSHA
136 		if(m_vpu.IsVuRunning())
137 		{
138 			m_STAT.nVEW = 1;
139 		}
140 		else
141 		{
142 			m_STAT.nVEW = 0;
143 		}
144 		if(ResumeDelayedMicroProgram())
145 		{
146 			m_STAT.nVEW = 1;
147 			return;
148 		}
149 		break;
150 	case 0x50:
151 	case 0x51:
152 		//DIRECT/DIRECTHL
153 		Cmd_DIRECT(stream, nCommand);
154 		break;
155 	default:
156 		CVif::ExecuteCommand(stream, nCommand);
157 		break;
158 	}
159 }
160 
Cmd_DIRECT(StreamType & stream,CODE nCommand)161 void CVif1::Cmd_DIRECT(StreamType& stream, CODE nCommand)
162 {
163 	uint32 nSize = stream.GetAvailableReadBytes();
164 	assert((nSize & 0x03) == 0);
165 
166 	if(nSize != 0)
167 	{
168 		//Check if we have data but less than a qword
169 		//If we do, we have to go inside a different path to complete a full qword
170 		bool hasPartialQword = (m_directQwordBufferIndex != 0) || (nSize < QWORD_SIZE);
171 		if(hasPartialQword)
172 		{
173 			//Read enough bytes to try to complete our qword
174 			assert(m_directQwordBufferIndex < QWORD_SIZE);
175 			uint32 readAmount = std::min(nSize, QWORD_SIZE - m_directQwordBufferIndex);
176 			stream.Read(m_directQwordBuffer + m_directQwordBufferIndex, readAmount);
177 			m_directQwordBufferIndex += readAmount;
178 			nSize -= readAmount;
179 
180 			//If our qword is complete, send to GIF
181 			if(m_directQwordBufferIndex == QWORD_SIZE)
182 			{
183 				assert(m_CODE.nIMM != 0);
184 				uint32 processed = m_gif.ProcessMultiplePackets(m_directQwordBuffer, QWORD_SIZE, 0, QWORD_SIZE, CGsPacketMetadata(2));
185 				assert(processed == QWORD_SIZE);
186 				m_CODE.nIMM--;
187 				m_directQwordBufferIndex = 0;
188 			}
189 		}
190 
191 		//If no data pending in our partial qword buffer, go forward with multiple qword transfer
192 		if(m_directQwordBufferIndex == 0)
193 		{
194 			nSize = std::min<uint32>(m_CODE.nIMM * 0x10, nSize & ~0xF);
195 
196 			auto packet = stream.GetDirectPointer();
197 			uint32 processed = m_gif.ProcessMultiplePackets(packet, nSize, 0, nSize, CGsPacketMetadata(2));
198 			assert(processed <= nSize);
199 			stream.Advance(processed);
200 			//Adjust size in case not everything was processed by GIF
201 			nSize = processed;
202 
203 			m_CODE.nIMM -= (nSize / 0x10);
204 		}
205 	}
206 
207 	if(m_CODE.nIMM == 0)
208 	{
209 		m_STAT.nVPS = 0;
210 	}
211 	else
212 	{
213 		m_STAT.nVPS = 1;
214 	}
215 }
216 
Cmd_UNPACK(StreamType & stream,CODE nCommand,uint32 nDstAddr)217 void CVif1::Cmd_UNPACK(StreamType& stream, CODE nCommand, uint32 nDstAddr)
218 {
219 	bool nFlg = (m_CODE.nIMM & 0x8000) != 0;
220 	if(nFlg)
221 	{
222 		nDstAddr += m_TOPS;
223 	}
224 
225 	return CVif::Cmd_UNPACK(stream, nCommand, nDstAddr);
226 }
227 
PrepareMicroProgram()228 void CVif1::PrepareMicroProgram()
229 {
230 	CVif::PrepareMicroProgram();
231 
232 	m_TOP = m_TOPS;
233 
234 	if(m_STAT.nDBF == 1)
235 	{
236 		m_TOPS = m_BASE;
237 	}
238 	else
239 	{
240 		m_TOPS = m_BASE + m_OFST;
241 	}
242 	m_STAT.nDBF = ~m_STAT.nDBF;
243 }
244