1 #include <cstring>
2 #include "PsxBios.h"
3 #include "COP_SCU.h"
4 #include "Log.h"
5 #include "iop/Iop_Intc.h"
6 #include "MIPSAssembler.h"
7 #include "xml/Node.h"
8 #include "xml/Writer.h"
9 
10 #define LOG_NAME ("psxbios")
11 #define SC_PARAM0 (CMIPS::A0)
12 #define SC_PARAM1 (CMIPS::A1)
13 #define SC_PARAM2 (CMIPS::A2)
14 #define SC_PARAM3 (CMIPS::A3)
15 #define SC_RETURN (CMIPS::V0)
16 
17 using namespace Iop;
18 
19 #define PCB_TABLE_ADDRESS (0x0108)
20 #define TCB_TABLE_ADDRESS (0x0110)
21 #define EXITFROMEXCEPTION_STATE_ADDR (0x0200)
22 #define SYSHEAP_POINTER_ADDR (0x0204)
23 #define INTR_HANDLER (0x1000)
24 #define EVENT_CHECKER (0x1200)
25 #define EVENTS_BEGIN (0x3000)
26 #define EVENTS_SIZE (sizeof(CPsxBios::EVENT) * CPsxBios::MAX_EVENT)
27 #define B0TABLE_BEGIN (EVENTS_BEGIN + EVENTS_SIZE)
28 #define B0TABLE_SIZE (0x5D * 4)
29 #define C0TABLE_BEGIN (B0TABLE_BEGIN + B0TABLE_SIZE)
30 #define C0TABLE_SIZE (0x1C * 4)
31 #define C0_EXCEPTIONHANDLER_BEGIN (C0TABLE_BEGIN + C0TABLE_SIZE)
32 #define C0_EXCEPTIONHANDLER_SIZE (0x1000)
33 #define HEAP_START (C0_EXCEPTIONHANDLER_BEGIN + C0_EXCEPTIONHANDLER_SIZE)
34 #define HEAP_SIZE (0x2000)
35 #define BIOS_MEMORY_END (HEAP_START + HEAP_SIZE)
36 
CPsxBios(CMIPS & cpu,uint8 * ram,uint32 ramSize)37 CPsxBios::CPsxBios(CMIPS& cpu, uint8* ram, uint32 ramSize)
38     : m_cpu(cpu)
39     , m_ram(ram)
40     , m_ramSize(ramSize)
41     , m_exitFromExceptionStateAddr(reinterpret_cast<uint32*>(m_ram + EXITFROMEXCEPTION_STATE_ADDR))
42     , m_sysHeapPointerAddr(reinterpret_cast<uint32*>(m_ram + SYSHEAP_POINTER_ADDR))
43     , m_events(reinterpret_cast<EVENT*>(&m_ram[EVENTS_BEGIN]), 1, MAX_EVENT)
44 {
45 	static_assert(BIOS_MEMORY_END <= 0x10000, "BIOS memory size must not exceed 64k");
46 	Reset();
47 }
48 
Reset()49 void CPsxBios::Reset()
50 {
51 	uint32 syscallAddress[3] = {0xA0, 0xB0, 0xC0};
52 
53 	for(unsigned int i = 0; i < 3; i++)
54 	{
55 		CMIPSAssembler assembler(reinterpret_cast<uint32*>(m_ram + syscallAddress[i]));
56 		assembler.SYSCALL();
57 		assembler.JR(CMIPS::RA);
58 		assembler.NOP();
59 	}
60 
61 	//Assembly a dummy JR RA at 0 because Vagrant Story jumps at 0
62 	{
63 		CMIPSAssembler assembler(reinterpret_cast<uint32*>(m_ram + 0x0));
64 		assembler.LUI(CMIPS::K0, 0x0000); //This is required by Xenogears' SFX PSFs
65 		assembler.JR(CMIPS::RA);
66 		assembler.NOP();
67 	}
68 
69 	m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE;
70 
71 	AssembleEventChecker();
72 	AssembleInterruptHandler();
73 
74 	//Setup B0 table
75 	{
76 		uint32* table = reinterpret_cast<uint32*>(&m_ram[B0TABLE_BEGIN]);
77 		table[0x5B] = C0_EXCEPTIONHANDLER_BEGIN;
78 	}
79 
80 	//Setup C0 table
81 	{
82 		uint32* table = reinterpret_cast<uint32*>(&m_ram[C0TABLE_BEGIN]);
83 		table[0x06] = C0_EXCEPTIONHANDLER_BEGIN;
84 	}
85 
86 	//Assemble dummy exception handler
87 	{
88 		//0x70 = LUI
89 		//0x74 = ADDIU
90 		//Chrono Cross will overwrite the stuff present at the address that would be computed
91 		//by these two instructions and use something else
92 		CMIPSAssembler assembler(reinterpret_cast<uint32*>(m_ram + C0_EXCEPTIONHANDLER_BEGIN + 0x70));
93 		assembler.LI(CMIPS::T0, C0_EXCEPTIONHANDLER_BEGIN);
94 	}
95 
96 	m_exitFromExceptionStateAddr = 0;
97 	m_sysHeapPointerAddr = HEAP_START;
98 
99 	memset(m_events.GetBase(), 0, EVENTS_SIZE);
100 
101 	//Allocate process control block
102 	{
103 		auto cbTable = reinterpret_cast<CB_TABLE*>(m_ram + PCB_TABLE_ADDRESS);
104 		cbTable->address = AllocateSysMemory(sizeof(PROCESS));
105 		cbTable->size = sizeof(PROCESS);
106 	}
107 
108 	//Allocate thread control block
109 	{
110 		static const uint32 maxTcb = 4;
111 		auto cbTable = reinterpret_cast<CB_TABLE*>(m_ram + TCB_TABLE_ADDRESS);
112 		cbTable->address = AllocateSysMemory(sizeof(THREAD) * maxTcb);
113 		cbTable->size = sizeof(THREAD) * maxTcb;
114 	}
115 
116 	//Setup main thread
117 	{
118 		auto process = GetProcess();
119 		auto threadCbAddr =
120 		    [&]() {
121 			    auto cbTable = reinterpret_cast<CB_TABLE*>(m_ram + TCB_TABLE_ADDRESS);
122 			    auto threadCb = reinterpret_cast<THREAD*>(m_ram + cbTable->address);
123 			    threadCb->status = THREAD_STATUS_ALLOCATED;
124 			    return cbTable->address;
125 		    }();
126 		process->currentThreadControlBlockAddr = threadCbAddr;
127 	}
128 }
129 
LoadExe(const uint8 * exe)130 void CPsxBios::LoadExe(const uint8* exe)
131 {
132 	auto exeHeader = reinterpret_cast<const EXEHEADER*>(exe);
133 	if(strncmp(reinterpret_cast<const char*>(exeHeader->id), "PS-X EXE", 8))
134 	{
135 		throw std::runtime_error("Invalid PSX executable.");
136 	}
137 
138 	m_cpu.m_State.nPC = exeHeader->pc0 & 0x1FFFFFFF;
139 	m_cpu.m_State.nGPR[CMIPS::GP].nD0 = exeHeader->gp0;
140 	m_cpu.m_State.nGPR[CMIPS::SP].nD0 = exeHeader->stackAddr;
141 
142 	exe += 0x800;
143 	if(exeHeader->textAddr != 0)
144 	{
145 		uint32 realAddr = exeHeader->textAddr & 0x1FFFFFFF;
146 		assert(realAddr + exeHeader->textSize <= m_ramSize);
147 		memcpy(m_ram + realAddr, exe, exeHeader->textSize);
148 		exe += exeHeader->textSize;
149 
150 #ifdef DEBUGGER_INCLUDED
151 		m_cpu.m_analysis->Analyse(realAddr, realAddr + exeHeader->textSize, m_cpu.m_State.nPC);
152 #endif
153 	}
154 }
155 
SaveState(Framework::CZipArchiveWriter & archive)156 void CPsxBios::SaveState(Framework::CZipArchiveWriter& archive)
157 {
158 }
159 
LoadState(Framework::CZipArchiveReader & archive)160 void CPsxBios::LoadState(Framework::CZipArchiveReader& archive)
161 {
162 }
163 
NotifyVBlankStart()164 void CPsxBios::NotifyVBlankStart()
165 {
166 }
167 
NotifyVBlankEnd()168 void CPsxBios::NotifyVBlankEnd()
169 {
170 }
171 
IsIdle()172 bool CPsxBios::IsIdle()
173 {
174 	return false;
175 }
176 
177 #ifdef DEBUGGER_INCLUDED
178 
LoadDebugTags(Framework::Xml::CNode * root)179 void CPsxBios::LoadDebugTags(Framework::Xml::CNode* root)
180 {
181 }
182 
SaveDebugTags(Framework::Xml::CNode * root)183 void CPsxBios::SaveDebugTags(Framework::Xml::CNode* root)
184 {
185 }
186 
GetModulesDebugInfo() const187 BiosDebugModuleInfoArray CPsxBios::GetModulesDebugInfo() const
188 {
189 	return BiosDebugModuleInfoArray();
190 }
191 
GetThreadsDebugInfo() const192 BiosDebugThreadInfoArray CPsxBios::GetThreadsDebugInfo() const
193 {
194 	return BiosDebugThreadInfoArray();
195 }
196 
197 #endif
198 
CountTicks(uint32 ticks)199 void CPsxBios::CountTicks(uint32 ticks)
200 {
201 }
202 
AssembleEventChecker()203 void CPsxBios::AssembleEventChecker()
204 {
205 	CMIPSAssembler assembler(reinterpret_cast<uint32*>(m_ram + EVENT_CHECKER));
206 	CMIPSAssembler::LABEL checkEventLabel = assembler.CreateLabel();
207 	CMIPSAssembler::LABEL doneEventLabel = assembler.CreateLabel();
208 
209 	unsigned int currentEvent = CMIPS::S0;
210 	unsigned int eventMax = CMIPS::S1;
211 	unsigned int eventToCheck = CMIPS::S2;
212 	unsigned int needClearInt = CMIPS::S3;
213 	int stackAlloc = 5 * 4;
214 
215 	//prolog
216 	assembler.ADDIU(CMIPS::SP, CMIPS::SP, -stackAlloc);
217 	assembler.SW(CMIPS::RA, 0x00, CMIPS::SP);
218 	assembler.SW(CMIPS::S0, 0x04, CMIPS::SP);
219 	assembler.SW(CMIPS::S1, 0x08, CMIPS::SP);
220 	assembler.SW(CMIPS::S2, 0x0C, CMIPS::SP);
221 	assembler.SW(CMIPS::S3, 0x10, CMIPS::SP);
222 
223 	assembler.LI(currentEvent, EVENTS_BEGIN);
224 	assembler.LI(eventMax, EVENTS_BEGIN + EVENTS_SIZE);
225 	assembler.MOV(eventToCheck, CMIPS::A0);
226 	assembler.ADDU(needClearInt, CMIPS::R0, CMIPS::R0);
227 
228 	//checkEvent
229 	{
230 		assembler.MarkLabel(checkEventLabel);
231 
232 		//check if valid
233 		assembler.LW(CMIPS::T0, offsetof(EVENT, isValid), currentEvent);
234 		assembler.BEQ(CMIPS::T0, CMIPS::R0, doneEventLabel);
235 		assembler.NOP();
236 
237 		//check if good event class
238 		assembler.LW(CMIPS::T0, offsetof(EVENT, classId), currentEvent);
239 		assembler.BNE(CMIPS::T0, eventToCheck, doneEventLabel);
240 		assembler.NOP();
241 
242 		//Tell that we need to clear interrupt later (experimental)
243 		assembler.ADDIU(needClearInt, CMIPS::R0, 1);
244 
245 		//check if enabled
246 		assembler.LW(CMIPS::T0, offsetof(EVENT, enabled), currentEvent);
247 		assembler.BEQ(CMIPS::T0, CMIPS::R0, doneEventLabel);
248 		assembler.NOP();
249 
250 		//Start handler if present
251 		assembler.LW(CMIPS::T0, offsetof(EVENT, func), currentEvent);
252 		assembler.BEQ(CMIPS::T0, CMIPS::R0, doneEventLabel);
253 		assembler.NOP();
254 
255 		assembler.JALR(CMIPS::T0);
256 		assembler.NOP();
257 	}
258 
259 	//doneEvent
260 	assembler.MarkLabel(doneEventLabel);
261 	assembler.ADDIU(currentEvent, currentEvent, sizeof(EVENT));
262 	assembler.BNE(currentEvent, eventMax, checkEventLabel);
263 	assembler.NOP();
264 
265 	//Result
266 	assembler.ADDU(CMIPS::V0, needClearInt, CMIPS::R0);
267 
268 	//epilog
269 	assembler.LW(CMIPS::RA, 0x00, CMIPS::SP);
270 	assembler.LW(CMIPS::S0, 0x04, CMIPS::SP);
271 	assembler.LW(CMIPS::S1, 0x08, CMIPS::SP);
272 	assembler.LW(CMIPS::S2, 0x0C, CMIPS::SP);
273 	assembler.LW(CMIPS::S3, 0x10, CMIPS::SP);
274 	assembler.ADDIU(CMIPS::SP, CMIPS::SP, stackAlloc);
275 
276 	assembler.JR(CMIPS::RA);
277 	assembler.NOP();
278 }
279 
AssembleInterruptHandler()280 void CPsxBios::AssembleInterruptHandler()
281 {
282 	//Assemble interrupt handler
283 	CMIPSAssembler assembler(reinterpret_cast<uint32*>(m_ram + INTR_HANDLER));
284 	CMIPSAssembler::LABEL skipRootCounter2EventLabel = assembler.CreateLabel();
285 	CMIPSAssembler::LABEL returnExceptionLabel = assembler.CreateLabel();
286 	CMIPSAssembler::LABEL clearIntcCause = assembler.CreateLabel();
287 
288 	//Get cause
289 	unsigned int cause = CMIPS::S3;
290 	assembler.LI(CMIPS::T0, CIntc::STATUS0);
291 	assembler.LW(CMIPS::T0, 0, CMIPS::T0);
292 	assembler.LI(CMIPS::T1, CIntc::MASK0);
293 	assembler.LW(CMIPS::T1, 0, CMIPS::T1);
294 	assembler.AND(cause, CMIPS::T0, CMIPS::T1);
295 
296 	//Check if cause is root counter 2
297 	assembler.ANDI(CMIPS::T0, cause, 0x40);
298 	assembler.BEQ(CMIPS::T0, CMIPS::R0, skipRootCounter2EventLabel);
299 	assembler.NOP();
300 
301 	assembler.LI(CMIPS::A0, EVENT_ID_RCNT2);
302 	assembler.JAL(EVENT_CHECKER);
303 	assembler.NOP();
304 
305 	assembler.BEQ(CMIPS::V0, CMIPS::R0, skipRootCounter2EventLabel);
306 	assembler.NOP();
307 
308 	//Clear root counter 2 cause
309 	assembler.LI(CMIPS::T0, CIntc::STATUS0);
310 	assembler.LI(CMIPS::T1, ~0x40);
311 	assembler.SW(CMIPS::T1, 0, CMIPS::T0);
312 
313 	assembler.MarkLabel(skipRootCounter2EventLabel);
314 
315 	//checkIntHook
316 	assembler.LI(CMIPS::T0, EXITFROMEXCEPTION_STATE_ADDR);
317 	assembler.LW(CMIPS::T0, 0, CMIPS::T0);
318 	assembler.BEQ(CMIPS::T0, CMIPS::R0, clearIntcCause);
319 	assembler.NOP();
320 
321 	//callIntHook
322 	assembler.ADDIU(CMIPS::A0, CMIPS::T0, CMIPS::R0);
323 	assembler.ADDIU(CMIPS::A1, CMIPS::R0, CMIPS::R0);
324 	assembler.ADDIU(CMIPS::T0, CMIPS::R0, 0xA0);
325 	assembler.ADDIU(CMIPS::T1, CMIPS::R0, 0x14);
326 	assembler.JR(CMIPS::T0);
327 	assembler.NOP();
328 
329 	assembler.BEQ(CMIPS::R0, CMIPS::R0, returnExceptionLabel);
330 	assembler.NOP();
331 
332 	//Clear any interrupt that might have triggered this exception handler (to prevent infinite loop)
333 	assembler.MarkLabel(clearIntcCause);
334 	assembler.LI(CMIPS::T0, CIntc::STATUS0);
335 	assembler.NOR(CMIPS::T1, CMIPS::R0, cause);
336 	assembler.SW(CMIPS::T1, 0, CMIPS::T0);
337 
338 	//ReturnFromException
339 	assembler.MarkLabel(returnExceptionLabel);
340 	assembler.ADDIU(CMIPS::T0, CMIPS::R0, 0xB0);
341 	assembler.ADDIU(CMIPS::T1, CMIPS::R0, 0x17);
342 	assembler.JR(CMIPS::T0);
343 	assembler.NOP();
344 }
345 
LongJump(uint32 bufferAddress,uint32 value)346 void CPsxBios::LongJump(uint32 bufferAddress, uint32 value)
347 {
348 	bufferAddress = m_cpu.m_pAddrTranslator(&m_cpu, bufferAddress);
349 	m_cpu.m_State.nGPR[CMIPS::RA].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x00));
350 	m_cpu.m_State.nGPR[CMIPS::SP].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x04));
351 	m_cpu.m_State.nGPR[CMIPS::FP].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x08));
352 	m_cpu.m_State.nGPR[CMIPS::S0].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x0C));
353 	m_cpu.m_State.nGPR[CMIPS::S1].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x10));
354 	m_cpu.m_State.nGPR[CMIPS::S2].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x14));
355 	m_cpu.m_State.nGPR[CMIPS::S3].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x18));
356 	m_cpu.m_State.nGPR[CMIPS::S4].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x1C));
357 	m_cpu.m_State.nGPR[CMIPS::S5].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x20));
358 	m_cpu.m_State.nGPR[CMIPS::S6].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x24));
359 	m_cpu.m_State.nGPR[CMIPS::S7].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x28));
360 	m_cpu.m_State.nGPR[CMIPS::GP].nD0 = static_cast<int32>(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x2C));
361 	m_cpu.m_State.nGPR[CMIPS::V0].nD0 = value == 0 ? 1 : value;
362 }
363 
GetProcess()364 CPsxBios::PROCESS* CPsxBios::GetProcess()
365 {
366 	auto processCbTable = reinterpret_cast<CB_TABLE*>(m_ram + PCB_TABLE_ADDRESS);
367 	assert(processCbTable->address != 0);
368 	assert(processCbTable->size != 0);
369 
370 	return reinterpret_cast<PROCESS*>(m_ram + processCbTable->address);
371 }
372 
SaveCpuState()373 void CPsxBios::SaveCpuState()
374 {
375 	auto process = GetProcess();
376 	assert(process->currentThreadControlBlockAddr != 0);
377 
378 	auto thread = reinterpret_cast<THREAD*>(m_ram + process->currentThreadControlBlockAddr);
379 	assert(thread->status == THREAD_STATUS_ALLOCATED);
380 	thread->pc = m_cpu.m_State.nPC;
381 	for(uint32 i = 0; i < 32; i++)
382 	{
383 		if(i == CMIPS::R0) continue;
384 		if(i == CMIPS::K0) continue;
385 		if(i == CMIPS::K1) continue;
386 		thread->gpr[i] = m_cpu.m_State.nGPR[i].nV0;
387 	}
388 	thread->sr = m_cpu.m_State.nCOP0[CCOP_SCU::STATUS];
389 	thread->sr &= ~(CMIPS::STATUS_EXL | CMIPS::STATUS_ERL);
390 }
391 
LoadCpuState()392 void CPsxBios::LoadCpuState()
393 {
394 	auto process = GetProcess();
395 	assert(process->currentThreadControlBlockAddr != 0);
396 
397 	auto thread = reinterpret_cast<THREAD*>(m_ram + process->currentThreadControlBlockAddr);
398 	assert(thread->status == THREAD_STATUS_ALLOCATED);
399 	m_cpu.m_State.nPC = thread->pc;
400 	for(uint32 i = 0; i < 32; i++)
401 	{
402 		if(i == CMIPS::R0) continue;
403 		if(i == CMIPS::K0) continue;
404 		if(i == CMIPS::K1) continue;
405 		m_cpu.m_State.nGPR[i].nV0 = thread->gpr[i];
406 	}
407 	m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] = thread->sr;
408 }
409 
AllocateSysMemory(uint32 size)410 uint32 CPsxBios::AllocateSysMemory(uint32 size)
411 {
412 	assert((m_sysHeapPointerAddr + size) <= (HEAP_START + HEAP_SIZE));
413 	uint32 result = m_sysHeapPointerAddr;
414 	m_sysHeapPointerAddr += size;
415 	return result;
416 }
417 
HandleInterrupt()418 void CPsxBios::HandleInterrupt()
419 {
420 	if(m_cpu.GenerateInterrupt(m_cpu.m_State.nPC))
421 	{
422 		SaveCpuState();
423 		uint32 status = m_cpu.m_pMemoryMap->GetWord(CIntc::STATUS0);
424 		uint32 mask = m_cpu.m_pMemoryMap->GetWord(CIntc::MASK0);
425 		uint32 cause = status & mask;
426 		for(unsigned int i = 1; i <= MAX_EVENT; i++)
427 		{
428 			EVENT* eventPtr = m_events[i];
429 			if(eventPtr == NULL) continue;
430 			if((cause & (1 << CIntc::LINE_DMAC)) && eventPtr->classId == 0xF0000009)
431 			{
432 				eventPtr->fired = 1;
433 			}
434 		}
435 		m_cpu.m_State.nPC = INTR_HANDLER;
436 	}
437 }
438 
HandleException()439 void CPsxBios::HandleException()
440 {
441 	assert(m_cpu.m_State.nHasException);
442 	uint32 searchAddress = m_cpu.m_State.nCOP0[CCOP_SCU::EPC];
443 	uint32 callInstruction = m_cpu.m_pMemoryMap->GetWord(searchAddress);
444 	if(callInstruction != 0x0000000C)
445 	{
446 		throw std::runtime_error("Not a SYSCALL.");
447 	}
448 #ifdef _DEBUG
449 	DisassembleSyscall(searchAddress);
450 #endif
451 	if(searchAddress == 0xA0)
452 	{
453 		ProcessSubFunction(m_handlerA0, MAX_HANDLER_A0);
454 	}
455 	else if(searchAddress == 0xB0)
456 	{
457 		ProcessSubFunction(m_handlerB0, MAX_HANDLER_B0);
458 	}
459 	else if(searchAddress == 0xC0)
460 	{
461 		ProcessSubFunction(m_handlerC0, MAX_HANDLER_C0);
462 	}
463 	else
464 	{
465 		uint32 functionId = m_cpu.m_State.nGPR[CMIPS::A0].nV0;
466 		switch(functionId)
467 		{
468 		case 0x01:
469 			sc_EnterCriticalSection();
470 			break;
471 		case 0x02:
472 			sc_ExitCriticalSection();
473 			break;
474 		default:
475 			sc_Illegal();
476 			break;
477 		}
478 	}
479 	m_cpu.m_State.nHasException = 0;
480 }
481 
ProcessSubFunction(SyscallHandler * handlerTable,unsigned int handlerTableLength)482 void CPsxBios::ProcessSubFunction(SyscallHandler* handlerTable, unsigned int handlerTableLength)
483 {
484 	uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0;
485 	if(functionId >= handlerTableLength)
486 	{
487 		sc_Illegal();
488 	}
489 	functionId %= handlerTableLength;
490 	((this)->*(handlerTable[functionId]))();
491 }
492 
DisassembleSyscall(uint32 searchAddress)493 void CPsxBios::DisassembleSyscall(uint32 searchAddress)
494 {
495 	if(searchAddress == 0x00A0)
496 	{
497 		uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0;
498 		switch(functionId)
499 		{
500 		case 0x13:
501 			CLog::GetInstance().Print(LOG_NAME, "setjmp(buffer = 0x%0.8X);\r\n",
502 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
503 			break;
504 		case 0x14:
505 			CLog::GetInstance().Print(LOG_NAME, "longjmp(buffer = 0x%0.8X, value = %i);\r\n",
506 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
507 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
508 			break;
509 		case 0x19:
510 			CLog::GetInstance().Print(LOG_NAME, "strcpy(dst = 0x%0.8X, src = 0x%0.8X);\r\n",
511 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
512 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
513 			break;
514 		case 0x28:
515 			CLog::GetInstance().Print(LOG_NAME, "bzero(address = 0x%0.8X, length = 0x%x);\r\n",
516 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
517 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
518 			break;
519 		case 0x2A:
520 			CLog::GetInstance().Print(LOG_NAME, "memcpy(dst = 0x%0.8X, src = 0x%0.8X, length = 0x%x);\r\n",
521 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
522 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0,
523 			                          m_cpu.m_State.nGPR[SC_PARAM2].nV0);
524 			break;
525 		case 0x2B:
526 			CLog::GetInstance().Print(LOG_NAME, "memset(address = 0x%0.8X, value = 0x%x, length = 0x%x);\r\n",
527 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
528 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0,
529 			                          m_cpu.m_State.nGPR[SC_PARAM2].nV0);
530 			break;
531 		case 0x2F:
532 			CLog::GetInstance().Print(LOG_NAME, "rand();\r\n");
533 			break;
534 		case 0x30:
535 			CLog::GetInstance().Print(LOG_NAME, "srand(seed = %d);\r\n",
536 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
537 			break;
538 		case 0x39:
539 			CLog::GetInstance().Print(LOG_NAME, "InitHeap(block = 0x%0.8X, n = 0x%0.8X);\r\n",
540 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
541 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
542 			break;
543 		case 0x3F:
544 			CLog::GetInstance().Print(LOG_NAME, "printf(fmt = 0x%0.8X);\r\n",
545 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
546 			break;
547 		case 0x44:
548 			CLog::GetInstance().Print(LOG_NAME, "FlushCache();\r\n");
549 			break;
550 		case 0x70:
551 			CLog::GetInstance().Print(LOG_NAME, "_bu_init();\r\n");
552 			break;
553 		case 0x72:
554 			CLog::GetInstance().Print(LOG_NAME, "_96_remove();\r\n");
555 			break;
556 		case 0x9F:
557 			CLog::GetInstance().Print(LOG_NAME, "SetMem(size = %i);\r\n",
558 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
559 			break;
560 		default:
561 			CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered (0xA0, 0x%X).\r\n", functionId);
562 			break;
563 		}
564 	}
565 	else if(searchAddress == 0x00B0)
566 	{
567 		uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0;
568 		switch(functionId)
569 		{
570 		case 0x00:
571 			CLog::GetInstance().Print(LOG_NAME, "SysMalloc(size = 0x%X);\r\n",
572 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
573 			break;
574 		case 0x07:
575 			CLog::GetInstance().Print(LOG_NAME, "DeliverEvent(class = 0x%X, event = 0x%X);\r\n",
576 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
577 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
578 			break;
579 		case 0x08:
580 			CLog::GetInstance().Print(LOG_NAME, "OpenEvent(class = 0x%X, spec = 0x%X, mode = 0x%X, func = 0x%X);\r\n",
581 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
582 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0,
583 			                          m_cpu.m_State.nGPR[SC_PARAM2].nV0,
584 			                          m_cpu.m_State.nGPR[SC_PARAM3].nV0);
585 			break;
586 		case 0x09:
587 			CLog::GetInstance().Print(LOG_NAME, "CloseEvent(event = 0x%X);\r\n",
588 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
589 			break;
590 		case 0x0A:
591 			CLog::GetInstance().Print(LOG_NAME, "WaitEvent(event = 0x%X);\r\n",
592 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
593 			break;
594 		case 0x0B:
595 			CLog::GetInstance().Print(LOG_NAME, "TestEvent(event = 0x%X);\r\n",
596 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
597 			break;
598 		case 0x0C:
599 			CLog::GetInstance().Print(LOG_NAME, "EnableEvent(event = 0x%X);\r\n",
600 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
601 			break;
602 		case 0x0D:
603 			CLog::GetInstance().Print(LOG_NAME, "DisableEvent(event = 0x%X);\r\n",
604 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
605 			break;
606 		case 0x0E:
607 			CLog::GetInstance().Print(LOG_NAME, "OpenThread(pc = 0x%08X, sp = 0x%08X, gp = 0x%08X);\r\n",
608 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
609 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0,
610 			                          m_cpu.m_State.nGPR[SC_PARAM2].nV0);
611 			break;
612 		case 0x10:
613 			CLog::GetInstance().Print(LOG_NAME, "ChangeThread(threadId = 0x%X);\r\n",
614 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
615 			break;
616 		case 0x14:
617 			CLog::GetInstance().Print(LOG_NAME, "StopPAD();\r\n");
618 			break;
619 		case 0x16:
620 			CLog::GetInstance().Print(LOG_NAME, "PAD_dr();\r\n");
621 			break;
622 		case 0x17:
623 			CLog::GetInstance().Print(LOG_NAME, "ReturnFromException();\r\n");
624 			break;
625 		case 0x18:
626 			CLog::GetInstance().Print(LOG_NAME, "SetDefaultExitFromException();\r\n");
627 			break;
628 		case 0x19:
629 			CLog::GetInstance().Print(LOG_NAME, "SetCustomExitFromException(stateAddress = 0x%0.8X);\r\n",
630 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
631 			break;
632 		case 0x3F:
633 			CLog::GetInstance().Print(LOG_NAME, "puts(s = 0x%0.8X);\r\n",
634 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
635 			break;
636 		case 0x4A:
637 			CLog::GetInstance().Print(LOG_NAME, "InitCARD();\r\n");
638 			break;
639 		case 0x4B:
640 			CLog::GetInstance().Print(LOG_NAME, "StartCARD();\r\n");
641 			break;
642 		case 0x56:
643 			CLog::GetInstance().Print(LOG_NAME, "GetC0Table();\r\n");
644 			break;
645 		case 0x57:
646 			CLog::GetInstance().Print(LOG_NAME, "GetB0Table();\r\n");
647 			break;
648 		case 0x5B:
649 			CLog::GetInstance().Print(LOG_NAME, "ChangeClearPad(param = %i);\r\n",
650 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
651 			break;
652 		default:
653 			CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered (0xB0, 0x%X).\r\n", functionId);
654 			break;
655 		}
656 	}
657 	else if(searchAddress == 0x00C0)
658 	{
659 		uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0;
660 		switch(functionId)
661 		{
662 		case 0x00:
663 			CLog::GetInstance().Print(LOG_NAME, "EnqueueTimerAndVblankIrqs(priority = %d);\r\n",
664 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
665 			break;
666 		case 0x01:
667 			CLog::GetInstance().Print(LOG_NAME, "EnqueueSyscallHandler(priority = %d);\r\n",
668 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
669 			break;
670 		case 0x03:
671 			CLog::GetInstance().Print(LOG_NAME, "SysDeqIntRP(index = %i, queue = 0x%X);\r\n",
672 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
673 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
674 			break;
675 		case 0x08:
676 			CLog::GetInstance().Print(LOG_NAME, "SysInitMemory(address = 0x%08X, size = 0x%X);\r\n",
677 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
678 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
679 			break;
680 		case 0x0A:
681 			CLog::GetInstance().Print(LOG_NAME, "ChangeClearRCnt(param0 = %i, param1 = %i);\r\n",
682 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0,
683 			                          m_cpu.m_State.nGPR[SC_PARAM1].nV0);
684 			break;
685 		case 0x0C:
686 			CLog::GetInstance().Print(LOG_NAME, "InitDefInt(priority = %d);\r\n",
687 			                          m_cpu.m_State.nGPR[SC_PARAM0].nV0);
688 			break;
689 		default:
690 			CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered (0xC0, 0x%X).\r\n", functionId);
691 			break;
692 		}
693 	}
694 	else
695 	{
696 		uint32 functionId = m_cpu.m_State.nGPR[CMIPS::A0].nV0;
697 		switch(functionId)
698 		{
699 		case 0x01:
700 			CLog::GetInstance().Print(LOG_NAME, "EnterCriticalSection();\r\n");
701 			break;
702 		case 0x02:
703 			CLog::GetInstance().Print(LOG_NAME, "ExitCriticalSection();\r\n");
704 			break;
705 		default:
706 			CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered.\r\n");
707 			break;
708 		}
709 	}
710 }
711 
712 //A0 - 13
sc_setjmp()713 void CPsxBios::sc_setjmp()
714 {
715 	uint32 bufferAddress = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0);
716 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x00, m_cpu.m_State.nGPR[CMIPS::RA].nV0);
717 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x04, m_cpu.m_State.nGPR[CMIPS::SP].nV0);
718 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x08, m_cpu.m_State.nGPR[CMIPS::FP].nV0);
719 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x0C, m_cpu.m_State.nGPR[CMIPS::S0].nV0);
720 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x10, m_cpu.m_State.nGPR[CMIPS::S1].nV0);
721 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x14, m_cpu.m_State.nGPR[CMIPS::S2].nV0);
722 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x18, m_cpu.m_State.nGPR[CMIPS::S3].nV0);
723 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x1C, m_cpu.m_State.nGPR[CMIPS::S4].nV0);
724 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x20, m_cpu.m_State.nGPR[CMIPS::S5].nV0);
725 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x24, m_cpu.m_State.nGPR[CMIPS::S6].nV0);
726 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x28, m_cpu.m_State.nGPR[CMIPS::S7].nV0);
727 	m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x2C, m_cpu.m_State.nGPR[CMIPS::GP].nV0);
728 	m_cpu.m_State.nGPR[CMIPS::V0].nD0 = 0;
729 }
730 
731 //A0 - 14
sc_longjmp()732 void CPsxBios::sc_longjmp()
733 {
734 	uint32 buffer = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
735 	uint32 value = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
736 	LongJump(buffer, value);
737 }
738 
739 //A0 - 19
sc_strcpy()740 void CPsxBios::sc_strcpy()
741 {
742 	uint32 dst = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0);
743 	uint32 src = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM1].nV0);
744 
745 	strcpy(
746 	    reinterpret_cast<char*>(m_ram + dst),
747 	    reinterpret_cast<char*>(m_ram + src));
748 
749 	m_cpu.m_State.nGPR[SC_RETURN].nV0 = dst;
750 }
751 
752 //A0 - 28
sc_bzero()753 void CPsxBios::sc_bzero()
754 {
755 	uint32 address = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0);
756 	uint32 length = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
757 	if((address + length) > m_ramSize)
758 	{
759 		throw std::exception();
760 	}
761 	memset(m_ram + address, 0, length);
762 }
763 
764 //A0 - 2A
sc_memcpy()765 void CPsxBios::sc_memcpy()
766 {
767 	uint32 dst = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0);
768 	uint32 src = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM1].nV0);
769 	uint32 length = m_cpu.m_State.nGPR[SC_PARAM2].nV0;
770 
771 	memcpy(m_ram + dst, m_ram + src, length);
772 
773 	m_cpu.m_State.nGPR[SC_RETURN].nV0 = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
774 }
775 
776 //A0 - 2B
sc_memset()777 void CPsxBios::sc_memset()
778 {
779 	uint32 address = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0);
780 	uint8 value = static_cast<uint8>(m_cpu.m_State.nGPR[SC_PARAM1].nV0);
781 	uint32 length = m_cpu.m_State.nGPR[SC_PARAM2].nV0;
782 	if((address + length) > m_ramSize)
783 	{
784 		throw std::exception();
785 	}
786 	memset(m_ram + address, value, length);
787 
788 	m_cpu.m_State.nGPR[SC_RETURN].nV0 = address;
789 }
790 
791 //A0 - 2F
sc_rand()792 void CPsxBios::sc_rand()
793 {
794 	m_cpu.m_State.nGPR[SC_RETURN].nV0 = rand();
795 }
796 
797 //A0 - 30
sc_srand()798 void CPsxBios::sc_srand()
799 {
800 	uint32 seed = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
801 	srand(seed);
802 }
803 
804 //A0 - 39
sc_InitHeap()805 void CPsxBios::sc_InitHeap()
806 {
807 	uint32 block = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
808 	uint32 n = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
809 }
810 
811 //A0 - 3F
sc_printf()812 void CPsxBios::sc_printf()
813 {
814 	uint32 formatAddress = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
815 }
816 
817 //A0 - 44
sc_FlushCache()818 void CPsxBios::sc_FlushCache()
819 {
820 }
821 
822 //A0 - 70
sc_bu_init()823 void CPsxBios::sc_bu_init()
824 {
825 }
826 
827 //A0 - 72
sc_96_remove()828 void CPsxBios::sc_96_remove()
829 {
830 }
831 
832 //A0 - 9F
sc_SetMem()833 void CPsxBios::sc_SetMem()
834 {
835 	uint32 n = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
836 }
837 
838 //B0 - 00
sc_SysMalloc()839 void CPsxBios::sc_SysMalloc()
840 {
841 	uint32 size = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
842 	uint32 result = AllocateSysMemory(size);
843 	m_cpu.m_State.nGPR[SC_RETURN].nV0 = result;
844 }
845 
846 //B0 - 07
sc_DeliverEvent()847 void CPsxBios::sc_DeliverEvent()
848 {
849 	uint32 classId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
850 	uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
851 }
852 
853 //B0 - 08
sc_OpenEvent()854 void CPsxBios::sc_OpenEvent()
855 {
856 	uint32 classId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
857 	uint32 spec = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
858 	uint32 mode = m_cpu.m_State.nGPR[SC_PARAM2].nV0;
859 	uint32 func = m_cpu.m_State.nGPR[SC_PARAM3].nV0;
860 
861 	uint32 eventId = m_events.Allocate();
862 	if(eventId == -1)
863 	{
864 		throw std::exception();
865 	}
866 	EVENT* eventPtr = m_events[eventId];
867 	eventPtr->classId = classId;
868 	eventPtr->spec = spec;
869 	eventPtr->mode = mode;
870 	eventPtr->func = func;
871 	eventPtr->fired = 0;
872 
873 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(eventId);
874 }
875 
876 //B0 - 09
sc_CloseEvent()877 void CPsxBios::sc_CloseEvent()
878 {
879 	uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
880 	EVENT* eventPtr = m_events[eventId];
881 	if(eventPtr != NULL)
882 	{
883 		m_events.Free(eventId);
884 	}
885 
886 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = 0;
887 }
888 
889 //B0 - 0A
sc_WaitEvent()890 void CPsxBios::sc_WaitEvent()
891 {
892 	uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
893 	auto event = m_events[eventId];
894 	if(!event)
895 	{
896 		m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1;
897 		return;
898 	}
899 	assert(event->fired);
900 }
901 
902 //B0 - 0B
sc_TestEvent()903 void CPsxBios::sc_TestEvent()
904 {
905 	uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
906 	auto eventPtr = m_events[eventId];
907 
908 	assert(eventPtr);
909 	if(!eventPtr)
910 	{
911 		m_cpu.m_State.nGPR[SC_RETURN].nD0 = 0;
912 		return;
913 	}
914 
915 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = eventPtr->fired;
916 	eventPtr->fired = 0;
917 }
918 
919 //B0 - 0C
sc_EnableEvent()920 void CPsxBios::sc_EnableEvent()
921 {
922 	try
923 	{
924 		uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
925 		EVENT* eventPtr = m_events[eventId];
926 		if(eventPtr != NULL)
927 		{
928 			eventPtr->enabled = true;
929 			eventPtr->fired = 0;
930 		}
931 	}
932 	catch(...)
933 	{
934 		m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1;
935 	}
936 }
937 
938 //B0 - 0D
sc_DisableEvent()939 void CPsxBios::sc_DisableEvent()
940 {
941 	try
942 	{
943 		uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
944 		EVENT* eventPtr = m_events[eventId];
945 		if(eventPtr != NULL)
946 		{
947 			eventPtr->enabled = false;
948 		}
949 	}
950 	catch(...)
951 	{
952 		m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1;
953 	}
954 }
955 
956 //B0 - 0E
sc_OpenThread()957 void CPsxBios::sc_OpenThread()
958 {
959 	uint32 threadPc = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
960 	uint32 threadSp = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
961 	uint32 threadGp = m_cpu.m_State.nGPR[SC_PARAM2].nV0;
962 
963 	auto threadCbTable = reinterpret_cast<CB_TABLE*>(m_ram + TCB_TABLE_ADDRESS);
964 	assert(threadCbTable->address != 0);
965 	assert(threadCbTable->size != 0);
966 	assert((threadCbTable->size % sizeof(THREAD)) == 0);
967 
968 	auto threads = reinterpret_cast<THREAD*>(m_ram + threadCbTable->address);
969 	auto threadCount = threadCbTable->size / sizeof(THREAD);
970 
971 	uint32 threadId = -1;
972 	for(uint32 i = 0; i < threadCount; i++)
973 	{
974 		auto thread = &threads[i];
975 		if(thread->status == THREAD_STATUS_ALLOCATED) continue;
976 		assert(thread->status == THREAD_STATUS_FREE);
977 		threadId = i;
978 		break;
979 	}
980 
981 	if(threadId == -1)
982 	{
983 		m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1;
984 		return;
985 	}
986 
987 	auto thread = &threads[threadId];
988 	thread->status = THREAD_STATUS_ALLOCATED;
989 	thread->pc = threadPc;
990 	thread->gpr[CMIPS::SP] = threadSp;
991 	thread->gpr[CMIPS::FP] = threadSp;
992 	thread->gpr[CMIPS::GP] = threadGp;
993 
994 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = threadId;
995 }
996 
997 //B0 - 10
sc_ChangeThread()998 void CPsxBios::sc_ChangeThread()
999 {
1000 	uint32 threadId = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
1001 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = 1;
1002 
1003 	SaveCpuState();
1004 
1005 	//Update current thread id
1006 	{
1007 		auto process = GetProcess();
1008 
1009 		auto threadCbTable = reinterpret_cast<CB_TABLE*>(m_ram + TCB_TABLE_ADDRESS);
1010 		assert(threadCbTable->address != 0);
1011 		assert(threadCbTable->size != 0);
1012 		assert((threadCbTable->size % sizeof(THREAD)) == 0);
1013 
1014 		auto threadCount = threadCbTable->size / sizeof(THREAD);
1015 		assert(threadId < threadCount);
1016 
1017 		process->currentThreadControlBlockAddr = threadCbTable->address + threadId * sizeof(THREAD);
1018 	}
1019 
1020 	LoadCpuState();
1021 }
1022 
1023 //B0 - 14
sc_StopPAD()1024 void CPsxBios::sc_StopPAD()
1025 {
1026 }
1027 
1028 //B0 - 16
sc_PAD_dr()1029 void CPsxBios::sc_PAD_dr()
1030 {
1031 }
1032 
1033 //B0 - 17
sc_ReturnFromException()1034 void CPsxBios::sc_ReturnFromException()
1035 {
1036 	uint32& status = m_cpu.m_State.nCOP0[CCOP_SCU::STATUS];
1037 	assert(status & (CMIPS::STATUS_ERL | CMIPS::STATUS_EXL));
1038 	if(status & CMIPS::STATUS_ERL)
1039 	{
1040 		status &= ~CMIPS::STATUS_ERL;
1041 	}
1042 	else if(status & CMIPS::STATUS_EXL)
1043 	{
1044 		status &= ~CMIPS::STATUS_EXL;
1045 	}
1046 	LoadCpuState();
1047 }
1048 
1049 //B0 - 18
sc_SetDefaultExitFromException()1050 void CPsxBios::sc_SetDefaultExitFromException()
1051 {
1052 	m_exitFromExceptionStateAddr = 0;
1053 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = 0;
1054 }
1055 
1056 //B0 - 19
sc_SetCustomExitFromException()1057 void CPsxBios::sc_SetCustomExitFromException()
1058 {
1059 	uint32 stateAddr = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
1060 	m_exitFromExceptionStateAddr = stateAddr;
1061 }
1062 
1063 //B0 - 3F
sc_puts()1064 void CPsxBios::sc_puts()
1065 {
1066 	uint32 stringAddress = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
1067 }
1068 
1069 //B0 - 4A
sc_InitCARD()1070 void CPsxBios::sc_InitCARD()
1071 {
1072 }
1073 
1074 //B0 - 4B
sc_StartCARD()1075 void CPsxBios::sc_StartCARD()
1076 {
1077 }
1078 
1079 //B0 - 56
sc_GetC0Table()1080 void CPsxBios::sc_GetC0Table()
1081 {
1082 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(C0TABLE_BEGIN);
1083 }
1084 
1085 //B0 - 57
sc_GetB0Table()1086 void CPsxBios::sc_GetB0Table()
1087 {
1088 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(B0TABLE_BEGIN);
1089 }
1090 
1091 //B0 - 5B
sc_ChangeClearPad()1092 void CPsxBios::sc_ChangeClearPad()
1093 {
1094 	uint32 param = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
1095 }
1096 
1097 //C0 - 00
sc_EnqueueTimerAndVblankIrqs()1098 void CPsxBios::sc_EnqueueTimerAndVblankIrqs()
1099 {
1100 }
1101 
1102 //C0 - 01
sc_EnqueueSyscallHandler()1103 void CPsxBios::sc_EnqueueSyscallHandler()
1104 {
1105 }
1106 
1107 //C0 - 03
sc_SysDeqIntRP()1108 void CPsxBios::sc_SysDeqIntRP()
1109 {
1110 	uint32 index = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
1111 	uint32 queue = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
1112 }
1113 
1114 //C0 - 08
sc_SysInitMemory()1115 void CPsxBios::sc_SysInitMemory()
1116 {
1117 	//Two parameters are passed, but don't seem to be used somehow?
1118 	//TODO: Reset PCB and TCB addresses
1119 	m_sysHeapPointerAddr = HEAP_START;
1120 	memset(m_ram + HEAP_START, 0, HEAP_SIZE);
1121 }
1122 
1123 //C0 - 0A
sc_ChangeClearRCnt()1124 void CPsxBios::sc_ChangeClearRCnt()
1125 {
1126 	uint32 param0 = m_cpu.m_State.nGPR[SC_PARAM0].nV0;
1127 	uint32 param1 = m_cpu.m_State.nGPR[SC_PARAM1].nV0;
1128 }
1129 
1130 //C0 - 0C
sc_InitDefInt()1131 void CPsxBios::sc_InitDefInt()
1132 {
1133 }
1134 
sc_EnterCriticalSection()1135 void CPsxBios::sc_EnterCriticalSection()
1136 {
1137 	bool isIntEnabled = (m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_IE) != 0;
1138 	m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] &= ~CMIPS::STATUS_IE;
1139 
1140 	m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast<int32>(isIntEnabled ? 1 : 0);
1141 }
1142 
sc_ExitCriticalSection()1143 void CPsxBios::sc_ExitCriticalSection()
1144 {
1145 	m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE;
1146 }
1147 
sc_Illegal()1148 void CPsxBios::sc_Illegal()
1149 {
1150 #ifdef _DEBUG
1151 	throw std::runtime_error("Illegal system call.");
1152 #endif
1153 }
1154 
1155 // clang-format off
1156 CPsxBios::SyscallHandler CPsxBios::m_handlerA0[MAX_HANDLER_A0] =
1157 {
1158 	//0x00
1159 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1160 	//0x08
1161 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1162 	//0x10
1163 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_setjmp,  &CPsxBios::sc_longjmp,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1164 	//0x18
1165 	&CPsxBios::sc_Illegal, &CPsxBios::sc_strcpy,   &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1166 	//0x20
1167 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1168 	//0x28
1169 	&CPsxBios::sc_bzero,   &CPsxBios::sc_Illegal,  &CPsxBios::sc_memcpy,    &CPsxBios::sc_memset,  &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_rand,
1170 	//0x30
1171 	&CPsxBios::sc_srand,   &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1172 	//0x38
1173 	&CPsxBios::sc_Illegal, &CPsxBios::sc_InitHeap, &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_printf,
1174 	//0x40
1175 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_FlushCache, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1176 	//0x48
1177 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1178 	//0x50
1179 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1180 	//0x58
1181 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1182 	//0x60
1183 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1184 	//0x68
1185 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1186 	//0x70
1187 	&CPsxBios::sc_bu_init, &CPsxBios::sc_Illegal,  &CPsxBios::sc_96_remove, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1188 	//0x78
1189 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1190 	//0x80
1191 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1192 	//0x88
1193 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1194 	//0x90
1195 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1196 	//0x98
1197 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_SetMem,
1198 	//0xA0
1199 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1200 	//0xA8
1201 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1202 	//0xB0
1203 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1204 	//0xB8
1205 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1206 	//0xC0
1207 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1208 	//0xC8
1209 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1210 	//0xD0
1211 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1212 	//0xD8
1213 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1214 	//0xE0
1215 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1216 	//0xE8
1217 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1218 	//0xF0
1219 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1220 	//0xF8
1221 	&CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,  &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal
1222 };
1223 
1224 CPsxBios::SyscallHandler CPsxBios::m_handlerB0[MAX_HANDLER_B0] =
1225 {
1226 	//0x00
1227 	&CPsxBios::sc_SysMalloc,                   &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_DeliverEvent,
1228 	//0x08
1229 	&CPsxBios::sc_OpenEvent,                   &CPsxBios::sc_CloseEvent,                 &CPsxBios::sc_WaitEvent, &CPsxBios::sc_TestEvent,      &CPsxBios::sc_EnableEvent, &CPsxBios::sc_DisableEvent, &CPsxBios::sc_OpenThread, &CPsxBios::sc_Illegal,
1230 	//0x10
1231 	&CPsxBios::sc_ChangeThread,                &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_StopPAD,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_PAD_dr,     &CPsxBios::sc_ReturnFromException,
1232 	//0x18
1233 	&CPsxBios::sc_SetDefaultExitFromException, &CPsxBios::sc_SetCustomExitFromException, &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1234 	//0x20
1235 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1236 	//0x28
1237 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1238 	//0x30
1239 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1240 	//0x38
1241 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_puts,
1242 	//0x40
1243 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1244 	//0x48
1245 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_InitCARD,  &CPsxBios::sc_StartCARD,      &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1246 	//0x50
1247 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_GetC0Table, &CPsxBios::sc_GetB0Table,
1248 	//0x58
1249 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_ChangeClearPad, &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1250 	//0x60
1251 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1252 	//0x68
1253 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1254 	//0x70
1255 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal,
1256 	//0x78
1257 	&CPsxBios::sc_Illegal,                     &CPsxBios::sc_Illegal,                    &CPsxBios::sc_Illegal,   &CPsxBios::sc_Illegal,        &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,      &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal
1258 };
1259 
1260 CPsxBios::SyscallHandler CPsxBios::m_handlerC0[MAX_HANDLER_C0] =
1261 {
1262 	//0x00
1263 	&CPsxBios::sc_EnqueueTimerAndVblankIrqs, &CPsxBios::sc_EnqueueSyscallHandler, &CPsxBios::sc_Illegal,         &CPsxBios::sc_SysDeqIntRP, &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1264 	//0x08
1265 	&CPsxBios::sc_SysInitMemory,             &CPsxBios::sc_Illegal,               &CPsxBios::sc_ChangeClearRCnt, &CPsxBios::sc_Illegal,     &CPsxBios::sc_InitDefInt, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1266 	//0x10
1267 	&CPsxBios::sc_Illegal,                   &CPsxBios::sc_Illegal,               &CPsxBios::sc_Illegal,         &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal,
1268 	//0x18
1269 	&CPsxBios::sc_Illegal,                   &CPsxBios::sc_Illegal,               &CPsxBios::sc_Illegal,         &CPsxBios::sc_Illegal,     &CPsxBios::sc_Illegal,    &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal
1270 };
1271 // clang-format on
1272