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