1 #include "Iop_SubSystem.h"
2 #include "IopBios.h"
3 #include "GenericMipsExecutor.h"
4 #include "../psx/PsxBios.h"
5 #include "../states/MemoryStateFile.h"
6 #include "../Ps2Const.h"
7 #include "../Log.h"
8 #include "placeholder_def.h"
9 
10 using namespace Iop;
11 using namespace PS2;
12 
13 #define LOG_NAME ("iop_subsystem")
14 
15 #define STATE_CPU ("iop_cpu")
16 #define STATE_RAM ("iop_ram")
17 #define STATE_SCRATCH ("iop_scratch")
18 #define STATE_SPURAM ("iop_spuram")
19 
CSubSystem(bool ps2Mode)20 CSubSystem::CSubSystem(bool ps2Mode)
21     : m_cpu(MEMORYMAP_ENDIAN_LSBF, true)
22     , m_ram(new uint8[IOP_RAM_SIZE])
23     , m_scratchPad(new uint8[IOP_SCRATCH_SIZE])
24     , m_spuRam(new uint8[SPU_RAM_SIZE])
25     , m_dmac(m_ram, m_intc)
26     , m_counters(ps2Mode ? IOP_CLOCK_OVER_FREQ : IOP_CLOCK_BASE_FREQ, m_intc)
27     , m_spuCore0(m_spuRam, SPU_RAM_SIZE, 0)
28     , m_spuCore1(m_spuRam, SPU_RAM_SIZE, 1)
29     , m_spu(m_spuCore0)
30     , m_spu2(m_spuCore0, m_spuCore1)
31 #ifdef _IOP_EMULATE_MODULES
32     , m_sio2(m_intc)
33 #endif
34     , m_speed(m_intc)
35     , m_cpuArch(MIPS_REGSIZE_32)
36     , m_copScu(MIPS_REGSIZE_32)
37     , m_dmaUpdateTicks(0)
38 {
39 	if(ps2Mode)
40 	{
41 		m_bios = std::make_shared<CIopBios>(m_cpu, m_ram, PS2::IOP_RAM_SIZE, m_scratchPad);
42 	}
43 	else
44 	{
45 		m_bios = std::make_shared<CPsxBios>(m_cpu, m_ram, PS2::IOP_RAM_SIZE);
46 	}
47 
48 	m_cpu.m_executor = std::make_unique<CGenericMipsExecutor<BlockLookupOneWay>>(m_cpu, (IOP_RAM_SIZE * 4));
49 
50 	//Read memory map
51 	m_cpu.m_pMemoryMap->InsertReadMap((0 * IOP_RAM_SIZE), (0 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x01);
52 	m_cpu.m_pMemoryMap->InsertReadMap((1 * IOP_RAM_SIZE), (1 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x02);
53 	m_cpu.m_pMemoryMap->InsertReadMap((2 * IOP_RAM_SIZE), (2 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x03);
54 	m_cpu.m_pMemoryMap->InsertReadMap((3 * IOP_RAM_SIZE), (3 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x04);
55 	m_cpu.m_pMemoryMap->InsertReadMap(SPEED_REG_BEGIN, SPEED_REG_END, std::bind(&CSubSystem::ReadIoRegister, this, std::placeholders::_1), 0x05);
56 	m_cpu.m_pMemoryMap->InsertReadMap(IOP_SCRATCH_ADDR, IOP_SCRATCH_ADDR + IOP_SCRATCH_SIZE - 1, m_scratchPad, 0x06);
57 	m_cpu.m_pMemoryMap->InsertReadMap(HW_REG_BEGIN, HW_REG_END, std::bind(&CSubSystem::ReadIoRegister, this, std::placeholders::_1), 0x07);
58 
59 	//Write memory map
60 	m_cpu.m_pMemoryMap->InsertWriteMap((0 * IOP_RAM_SIZE), (0 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x01);
61 	m_cpu.m_pMemoryMap->InsertWriteMap((1 * IOP_RAM_SIZE), (1 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x02);
62 	m_cpu.m_pMemoryMap->InsertWriteMap((2 * IOP_RAM_SIZE), (2 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x03);
63 	m_cpu.m_pMemoryMap->InsertWriteMap((3 * IOP_RAM_SIZE), (3 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x04);
64 	m_cpu.m_pMemoryMap->InsertWriteMap(SPEED_REG_BEGIN, SPEED_REG_END, std::bind(&CSubSystem::WriteIoRegister, this, std::placeholders::_1, std::placeholders::_2), 0x05);
65 	m_cpu.m_pMemoryMap->InsertWriteMap(IOP_SCRATCH_ADDR, IOP_SCRATCH_ADDR + IOP_SCRATCH_SIZE - 1, m_scratchPad, 0x06);
66 	m_cpu.m_pMemoryMap->InsertWriteMap(HW_REG_BEGIN, HW_REG_END, std::bind(&CSubSystem::WriteIoRegister, this, std::placeholders::_1, std::placeholders::_2), 0x07);
67 
68 	//Instruction memory map
69 	m_cpu.m_pMemoryMap->InsertInstructionMap((0 * IOP_RAM_SIZE), (0 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x01);
70 	m_cpu.m_pMemoryMap->InsertInstructionMap((1 * IOP_RAM_SIZE), (1 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x02);
71 	m_cpu.m_pMemoryMap->InsertInstructionMap((2 * IOP_RAM_SIZE), (2 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x03);
72 	m_cpu.m_pMemoryMap->InsertInstructionMap((3 * IOP_RAM_SIZE), (3 * IOP_RAM_SIZE) + IOP_RAM_SIZE - 1, m_ram, 0x04);
73 
74 	m_cpu.m_pArch = &m_cpuArch;
75 	m_cpu.m_pCOP[0] = &m_copScu;
76 	m_cpu.m_pAddrTranslator = &CMIPS::TranslateAddress64;
77 
78 	m_dmac.SetReceiveFunction(CDmac::CHANNEL_SPU0, std::bind(&CSpuBase::ReceiveDma, &m_spuCore0, PLACEHOLDER_1, PLACEHOLDER_2, PLACEHOLDER_3));
79 	m_dmac.SetReceiveFunction(CDmac::CHANNEL_SPU1, std::bind(&CSpuBase::ReceiveDma, &m_spuCore1, PLACEHOLDER_1, PLACEHOLDER_2, PLACEHOLDER_3));
80 	m_dmac.SetReceiveFunction(CDmac::CHANNEL_DEV9, std::bind(&CSpeed::ReceiveDma, &m_speed, PLACEHOLDER_1, PLACEHOLDER_2, PLACEHOLDER_3));
81 	m_dmac.SetReceiveFunction(CDmac::CHANNEL_SIO2in, std::bind(&CSio2::ReceiveDmaIn, &m_sio2, PLACEHOLDER_1, PLACEHOLDER_2, PLACEHOLDER_3));
82 	m_dmac.SetReceiveFunction(CDmac::CHANNEL_SIO2out, std::bind(&CSio2::ReceiveDmaOut, &m_sio2, PLACEHOLDER_1, PLACEHOLDER_2, PLACEHOLDER_3));
83 
84 	SetupPageTable();
85 }
86 
~CSubSystem()87 CSubSystem::~CSubSystem()
88 {
89 	m_bios.reset();
90 	delete[] m_ram;
91 	delete[] m_scratchPad;
92 	delete[] m_spuRam;
93 }
94 
NotifyVBlankStart()95 void CSubSystem::NotifyVBlankStart()
96 {
97 	m_bios->NotifyVBlankStart();
98 	m_intc.AssertLine(Iop::CIntc::LINE_VBLANK);
99 }
100 
NotifyVBlankEnd()101 void CSubSystem::NotifyVBlankEnd()
102 {
103 	m_bios->NotifyVBlankEnd();
104 	m_intc.AssertLine(Iop::CIntc::LINE_EVBLANK);
105 }
106 
SaveState(Framework::CZipArchiveWriter & archive)107 void CSubSystem::SaveState(Framework::CZipArchiveWriter& archive)
108 {
109 	archive.InsertFile(new CMemoryStateFile(STATE_CPU, &m_cpu.m_State, sizeof(MIPSSTATE)));
110 	archive.InsertFile(new CMemoryStateFile(STATE_RAM, m_ram, IOP_RAM_SIZE));
111 	archive.InsertFile(new CMemoryStateFile(STATE_SCRATCH, m_scratchPad, IOP_SCRATCH_SIZE));
112 	archive.InsertFile(new CMemoryStateFile(STATE_SPURAM, m_spuRam, SPU_RAM_SIZE));
113 	m_intc.SaveState(archive);
114 	m_dmac.SaveState(archive);
115 	m_counters.SaveState(archive);
116 	m_spuCore0.SaveState(archive);
117 	m_spuCore1.SaveState(archive);
118 #ifdef _IOP_EMULATE_MODULES
119 	m_sio2.SaveState(archive);
120 #endif
121 	m_bios->SaveState(archive);
122 }
123 
LoadState(Framework::CZipArchiveReader & archive)124 void CSubSystem::LoadState(Framework::CZipArchiveReader& archive)
125 {
126 	archive.BeginReadFile(STATE_CPU)->Read(&m_cpu.m_State, sizeof(MIPSSTATE));
127 	archive.BeginReadFile(STATE_RAM)->Read(m_ram, IOP_RAM_SIZE);
128 	archive.BeginReadFile(STATE_SCRATCH)->Read(m_scratchPad, IOP_SCRATCH_SIZE);
129 	archive.BeginReadFile(STATE_SPURAM)->Read(m_spuRam, SPU_RAM_SIZE);
130 	m_intc.LoadState(archive);
131 	m_dmac.LoadState(archive);
132 	m_counters.LoadState(archive);
133 	m_spuCore0.LoadState(archive);
134 	m_spuCore1.LoadState(archive);
135 #ifdef _IOP_EMULATE_MODULES
136 	m_sio2.LoadState(archive);
137 #endif
138 	m_bios->LoadState(archive);
139 }
140 
Reset()141 void CSubSystem::Reset()
142 {
143 	memset(m_ram, 0, IOP_RAM_SIZE);
144 	memset(m_scratchPad, 0, IOP_SCRATCH_SIZE);
145 	memset(m_spuRam, 0, SPU_RAM_SIZE);
146 	m_cpu.Reset();
147 	m_cpu.m_executor->Reset();
148 	m_cpu.m_analysis->Clear();
149 	m_spuCore0.Reset();
150 	m_spuCore1.Reset();
151 	m_spu.Reset();
152 	m_spu2.Reset();
153 #ifdef _IOP_EMULATE_MODULES
154 	m_sio2.Reset();
155 #endif
156 	m_speed.Reset();
157 	m_counters.Reset();
158 	m_dmac.Reset();
159 	m_intc.Reset();
160 
161 	m_cpu.m_Comments.RemoveTags();
162 	m_cpu.m_Functions.RemoveTags();
163 
164 	m_dmaUpdateTicks = 0;
165 }
166 
SetupPageTable()167 void CSubSystem::SetupPageTable()
168 {
169 	for(uint32 i = 0; i < 2; i++)
170 	{
171 		uint32 addressBit = (i == 0) ? 0 : 0x80000000;
172 
173 		m_cpu.MapPages(addressBit | (PS2::IOP_RAM_SIZE * 0), PS2::IOP_RAM_SIZE, m_ram);
174 		m_cpu.MapPages(addressBit | (PS2::IOP_RAM_SIZE * 1), PS2::IOP_RAM_SIZE, m_ram);
175 		m_cpu.MapPages(addressBit | (PS2::IOP_RAM_SIZE * 2), PS2::IOP_RAM_SIZE, m_ram);
176 		m_cpu.MapPages(addressBit | (PS2::IOP_RAM_SIZE * 3), PS2::IOP_RAM_SIZE, m_ram);
177 
178 		m_cpu.MapPages(addressBit | PS2::IOP_SCRATCH_ADDR, PS2::IOP_SCRATCH_SIZE, m_scratchPad);
179 	}
180 }
181 
ReadIoRegister(uint32 address)182 uint32 CSubSystem::ReadIoRegister(uint32 address)
183 {
184 	if(address == 0x1F801814)
185 	{
186 		return 0x14802000;
187 	}
188 	else if(address >= CSpu::SPU_BEGIN && address <= CSpu::SPU_END)
189 	{
190 		return m_spu.ReadRegister(address);
191 	}
192 	else if(
193 	    (address >= CDmac::DMAC_ZONE1_START && address <= CDmac::DMAC_ZONE1_END) ||
194 	    (address >= CDmac::DMAC_ZONE2_START && address <= CDmac::DMAC_ZONE2_END) ||
195 	    (address >= CDmac::DMAC_ZONE3_START && address <= CDmac::DMAC_ZONE3_END))
196 	{
197 		return m_dmac.ReadRegister(address);
198 	}
199 	else if(address >= CIntc::ADDR_BEGIN && address <= CIntc::ADDR_END)
200 	{
201 		return m_intc.ReadRegister(address);
202 	}
203 	else if(
204 	    (address >= CRootCounters::ADDR_BEGIN1 && address <= CRootCounters::ADDR_END1) ||
205 	    (address >= CRootCounters::ADDR_BEGIN2 && address <= CRootCounters::ADDR_END2))
206 	{
207 		return m_counters.ReadRegister(address);
208 	}
209 #ifdef _IOP_EMULATE_MODULES
210 	else if(address >= CSio2::ADDR_BEGIN && address <= CSio2::ADDR_END)
211 	{
212 		return m_sio2.ReadRegister(address);
213 	}
214 #endif
215 	else if(address >= CSpu2::REGS_BEGIN && address <= CSpu2::REGS_END)
216 	{
217 		return m_spu2.ReadRegister(address);
218 	}
219 	else if((address >= 0x1F801000 && address <= 0x1F801020) || (address >= 0x1F801400 && address <= 0x1F801420))
220 	{
221 		CLog::GetInstance().Print(LOG_NAME, "Reading from SSBUS.\r\n");
222 	}
223 	else if(address >= CDev9::ADDR_BEGIN && address <= CDev9::ADDR_END)
224 	{
225 		return m_dev9.ReadRegister(address);
226 	}
227 	else if(address >= SPEED_REG_BEGIN && address <= SPEED_REG_END)
228 	{
229 		return m_speed.ReadRegister(address);
230 	}
231 	else if(address >= 0x1F808400 && address <= 0x1F808500)
232 	{
233 		//iLink (aka Firewire) stuff
234 		return 0x08;
235 	}
236 	else
237 	{
238 		CLog::GetInstance().Print(LOG_NAME, "Reading an unknown hardware register (0x%08X).\r\n", address);
239 	}
240 	return 0;
241 }
242 
WriteIoRegister(uint32 address,uint32 value)243 uint32 CSubSystem::WriteIoRegister(uint32 address, uint32 value)
244 {
245 	if(address >= CSpu::SPU_BEGIN && address <= CSpu::SPU_END)
246 	{
247 		m_spu.WriteRegister(address, static_cast<uint16>(value));
248 	}
249 	else if(
250 	    (address >= CDmac::DMAC_ZONE1_START && address <= CDmac::DMAC_ZONE1_END) ||
251 	    (address >= CDmac::DMAC_ZONE2_START && address <= CDmac::DMAC_ZONE2_END) ||
252 	    (address >= CDmac::DMAC_ZONE3_START && address <= CDmac::DMAC_ZONE3_END))
253 	{
254 		m_dmac.WriteRegister(address, value);
255 	}
256 	else if(address >= CIntc::ADDR_BEGIN && address <= CIntc::ADDR_END)
257 	{
258 		m_intc.WriteRegister(address, value);
259 	}
260 	else if(
261 	    (address >= CRootCounters::ADDR_BEGIN1 && address <= CRootCounters::ADDR_END1) ||
262 	    (address >= CRootCounters::ADDR_BEGIN2 && address <= CRootCounters::ADDR_END2))
263 	{
264 		m_counters.WriteRegister(address, value);
265 	}
266 #ifdef _IOP_EMULATE_MODULES
267 	else if(address >= CSio2::ADDR_BEGIN && address <= CSio2::ADDR_END)
268 	{
269 		m_sio2.WriteRegister(address, value);
270 	}
271 #endif
272 	else if(address >= CSpu2::REGS_BEGIN && address <= CSpu2::REGS_END)
273 	{
274 		return m_spu2.WriteRegister(address, value);
275 	}
276 	else if((address >= 0x1F801000 && address <= 0x1F801020) || (address >= 0x1F801400 && address <= 0x1F801420))
277 	{
278 		CLog::GetInstance().Print(LOG_NAME, "Writing to SSBUS (0x%08X).\r\n", value);
279 	}
280 	else if(address >= CDev9::ADDR_BEGIN && address <= CDev9::ADDR_END)
281 	{
282 		m_dev9.WriteRegister(address, value);
283 	}
284 	else if(address >= SPEED_REG_BEGIN && address <= SPEED_REG_END)
285 	{
286 		m_speed.WriteRegister(address, value);
287 	}
288 	else
289 	{
290 		CLog::GetInstance().Warn(LOG_NAME, "Writing to an unknown hardware register (0x%08X, 0x%08X).\r\n", address, value);
291 	}
292 
293 	if(
294 	    m_intc.HasPendingInterrupt() &&
295 	    (m_cpu.m_State.nHasException == MIPS_EXCEPTION_NONE) &&
296 	    ((m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_IE) == CMIPS::STATUS_IE))
297 	{
298 		m_cpu.m_State.nHasException = MIPS_EXCEPTION_CHECKPENDINGINT;
299 	}
300 
301 	return 0;
302 }
303 
CheckPendingInterrupts()304 void CSubSystem::CheckPendingInterrupts()
305 {
306 	if(!m_cpu.m_State.nHasException)
307 	{
308 		if(m_intc.HasPendingInterrupt())
309 		{
310 			m_bios->HandleInterrupt();
311 		}
312 	}
313 }
314 
IsCpuIdle()315 bool CSubSystem::IsCpuIdle()
316 {
317 	return m_bios->IsIdle();
318 }
319 
CountTicks(int ticks)320 void CSubSystem::CountTicks(int ticks)
321 {
322 	static const int g_dmaUpdateDelay = 10000;
323 	m_counters.Update(ticks);
324 	m_speed.CountTicks(ticks);
325 	m_bios->CountTicks(ticks);
326 	m_dmaUpdateTicks += ticks;
327 	if(m_dmaUpdateTicks >= g_dmaUpdateDelay)
328 	{
329 		m_dmac.ResumeDma(4);
330 		m_dmac.ResumeDma(8);
331 		m_dmaUpdateTicks -= g_dmaUpdateDelay;
332 	}
333 	{
334 		bool irqPending = false;
335 		irqPending |= m_spuCore0.GetIrqPending();
336 		irqPending |= m_spuCore1.GetIrqPending();
337 		if(irqPending)
338 		{
339 			m_intc.AssertLine(CIntc::LINE_SPU2);
340 		}
341 		else
342 		{
343 			m_intc.ClearLine(CIntc::LINE_SPU2);
344 		}
345 	}
346 }
347 
ExecuteCpu(int quota)348 int CSubSystem::ExecuteCpu(int quota)
349 {
350 	int executed = 0;
351 	CheckPendingInterrupts();
352 	if(!m_cpu.m_State.nHasException)
353 	{
354 		executed = (quota - m_cpu.m_executor->Execute(quota));
355 	}
356 	if(m_cpu.m_State.nHasException)
357 	{
358 		switch(m_cpu.m_State.nHasException)
359 		{
360 		case MIPS_EXCEPTION_SYSCALL:
361 			m_bios->HandleException();
362 			assert(m_cpu.m_State.nHasException == MIPS_EXCEPTION_NONE);
363 			break;
364 		case MIPS_EXCEPTION_CHECKPENDINGINT:
365 		{
366 			m_cpu.m_State.nHasException = MIPS_EXCEPTION_NONE;
367 			CheckPendingInterrupts();
368 			//Needs to be cleared again because exception flag might be set by BIOS interrupt handler
369 			m_cpu.m_State.nHasException = MIPS_EXCEPTION_NONE;
370 		}
371 		break;
372 		}
373 		assert(m_cpu.m_State.nHasException == MIPS_EXCEPTION_NONE);
374 	}
375 	return executed;
376 }
377