#include #include "PsxBios.h" #include "COP_SCU.h" #include "Log.h" #include "iop/Iop_Intc.h" #include "MIPSAssembler.h" #include "xml/Node.h" #include "xml/Writer.h" #define LOG_NAME ("psxbios") #define SC_PARAM0 (CMIPS::A0) #define SC_PARAM1 (CMIPS::A1) #define SC_PARAM2 (CMIPS::A2) #define SC_PARAM3 (CMIPS::A3) #define SC_RETURN (CMIPS::V0) using namespace Iop; #define PCB_TABLE_ADDRESS (0x0108) #define TCB_TABLE_ADDRESS (0x0110) #define EXITFROMEXCEPTION_STATE_ADDR (0x0200) #define SYSHEAP_POINTER_ADDR (0x0204) #define INTR_HANDLER (0x1000) #define EVENT_CHECKER (0x1200) #define EVENTS_BEGIN (0x3000) #define EVENTS_SIZE (sizeof(CPsxBios::EVENT) * CPsxBios::MAX_EVENT) #define B0TABLE_BEGIN (EVENTS_BEGIN + EVENTS_SIZE) #define B0TABLE_SIZE (0x5D * 4) #define C0TABLE_BEGIN (B0TABLE_BEGIN + B0TABLE_SIZE) #define C0TABLE_SIZE (0x1C * 4) #define C0_EXCEPTIONHANDLER_BEGIN (C0TABLE_BEGIN + C0TABLE_SIZE) #define C0_EXCEPTIONHANDLER_SIZE (0x1000) #define HEAP_START (C0_EXCEPTIONHANDLER_BEGIN + C0_EXCEPTIONHANDLER_SIZE) #define HEAP_SIZE (0x2000) #define BIOS_MEMORY_END (HEAP_START + HEAP_SIZE) CPsxBios::CPsxBios(CMIPS& cpu, uint8* ram, uint32 ramSize) : m_cpu(cpu) , m_ram(ram) , m_ramSize(ramSize) , m_exitFromExceptionStateAddr(reinterpret_cast(m_ram + EXITFROMEXCEPTION_STATE_ADDR)) , m_sysHeapPointerAddr(reinterpret_cast(m_ram + SYSHEAP_POINTER_ADDR)) , m_events(reinterpret_cast(&m_ram[EVENTS_BEGIN]), 1, MAX_EVENT) { static_assert(BIOS_MEMORY_END <= 0x10000, "BIOS memory size must not exceed 64k"); Reset(); } void CPsxBios::Reset() { uint32 syscallAddress[3] = {0xA0, 0xB0, 0xC0}; for(unsigned int i = 0; i < 3; i++) { CMIPSAssembler assembler(reinterpret_cast(m_ram + syscallAddress[i])); assembler.SYSCALL(); assembler.JR(CMIPS::RA); assembler.NOP(); } //Assembly a dummy JR RA at 0 because Vagrant Story jumps at 0 { CMIPSAssembler assembler(reinterpret_cast(m_ram + 0x0)); assembler.LUI(CMIPS::K0, 0x0000); //This is required by Xenogears' SFX PSFs assembler.JR(CMIPS::RA); assembler.NOP(); } m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE; AssembleEventChecker(); AssembleInterruptHandler(); //Setup B0 table { uint32* table = reinterpret_cast(&m_ram[B0TABLE_BEGIN]); table[0x5B] = C0_EXCEPTIONHANDLER_BEGIN; } //Setup C0 table { uint32* table = reinterpret_cast(&m_ram[C0TABLE_BEGIN]); table[0x06] = C0_EXCEPTIONHANDLER_BEGIN; } //Assemble dummy exception handler { //0x70 = LUI //0x74 = ADDIU //Chrono Cross will overwrite the stuff present at the address that would be computed //by these two instructions and use something else CMIPSAssembler assembler(reinterpret_cast(m_ram + C0_EXCEPTIONHANDLER_BEGIN + 0x70)); assembler.LI(CMIPS::T0, C0_EXCEPTIONHANDLER_BEGIN); } m_exitFromExceptionStateAddr = 0; m_sysHeapPointerAddr = HEAP_START; memset(m_events.GetBase(), 0, EVENTS_SIZE); //Allocate process control block { auto cbTable = reinterpret_cast(m_ram + PCB_TABLE_ADDRESS); cbTable->address = AllocateSysMemory(sizeof(PROCESS)); cbTable->size = sizeof(PROCESS); } //Allocate thread control block { static const uint32 maxTcb = 4; auto cbTable = reinterpret_cast(m_ram + TCB_TABLE_ADDRESS); cbTable->address = AllocateSysMemory(sizeof(THREAD) * maxTcb); cbTable->size = sizeof(THREAD) * maxTcb; } //Setup main thread { auto process = GetProcess(); auto threadCbAddr = [&]() { auto cbTable = reinterpret_cast(m_ram + TCB_TABLE_ADDRESS); auto threadCb = reinterpret_cast(m_ram + cbTable->address); threadCb->status = THREAD_STATUS_ALLOCATED; return cbTable->address; }(); process->currentThreadControlBlockAddr = threadCbAddr; } } void CPsxBios::LoadExe(const uint8* exe) { auto exeHeader = reinterpret_cast(exe); if(strncmp(reinterpret_cast(exeHeader->id), "PS-X EXE", 8)) { throw std::runtime_error("Invalid PSX executable."); } m_cpu.m_State.nPC = exeHeader->pc0 & 0x1FFFFFFF; m_cpu.m_State.nGPR[CMIPS::GP].nD0 = exeHeader->gp0; m_cpu.m_State.nGPR[CMIPS::SP].nD0 = exeHeader->stackAddr; exe += 0x800; if(exeHeader->textAddr != 0) { uint32 realAddr = exeHeader->textAddr & 0x1FFFFFFF; assert(realAddr + exeHeader->textSize <= m_ramSize); memcpy(m_ram + realAddr, exe, exeHeader->textSize); exe += exeHeader->textSize; #ifdef DEBUGGER_INCLUDED m_cpu.m_analysis->Analyse(realAddr, realAddr + exeHeader->textSize, m_cpu.m_State.nPC); #endif } } void CPsxBios::SaveState(Framework::CZipArchiveWriter& archive) { } void CPsxBios::LoadState(Framework::CZipArchiveReader& archive) { } void CPsxBios::NotifyVBlankStart() { } void CPsxBios::NotifyVBlankEnd() { } bool CPsxBios::IsIdle() { return false; } #ifdef DEBUGGER_INCLUDED void CPsxBios::LoadDebugTags(Framework::Xml::CNode* root) { } void CPsxBios::SaveDebugTags(Framework::Xml::CNode* root) { } BiosDebugModuleInfoArray CPsxBios::GetModulesDebugInfo() const { return BiosDebugModuleInfoArray(); } BiosDebugThreadInfoArray CPsxBios::GetThreadsDebugInfo() const { return BiosDebugThreadInfoArray(); } #endif void CPsxBios::CountTicks(uint32 ticks) { } void CPsxBios::AssembleEventChecker() { CMIPSAssembler assembler(reinterpret_cast(m_ram + EVENT_CHECKER)); CMIPSAssembler::LABEL checkEventLabel = assembler.CreateLabel(); CMIPSAssembler::LABEL doneEventLabel = assembler.CreateLabel(); unsigned int currentEvent = CMIPS::S0; unsigned int eventMax = CMIPS::S1; unsigned int eventToCheck = CMIPS::S2; unsigned int needClearInt = CMIPS::S3; int stackAlloc = 5 * 4; //prolog assembler.ADDIU(CMIPS::SP, CMIPS::SP, -stackAlloc); assembler.SW(CMIPS::RA, 0x00, CMIPS::SP); assembler.SW(CMIPS::S0, 0x04, CMIPS::SP); assembler.SW(CMIPS::S1, 0x08, CMIPS::SP); assembler.SW(CMIPS::S2, 0x0C, CMIPS::SP); assembler.SW(CMIPS::S3, 0x10, CMIPS::SP); assembler.LI(currentEvent, EVENTS_BEGIN); assembler.LI(eventMax, EVENTS_BEGIN + EVENTS_SIZE); assembler.MOV(eventToCheck, CMIPS::A0); assembler.ADDU(needClearInt, CMIPS::R0, CMIPS::R0); //checkEvent { assembler.MarkLabel(checkEventLabel); //check if valid assembler.LW(CMIPS::T0, offsetof(EVENT, isValid), currentEvent); assembler.BEQ(CMIPS::T0, CMIPS::R0, doneEventLabel); assembler.NOP(); //check if good event class assembler.LW(CMIPS::T0, offsetof(EVENT, classId), currentEvent); assembler.BNE(CMIPS::T0, eventToCheck, doneEventLabel); assembler.NOP(); //Tell that we need to clear interrupt later (experimental) assembler.ADDIU(needClearInt, CMIPS::R0, 1); //check if enabled assembler.LW(CMIPS::T0, offsetof(EVENT, enabled), currentEvent); assembler.BEQ(CMIPS::T0, CMIPS::R0, doneEventLabel); assembler.NOP(); //Start handler if present assembler.LW(CMIPS::T0, offsetof(EVENT, func), currentEvent); assembler.BEQ(CMIPS::T0, CMIPS::R0, doneEventLabel); assembler.NOP(); assembler.JALR(CMIPS::T0); assembler.NOP(); } //doneEvent assembler.MarkLabel(doneEventLabel); assembler.ADDIU(currentEvent, currentEvent, sizeof(EVENT)); assembler.BNE(currentEvent, eventMax, checkEventLabel); assembler.NOP(); //Result assembler.ADDU(CMIPS::V0, needClearInt, CMIPS::R0); //epilog assembler.LW(CMIPS::RA, 0x00, CMIPS::SP); assembler.LW(CMIPS::S0, 0x04, CMIPS::SP); assembler.LW(CMIPS::S1, 0x08, CMIPS::SP); assembler.LW(CMIPS::S2, 0x0C, CMIPS::SP); assembler.LW(CMIPS::S3, 0x10, CMIPS::SP); assembler.ADDIU(CMIPS::SP, CMIPS::SP, stackAlloc); assembler.JR(CMIPS::RA); assembler.NOP(); } void CPsxBios::AssembleInterruptHandler() { //Assemble interrupt handler CMIPSAssembler assembler(reinterpret_cast(m_ram + INTR_HANDLER)); CMIPSAssembler::LABEL skipRootCounter2EventLabel = assembler.CreateLabel(); CMIPSAssembler::LABEL returnExceptionLabel = assembler.CreateLabel(); CMIPSAssembler::LABEL clearIntcCause = assembler.CreateLabel(); //Get cause unsigned int cause = CMIPS::S3; assembler.LI(CMIPS::T0, CIntc::STATUS0); assembler.LW(CMIPS::T0, 0, CMIPS::T0); assembler.LI(CMIPS::T1, CIntc::MASK0); assembler.LW(CMIPS::T1, 0, CMIPS::T1); assembler.AND(cause, CMIPS::T0, CMIPS::T1); //Check if cause is root counter 2 assembler.ANDI(CMIPS::T0, cause, 0x40); assembler.BEQ(CMIPS::T0, CMIPS::R0, skipRootCounter2EventLabel); assembler.NOP(); assembler.LI(CMIPS::A0, EVENT_ID_RCNT2); assembler.JAL(EVENT_CHECKER); assembler.NOP(); assembler.BEQ(CMIPS::V0, CMIPS::R0, skipRootCounter2EventLabel); assembler.NOP(); //Clear root counter 2 cause assembler.LI(CMIPS::T0, CIntc::STATUS0); assembler.LI(CMIPS::T1, ~0x40); assembler.SW(CMIPS::T1, 0, CMIPS::T0); assembler.MarkLabel(skipRootCounter2EventLabel); //checkIntHook assembler.LI(CMIPS::T0, EXITFROMEXCEPTION_STATE_ADDR); assembler.LW(CMIPS::T0, 0, CMIPS::T0); assembler.BEQ(CMIPS::T0, CMIPS::R0, clearIntcCause); assembler.NOP(); //callIntHook assembler.ADDIU(CMIPS::A0, CMIPS::T0, CMIPS::R0); assembler.ADDIU(CMIPS::A1, CMIPS::R0, CMIPS::R0); assembler.ADDIU(CMIPS::T0, CMIPS::R0, 0xA0); assembler.ADDIU(CMIPS::T1, CMIPS::R0, 0x14); assembler.JR(CMIPS::T0); assembler.NOP(); assembler.BEQ(CMIPS::R0, CMIPS::R0, returnExceptionLabel); assembler.NOP(); //Clear any interrupt that might have triggered this exception handler (to prevent infinite loop) assembler.MarkLabel(clearIntcCause); assembler.LI(CMIPS::T0, CIntc::STATUS0); assembler.NOR(CMIPS::T1, CMIPS::R0, cause); assembler.SW(CMIPS::T1, 0, CMIPS::T0); //ReturnFromException assembler.MarkLabel(returnExceptionLabel); assembler.ADDIU(CMIPS::T0, CMIPS::R0, 0xB0); assembler.ADDIU(CMIPS::T1, CMIPS::R0, 0x17); assembler.JR(CMIPS::T0); assembler.NOP(); } void CPsxBios::LongJump(uint32 bufferAddress, uint32 value) { bufferAddress = m_cpu.m_pAddrTranslator(&m_cpu, bufferAddress); m_cpu.m_State.nGPR[CMIPS::RA].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x00)); m_cpu.m_State.nGPR[CMIPS::SP].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x04)); m_cpu.m_State.nGPR[CMIPS::FP].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x08)); m_cpu.m_State.nGPR[CMIPS::S0].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x0C)); m_cpu.m_State.nGPR[CMIPS::S1].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x10)); m_cpu.m_State.nGPR[CMIPS::S2].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x14)); m_cpu.m_State.nGPR[CMIPS::S3].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x18)); m_cpu.m_State.nGPR[CMIPS::S4].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x1C)); m_cpu.m_State.nGPR[CMIPS::S5].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x20)); m_cpu.m_State.nGPR[CMIPS::S6].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x24)); m_cpu.m_State.nGPR[CMIPS::S7].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x28)); m_cpu.m_State.nGPR[CMIPS::GP].nD0 = static_cast(m_cpu.m_pMemoryMap->GetWord(bufferAddress + 0x2C)); m_cpu.m_State.nGPR[CMIPS::V0].nD0 = value == 0 ? 1 : value; } CPsxBios::PROCESS* CPsxBios::GetProcess() { auto processCbTable = reinterpret_cast(m_ram + PCB_TABLE_ADDRESS); assert(processCbTable->address != 0); assert(processCbTable->size != 0); return reinterpret_cast(m_ram + processCbTable->address); } void CPsxBios::SaveCpuState() { auto process = GetProcess(); assert(process->currentThreadControlBlockAddr != 0); auto thread = reinterpret_cast(m_ram + process->currentThreadControlBlockAddr); assert(thread->status == THREAD_STATUS_ALLOCATED); thread->pc = m_cpu.m_State.nPC; for(uint32 i = 0; i < 32; i++) { if(i == CMIPS::R0) continue; if(i == CMIPS::K0) continue; if(i == CMIPS::K1) continue; thread->gpr[i] = m_cpu.m_State.nGPR[i].nV0; } thread->sr = m_cpu.m_State.nCOP0[CCOP_SCU::STATUS]; thread->sr &= ~(CMIPS::STATUS_EXL | CMIPS::STATUS_ERL); } void CPsxBios::LoadCpuState() { auto process = GetProcess(); assert(process->currentThreadControlBlockAddr != 0); auto thread = reinterpret_cast(m_ram + process->currentThreadControlBlockAddr); assert(thread->status == THREAD_STATUS_ALLOCATED); m_cpu.m_State.nPC = thread->pc; for(uint32 i = 0; i < 32; i++) { if(i == CMIPS::R0) continue; if(i == CMIPS::K0) continue; if(i == CMIPS::K1) continue; m_cpu.m_State.nGPR[i].nV0 = thread->gpr[i]; } m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] = thread->sr; } uint32 CPsxBios::AllocateSysMemory(uint32 size) { assert((m_sysHeapPointerAddr + size) <= (HEAP_START + HEAP_SIZE)); uint32 result = m_sysHeapPointerAddr; m_sysHeapPointerAddr += size; return result; } void CPsxBios::HandleInterrupt() { if(m_cpu.GenerateInterrupt(m_cpu.m_State.nPC)) { SaveCpuState(); uint32 status = m_cpu.m_pMemoryMap->GetWord(CIntc::STATUS0); uint32 mask = m_cpu.m_pMemoryMap->GetWord(CIntc::MASK0); uint32 cause = status & mask; for(unsigned int i = 1; i <= MAX_EVENT; i++) { EVENT* eventPtr = m_events[i]; if(eventPtr == NULL) continue; if((cause & (1 << CIntc::LINE_DMAC)) && eventPtr->classId == 0xF0000009) { eventPtr->fired = 1; } } m_cpu.m_State.nPC = INTR_HANDLER; } } void CPsxBios::HandleException() { assert(m_cpu.m_State.nHasException); uint32 searchAddress = m_cpu.m_State.nCOP0[CCOP_SCU::EPC]; uint32 callInstruction = m_cpu.m_pMemoryMap->GetWord(searchAddress); if(callInstruction != 0x0000000C) { throw std::runtime_error("Not a SYSCALL."); } #ifdef _DEBUG DisassembleSyscall(searchAddress); #endif if(searchAddress == 0xA0) { ProcessSubFunction(m_handlerA0, MAX_HANDLER_A0); } else if(searchAddress == 0xB0) { ProcessSubFunction(m_handlerB0, MAX_HANDLER_B0); } else if(searchAddress == 0xC0) { ProcessSubFunction(m_handlerC0, MAX_HANDLER_C0); } else { uint32 functionId = m_cpu.m_State.nGPR[CMIPS::A0].nV0; switch(functionId) { case 0x01: sc_EnterCriticalSection(); break; case 0x02: sc_ExitCriticalSection(); break; default: sc_Illegal(); break; } } m_cpu.m_State.nHasException = 0; } void CPsxBios::ProcessSubFunction(SyscallHandler* handlerTable, unsigned int handlerTableLength) { uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0; if(functionId >= handlerTableLength) { sc_Illegal(); } functionId %= handlerTableLength; ((this)->*(handlerTable[functionId]))(); } void CPsxBios::DisassembleSyscall(uint32 searchAddress) { if(searchAddress == 0x00A0) { uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0; switch(functionId) { case 0x13: CLog::GetInstance().Print(LOG_NAME, "setjmp(buffer = 0x%0.8X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x14: CLog::GetInstance().Print(LOG_NAME, "longjmp(buffer = 0x%0.8X, value = %i);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x19: CLog::GetInstance().Print(LOG_NAME, "strcpy(dst = 0x%0.8X, src = 0x%0.8X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x28: CLog::GetInstance().Print(LOG_NAME, "bzero(address = 0x%0.8X, length = 0x%x);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x2A: CLog::GetInstance().Print(LOG_NAME, "memcpy(dst = 0x%0.8X, src = 0x%0.8X, length = 0x%x);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0, m_cpu.m_State.nGPR[SC_PARAM2].nV0); break; case 0x2B: CLog::GetInstance().Print(LOG_NAME, "memset(address = 0x%0.8X, value = 0x%x, length = 0x%x);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0, m_cpu.m_State.nGPR[SC_PARAM2].nV0); break; case 0x2F: CLog::GetInstance().Print(LOG_NAME, "rand();\r\n"); break; case 0x30: CLog::GetInstance().Print(LOG_NAME, "srand(seed = %d);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x39: CLog::GetInstance().Print(LOG_NAME, "InitHeap(block = 0x%0.8X, n = 0x%0.8X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x3F: CLog::GetInstance().Print(LOG_NAME, "printf(fmt = 0x%0.8X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x44: CLog::GetInstance().Print(LOG_NAME, "FlushCache();\r\n"); break; case 0x70: CLog::GetInstance().Print(LOG_NAME, "_bu_init();\r\n"); break; case 0x72: CLog::GetInstance().Print(LOG_NAME, "_96_remove();\r\n"); break; case 0x9F: CLog::GetInstance().Print(LOG_NAME, "SetMem(size = %i);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; default: CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered (0xA0, 0x%X).\r\n", functionId); break; } } else if(searchAddress == 0x00B0) { uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0; switch(functionId) { case 0x00: CLog::GetInstance().Print(LOG_NAME, "SysMalloc(size = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x07: CLog::GetInstance().Print(LOG_NAME, "DeliverEvent(class = 0x%X, event = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x08: CLog::GetInstance().Print(LOG_NAME, "OpenEvent(class = 0x%X, spec = 0x%X, mode = 0x%X, func = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0, m_cpu.m_State.nGPR[SC_PARAM2].nV0, m_cpu.m_State.nGPR[SC_PARAM3].nV0); break; case 0x09: CLog::GetInstance().Print(LOG_NAME, "CloseEvent(event = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x0A: CLog::GetInstance().Print(LOG_NAME, "WaitEvent(event = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x0B: CLog::GetInstance().Print(LOG_NAME, "TestEvent(event = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x0C: CLog::GetInstance().Print(LOG_NAME, "EnableEvent(event = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x0D: CLog::GetInstance().Print(LOG_NAME, "DisableEvent(event = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x0E: CLog::GetInstance().Print(LOG_NAME, "OpenThread(pc = 0x%08X, sp = 0x%08X, gp = 0x%08X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0, m_cpu.m_State.nGPR[SC_PARAM2].nV0); break; case 0x10: CLog::GetInstance().Print(LOG_NAME, "ChangeThread(threadId = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x14: CLog::GetInstance().Print(LOG_NAME, "StopPAD();\r\n"); break; case 0x16: CLog::GetInstance().Print(LOG_NAME, "PAD_dr();\r\n"); break; case 0x17: CLog::GetInstance().Print(LOG_NAME, "ReturnFromException();\r\n"); break; case 0x18: CLog::GetInstance().Print(LOG_NAME, "SetDefaultExitFromException();\r\n"); break; case 0x19: CLog::GetInstance().Print(LOG_NAME, "SetCustomExitFromException(stateAddress = 0x%0.8X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x3F: CLog::GetInstance().Print(LOG_NAME, "puts(s = 0x%0.8X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x4A: CLog::GetInstance().Print(LOG_NAME, "InitCARD();\r\n"); break; case 0x4B: CLog::GetInstance().Print(LOG_NAME, "StartCARD();\r\n"); break; case 0x56: CLog::GetInstance().Print(LOG_NAME, "GetC0Table();\r\n"); break; case 0x57: CLog::GetInstance().Print(LOG_NAME, "GetB0Table();\r\n"); break; case 0x5B: CLog::GetInstance().Print(LOG_NAME, "ChangeClearPad(param = %i);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; default: CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered (0xB0, 0x%X).\r\n", functionId); break; } } else if(searchAddress == 0x00C0) { uint32 functionId = m_cpu.m_State.nGPR[CMIPS::T1].nV0; switch(functionId) { case 0x00: CLog::GetInstance().Print(LOG_NAME, "EnqueueTimerAndVblankIrqs(priority = %d);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x01: CLog::GetInstance().Print(LOG_NAME, "EnqueueSyscallHandler(priority = %d);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; case 0x03: CLog::GetInstance().Print(LOG_NAME, "SysDeqIntRP(index = %i, queue = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x08: CLog::GetInstance().Print(LOG_NAME, "SysInitMemory(address = 0x%08X, size = 0x%X);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x0A: CLog::GetInstance().Print(LOG_NAME, "ChangeClearRCnt(param0 = %i, param1 = %i);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0, m_cpu.m_State.nGPR[SC_PARAM1].nV0); break; case 0x0C: CLog::GetInstance().Print(LOG_NAME, "InitDefInt(priority = %d);\r\n", m_cpu.m_State.nGPR[SC_PARAM0].nV0); break; default: CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered (0xC0, 0x%X).\r\n", functionId); break; } } else { uint32 functionId = m_cpu.m_State.nGPR[CMIPS::A0].nV0; switch(functionId) { case 0x01: CLog::GetInstance().Print(LOG_NAME, "EnterCriticalSection();\r\n"); break; case 0x02: CLog::GetInstance().Print(LOG_NAME, "ExitCriticalSection();\r\n"); break; default: CLog::GetInstance().Print(LOG_NAME, "Unknown system call encountered.\r\n"); break; } } } //A0 - 13 void CPsxBios::sc_setjmp() { uint32 bufferAddress = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x00, m_cpu.m_State.nGPR[CMIPS::RA].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x04, m_cpu.m_State.nGPR[CMIPS::SP].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x08, m_cpu.m_State.nGPR[CMIPS::FP].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x0C, m_cpu.m_State.nGPR[CMIPS::S0].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x10, m_cpu.m_State.nGPR[CMIPS::S1].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x14, m_cpu.m_State.nGPR[CMIPS::S2].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x18, m_cpu.m_State.nGPR[CMIPS::S3].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x1C, m_cpu.m_State.nGPR[CMIPS::S4].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x20, m_cpu.m_State.nGPR[CMIPS::S5].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x24, m_cpu.m_State.nGPR[CMIPS::S6].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x28, m_cpu.m_State.nGPR[CMIPS::S7].nV0); m_cpu.m_pMemoryMap->SetWord(bufferAddress + 0x2C, m_cpu.m_State.nGPR[CMIPS::GP].nV0); m_cpu.m_State.nGPR[CMIPS::V0].nD0 = 0; } //A0 - 14 void CPsxBios::sc_longjmp() { uint32 buffer = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 value = m_cpu.m_State.nGPR[SC_PARAM1].nV0; LongJump(buffer, value); } //A0 - 19 void CPsxBios::sc_strcpy() { uint32 dst = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0); uint32 src = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM1].nV0); strcpy( reinterpret_cast(m_ram + dst), reinterpret_cast(m_ram + src)); m_cpu.m_State.nGPR[SC_RETURN].nV0 = dst; } //A0 - 28 void CPsxBios::sc_bzero() { uint32 address = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0); uint32 length = m_cpu.m_State.nGPR[SC_PARAM1].nV0; if((address + length) > m_ramSize) { throw std::exception(); } memset(m_ram + address, 0, length); } //A0 - 2A void CPsxBios::sc_memcpy() { uint32 dst = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0); uint32 src = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM1].nV0); uint32 length = m_cpu.m_State.nGPR[SC_PARAM2].nV0; memcpy(m_ram + dst, m_ram + src, length); m_cpu.m_State.nGPR[SC_RETURN].nV0 = m_cpu.m_State.nGPR[SC_PARAM0].nV0; } //A0 - 2B void CPsxBios::sc_memset() { uint32 address = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nGPR[SC_PARAM0].nV0); uint8 value = static_cast(m_cpu.m_State.nGPR[SC_PARAM1].nV0); uint32 length = m_cpu.m_State.nGPR[SC_PARAM2].nV0; if((address + length) > m_ramSize) { throw std::exception(); } memset(m_ram + address, value, length); m_cpu.m_State.nGPR[SC_RETURN].nV0 = address; } //A0 - 2F void CPsxBios::sc_rand() { m_cpu.m_State.nGPR[SC_RETURN].nV0 = rand(); } //A0 - 30 void CPsxBios::sc_srand() { uint32 seed = m_cpu.m_State.nGPR[SC_PARAM0].nV0; srand(seed); } //A0 - 39 void CPsxBios::sc_InitHeap() { uint32 block = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 n = m_cpu.m_State.nGPR[SC_PARAM1].nV0; } //A0 - 3F void CPsxBios::sc_printf() { uint32 formatAddress = m_cpu.m_State.nGPR[SC_PARAM0].nV0; } //A0 - 44 void CPsxBios::sc_FlushCache() { } //A0 - 70 void CPsxBios::sc_bu_init() { } //A0 - 72 void CPsxBios::sc_96_remove() { } //A0 - 9F void CPsxBios::sc_SetMem() { uint32 n = m_cpu.m_State.nGPR[SC_PARAM0].nV0; } //B0 - 00 void CPsxBios::sc_SysMalloc() { uint32 size = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 result = AllocateSysMemory(size); m_cpu.m_State.nGPR[SC_RETURN].nV0 = result; } //B0 - 07 void CPsxBios::sc_DeliverEvent() { uint32 classId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM1].nV0; } //B0 - 08 void CPsxBios::sc_OpenEvent() { uint32 classId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 spec = m_cpu.m_State.nGPR[SC_PARAM1].nV0; uint32 mode = m_cpu.m_State.nGPR[SC_PARAM2].nV0; uint32 func = m_cpu.m_State.nGPR[SC_PARAM3].nV0; uint32 eventId = m_events.Allocate(); if(eventId == -1) { throw std::exception(); } EVENT* eventPtr = m_events[eventId]; eventPtr->classId = classId; eventPtr->spec = spec; eventPtr->mode = mode; eventPtr->func = func; eventPtr->fired = 0; m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast(eventId); } //B0 - 09 void CPsxBios::sc_CloseEvent() { uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; EVENT* eventPtr = m_events[eventId]; if(eventPtr != NULL) { m_events.Free(eventId); } m_cpu.m_State.nGPR[SC_RETURN].nD0 = 0; } //B0 - 0A void CPsxBios::sc_WaitEvent() { uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; auto event = m_events[eventId]; if(!event) { m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1; return; } assert(event->fired); } //B0 - 0B void CPsxBios::sc_TestEvent() { uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; auto eventPtr = m_events[eventId]; assert(eventPtr); if(!eventPtr) { m_cpu.m_State.nGPR[SC_RETURN].nD0 = 0; return; } m_cpu.m_State.nGPR[SC_RETURN].nD0 = eventPtr->fired; eventPtr->fired = 0; } //B0 - 0C void CPsxBios::sc_EnableEvent() { try { uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; EVENT* eventPtr = m_events[eventId]; if(eventPtr != NULL) { eventPtr->enabled = true; eventPtr->fired = 0; } } catch(...) { m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1; } } //B0 - 0D void CPsxBios::sc_DisableEvent() { try { uint32 eventId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; EVENT* eventPtr = m_events[eventId]; if(eventPtr != NULL) { eventPtr->enabled = false; } } catch(...) { m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1; } } //B0 - 0E void CPsxBios::sc_OpenThread() { uint32 threadPc = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 threadSp = m_cpu.m_State.nGPR[SC_PARAM1].nV0; uint32 threadGp = m_cpu.m_State.nGPR[SC_PARAM2].nV0; auto threadCbTable = reinterpret_cast(m_ram + TCB_TABLE_ADDRESS); assert(threadCbTable->address != 0); assert(threadCbTable->size != 0); assert((threadCbTable->size % sizeof(THREAD)) == 0); auto threads = reinterpret_cast(m_ram + threadCbTable->address); auto threadCount = threadCbTable->size / sizeof(THREAD); uint32 threadId = -1; for(uint32 i = 0; i < threadCount; i++) { auto thread = &threads[i]; if(thread->status == THREAD_STATUS_ALLOCATED) continue; assert(thread->status == THREAD_STATUS_FREE); threadId = i; break; } if(threadId == -1) { m_cpu.m_State.nGPR[SC_RETURN].nD0 = -1; return; } auto thread = &threads[threadId]; thread->status = THREAD_STATUS_ALLOCATED; thread->pc = threadPc; thread->gpr[CMIPS::SP] = threadSp; thread->gpr[CMIPS::FP] = threadSp; thread->gpr[CMIPS::GP] = threadGp; m_cpu.m_State.nGPR[SC_RETURN].nD0 = threadId; } //B0 - 10 void CPsxBios::sc_ChangeThread() { uint32 threadId = m_cpu.m_State.nGPR[SC_PARAM0].nV0; m_cpu.m_State.nGPR[SC_RETURN].nD0 = 1; SaveCpuState(); //Update current thread id { auto process = GetProcess(); auto threadCbTable = reinterpret_cast(m_ram + TCB_TABLE_ADDRESS); assert(threadCbTable->address != 0); assert(threadCbTable->size != 0); assert((threadCbTable->size % sizeof(THREAD)) == 0); auto threadCount = threadCbTable->size / sizeof(THREAD); assert(threadId < threadCount); process->currentThreadControlBlockAddr = threadCbTable->address + threadId * sizeof(THREAD); } LoadCpuState(); } //B0 - 14 void CPsxBios::sc_StopPAD() { } //B0 - 16 void CPsxBios::sc_PAD_dr() { } //B0 - 17 void CPsxBios::sc_ReturnFromException() { uint32& status = m_cpu.m_State.nCOP0[CCOP_SCU::STATUS]; assert(status & (CMIPS::STATUS_ERL | CMIPS::STATUS_EXL)); if(status & CMIPS::STATUS_ERL) { status &= ~CMIPS::STATUS_ERL; } else if(status & CMIPS::STATUS_EXL) { status &= ~CMIPS::STATUS_EXL; } LoadCpuState(); } //B0 - 18 void CPsxBios::sc_SetDefaultExitFromException() { m_exitFromExceptionStateAddr = 0; m_cpu.m_State.nGPR[SC_RETURN].nD0 = 0; } //B0 - 19 void CPsxBios::sc_SetCustomExitFromException() { uint32 stateAddr = m_cpu.m_State.nGPR[SC_PARAM0].nV0; m_exitFromExceptionStateAddr = stateAddr; } //B0 - 3F void CPsxBios::sc_puts() { uint32 stringAddress = m_cpu.m_State.nGPR[SC_PARAM0].nV0; } //B0 - 4A void CPsxBios::sc_InitCARD() { } //B0 - 4B void CPsxBios::sc_StartCARD() { } //B0 - 56 void CPsxBios::sc_GetC0Table() { m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast(C0TABLE_BEGIN); } //B0 - 57 void CPsxBios::sc_GetB0Table() { m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast(B0TABLE_BEGIN); } //B0 - 5B void CPsxBios::sc_ChangeClearPad() { uint32 param = m_cpu.m_State.nGPR[SC_PARAM0].nV0; } //C0 - 00 void CPsxBios::sc_EnqueueTimerAndVblankIrqs() { } //C0 - 01 void CPsxBios::sc_EnqueueSyscallHandler() { } //C0 - 03 void CPsxBios::sc_SysDeqIntRP() { uint32 index = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 queue = m_cpu.m_State.nGPR[SC_PARAM1].nV0; } //C0 - 08 void CPsxBios::sc_SysInitMemory() { //Two parameters are passed, but don't seem to be used somehow? //TODO: Reset PCB and TCB addresses m_sysHeapPointerAddr = HEAP_START; memset(m_ram + HEAP_START, 0, HEAP_SIZE); } //C0 - 0A void CPsxBios::sc_ChangeClearRCnt() { uint32 param0 = m_cpu.m_State.nGPR[SC_PARAM0].nV0; uint32 param1 = m_cpu.m_State.nGPR[SC_PARAM1].nV0; } //C0 - 0C void CPsxBios::sc_InitDefInt() { } void CPsxBios::sc_EnterCriticalSection() { bool isIntEnabled = (m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_IE) != 0; m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] &= ~CMIPS::STATUS_IE; m_cpu.m_State.nGPR[SC_RETURN].nD0 = static_cast(isIntEnabled ? 1 : 0); } void CPsxBios::sc_ExitCriticalSection() { m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE; } void CPsxBios::sc_Illegal() { #ifdef _DEBUG throw std::runtime_error("Illegal system call."); #endif } // clang-format off CPsxBios::SyscallHandler CPsxBios::m_handlerA0[MAX_HANDLER_A0] = { //0x00 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x08 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x10 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_setjmp, &CPsxBios::sc_longjmp, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x18 &CPsxBios::sc_Illegal, &CPsxBios::sc_strcpy, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x20 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x28 &CPsxBios::sc_bzero, &CPsxBios::sc_Illegal, &CPsxBios::sc_memcpy, &CPsxBios::sc_memset, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_rand, //0x30 &CPsxBios::sc_srand, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x38 &CPsxBios::sc_Illegal, &CPsxBios::sc_InitHeap, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_printf, //0x40 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_FlushCache, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x48 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x50 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x58 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x60 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x68 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x70 &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, //0x78 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x80 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x88 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x90 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x98 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_SetMem, //0xA0 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xA8 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xB0 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xB8 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xC0 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xC8 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xD0 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xD8 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xE0 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xE8 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xF0 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0xF8 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal }; CPsxBios::SyscallHandler CPsxBios::m_handlerB0[MAX_HANDLER_B0] = { //0x00 &CPsxBios::sc_SysMalloc, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_DeliverEvent, //0x08 &CPsxBios::sc_OpenEvent, &CPsxBios::sc_CloseEvent, &CPsxBios::sc_WaitEvent, &CPsxBios::sc_TestEvent, &CPsxBios::sc_EnableEvent, &CPsxBios::sc_DisableEvent, &CPsxBios::sc_OpenThread, &CPsxBios::sc_Illegal, //0x10 &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, //0x18 &CPsxBios::sc_SetDefaultExitFromException, &CPsxBios::sc_SetCustomExitFromException, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x20 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x28 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x30 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x38 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_puts, //0x40 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x48 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_InitCARD, &CPsxBios::sc_StartCARD, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x50 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_GetC0Table, &CPsxBios::sc_GetB0Table, //0x58 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_ChangeClearPad, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x60 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x68 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x70 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x78 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal }; CPsxBios::SyscallHandler CPsxBios::m_handlerC0[MAX_HANDLER_C0] = { //0x00 &CPsxBios::sc_EnqueueTimerAndVblankIrqs, &CPsxBios::sc_EnqueueSyscallHandler, &CPsxBios::sc_Illegal, &CPsxBios::sc_SysDeqIntRP, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x08 &CPsxBios::sc_SysInitMemory, &CPsxBios::sc_Illegal, &CPsxBios::sc_ChangeClearRCnt, &CPsxBios::sc_Illegal, &CPsxBios::sc_InitDefInt, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x10 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, //0x18 &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal, &CPsxBios::sc_Illegal }; // clang-format on