/
Sif1.cpp
354 lines (293 loc) · 8.21 KB
/
Sif1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+
#define _PC_ // disables MIPS opcode macros.
#include "R3000A.h"
#include "Common.h"
#include "Sif.h"
#include "IopHw.h"
_sif sif1;
static bool done = false;
static bool sif1_dma_stall = false;
static __fi void Sif1Init()
{
SIF_LOG("SIF1 DMA start...");
done = false;
sif1.ee.cycles = 0;
sif1.iop.cycles = 0;
}
// Write from the EE to Fifo.
static __fi bool WriteEEtoFifo()
{
// There's some data ready to transfer into the fifo..
SIF_LOG("Sif 1: Write EE to Fifo");
const int writeSize = std::min((s32)sif1ch.qwc, sif1.fifo.sif_free() >> 2);
tDMA_TAG *ptag;
ptag = sif1ch.getAddr(sif1ch.madr, DMAC_SIF1, false);
if (ptag == NULL)
{
DevCon.Warning("Write EE to Fifo: ptag == NULL");
return false;
}
sif1.fifo.write((u32*)ptag, writeSize << 2);
sif1ch.madr += writeSize << 4;
hwDmacSrcTadrInc(sif1ch);
sif1.ee.cycles += writeSize; // fixme : BIAS is factored in above
sif1ch.qwc -= writeSize;
return true;
}
// Read from the fifo and write to IOP
static __fi bool WriteFifoToIOP()
{
// If we're reading something, continue to do so.
SIF_LOG("Sif1: Write Fifo to IOP");
const int readSize = std::min(sif1.iop.counter, sif1.fifo.size);
SIF_LOG("Sif 1 IOP doing transfer %04X to %08X", readSize, HW_DMA10_MADR);
sif1.fifo.read((u32*)iopPhysMem(hw_dma10.madr), readSize);
psxCpu->Clear(hw_dma10.madr, readSize);
hw_dma10.madr += readSize << 2;
sif1.iop.cycles += readSize >> 2; // fixme: should be >> 4
sif1.iop.counter -= readSize;
return true;
}
// Get a tag and process it.
static __fi bool ProcessEETag()
{
// Chain mode
tDMA_TAG *ptag;
SIF_LOG("Sif1: ProcessEETag");
// Process DMA tag at sif1ch.tadr
ptag = sif1ch.DMAtransfer(sif1ch.tadr, DMAC_SIF1);
if (ptag == NULL)
{
Console.WriteLn("Sif1 ProcessEETag: ptag = NULL");
return false;
}
if (sif1ch.chcr.TTE)
{
Console.WriteLn("SIF1 TTE");
sif1.fifo.write((u32*)ptag + 2, 2);
}
SIF_LOG("%s", ptag->tag_to_str().c_str());
sif1ch.madr = ptag[1]._u32;
sif1.ee.end = hwDmacSrcChain(sif1ch, ptag->ID);
if (sif1ch.chcr.TIE && ptag->IRQ)
{
//Console.WriteLn("SIF1 TIE");
sif1.ee.end = true;
}
return true;
}
// Write fifo to data, and put it in IOP.
static __fi bool SIFIOPReadTag()
{
// Read a tag.
sif1.fifo.read((u32*)&sif1.iop.data, 4);
//sif1words = (sif1words + 3) & 0xfffffffc; // Round up to nearest 4.
SIF_LOG("SIF 1 IOP: dest chain tag madr:%08X wc:%04X id:%X irq:%d",
sif1data & 0xffffff, sif1words, sif1tag.ID, sif1tag.IRQ);
// Only use the first 24 bits.
hw_dma10.madr = sif1data & 0xffffff;
if (sif1words > 0xFFFFC) DevCon.Warning("SIF1 Overrun %x", sif1words);
//Maximum transfer amount 1mb-16 also masking out top part which is a "Mode" cache stuff, we don't care :)
sif1.iop.counter = sif1words & 0xFFFFC;
if (sif1tag.IRQ || (sif1tag.ID & 4)) sif1.iop.end = true;
return true;
}
// Stop processing EE, and signal an interrupt.
static __fi void EndEE()
{
sif1.ee.end = false;
sif1.ee.busy = false;
SIF_LOG("Sif 1: End EE");
// Voodoocycles : Okami wants around 100 cycles when booting up
// Other games reach like 50k cycles here, but the EE will long have given up by then and just retry.
// (Cause of double interrupts on the EE)
if (sif1.ee.cycles == 0)
{
SIF_LOG("SIF1 EE: cycles = 0");
sif1.ee.cycles = 1;
}
CPU_SET_DMASTALL(DMAC_SIF1, false);
CPU_INT(DMAC_SIF1, /*std::min((int)(*/sif1.ee.cycles*BIAS/*), 384)*/);
}
// Stop processing IOP, and signal an interrupt.
static __fi void EndIOP()
{
sif1data = 0;
sif1.iop.end = false;
sif1.iop.busy = false;
SIF_LOG("Sif 1: End IOP");
//Fixme ( voodoocycles ):
//The *24 are needed for ecco the dolphin (CDVD hangs) and silver surfer (Pad not detected)
//Greater than *35 break rebooting when trying to play Tekken5 arcade history
//Total cycles over 1024 makes SIF too slow to keep up the sound stream in so3...
if (sif1.iop.cycles == 0)
{
DevCon.Warning("SIF1 IOP: cycles = 0");
sif1.iop.cycles = 1;
}
// iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords)
PSX_INT(IopEvt_SIF1, /*std::min((*/sif1.iop.cycles/* * 26*//*), 1024)*/);
}
// Handle the EE transfer.
static __fi void HandleEETransfer()
{
if(!sif1ch.chcr.STR)
{
//DevCon.Warning("Replacement for irq prevention hack EE SIF1");
sif1.ee.end = false;
sif1.ee.busy = false;
return;
}
/*if (sif1ch.qwc == 0)
if (sif1ch.chcr.MOD == NORMAL_MODE)
if (!sif1.ee.end){
DevCon.Warning("sif1 irq prevented CHCR %x QWC %x", sif1ch.chcr, sif1ch.qwc);
done = true;
return;
}*/
// If there's no more to transfer.
if (sif1ch.qwc <= 0)
{
// If NORMAL mode or end of CHAIN then stop DMA.
if ((sif1ch.chcr.MOD == NORMAL_MODE) || sif1.ee.end)
{
done = true;
EndEE();
}
else
{
done = false;
if (!ProcessEETag()) return;
}
}
else
{
if (dmacRegs.ctrl.STD == STD_SIF1)
{
if ((sif1ch.chcr.MOD == NORMAL_MODE) || ((sif1ch.chcr.TAG >> 28) & 0x7) == TAG_REFS)
{
//DevCon.Warning("SIF1 Stall Control");
const int writeSize = std::min((s32)sif1ch.qwc, sif1.fifo.sif_free() >> 2);
if ((sif1ch.madr + (writeSize * 16)) > dmacRegs.stadr.ADDR)
{
hwDmacIrq(DMAC_STALL_SIS);
sif1_dma_stall = true;
CPU_SET_DMASTALL(DMAC_SIF1, true);
return;
}
}
//DevCon.Warning("SIF1 stall control Not Implemented"); // STD == fromSIF1
}
if (sif1.fifo.sif_free() > 0)
{
WriteEEtoFifo();
}
}
}
// Handle the IOP transfer.
static __fi void HandleIOPTransfer()
{
if (sif1.iop.counter > 0)
{
if (sif1.fifo.size > 0)
{
WriteFifoToIOP();
}
}
if (sif1.iop.counter <= 0)
{
if (sif1.iop.end)
{
done = true;
EndIOP();
}
else if (sif1.fifo.size >= 4)
{
done = false;
SIFIOPReadTag();
}
}
}
static __fi void Sif1End()
{
psHu32(SBUS_F240) &= ~0x40;
psHu32(SBUS_F240) &= ~0x4000;
DMA_LOG("SIF1 DMA End");
}
// Transfer EE to IOP, putting data in the fifo as an intermediate step.
__fi void SIF1Dma()
{
int BusyCheck = 0;
if (sif1_dma_stall)
{
const int writeSize = std::min((s32)sif1ch.qwc, sif1.fifo.sif_free() >> 2);
if ((sif1ch.madr + (writeSize * 16)) > dmacRegs.stadr.ADDR)
return;
}
sif1_dma_stall = false;
Sif1Init();
do
{
//I realise this is very hacky in a way but its an easy way of checking if both are doing something
BusyCheck = 0;
if (sif1.ee.busy && !sif1_dma_stall)
{
if(sif1.fifo.sif_free() > 0 || (sif1.ee.end && sif1ch.qwc == 0))
{
BusyCheck++;
HandleEETransfer();
}
}
if (sif1.iop.busy)
{
if(sif1.fifo.size >= 4 || (sif1.iop.end && sif1.iop.counter == 0))
{
BusyCheck++;
HandleIOPTransfer();
}
}
} while (/*!done &&*/ BusyCheck > 0);
Sif1End();
}
__fi void sif1Interrupt()
{
HW_DMA10_CHCR &= ~0x01000000; //reset TR flag
psxDmaInterrupt2(3);
}
__fi void EEsif1Interrupt()
{
hwDmacIrq(DMAC_SIF1);
sif1ch.chcr.STR = false;
}
// Do almost exactly the same thing as psxDma10 in IopDma.cpp.
// Main difference is this checks for iop, where psxDma10 checks for ee.
__fi void dmaSIF1()
{
SIF_LOG("dmaSIF1 %s", sif1ch.cmqt_to_str().c_str());
if (sif1.fifo.readPos != sif1.fifo.writePos)
{
SIF_LOG("warning, sif1.fifoReadPos != sif1.fifoWritePos");
}
psHu32(SBUS_F240) |= 0x4000;
sif1.ee.busy = true;
CPU_SET_DMASTALL(DMAC_SIF1, false);
// Okay, this here is needed currently (r3644).
// FFX battles in the thunder plains map die otherwise, Phantasy Star 4 as well
// These 2 games could be made playable again by increasing the time the EE or the IOP run,
// showing that this is very timing sensible.
// Doing this DMA unfortunately brings back an old warning in Legend of Legaia though, but it still works.
//Updated 23/08/2011: The hangs are caused by the EE suspending SIF1 DMA and restarting it when in the middle
//of processing a "REFE" tag, so the hangs can be solved by forcing the ee.end to be false
// (as it should always be at the beginning of a DMA). using "if iop is busy" flags breaks Tom Clancy Rainbow Six.
// Legend of Legaia doesn't throw a warning either :)
sif1.ee.end = false;
if (sif1ch.chcr.MOD == CHAIN_MODE && sif1ch.qwc > 0)
{
if ((sif1ch.chcr.tag().ID == TAG_REFE) || (sif1ch.chcr.tag().ID == TAG_END) || (sif1ch.chcr.tag().IRQ && vif1ch.chcr.TIE))
{
sif1.ee.end = true;
}
}
SIF1Dma();
}