2019-09-16 18:52:14 -04:00
|
|
|
#include <vector>
|
2022-07-29 17:06:47 -04:00
|
|
|
#include <cstring>
|
2019-09-16 18:52:14 -04:00
|
|
|
|
|
|
|
#include "string_format.h"
|
|
|
|
#include "PtrStream.h"
|
|
|
|
#include "xml/FilteringNodeIterator.h"
|
|
|
|
#include "lexical_cast_ex.h"
|
2020-12-28 19:37:18 -05:00
|
|
|
#include "BitManip.h"
|
2022-10-10 12:14:21 -04:00
|
|
|
#include "StringUtils.h"
|
2022-10-30 21:46:14 -04:00
|
|
|
#include "std_experimental_map.h"
|
2019-09-16 18:52:14 -04:00
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
#include "IopBios.h"
|
|
|
|
#include "../COP_SCU.h"
|
2025-03-11 12:48:26 -04:00
|
|
|
#include "Log.h"
|
2008-10-20 04:14:13 +00:00
|
|
|
#include "../ElfFile.h"
|
2012-05-29 01:01:02 +00:00
|
|
|
#include "../Ps2Const.h"
|
2017-01-22 14:11:51 -05:00
|
|
|
#include "../MipsExecutor.h"
|
2008-10-28 01:48:21 +00:00
|
|
|
#include "Iop_Intc.h"
|
2021-12-01 14:32:18 -05:00
|
|
|
#include "../states/MemoryStateFile.h"
|
2023-12-07 15:10:58 -05:00
|
|
|
#include "../states/RegisterStateCollectionFile.h"
|
2008-10-20 04:14:13 +00:00
|
|
|
|
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2022-02-28 13:35:13 -05:00
|
|
|
#include "Iop_IomanX.h"
|
2016-12-18 21:34:38 -05:00
|
|
|
#include "Iop_Naplink.h"
|
2008-10-20 04:14:13 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "Iop_SifManNull.h"
|
2016-01-24 15:30:20 -05:00
|
|
|
#include "Iop_SifModuleProvider.h"
|
2008-10-20 04:14:13 +00:00
|
|
|
#include "Iop_Sysclib.h"
|
|
|
|
#include "Iop_Loadcore.h"
|
2008-01-15 20:27:44 +00:00
|
|
|
#include "Iop_Thbase.h"
|
|
|
|
#include "Iop_Thsema.h"
|
2019-04-22 21:30:16 -04:00
|
|
|
#include "Iop_Thfpool.h"
|
2016-02-28 15:55:45 -05:00
|
|
|
#include "Iop_Thvpool.h"
|
2012-06-30 02:31:22 +00:00
|
|
|
#include "Iop_Thmsgbx.h"
|
2008-01-15 20:27:44 +00:00
|
|
|
#include "Iop_Thevent.h"
|
2016-12-07 10:14:01 -05:00
|
|
|
#include "Iop_Heaplib.h"
|
2008-01-15 20:27:44 +00:00
|
|
|
#include "Iop_Timrman.h"
|
|
|
|
#include "Iop_Intrman.h"
|
2020-03-15 14:59:54 -04:00
|
|
|
#include "Iop_Dmacman.h"
|
|
|
|
#include "Iop_Secrman.h"
|
2008-12-01 04:00:36 +00:00
|
|
|
#include "Iop_Vblank.h"
|
2016-01-15 00:36:04 -05:00
|
|
|
#include "Iop_Dynamic.h"
|
2008-01-15 20:27:44 +00:00
|
|
|
|
2019-09-16 18:50:28 -04:00
|
|
|
#include "Ioman_ScopedFile.h"
|
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
#define LOGNAME "iop_bios"
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
#define STATE_MODULES ("iopbios/dyn_modules.xml")
|
|
|
|
#define STATE_MODULE_IMPORT_TABLE_ADDRESS ("ImportTableAddress")
|
|
|
|
|
2021-12-01 14:32:18 -05:00
|
|
|
#define STATE_MODULESTARTREQUESTS ("iopbios/module_start_requests")
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
#define BIOS_THREAD_LINK_HEAD_BASE (CIopBios::CONTROL_BLOCK_START + 0x0000)
|
|
|
|
#define BIOS_CURRENT_THREAD_ID_BASE (CIopBios::CONTROL_BLOCK_START + 0x0008)
|
|
|
|
#define BIOS_CURRENT_TIME_BASE (CIopBios::CONTROL_BLOCK_START + 0x0010)
|
|
|
|
#define BIOS_MODULESTARTREQUEST_HEAD_BASE (CIopBios::CONTROL_BLOCK_START + 0x0018)
|
|
|
|
#define BIOS_MODULESTARTREQUEST_FREE_BASE (CIopBios::CONTROL_BLOCK_START + 0x0020)
|
|
|
|
#define BIOS_HANDLERS_BASE (CIopBios::CONTROL_BLOCK_START + 0x0100)
|
2019-11-27 00:41:10 +00:00
|
|
|
#define BIOS_HANDLERS_END (BIOS_HANDLERS_BASE + 0x100 - 1)
|
|
|
|
#define BIOS_MESSAGEBOX_BASE (BIOS_HANDLERS_END + 1)
|
|
|
|
#define BIOS_MESSAGEBOX_SIZE (sizeof(CIopBios::MESSAGEBOX) * CIopBios::MAX_MESSAGEBOX)
|
2019-11-26 13:19:31 +00:00
|
|
|
#define BIOS_SYSTEM_INTRHANDLER_TABLE_BASE (0x480)
|
|
|
|
#define BIOS_SYSTEM_INTRHANDLER_TABLE_SIZE (0x3 * (sizeof(CIopBios::SYSTEM_INTRHANDLER)))
|
2019-11-27 00:41:10 +00:00
|
|
|
#define BIOS_THREADS_BASE (BIOS_SYSTEM_INTRHANDLER_TABLE_BASE + BIOS_SYSTEM_INTRHANDLER_TABLE_SIZE)
|
2018-04-30 21:01:23 +01:00
|
|
|
#define BIOS_THREADS_SIZE (sizeof(CIopBios::THREAD) * CIopBios::MAX_THREAD)
|
|
|
|
#define BIOS_SEMAPHORES_BASE (BIOS_THREADS_BASE + BIOS_THREADS_SIZE)
|
|
|
|
#define BIOS_SEMAPHORES_SIZE (sizeof(CIopBios::SEMAPHORE) * CIopBios::MAX_SEMAPHORE)
|
|
|
|
#define BIOS_EVENTFLAGS_BASE (BIOS_SEMAPHORES_BASE + BIOS_SEMAPHORES_SIZE)
|
|
|
|
#define BIOS_EVENTFLAGS_SIZE (sizeof(CIopBios::EVENTFLAG) * CIopBios::MAX_EVENTFLAG)
|
|
|
|
#define BIOS_INTRHANDLER_BASE (BIOS_EVENTFLAGS_BASE + BIOS_EVENTFLAGS_SIZE)
|
|
|
|
#define BIOS_INTRHANDLER_SIZE (sizeof(CIopBios::INTRHANDLER) * CIopBios::MAX_INTRHANDLER)
|
2018-07-17 12:41:13 -04:00
|
|
|
#define BIOS_VBLANKHANDLER_BASE (BIOS_INTRHANDLER_BASE + BIOS_INTRHANDLER_SIZE)
|
|
|
|
#define BIOS_VBLANKHANDLER_SIZE (sizeof(CIopBios::VBLANKHANDLER) * CIopBios::MAX_VBLANKHANDLER)
|
2019-11-27 00:41:10 +00:00
|
|
|
#define BIOS_FPL_BASE (BIOS_VBLANKHANDLER_BASE + BIOS_VBLANKHANDLER_SIZE)
|
2019-04-28 18:07:48 -04:00
|
|
|
#define BIOS_FPL_SIZE (sizeof(CIopBios::FPL) * CIopBios::MAX_FPL)
|
|
|
|
#define BIOS_VPL_BASE (BIOS_FPL_BASE + BIOS_FPL_SIZE)
|
2018-04-30 21:01:23 +01:00
|
|
|
#define BIOS_VPL_SIZE (sizeof(CIopBios::VPL) * CIopBios::MAX_VPL)
|
|
|
|
#define BIOS_MEMORYBLOCK_BASE (BIOS_VPL_BASE + BIOS_VPL_SIZE)
|
|
|
|
#define BIOS_MEMORYBLOCK_SIZE (sizeof(Iop::MEMORYBLOCK) * CIopBios::MAX_MEMORYBLOCK)
|
2021-12-01 14:32:18 -05:00
|
|
|
#define BIOS_LOADEDMODULE_BASE (BIOS_MEMORYBLOCK_BASE + BIOS_MEMORYBLOCK_SIZE)
|
2018-04-30 21:01:23 +01:00
|
|
|
#define BIOS_LOADEDMODULE_SIZE (sizeof(CIopBios::LOADEDMODULE) * CIopBios::MAX_LOADEDMODULE)
|
2022-01-25 15:15:42 -05:00
|
|
|
#define BIOS_INTSTACK_BASE (BIOS_LOADEDMODULE_BASE + BIOS_LOADEDMODULE_SIZE)
|
|
|
|
#define BIOS_INTSTACK_SIZE (0x800)
|
|
|
|
#define BIOS_CALCULATED_END (BIOS_INTSTACK_BASE + BIOS_INTSTACK_SIZE)
|
2018-04-30 21:01:23 +01:00
|
|
|
|
|
|
|
#define SYSCALL_EXITTHREAD 0x666
|
|
|
|
#define SYSCALL_RETURNFROMEXCEPTION 0x667
|
|
|
|
#define SYSCALL_RESCHEDULE 0x668
|
|
|
|
#define SYSCALL_SLEEPTHREAD 0x669
|
|
|
|
#define SYSCALL_PROCESSMODULESTART 0x66A
|
|
|
|
#define SYSCALL_FINISHMODULESTART 0x66B
|
|
|
|
#define SYSCALL_DELAYTHREADTICKS 0x66C
|
2009-02-04 02:15:56 +00:00
|
|
|
|
2015-04-09 01:02:46 -04:00
|
|
|
//This is the space needed to preserve at most four arguments in the stack frame (as per MIPS calling convention)
|
2018-04-30 21:01:23 +01:00
|
|
|
#define STACK_FRAME_RESERVE_SIZE 0x10
|
2012-09-23 02:44:14 +00:00
|
|
|
|
2019-12-20 21:27:21 -05:00
|
|
|
#define MODULE_ID_CDVD_EE_DRIVER 0x70000000
|
|
|
|
|
2023-06-08 17:12:43 -04:00
|
|
|
CIopBios::CIopBios(CMIPS& cpu, uint8* ram, uint8* spr)
|
2018-04-30 21:01:23 +01:00
|
|
|
: m_cpu(cpu)
|
|
|
|
, m_ram(ram)
|
|
|
|
, m_spr(spr)
|
|
|
|
, m_threadFinishAddress(0)
|
|
|
|
, m_returnFromExceptionAddress(0)
|
|
|
|
, m_idleFunctionAddress(0)
|
2022-02-24 11:37:55 -05:00
|
|
|
, m_moduleStarterProcAddress(0)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_alarmThreadProcAddress(0)
|
2018-12-20 12:36:53 -05:00
|
|
|
, m_vblankHandlerAddress(0)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_threads(reinterpret_cast<THREAD*>(&m_ram[BIOS_THREADS_BASE]), 1, MAX_THREAD)
|
|
|
|
, m_memoryBlocks(reinterpret_cast<Iop::MEMORYBLOCK*>(&ram[BIOS_MEMORYBLOCK_BASE]), 1, MAX_MEMORYBLOCK)
|
|
|
|
, m_semaphores(reinterpret_cast<SEMAPHORE*>(&m_ram[BIOS_SEMAPHORES_BASE]), 1, MAX_SEMAPHORE)
|
|
|
|
, m_eventFlags(reinterpret_cast<EVENTFLAG*>(&m_ram[BIOS_EVENTFLAGS_BASE]), 1, MAX_EVENTFLAG)
|
|
|
|
, m_intrHandlers(reinterpret_cast<INTRHANDLER*>(&m_ram[BIOS_INTRHANDLER_BASE]), 1, MAX_INTRHANDLER)
|
2018-07-17 12:41:13 -04:00
|
|
|
, m_vblankHandlers(reinterpret_cast<VBLANKHANDLER*>(&m_ram[BIOS_VBLANKHANDLER_BASE]), 1, MAX_VBLANKHANDLER)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_messageBoxes(reinterpret_cast<MESSAGEBOX*>(&m_ram[BIOS_MESSAGEBOX_BASE]), 1, MAX_MESSAGEBOX)
|
2019-04-28 18:07:48 -04:00
|
|
|
, m_fpls(reinterpret_cast<FPL*>(&m_ram[BIOS_FPL_BASE]), 1, MAX_FPL)
|
2018-04-30 21:01:23 +01:00
|
|
|
, m_vpls(reinterpret_cast<VPL*>(&m_ram[BIOS_VPL_BASE]), 1, MAX_VPL)
|
|
|
|
, m_loadedModules(reinterpret_cast<LOADEDMODULE*>(&m_ram[BIOS_LOADEDMODULE_BASE]), 1, MAX_LOADEDMODULE)
|
|
|
|
, m_currentThreadId(reinterpret_cast<uint32*>(m_ram + BIOS_CURRENT_THREAD_ID_BASE))
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
static_assert(BIOS_CALCULATED_END <= CIopBios::CONTROL_BLOCK_END, "Control block size is too small");
|
2019-11-26 13:19:31 +00:00
|
|
|
static_assert(BIOS_SYSTEM_INTRHANDLER_TABLE_BASE > CIopBios::CONTROL_BLOCK_START, "Intr handler table is outside reserved block");
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CIopBios::~CIopBios()
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
DeleteModules();
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2023-06-08 17:12:43 -04:00
|
|
|
void CIopBios::Reset(uint32 ramSize, const Iop::SifManPtr& sifMan)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2023-06-08 17:12:43 -04:00
|
|
|
m_ramSize = ramSize;
|
2023-06-09 12:51:41 -04:00
|
|
|
|
2022-10-15 11:32:30 -04:00
|
|
|
SetDefaultImageVersion(DEFAULT_IMAGE_VERSION);
|
2019-11-26 13:19:31 +00:00
|
|
|
PopulateSystemIntcHandlers();
|
|
|
|
|
2009-01-28 23:06:08 +00:00
|
|
|
//Assemble handlers
|
2008-10-20 22:19:56 +00:00
|
|
|
{
|
2009-01-26 02:53:10 +00:00
|
|
|
CMIPSAssembler assembler(reinterpret_cast<uint32*>(&m_ram[BIOS_HANDLERS_BASE]));
|
2008-10-20 22:19:56 +00:00
|
|
|
m_threadFinishAddress = AssembleThreadFinish(assembler);
|
2008-10-28 21:32:18 +00:00
|
|
|
m_returnFromExceptionAddress = AssembleReturnFromException(assembler);
|
|
|
|
m_idleFunctionAddress = AssembleIdleFunction(assembler);
|
2022-02-24 11:37:55 -05:00
|
|
|
m_moduleStarterProcAddress = AssembleModuleStarterProc(assembler);
|
2012-10-21 02:37:23 +00:00
|
|
|
m_alarmThreadProcAddress = AssembleAlarmThreadProc(assembler);
|
2018-07-17 12:41:13 -04:00
|
|
|
m_vblankHandlerAddress = AssembleVblankHandler(assembler);
|
2009-01-28 23:06:08 +00:00
|
|
|
assert(BIOS_HANDLERS_END > ((assembler.GetProgramSize() * 4) + BIOS_HANDLERS_BASE));
|
2008-10-20 22:19:56 +00:00
|
|
|
}
|
|
|
|
|
2008-12-05 17:13:25 +00:00
|
|
|
//0xBE00000 = Stupid constant to make FFX PSF happy
|
2009-01-26 02:53:10 +00:00
|
|
|
CurrentTime() = 0xBE00000;
|
2012-03-30 05:41:11 +00:00
|
|
|
ThreadLinkHead() = 0;
|
2015-11-04 01:03:32 -05:00
|
|
|
m_currentThreadId = -1;
|
2008-12-05 17:13:25 +00:00
|
|
|
|
2015-04-13 00:24:35 -04:00
|
|
|
m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE;
|
2008-10-28 01:48:21 +00:00
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
m_threads.FreeAll();
|
2009-01-30 22:10:15 +00:00
|
|
|
m_semaphores.FreeAll();
|
2012-03-30 05:41:11 +00:00
|
|
|
m_intrHandlers.FreeAll();
|
2014-07-04 02:53:10 -04:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
2012-03-30 05:41:11 +00:00
|
|
|
m_moduleTags.clear();
|
2014-07-04 02:53:10 -04:00
|
|
|
#endif
|
2012-03-30 05:41:11 +00:00
|
|
|
|
|
|
|
DeleteModules();
|
|
|
|
|
2016-01-13 22:18:10 -05:00
|
|
|
if(!sifMan)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_sifMan = std::make_shared<Iop::CSifManNull>();
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_sifMan = sifMan;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Register built-in modules
|
|
|
|
{
|
2019-09-16 18:29:18 -04:00
|
|
|
m_ioman = std::make_shared<Iop::CIoman>(*this, m_ram);
|
2012-03-30 05:41:11 +00:00
|
|
|
RegisterModule(m_ioman);
|
|
|
|
}
|
2014-08-02 21:23:25 -04:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_stdio = std::make_shared<Iop::CStdio>(m_ram, *m_ioman);
|
2014-08-02 21:23:25 -04:00
|
|
|
RegisterModule(m_stdio);
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-02-27 18:34:51 -05:00
|
|
|
m_sysmem = std::make_shared<Iop::CSysmem>(m_ram, CONTROL_BLOCK_END, m_ramSize, m_memoryBlocks, *m_stdio, *m_ioman, *m_sifMan);
|
2012-03-30 05:41:11 +00:00
|
|
|
RegisterModule(m_sysmem);
|
|
|
|
}
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_modload = std::make_shared<Iop::CModload>(*this, m_ram);
|
2012-03-30 05:41:11 +00:00
|
|
|
RegisterModule(m_modload);
|
|
|
|
}
|
|
|
|
{
|
2016-03-17 16:20:02 -07:00
|
|
|
RegisterModule(std::make_shared<Iop::CSysclib>(m_ram, m_spr, *m_stdio));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_loadcore = std::make_shared<Iop::CLoadcore>(*this, m_ram, *m_sifMan);
|
2012-10-24 06:39:29 +00:00
|
|
|
RegisterModule(m_loadcore);
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2015-02-07 23:06:08 -05:00
|
|
|
{
|
|
|
|
m_libsd = std::make_shared<Iop::CLibSd>();
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CThbase>(*this, m_ram));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CThmsgbx>(*this, m_ram));
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CThsema>(*this, m_ram));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2019-04-22 21:30:16 -04:00
|
|
|
{
|
|
|
|
RegisterModule(std::make_shared<Iop::CThfpool>(*this));
|
|
|
|
}
|
2016-02-28 15:55:45 -05:00
|
|
|
{
|
|
|
|
RegisterModule(std::make_shared<Iop::CThvpool>(*this));
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CThevent>(*this, m_ram));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2016-12-07 10:14:01 -05:00
|
|
|
{
|
|
|
|
RegisterModule(std::make_shared<Iop::CHeaplib>(*m_sysmem));
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CTimrman>(*this));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CIntrman>(*this, m_ram));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2020-03-23 07:19:43 -04:00
|
|
|
{
|
|
|
|
RegisterModule(std::make_shared<Iop::CDmacman>());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
RegisterModule(std::make_shared<Iop::CSecrman>());
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CVblank>(*this));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_cdvdman = std::make_shared<Iop::CCdvdman>(*this, m_ram);
|
2012-03-30 05:41:11 +00:00
|
|
|
RegisterModule(m_cdvdman);
|
|
|
|
}
|
2008-11-14 17:10:21 +00:00
|
|
|
{
|
|
|
|
RegisterModule(m_sifMan);
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_sifCmd = std::make_shared<Iop::CSifCmd>(*this, *m_sifMan, *m_sysmem, m_ram);
|
2012-03-30 05:41:11 +00:00
|
|
|
RegisterModule(m_sifCmd);
|
|
|
|
}
|
2008-10-20 04:14:13 +00:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2019-09-17 08:29:09 -04:00
|
|
|
m_fileIo = std::make_shared<Iop::CFileIo>(*this, m_ram, *m_sifMan, *m_ioman);
|
2015-01-26 03:39:44 -05:00
|
|
|
RegisterModule(m_fileIo);
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_cdvdfsv = std::make_shared<Iop::CCdvdfsv>(*m_sifMan, *m_cdvdman, m_ram);
|
2012-03-30 05:41:11 +00:00
|
|
|
RegisterModule(m_cdvdfsv);
|
|
|
|
}
|
2018-02-06 19:07:57 -05:00
|
|
|
{
|
2018-12-19 19:54:26 -05:00
|
|
|
m_mcserv = std::make_shared<Iop::CMcServ>(*this, *m_sifMan, *m_sifCmd, *m_sysmem, m_ram);
|
2018-02-06 19:07:57 -05:00
|
|
|
RegisterModule(m_mcserv);
|
|
|
|
}
|
2022-03-16 14:12:20 +01:00
|
|
|
{
|
|
|
|
m_powerOff = std::make_shared<Iop::CPowerOff>(*m_sifMan);
|
|
|
|
RegisterModule(m_powerOff);
|
|
|
|
}
|
2024-05-09 17:22:26 -04:00
|
|
|
{
|
2024-05-13 16:09:41 -04:00
|
|
|
m_usbd = std::make_shared<Iop::CUsbd>(*this, m_ram);
|
|
|
|
RegisterModule(m_usbd);
|
2024-05-09 17:22:26 -04:00
|
|
|
}
|
2022-02-28 13:35:13 -05:00
|
|
|
RegisterModule(std::make_shared<Iop::CIomanX>(*m_ioman));
|
2016-12-18 21:34:38 -05:00
|
|
|
//RegisterModule(std::make_shared<Iop::CNaplink>(*m_sifMan, *m_ioman));
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-13 23:13:43 -05:00
|
|
|
m_padman = std::make_shared<Iop::CPadMan>();
|
2016-01-16 18:43:41 -05:00
|
|
|
m_mtapman = std::make_shared<Iop::CMtapMan>();
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2018-09-11 17:53:00 -04:00
|
|
|
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:SIO2MAN", m_padman));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:PADMAN", m_padman));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:XSIO2MAN", m_padman));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:XPADMAN", m_padman));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:XMTAPMAN", m_mtapman));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:MCMAN", m_mcserv));
|
2023-01-05 19:19:41 -05:00
|
|
|
m_hleModules.insert(std::make_pair("rom0:MCMANO", m_mcserv));
|
2018-09-11 17:53:00 -04:00
|
|
|
m_hleModules.insert(std::make_pair("rom0:MCSERV", m_mcserv));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:XMCMAN", m_mcserv));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:XMCSERV", m_mcserv));
|
2023-01-05 19:19:41 -05:00
|
|
|
m_hleModules.insert(std::make_pair("rom0:CDVDMAN", m_cdvdman));
|
|
|
|
m_hleModules.insert(std::make_pair("rom0:CDVDFSV", m_cdvdfsv));
|
2018-09-11 17:53:00 -04:00
|
|
|
|
2008-10-20 04:14:13 +00:00
|
|
|
#endif
|
2008-05-26 00:04:29 +00:00
|
|
|
|
2014-12-12 20:20:51 +00:00
|
|
|
{
|
|
|
|
const int sifDmaBufferSize = 0x1000;
|
|
|
|
uint32 sifDmaBufferPtr = m_sysmem->AllocateMemory(sifDmaBufferSize, 0, 0);
|
|
|
|
m_sifMan->SetDmaBuffer(sifDmaBufferPtr, sifDmaBufferSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const int sifCmdBufferSize = 0x100;
|
|
|
|
uint32 sifCmdBufferPtr = m_sysmem->AllocateMemory(sifCmdBufferSize, 0, 0);
|
|
|
|
m_sifMan->SetCmdBuffer(sifCmdBufferPtr, sifCmdBufferSize);
|
|
|
|
}
|
|
|
|
|
2024-09-11 16:57:32 -04:00
|
|
|
m_sifMan->PrepareModuleData(m_ram, *m_sysmem);
|
2008-11-25 02:00:42 +00:00
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
InitializeModuleStarter();
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
Reschedule();
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2009-01-28 23:06:08 +00:00
|
|
|
uint32& CIopBios::ThreadLinkHead() const
|
2009-01-26 02:53:10 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
return *reinterpret_cast<uint32*>(m_ram + BIOS_THREAD_LINK_HEAD_BASE);
|
2009-01-26 02:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64& CIopBios::CurrentTime() const
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
return *reinterpret_cast<uint64*>(m_ram + BIOS_CURRENT_TIME_BASE);
|
2009-01-26 02:53:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
uint32& CIopBios::ModuleStartRequestHead() const
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2015-06-27 00:38:30 -04:00
|
|
|
return *reinterpret_cast<uint32*>(m_ram + BIOS_MODULESTARTREQUEST_HEAD_BASE);
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
uint32& CIopBios::ModuleStartRequestFree() const
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2015-06-27 00:38:30 -04:00
|
|
|
return *reinterpret_cast<uint32*>(m_ram + BIOS_MODULESTARTREQUEST_FREE_BASE);
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2024-08-07 13:00:19 -04:00
|
|
|
void CIopBios::PreLoadState()
|
|
|
|
{
|
|
|
|
//Remove all dynamic modules
|
|
|
|
for(auto modulePairIterator = m_modules.begin();
|
|
|
|
modulePairIterator != m_modules.end();)
|
|
|
|
{
|
|
|
|
if(dynamic_cast<Iop::CDynamic*>(modulePairIterator->second.get()) != nullptr)
|
|
|
|
{
|
|
|
|
modulePairIterator = m_modules.erase(modulePairIterator);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
modulePairIterator++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_sifCmd->ClearServers();
|
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
void CIopBios::SaveState(Framework::CZipArchiveWriter& archive)
|
2009-01-26 02:53:10 +00:00
|
|
|
{
|
2023-12-07 15:10:58 -05:00
|
|
|
auto modulesFile = std::make_unique<CRegisterStateCollectionFile>(STATE_MODULES);
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-15 00:36:04 -05:00
|
|
|
for(const auto& modulePair : m_modules)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-01-15 00:36:04 -05:00
|
|
|
if(auto dynamicModule = std::dynamic_pointer_cast<Iop::CDynamic>(modulePair.second))
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2023-12-07 15:10:58 -05:00
|
|
|
CRegisterState moduleState;
|
2016-01-15 00:36:04 -05:00
|
|
|
{
|
2024-07-22 12:54:35 -04:00
|
|
|
uint32 importTableAddress = reinterpret_cast<const uint8*>(dynamicModule->GetExportTable()) - m_ram;
|
2023-12-07 15:10:58 -05:00
|
|
|
moduleState.SetRegister32(STATE_MODULE_IMPORT_TABLE_ADDRESS, importTableAddress);
|
2016-01-15 00:36:04 -05:00
|
|
|
}
|
2023-12-07 15:47:43 -05:00
|
|
|
modulesFile->InsertRegisterState(dynamicModule->GetId().c_str(), std::move(moduleState));
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-07-23 17:22:18 -04:00
|
|
|
archive.InsertFile(std::move(modulesFile));
|
2009-02-06 05:10:51 +00:00
|
|
|
|
2020-09-23 18:02:40 -04:00
|
|
|
auto builtInModules = GetBuiltInModules();
|
|
|
|
for(const auto& module : builtInModules)
|
|
|
|
{
|
|
|
|
module->SaveState(archive);
|
|
|
|
}
|
2021-12-01 14:32:18 -05:00
|
|
|
|
2023-07-23 17:22:18 -04:00
|
|
|
archive.InsertFile(std::make_unique<CMemoryStateFile>(STATE_MODULESTARTREQUESTS, m_moduleStartRequests, sizeof(m_moduleStartRequests)));
|
2009-01-26 02:53:10 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
void CIopBios::LoadState(Framework::CZipArchiveReader& archive)
|
2009-01-26 02:53:10 +00:00
|
|
|
{
|
2020-09-23 18:02:40 -04:00
|
|
|
auto builtInModules = GetBuiltInModules();
|
|
|
|
for(const auto& module : builtInModules)
|
|
|
|
{
|
|
|
|
module->LoadState(archive);
|
|
|
|
}
|
|
|
|
|
2023-12-07 15:10:58 -05:00
|
|
|
CRegisterStateCollectionFile modulesFile(*archive.BeginReadFile(STATE_MODULES));
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2023-12-07 15:10:58 -05:00
|
|
|
for(const auto& moduleStatePair : modulesFile)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2023-12-07 15:10:58 -05:00
|
|
|
const auto& moduleState(moduleStatePair.second);
|
|
|
|
uint32 importTableAddress = moduleState.GetRegister32(STATE_MODULE_IMPORT_TABLE_ADDRESS);
|
2016-01-13 22:18:10 -05:00
|
|
|
auto module = std::make_shared<Iop::CDynamic>(reinterpret_cast<uint32*>(m_ram + importTableAddress));
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED bool result = RegisterModule(module);
|
2016-01-15 00:36:04 -05:00
|
|
|
assert(result);
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-06 05:10:51 +00:00
|
|
|
|
2021-12-01 14:32:18 -05:00
|
|
|
archive.BeginReadFile(STATE_MODULESTARTREQUESTS)->Read(m_moduleStartRequests, sizeof(m_moduleStartRequests));
|
|
|
|
|
2016-03-27 17:54:30 -04:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2018-09-11 17:53:00 -04:00
|
|
|
//Make sure HLE modules are properly registered
|
|
|
|
for(const auto& loadedModule : m_loadedModules)
|
|
|
|
{
|
|
|
|
if(!loadedModule) continue;
|
|
|
|
if(loadedModule->state == MODULE_STATE::HLE)
|
|
|
|
{
|
|
|
|
auto moduleIterator = std::find_if(m_hleModules.begin(), m_hleModules.end(),
|
|
|
|
[&](const auto& modulePair) {
|
2020-09-22 17:44:07 -04:00
|
|
|
return strcmp(loadedModule->name, modulePair.second->GetId().c_str()) == 0;
|
2018-09-11 17:53:00 -04:00
|
|
|
});
|
|
|
|
assert(moduleIterator != std::end(m_hleModules));
|
|
|
|
if(moduleIterator != std::end(m_hleModules))
|
|
|
|
{
|
|
|
|
RegisterHleModule(moduleIterator->second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 17:54:30 -04:00
|
|
|
#endif
|
2013-02-24 08:59:50 +00:00
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
2014-08-16 21:42:19 -04:00
|
|
|
m_cpu.m_analysis->Clear();
|
2013-02-24 08:59:50 +00:00
|
|
|
for(const auto& moduleTag : m_moduleTags)
|
|
|
|
{
|
2014-08-16 21:42:19 -04:00
|
|
|
m_cpu.m_analysis->Analyse(moduleTag.begin, moduleTag.end);
|
2013-02-24 08:59:50 +00:00
|
|
|
}
|
|
|
|
#endif
|
2009-01-26 02:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CIopBios::IsIdle()
|
|
|
|
{
|
2010-03-30 04:19:45 +00:00
|
|
|
return (m_cpu.m_State.nPC == m_idleFunctionAddress);
|
2009-01-26 02:53:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
void CIopBios::InitializeModuleStarter()
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2021-12-01 14:32:18 -05:00
|
|
|
memset(m_moduleStartRequests, 0, sizeof(m_moduleStartRequests));
|
|
|
|
|
|
|
|
ModuleStartRequestHead() = MODULESTARTREQUEST::INVALID_PTR;
|
|
|
|
ModuleStartRequestFree() = 0;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
|
|
|
//Initialize Module Load Request Free List
|
2015-06-27 00:38:30 -04:00
|
|
|
for(unsigned int i = 0; i < (MAX_MODULESTARTREQUEST - 1); i++)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2021-12-01 14:32:18 -05:00
|
|
|
auto moduleStartRequest = &m_moduleStartRequests[i];
|
|
|
|
moduleStartRequest->nextPtr = i + 1;
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2021-12-01 14:32:18 -05:00
|
|
|
m_moduleStartRequests[MAX_MODULESTARTREQUEST - 1].nextPtr = MODULESTARTREQUEST::INVALID_PTR;
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 13:59:36 -05:00
|
|
|
void CIopBios::RequestModuleStart(MODULESTARTREQUEST_SOURCE requestSource, bool stopRequest, uint32 moduleId, const char* path, const char* args, unsigned int argsLength)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2015-06-27 00:38:30 -04:00
|
|
|
uint32 requestPtr = ModuleStartRequestFree();
|
2021-12-01 14:32:18 -05:00
|
|
|
assert(requestPtr != MODULESTARTREQUEST::INVALID_PTR);
|
|
|
|
if(requestPtr == MODULESTARTREQUEST::INVALID_PTR)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2021-03-05 16:55:34 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Too many modules to be loaded.");
|
2012-09-18 03:21:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-01 14:32:18 -05:00
|
|
|
auto moduleStartRequest = &m_moduleStartRequests[requestPtr];
|
2012-09-18 03:21:43 +00:00
|
|
|
|
|
|
|
//Unlink from free list and link in active list (at the end)
|
|
|
|
{
|
2015-06-27 00:38:30 -04:00
|
|
|
ModuleStartRequestFree() = moduleStartRequest->nextPtr;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
uint32* currentPtr = &ModuleStartRequestHead();
|
2021-12-01 14:32:18 -05:00
|
|
|
while(*currentPtr != MODULESTARTREQUEST::INVALID_PTR)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2021-12-01 14:32:18 -05:00
|
|
|
auto currentModuleLoadRequest = &m_moduleStartRequests[*currentPtr];
|
2012-09-18 03:21:43 +00:00
|
|
|
currentPtr = ¤tModuleLoadRequest->nextPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
*currentPtr = requestPtr;
|
|
|
|
|
2021-12-01 14:32:18 -05:00
|
|
|
moduleStartRequest->nextPtr = MODULESTARTREQUEST::INVALID_PTR;
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 13:59:36 -05:00
|
|
|
int32 requesterThreadId = -1;
|
|
|
|
if(requestSource == MODULESTARTREQUEST_SOURCE::LOCAL)
|
|
|
|
{
|
|
|
|
requesterThreadId = m_currentThreadId;
|
|
|
|
SleepThread();
|
|
|
|
}
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
moduleStartRequest->moduleId = moduleId;
|
|
|
|
moduleStartRequest->stopRequest = stopRequest;
|
2022-02-22 13:59:36 -05:00
|
|
|
moduleStartRequest->requesterThreadId = requesterThreadId;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
assert((strlen(path) + 1) <= MODULESTARTREQUEST::MAX_PATH_SIZE);
|
|
|
|
strncpy(moduleStartRequest->path, path, MODULESTARTREQUEST::MAX_PATH_SIZE);
|
|
|
|
moduleStartRequest->path[MODULESTARTREQUEST::MAX_PATH_SIZE - 1] = 0;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
memcpy(moduleStartRequest->args, args, argsLength);
|
2018-04-30 21:01:23 +01:00
|
|
|
moduleStartRequest->argsLength = argsLength;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2022-02-24 11:37:55 -05:00
|
|
|
uint32 moduleStarterThreadId = TriggerCallback(m_moduleStarterProcAddress);
|
2016-09-10 21:38:52 -04:00
|
|
|
//Make sure thread runs at proper priority (Burnout 3 changes priority)
|
2022-02-24 11:37:55 -05:00
|
|
|
ChangeThreadPriority(moduleStarterThreadId, MODULE_INIT_PRIORITY);
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
void CIopBios::ProcessModuleStart()
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2015-05-02 22:11:22 -04:00
|
|
|
static const auto pushToStack =
|
2018-04-30 21:01:23 +01:00
|
|
|
[](uint8* dst, uint32& stackAddress, const uint8* src, uint32 size) {
|
|
|
|
uint32 fixedSize = ((size + 0x3) & ~0x3);
|
|
|
|
uint32 copyAddress = stackAddress - size;
|
|
|
|
stackAddress -= fixedSize;
|
|
|
|
memcpy(dst + copyAddress, src, size);
|
|
|
|
return copyAddress;
|
|
|
|
};
|
2015-05-02 22:11:22 -04:00
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
uint32 requestPtr = ModuleStartRequestHead();
|
2021-12-01 14:32:18 -05:00
|
|
|
assert(requestPtr != MODULESTARTREQUEST::INVALID_PTR);
|
|
|
|
if(requestPtr == MODULESTARTREQUEST::INVALID_PTR)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Asked to load module when none was requested.");
|
2012-09-18 03:21:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-01 14:32:18 -05:00
|
|
|
auto moduleStartRequest = &m_moduleStartRequests[requestPtr];
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2012-09-18 03:21:43 +00:00
|
|
|
//Unlink from active list and link in free list
|
|
|
|
{
|
2015-06-27 01:27:28 -04:00
|
|
|
ModuleStartRequestHead() = moduleStartRequest->nextPtr;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2015-06-27 01:27:28 -04:00
|
|
|
moduleStartRequest->nextPtr = ModuleStartRequestFree();
|
2015-06-27 00:38:30 -04:00
|
|
|
ModuleStartRequestFree() = requestPtr;
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 01:27:28 -04:00
|
|
|
auto loadedModule = m_loadedModules[moduleStartRequest->moduleId];
|
2015-06-27 00:54:47 -04:00
|
|
|
assert(loadedModule != nullptr);
|
|
|
|
|
2012-09-18 03:21:43 +00:00
|
|
|
//Patch loader thread context with proper info to invoke module entry proc
|
2015-06-27 19:21:13 -04:00
|
|
|
if(!moduleStartRequest->stopRequest)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2015-06-27 01:27:28 -04:00
|
|
|
const char* path = moduleStartRequest->path;
|
|
|
|
const char* args = moduleStartRequest->args;
|
|
|
|
uint32 argsLength = moduleStartRequest->argsLength;
|
2012-09-18 03:21:43 +00:00
|
|
|
|
|
|
|
typedef std::vector<uint32> ParamListType;
|
|
|
|
ParamListType paramList;
|
|
|
|
|
2015-05-02 22:11:22 -04:00
|
|
|
paramList.push_back(pushToStack(
|
2018-04-30 21:01:23 +01:00
|
|
|
m_ram,
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::SP].nV0,
|
|
|
|
reinterpret_cast<const uint8*>(path),
|
|
|
|
static_cast<uint32>(strlen(path)) + 1));
|
2012-09-18 03:21:43 +00:00
|
|
|
if(argsLength != 0)
|
|
|
|
{
|
2015-05-02 22:11:22 -04:00
|
|
|
uint32 stackArgsBase = pushToStack(
|
2018-04-30 21:01:23 +01:00
|
|
|
m_ram,
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::SP].nV0,
|
|
|
|
reinterpret_cast<const uint8*>(args),
|
|
|
|
argsLength);
|
2012-09-18 03:21:43 +00:00
|
|
|
unsigned int argsPos = 0;
|
|
|
|
while(argsPos < argsLength)
|
|
|
|
{
|
2015-05-02 22:11:22 -04:00
|
|
|
uint32 argAddress = stackArgsBase + argsPos;
|
|
|
|
const char* arg = reinterpret_cast<const char*>(m_ram) + argAddress;
|
2012-09-18 03:21:43 +00:00
|
|
|
unsigned int argLength = static_cast<unsigned int>(strlen(arg)) + 1;
|
|
|
|
argsPos += argLength;
|
|
|
|
paramList.push_back(argAddress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::A0].nV0 = static_cast<uint32>(paramList.size());
|
2018-11-08 08:37:25 -05:00
|
|
|
//Push an additional null parameter. This is needed by Chessmaster who reads
|
|
|
|
//out of the argv bounds (reads from argv[argc])
|
|
|
|
paramList.push_back(0);
|
2015-05-02 22:11:22 -04:00
|
|
|
for(auto param = paramList.rbegin(); paramList.rend() != param; param++)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
2015-05-02 22:11:22 -04:00
|
|
|
m_cpu.m_State.nGPR[CMIPS::A1].nV0 = pushToStack(
|
2018-04-30 21:01:23 +01:00
|
|
|
m_ram,
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::SP].nV0,
|
|
|
|
reinterpret_cast<const uint8*>(&(*param)),
|
|
|
|
4);
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-27 19:21:13 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
//When invoking entry routine for module stop, A0 needs to be -1
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::A0].nD0 = static_cast<int32>(-1);
|
|
|
|
}
|
|
|
|
|
2022-02-24 11:37:55 -05:00
|
|
|
//Allocate stack space for spilling params
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::SP].nV0 -= STACK_FRAME_RESERVE_SIZE;
|
2015-06-27 19:21:13 -04:00
|
|
|
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::S0].nV0 = moduleStartRequest->moduleId;
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::S1].nV0 = moduleStartRequest->stopRequest;
|
2022-02-22 13:59:36 -05:00
|
|
|
m_cpu.m_State.nGPR[CMIPS::S2].nV0 = moduleStartRequest->requesterThreadId;
|
2015-06-27 19:21:13 -04:00
|
|
|
m_cpu.m_State.nGPR[CMIPS::GP].nV0 = loadedModule->gp;
|
|
|
|
m_cpu.m_State.nGPR[CMIPS::RA].nV0 = m_cpu.m_State.nPC;
|
|
|
|
m_cpu.m_State.nPC = loadedModule->entryPoint;
|
2012-09-18 03:21:43 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
void CIopBios::FinishModuleStart()
|
2014-07-10 23:43:29 -04:00
|
|
|
{
|
2015-06-27 19:21:13 -04:00
|
|
|
uint32 moduleId = m_cpu.m_State.nGPR[CMIPS::S0].nV0;
|
|
|
|
uint32 stopRequest = m_cpu.m_State.nGPR[CMIPS::S1].nV0;
|
2022-02-22 13:59:36 -05:00
|
|
|
int32 requesterThreadId = static_cast<int32>(m_cpu.m_State.nGPR[CMIPS::S2].nV0);
|
2015-06-27 01:42:04 -04:00
|
|
|
auto moduleResidentState = static_cast<MODULE_RESIDENT_STATE>(m_cpu.m_State.nGPR[CMIPS::A0].nV0 & 0x3);
|
|
|
|
|
|
|
|
auto loadedModule = m_loadedModules[moduleId];
|
|
|
|
assert(loadedModule != nullptr);
|
2015-06-27 19:21:13 -04:00
|
|
|
|
|
|
|
if(!stopRequest)
|
|
|
|
{
|
|
|
|
assert(loadedModule->state == MODULE_STATE::STOPPED);
|
2018-04-30 21:01:23 +01:00
|
|
|
loadedModule->state = MODULE_STATE::STARTED;
|
|
|
|
loadedModule->residentState = moduleResidentState;
|
2015-07-03 22:17:22 -04:00
|
|
|
|
|
|
|
OnModuleStarted(moduleId);
|
2015-06-27 19:21:13 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(moduleResidentState == MODULE_RESIDENT_STATE::NO_RESIDENT_END);
|
|
|
|
assert(loadedModule->state == MODULE_STATE::STARTED);
|
|
|
|
loadedModule->state = MODULE_STATE::STOPPED;
|
|
|
|
}
|
2015-06-27 01:42:04 -04:00
|
|
|
|
2015-06-14 20:20:12 -04:00
|
|
|
//Make sure interrupts are enabled at the end of this
|
|
|
|
//some games disable interrupts but never enable them back! (The Mark of Kri)
|
|
|
|
m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE;
|
|
|
|
|
2022-02-22 13:59:36 -05:00
|
|
|
if(requesterThreadId == -1)
|
|
|
|
{
|
|
|
|
//If requesterthreadId is -1, we assume that it came from LOADCORE SIF module
|
|
|
|
//We need to notify the EE that the load request is over
|
|
|
|
m_sifMan->SendCallReply(Iop::CLoadcore::MODULE_ID, nullptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WakeupThread(requesterThreadId, false);
|
|
|
|
}
|
2022-02-24 12:27:32 -05:00
|
|
|
|
2022-02-24 11:37:55 -05:00
|
|
|
//Finish our thread
|
|
|
|
ExitThread();
|
2014-07-10 23:43:29 -04:00
|
|
|
}
|
|
|
|
|
2022-02-22 13:26:35 -05:00
|
|
|
int32 CIopBios::LoadModuleFromPath(const char* path, uint32 loadAddress, bool ownsMemory)
|
2008-10-20 04:14:13 +00:00
|
|
|
{
|
2016-01-16 20:46:14 -05:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2018-09-11 17:53:00 -04:00
|
|
|
//This is needed by some homebrew (ie.: doom.elf) that load modules from BIOS
|
|
|
|
auto hleModuleIterator = m_hleModules.find(path);
|
|
|
|
if(hleModuleIterator != std::end(m_hleModules))
|
2018-02-06 19:07:57 -05:00
|
|
|
{
|
2018-09-11 17:53:00 -04:00
|
|
|
return LoadHleModule(hleModuleIterator->second);
|
2018-02-06 19:07:57 -05:00
|
|
|
}
|
2016-01-16 20:46:14 -05:00
|
|
|
#endif
|
2012-09-05 20:40:23 +00:00
|
|
|
uint32 handle = m_ioman->Open(Iop::Ioman::CDevice::OPEN_FLAG_RDONLY, path);
|
2012-03-30 05:41:11 +00:00
|
|
|
if(handle & 0x80000000)
|
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Tried to load '%s' which couldn't be found.\r\n", path);
|
2015-05-01 21:47:26 -04:00
|
|
|
return -1;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2019-09-16 18:50:28 -04:00
|
|
|
Iop::Ioman::CScopedFile file(handle, *m_ioman);
|
2016-12-11 23:32:14 -05:00
|
|
|
auto stream = m_ioman->GetFileStream(file);
|
2022-07-22 17:01:59 -04:00
|
|
|
CElf32File module(*stream);
|
2022-02-22 13:26:35 -05:00
|
|
|
return LoadModule(module, path, loadAddress, ownsMemory);
|
2008-10-20 04:14:13 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 13:26:35 -05:00
|
|
|
int32 CIopBios::LoadModuleFromAddress(uint32 modulePtr, uint32 loadAddress, bool ownsMemory)
|
2008-10-20 04:14:13 +00:00
|
|
|
{
|
2022-07-22 17:01:59 -04:00
|
|
|
CELF32 module(m_ram + modulePtr);
|
2022-02-22 13:26:35 -05:00
|
|
|
return LoadModule(module, "", loadAddress, ownsMemory);
|
2008-10-20 04:14:13 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 22:17:22 -04:00
|
|
|
int32 CIopBios::LoadModuleFromHost(uint8* modulePtr)
|
|
|
|
{
|
2022-07-22 17:01:59 -04:00
|
|
|
CELF32 module(modulePtr);
|
2022-02-22 13:26:35 -05:00
|
|
|
return LoadModule(module, "", ~0U, true);
|
2015-07-03 22:17:22 -04:00
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
int32 CIopBios::LoadModule(CELF32& elf, const char* path, uint32 loadAddress, bool ownsMemory)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
//Find .iopmod section
|
2022-03-13 16:04:09 -04:00
|
|
|
const auto& header = elf.GetHeader();
|
2012-03-30 05:41:11 +00:00
|
|
|
const IOPMOD* iopMod = NULL;
|
|
|
|
for(unsigned int i = 0; i < header.nSectHeaderCount; i++)
|
|
|
|
{
|
2022-03-13 16:04:09 -04:00
|
|
|
auto sectionHeader = elf.GetSection(i);
|
2012-03-30 05:41:11 +00:00
|
|
|
if(sectionHeader->nType != IOPMOD_SECTION_ID) continue;
|
|
|
|
iopMod = reinterpret_cast<const IOPMOD*>(elf.GetSectionData(i));
|
|
|
|
}
|
|
|
|
|
2022-10-16 16:18:47 -04:00
|
|
|
std::string moduleName = iopMod ? iopMod->moduleName : "";
|
|
|
|
if(moduleName.empty())
|
|
|
|
{
|
|
|
|
moduleName = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
|
|
|
auto hleModuleIterator = m_hleModules.find(moduleName);
|
|
|
|
if(hleModuleIterator != std::end(m_hleModules))
|
|
|
|
{
|
|
|
|
return LoadHleModule(hleModuleIterator->second);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32 loadedModuleId = m_loadedModules.Allocate();
|
|
|
|
assert(loadedModuleId != -1);
|
|
|
|
if(loadedModuleId == -1) return -1;
|
|
|
|
|
|
|
|
auto loadedModule = m_loadedModules[loadedModuleId];
|
|
|
|
loadedModule->ownsMemory = ownsMemory;
|
|
|
|
|
|
|
|
ExecutableRange moduleRange;
|
|
|
|
uint32 entryPoint = LoadExecutable(elf, moduleRange, loadAddress);
|
|
|
|
|
2019-06-26 07:38:58 -04:00
|
|
|
assert(iopMod);
|
|
|
|
if(iopMod != nullptr)
|
|
|
|
{
|
|
|
|
//Clear BSS section
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED uint32 dataSectPos = iopMod->textSectionSize;
|
2019-06-26 07:38:58 -04:00
|
|
|
uint32 bssSectPos = iopMod->textSectionSize + iopMod->dataSectionSize;
|
2021-10-11 10:43:52 -04:00
|
|
|
uint32 bssSectSize = iopMod->bssSectionSize;
|
|
|
|
uint32 moduleSize = moduleRange.second - moduleRange.first;
|
|
|
|
if(bssSectSize == 0)
|
|
|
|
{
|
|
|
|
//Some games (Kya: Dark Lineage) seem to not report a proper value for bss size.
|
|
|
|
//Try to compute its value so we can properly clear the section.
|
|
|
|
assert(moduleSize >= bssSectPos);
|
|
|
|
bssSectSize = moduleSize - bssSectPos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Just make sure that everything checks out
|
2023-05-02 08:56:25 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED uint32 totalSize = bssSectPos + bssSectSize;
|
2021-10-11 10:43:52 -04:00
|
|
|
assert(totalSize == moduleSize);
|
|
|
|
}
|
|
|
|
memset(m_ram + moduleRange.first + bssSectPos, 0, bssSectSize);
|
2019-06-26 07:38:58 -04:00
|
|
|
}
|
|
|
|
|
2015-05-01 21:47:26 -04:00
|
|
|
//Fill in module info
|
|
|
|
strncpy(loadedModule->name, moduleName.c_str(), LOADEDMODULE::MAX_NAME_SIZE);
|
2021-02-20 08:34:12 -05:00
|
|
|
loadedModule->version = iopMod->moduleVersion;
|
2018-04-30 21:01:23 +01:00
|
|
|
loadedModule->start = moduleRange.first;
|
|
|
|
loadedModule->end = moduleRange.second;
|
|
|
|
loadedModule->entryPoint = entryPoint;
|
|
|
|
loadedModule->gp = iopMod ? (iopMod->gp + moduleRange.first) : 0;
|
|
|
|
loadedModule->state = MODULE_STATE::STOPPED;
|
2014-07-05 01:12:41 -04:00
|
|
|
|
2014-07-04 01:55:09 -04:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
PrepareModuleDebugInfo(elf, moduleRange, moduleName, path);
|
2008-01-15 20:27:44 +00:00
|
|
|
#endif
|
|
|
|
|
2020-04-03 08:50:55 -04:00
|
|
|
OnModuleLoaded(loadedModule->name);
|
|
|
|
|
2011-02-26 22:42:59 +00:00
|
|
|
//Patch for Shadow Hearts PSF set --------------------------------
|
|
|
|
if(strstr(path, "RSSD_patchmore.IRX") != NULL)
|
|
|
|
{
|
|
|
|
const uint32 patchAddress = moduleRange.first + 0xCE0;
|
|
|
|
uint32 instruction = m_cpu.m_pMemoryMap->GetWord(patchAddress);
|
|
|
|
if(instruction == 0x1200FFFB)
|
|
|
|
{
|
|
|
|
m_cpu.m_pMemoryMap->SetWord(patchAddress, 0x1000FFFB);
|
|
|
|
}
|
|
|
|
}
|
2012-09-18 03:21:43 +00:00
|
|
|
|
2015-05-01 21:47:26 -04:00
|
|
|
return loadedModuleId;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 19:21:13 -04:00
|
|
|
int32 CIopBios::UnloadModule(uint32 loadedModuleId)
|
|
|
|
{
|
2019-12-20 21:27:21 -05:00
|
|
|
if(loadedModuleId == MODULE_ID_CDVD_EE_DRIVER)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-27 19:21:13 -04:00
|
|
|
auto loadedModule = m_loadedModules[loadedModuleId];
|
|
|
|
if(loadedModule == nullptr)
|
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "UnloadModule failed because specified module (%d) doesn't exist.\r\n",
|
|
|
|
loadedModuleId);
|
2015-06-27 19:21:13 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(loadedModule->state != MODULE_STATE::STOPPED)
|
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "UnloadModule failed because specified module (%d) wasn't stopped.\r\n",
|
|
|
|
loadedModuleId);
|
2015-06-27 19:21:13 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-01-22 14:11:51 -05:00
|
|
|
//TODO: Remove module from IOP module list?
|
|
|
|
//TODO: Invalidate MIPS analysis range?
|
2018-07-21 20:49:58 -04:00
|
|
|
m_cpu.m_executor->ClearActiveBlocksInRange(loadedModule->start, loadedModule->end, false);
|
2017-01-22 14:11:51 -05:00
|
|
|
|
2022-02-22 13:26:35 -05:00
|
|
|
if(loadedModule->ownsMemory)
|
|
|
|
{
|
|
|
|
//TODO: Check return value here.
|
|
|
|
m_sysmem->FreeMemory(loadedModule->start);
|
|
|
|
}
|
2015-06-27 19:21:13 -04:00
|
|
|
m_loadedModules.Free(loadedModuleId);
|
|
|
|
return loadedModuleId;
|
|
|
|
}
|
|
|
|
|
2022-02-22 13:59:36 -05:00
|
|
|
int32 CIopBios::StartModule(MODULESTARTREQUEST_SOURCE requestSource, uint32 loadedModuleId, const char* path, const char* args, uint32 argsLength)
|
2014-07-05 01:12:41 -04:00
|
|
|
{
|
2015-05-01 21:47:26 -04:00
|
|
|
auto loadedModule = m_loadedModules[loadedModuleId];
|
|
|
|
if(loadedModule == nullptr)
|
2014-07-05 01:12:41 -04:00
|
|
|
{
|
2015-05-01 21:47:26 -04:00
|
|
|
return -1;
|
2014-07-05 01:12:41 -04:00
|
|
|
}
|
2016-01-16 20:46:14 -05:00
|
|
|
if(loadedModule->state == MODULE_STATE::HLE)
|
|
|
|
{
|
|
|
|
//HLE modules don't need to be started
|
|
|
|
return loadedModuleId;
|
|
|
|
}
|
2015-06-27 19:21:13 -04:00
|
|
|
assert(loadedModule->state == MODULE_STATE::STOPPED);
|
2022-02-22 13:59:36 -05:00
|
|
|
RequestModuleStart(requestSource, false, loadedModuleId, path, args, argsLength);
|
2015-06-27 19:21:13 -04:00
|
|
|
return loadedModuleId;
|
|
|
|
}
|
|
|
|
|
2022-02-22 13:59:36 -05:00
|
|
|
int32 CIopBios::StopModule(MODULESTARTREQUEST_SOURCE requestSource, uint32 loadedModuleId)
|
2015-06-27 19:21:13 -04:00
|
|
|
{
|
|
|
|
auto loadedModule = m_loadedModules[loadedModuleId];
|
|
|
|
if(loadedModule == nullptr)
|
|
|
|
{
|
2021-03-05 16:55:34 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "StopModule failed because specified module (%d) doesn't exist.\r\n",
|
|
|
|
loadedModuleId);
|
2015-06-27 19:21:13 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(loadedModule->state != MODULE_STATE::STARTED)
|
|
|
|
{
|
2021-03-05 16:55:34 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "StopModule failed because specified module (%d) wasn't started.\r\n",
|
|
|
|
loadedModuleId);
|
2015-06-27 19:21:13 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(loadedModule->residentState != MODULE_RESIDENT_STATE::REMOVABLE_RESIDENT_END)
|
|
|
|
{
|
2021-03-05 16:55:34 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "StopModule failed because specified module (%d) isn't removable.\r\n",
|
|
|
|
loadedModuleId);
|
2015-06-27 19:21:13 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2022-02-22 13:59:36 -05:00
|
|
|
RequestModuleStart(requestSource, true, loadedModuleId, "other", nullptr, 0);
|
2015-05-01 21:47:26 -04:00
|
|
|
return loadedModuleId;
|
2014-07-05 01:12:41 -04:00
|
|
|
}
|
|
|
|
|
2019-12-20 21:27:21 -05:00
|
|
|
bool CIopBios::CanStopModule(uint32 loadedModuleId) const
|
|
|
|
{
|
|
|
|
return (loadedModuleId != MODULE_ID_CDVD_EE_DRIVER);
|
|
|
|
}
|
|
|
|
|
2016-01-16 20:46:14 -05:00
|
|
|
bool CIopBios::IsModuleHle(uint32 loadedModuleId) const
|
|
|
|
{
|
|
|
|
auto loadedModule = m_loadedModules[loadedModuleId];
|
|
|
|
if(!loadedModule)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return (loadedModule->state == MODULE_STATE::HLE);
|
|
|
|
}
|
|
|
|
|
2015-05-01 21:47:26 -04:00
|
|
|
int32 CIopBios::SearchModuleByName(const char* moduleName) const
|
2014-07-05 01:12:41 -04:00
|
|
|
{
|
2015-05-01 21:47:26 -04:00
|
|
|
for(unsigned int i = 0; i < MAX_LOADEDMODULE; i++)
|
2014-07-05 01:12:41 -04:00
|
|
|
{
|
2015-05-01 21:47:26 -04:00
|
|
|
auto loadedModule = m_loadedModules[i];
|
|
|
|
if(loadedModule == nullptr) continue;
|
2014-07-05 01:12:41 -04:00
|
|
|
if(!strcmp(loadedModule->name, moduleName))
|
|
|
|
{
|
2015-05-01 21:47:26 -04:00
|
|
|
return i;
|
2014-07-05 01:12:41 -04:00
|
|
|
}
|
|
|
|
}
|
2019-12-20 21:27:21 -05:00
|
|
|
if(!strcmp(moduleName, "cdvd_ee_driver"))
|
|
|
|
{
|
|
|
|
return MODULE_ID_CDVD_EE_DRIVER;
|
|
|
|
}
|
2021-02-18 20:25:39 -05:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MODULE;
|
2014-07-05 01:12:41 -04:00
|
|
|
}
|
|
|
|
|
2021-02-20 08:34:12 -05:00
|
|
|
int32 CIopBios::ReferModuleStatus(uint32 moduleId, uint32 statusPtr)
|
|
|
|
{
|
|
|
|
auto loadedModule = m_loadedModules[moduleId];
|
|
|
|
if(loadedModule == nullptr)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MODULE;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto moduleStatus = reinterpret_cast<MODULE_INFO*>(m_ram + statusPtr);
|
|
|
|
strncpy(moduleStatus->name, loadedModule->name, MODULE_INFO::MAX_NAME_SIZE);
|
|
|
|
moduleStatus->version = loadedModule->version;
|
|
|
|
moduleStatus->id = moduleId;
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2022-10-10 12:14:21 -04:00
|
|
|
void CIopBios::ProcessModuleReset(const std::string& initCommand)
|
2015-10-24 21:12:42 -04:00
|
|
|
{
|
2022-10-10 12:14:21 -04:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "ProcessModuleReset(%s);\r\n", initCommand.c_str());
|
2022-03-31 10:39:19 +02:00
|
|
|
|
2022-10-30 21:46:14 -04:00
|
|
|
UnloadUserComponents();
|
2022-12-20 15:39:16 -05:00
|
|
|
|
2022-10-15 11:32:30 -04:00
|
|
|
uint32 imageVersion = m_defaultImageVersion;
|
2022-10-14 13:42:27 -04:00
|
|
|
|
2022-10-10 12:14:21 -04:00
|
|
|
auto initArguments = StringUtils::Split(initCommand);
|
|
|
|
assert(initArguments.size() >= 1);
|
|
|
|
if(initArguments.size() >= 1)
|
|
|
|
{
|
|
|
|
auto loaderPath = initArguments[0];
|
|
|
|
assert(loaderPath.find("UDNL") != std::string::npos);
|
|
|
|
if(initArguments.size() >= 2)
|
|
|
|
{
|
|
|
|
auto imagePath = initArguments[1];
|
|
|
|
bool found = false;
|
|
|
|
found = TryGetImageVersionFromContents(imagePath, &imageVersion);
|
|
|
|
if(!found) found = TryGetImageVersionFromPath(imagePath, &imageVersion);
|
2022-10-13 08:08:39 -04:00
|
|
|
if(!found)
|
|
|
|
{
|
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Failed to find IOP module version from '%s'.\r\n", imagePath.c_str());
|
|
|
|
}
|
2022-10-10 12:14:21 -04:00
|
|
|
}
|
|
|
|
}
|
2022-10-14 13:42:27 -04:00
|
|
|
|
2017-03-30 21:31:34 -04:00
|
|
|
m_loadcore->SetModuleVersion(imageVersion);
|
2015-10-24 21:12:42 -04:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
|
|
|
m_fileIo->SetModuleVersion(imageVersion);
|
2023-04-05 15:19:28 -04:00
|
|
|
m_mcserv->SetModuleVersion(imageVersion);
|
2015-10-24 21:12:42 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-10-15 11:32:30 -04:00
|
|
|
void CIopBios::SetDefaultImageVersion(uint32 defaultImageVersion)
|
|
|
|
{
|
|
|
|
m_defaultImageVersion = defaultImageVersion;
|
|
|
|
}
|
|
|
|
|
2015-10-24 21:12:42 -04:00
|
|
|
bool CIopBios::TryGetImageVersionFromPath(const std::string& imagePath, unsigned int* result)
|
2015-01-26 03:39:44 -05:00
|
|
|
{
|
2015-04-13 00:17:16 -04:00
|
|
|
struct IMAGE_FILE_PATTERN
|
|
|
|
{
|
|
|
|
const char* start;
|
|
|
|
const char* pattern;
|
|
|
|
};
|
2021-01-08 12:07:47 -05:00
|
|
|
// clang-format off
|
2015-04-13 00:17:16 -04:00
|
|
|
static const IMAGE_FILE_PATTERN g_imageFilePatterns[] =
|
2021-01-08 12:07:47 -05:00
|
|
|
{
|
|
|
|
{"IOPRP", "IOPRP%d.IMG;1"},
|
|
|
|
{"DNAS", "DNAS%d.IMG;1"}
|
|
|
|
};
|
|
|
|
// clang-format on
|
2015-04-13 00:17:16 -04:00
|
|
|
|
|
|
|
for(const auto imageFilePattern : g_imageFilePatterns)
|
2015-01-26 03:39:44 -05:00
|
|
|
{
|
2015-04-13 00:17:16 -04:00
|
|
|
auto imageFileName = strstr(imagePath.c_str(), imageFilePattern.start);
|
|
|
|
if(imageFileName != nullptr)
|
2015-01-26 03:39:44 -05:00
|
|
|
{
|
2015-10-24 21:12:42 -04:00
|
|
|
unsigned int imageVersion = 0;
|
2015-04-13 00:17:16 -04:00
|
|
|
auto cvtCount = sscanf(imageFileName, imageFilePattern.pattern, &imageVersion);
|
|
|
|
if(cvtCount == 1)
|
2015-01-26 03:39:44 -05:00
|
|
|
{
|
2015-04-13 00:17:16 -04:00
|
|
|
if(imageVersion < 100)
|
|
|
|
{
|
|
|
|
imageVersion = imageVersion * 100;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
imageVersion = imageVersion * 10;
|
|
|
|
}
|
2015-10-24 21:12:42 -04:00
|
|
|
if(result)
|
|
|
|
{
|
|
|
|
(*result) = imageVersion;
|
|
|
|
}
|
|
|
|
return true;
|
2015-01-26 03:39:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-24 21:12:42 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CIopBios::TryGetImageVersionFromContents(const std::string& imagePath, unsigned int* result)
|
|
|
|
{
|
2022-10-10 12:14:21 -04:00
|
|
|
int32 fd = m_ioman->Open(Iop::Ioman::CDevice::OPEN_FLAG_RDONLY, imagePath.c_str());
|
2015-10-24 21:12:42 -04:00
|
|
|
if(fd < 0) return false;
|
|
|
|
|
2021-01-08 12:09:11 -05:00
|
|
|
//Some notes about this:
|
|
|
|
//- FFXI/POLViewer resets the IOP with a image without version in the filename
|
|
|
|
// and it also doesn't have a fileio string that lets us find the required version,
|
|
|
|
// thus, we rely on the ioprp pattern for this game.
|
2022-12-20 15:39:16 -05:00
|
|
|
static const std::string fileIoPatternString = "PsIIfileio ";
|
|
|
|
static const std::string sysmemPatternString = "PsIIsysmem ";
|
2022-10-29 11:32:38 -04:00
|
|
|
static const std::string loadcorePatternString = "PsIIloadcore";
|
2021-01-08 12:09:11 -05:00
|
|
|
static const std::string ioprpPatternString = "ioprp";
|
|
|
|
|
|
|
|
auto tryMatchVersionPattern =
|
2021-01-08 18:30:28 -05:00
|
|
|
[result](auto moduleVersionString, const std::string& patternString) {
|
|
|
|
if(!strncmp(moduleVersionString, patternString.c_str(), patternString.size()))
|
|
|
|
{
|
|
|
|
//Found something
|
|
|
|
unsigned int imageVersion = atoi(moduleVersionString + patternString.size());
|
|
|
|
if(imageVersion < 1000) return false;
|
|
|
|
if(result)
|
|
|
|
{
|
|
|
|
(*result) = imageVersion;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-16 18:50:28 -04:00
|
|
|
Iop::Ioman::CScopedFile file(fd, *m_ioman);
|
2015-10-24 21:12:42 -04:00
|
|
|
auto stream = m_ioman->GetFileStream(file);
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
static const unsigned int moduleVersionStringSize = 0x10;
|
|
|
|
char moduleVersionString[moduleVersionStringSize + 1];
|
|
|
|
auto currentPos = stream->Tell();
|
2022-10-10 12:26:54 -04:00
|
|
|
auto readAmount = stream->Read(moduleVersionString, moduleVersionStringSize);
|
|
|
|
if(readAmount != moduleVersionStringSize)
|
|
|
|
{
|
|
|
|
//We've hit the end
|
|
|
|
break;
|
|
|
|
}
|
2015-10-24 21:12:42 -04:00
|
|
|
moduleVersionString[moduleVersionStringSize] = 0;
|
2021-01-08 12:09:11 -05:00
|
|
|
if(tryMatchVersionPattern(moduleVersionString, fileIoPatternString))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2022-10-18 21:41:46 -04:00
|
|
|
else if(tryMatchVersionPattern(moduleVersionString, sysmemPatternString))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2022-10-29 11:32:38 -04:00
|
|
|
else if(tryMatchVersionPattern(moduleVersionString, loadcorePatternString))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2021-01-08 12:09:11 -05:00
|
|
|
else if(tryMatchVersionPattern(moduleVersionString, ioprpPatternString))
|
2015-10-24 21:12:42 -04:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
stream->Seek(currentPos + 1, Framework::STREAM_SEEK_SET);
|
|
|
|
}
|
|
|
|
return false;
|
2015-01-26 03:39:44 -05:00
|
|
|
}
|
|
|
|
|
2011-04-25 01:33:32 +00:00
|
|
|
CIopBios::THREAD* CIopBios::GetThread(uint32 threadId)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
return m_threads[threadId];
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-04 01:03:32 -05:00
|
|
|
int32 CIopBios::GetCurrentThreadId()
|
|
|
|
{
|
|
|
|
int32 threadId = m_currentThreadId;
|
|
|
|
if(threadId < 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_CONTEXT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return threadId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 CIopBios::GetCurrentThreadIdRaw() const
|
|
|
|
{
|
|
|
|
return m_currentThreadId;
|
|
|
|
}
|
|
|
|
|
2016-07-09 18:02:47 -04:00
|
|
|
uint32 CIopBios::CreateThread(uint32 threadProc, uint32 priority, uint32 stackSize, uint32 optionData, uint32 attributes)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2019-01-29 22:39:50 +00:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: CreateThread(threadProc = 0x%08X, priority = %d, stackSize = 0x%08X, attributes = 0x%08X);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadProc, priority, stackSize, attributes);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-09 18:02:47 -04:00
|
|
|
//Thread proc address needs to be 4-bytes aligned
|
|
|
|
if((threadProc & 0x3) != 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_ENTRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Priority needs to be between [1, 126]
|
|
|
|
if((priority < 1) || (priority > 126))
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_PRIORITY;
|
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
if(stackSize == 0)
|
|
|
|
{
|
|
|
|
stackSize = DEFAULT_STACKSIZE;
|
|
|
|
}
|
2008-12-15 02:57:21 +00:00
|
|
|
|
2016-09-10 21:57:34 -04:00
|
|
|
//Make sure stack size is a multiple of 4
|
|
|
|
stackSize = (stackSize + 0x03) & ~0x03;
|
|
|
|
|
2016-07-09 18:02:47 -04:00
|
|
|
uint32 stackBase = m_sysmem->AllocateMemory(stackSize, 0, 0);
|
|
|
|
if(stackBase == 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
2009-01-28 23:06:08 +00:00
|
|
|
uint32 threadId = m_threads.Allocate();
|
|
|
|
assert(threadId != -1);
|
2009-01-30 22:10:15 +00:00
|
|
|
if(threadId == -1)
|
|
|
|
{
|
2016-07-09 18:02:47 -04:00
|
|
|
m_sysmem->FreeMemory(stackBase);
|
2009-01-30 22:10:15 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2009-01-28 23:06:08 +00:00
|
|
|
|
2016-07-09 18:02:47 -04:00
|
|
|
auto thread = m_threads[threadId];
|
2023-01-19 10:05:14 -05:00
|
|
|
thread->context = {};
|
2016-07-09 18:02:47 -04:00
|
|
|
thread->context.delayJump = MIPS_INVALID_PC;
|
2009-01-28 23:06:08 +00:00
|
|
|
thread->stackSize = stackSize;
|
2016-07-09 18:02:47 -04:00
|
|
|
thread->stackBase = stackBase;
|
2019-01-29 22:39:50 +00:00
|
|
|
memset(m_ram + thread->stackBase, 0xFF, thread->stackSize);
|
2009-01-28 23:06:08 +00:00
|
|
|
thread->id = threadId;
|
2016-07-09 18:02:47 -04:00
|
|
|
thread->priority = 0;
|
2012-09-12 04:03:41 +00:00
|
|
|
thread->initPriority = priority;
|
|
|
|
thread->status = THREAD_STATUS_DORMANT;
|
|
|
|
thread->threadProc = threadProc;
|
|
|
|
thread->optionData = optionData;
|
2016-07-09 18:02:47 -04:00
|
|
|
thread->attributes = attributes;
|
2012-03-30 05:41:11 +00:00
|
|
|
thread->nextActivateTime = 0;
|
2016-11-04 20:57:04 -04:00
|
|
|
thread->wakeupCount = 0;
|
2012-03-30 05:41:11 +00:00
|
|
|
thread->context.gpr[CMIPS::GP] = m_cpu.m_State.nGPR[CMIPS::GP].nV0;
|
2012-09-23 02:44:14 +00:00
|
|
|
thread->context.gpr[CMIPS::SP] = thread->stackBase + thread->stackSize - STACK_FRAME_RESERVE_SIZE;
|
2012-03-30 05:41:11 +00:00
|
|
|
return thread->id;
|
2009-01-28 23:06:08 +00:00
|
|
|
}
|
|
|
|
|
2016-05-07 20:26:27 -04:00
|
|
|
int32 CIopBios::DeleteThread(uint32 threadId)
|
2009-01-28 23:06:08 +00:00
|
|
|
{
|
2014-07-26 00:27:48 -04:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: DeleteThread(threadId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2014-07-26 00:27:48 -04:00
|
|
|
#endif
|
2016-05-07 20:26:27 -04:00
|
|
|
|
|
|
|
if(threadId == 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_THID;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
if(!thread)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_THID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(thread->status != THREAD_STATUS_DORMANT)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_NOT_DORMANT;
|
|
|
|
}
|
|
|
|
|
2009-01-28 23:06:08 +00:00
|
|
|
UnlinkThread(threadId);
|
|
|
|
m_sysmem->FreeMemory(thread->stackBase);
|
|
|
|
m_threads.Free(threadId);
|
2016-05-07 20:26:27 -04:00
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 19:53:13 -04:00
|
|
|
int32 CIopBios::StartThread(uint32 threadId, uint32 param)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%i: StartThread(threadId = %i, param = 0x%08X);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId, param);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2015-05-15 19:53:13 -04:00
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
assert(thread != nullptr);
|
|
|
|
if(thread == nullptr)
|
2012-10-21 02:37:23 +00:00
|
|
|
{
|
2015-05-15 19:53:13 -04:00
|
|
|
return -1;
|
2012-10-21 02:37:23 +00:00
|
|
|
}
|
|
|
|
|
2012-09-12 04:03:41 +00:00
|
|
|
if(thread->status != THREAD_STATUS_DORMANT)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%d: Failed to start thread %d, thread not dormant.\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2015-05-15 19:53:13 -04:00
|
|
|
assert(false);
|
|
|
|
return -1;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2015-05-15 19:53:13 -04:00
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
2012-09-12 04:03:41 +00:00
|
|
|
thread->priority = thread->initPriority;
|
2016-07-09 18:03:36 -04:00
|
|
|
LinkThread(threadId);
|
2012-09-12 04:03:41 +00:00
|
|
|
thread->context.epc = thread->threadProc;
|
2015-05-15 19:53:13 -04:00
|
|
|
thread->context.gpr[CMIPS::A0] = param;
|
2012-09-12 04:03:41 +00:00
|
|
|
thread->context.gpr[CMIPS::RA] = m_threadFinishAddress;
|
2012-09-23 02:44:14 +00:00
|
|
|
thread->context.gpr[CMIPS::SP] = thread->stackBase + thread->stackSize - STACK_FRAME_RESERVE_SIZE;
|
2014-11-23 23:52:21 +00:00
|
|
|
|
2021-02-19 11:57:45 -05:00
|
|
|
//IOP BIOS disassembly shows that this is done at thread start.
|
|
|
|
//Not sure why it's needed, but some games seem to rely on it (Armored Core 2: Another Age)
|
|
|
|
//because thread stack is initialized to 0xFF but it needs a part of it to be set to 0
|
|
|
|
{
|
|
|
|
uint32 alignedStackSize = thread->stackSize & ~0x3;
|
|
|
|
uint32 stackTopClearSize = std::min<uint32>(alignedStackSize, 0xB8);
|
|
|
|
uint32 clearArea = thread->stackBase + alignedStackSize - stackTopClearSize;
|
|
|
|
memset(m_ram + clearArea, 0, stackTopClearSize);
|
|
|
|
}
|
|
|
|
|
2015-10-12 20:47:52 -04:00
|
|
|
m_rescheduleNeeded = true;
|
2015-05-15 19:53:13 -04:00
|
|
|
return 0;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
|
2015-07-12 22:57:17 -04:00
|
|
|
int32 CIopBios::StartThreadArgs(uint32 threadId, uint32 args, uint32 argpPtr)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: StartThreadArgs(threadId = %d, args = %d, argp = 0x%08X);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId, args, argpPtr);
|
2015-07-12 22:57:17 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
assert(thread != nullptr);
|
|
|
|
if(thread == nullptr)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(thread->status != THREAD_STATUS_DORMANT)
|
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%d: Failed to start thread %d, thread not dormant.\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2015-07-12 22:57:17 -04:00
|
|
|
assert(false);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const auto pushToStack =
|
2018-04-30 21:01:23 +01:00
|
|
|
[](uint8* dst, uint32& stackAddress, const uint8* src, uint32 size) {
|
|
|
|
uint32 fixedSize = ((size + 0x3) & ~0x3);
|
2021-12-29 16:11:05 -05:00
|
|
|
uint32 copyAddress = stackAddress - fixedSize;
|
2018-04-30 21:01:23 +01:00
|
|
|
stackAddress -= fixedSize;
|
|
|
|
memcpy(dst + copyAddress, src, size);
|
|
|
|
return copyAddress;
|
|
|
|
};
|
2015-07-12 22:57:17 -04:00
|
|
|
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
2015-10-04 18:30:33 -04:00
|
|
|
LinkThread(threadId);
|
2015-07-12 22:57:17 -04:00
|
|
|
thread->priority = thread->initPriority;
|
|
|
|
thread->context.epc = thread->threadProc;
|
|
|
|
thread->context.gpr[CMIPS::RA] = m_threadFinishAddress;
|
|
|
|
thread->context.gpr[CMIPS::SP] = thread->stackBase + thread->stackSize;
|
|
|
|
|
|
|
|
thread->context.gpr[CMIPS::A0] = args;
|
|
|
|
thread->context.gpr[CMIPS::A1] = pushToStack(m_ram, thread->context.gpr[CMIPS::SP], m_ram + argpPtr, args);
|
|
|
|
|
|
|
|
thread->context.gpr[CMIPS::SP] -= STACK_FRAME_RESERVE_SIZE;
|
|
|
|
|
2015-10-12 20:47:52 -04:00
|
|
|
m_rescheduleNeeded = true;
|
2015-07-12 22:57:17 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-12 04:03:41 +00:00
|
|
|
void CIopBios::ExitThread()
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2015-11-04 01:03:32 -05:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%i: ExitThread();\r\n", m_currentThreadId.Get());
|
2012-09-12 04:03:41 +00:00
|
|
|
#endif
|
2015-11-04 01:03:32 -05:00
|
|
|
THREAD* thread = GetThread(m_currentThreadId);
|
2012-09-12 04:03:41 +00:00
|
|
|
thread->status = THREAD_STATUS_DORMANT;
|
2018-05-07 16:35:15 -04:00
|
|
|
assert(thread->waitSemaphore == 0);
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2012-03-30 05:41:11 +00:00
|
|
|
m_rescheduleNeeded = true;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2014-07-26 00:27:48 -04:00
|
|
|
uint32 CIopBios::TerminateThread(uint32 threadId)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: TerminateThread(threadId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2014-07-26 00:27:48 -04:00
|
|
|
#endif
|
|
|
|
|
2015-11-04 01:03:32 -05:00
|
|
|
assert(threadId != m_currentThreadId);
|
2016-03-02 00:32:40 -05:00
|
|
|
if(threadId == m_currentThreadId)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_THID;
|
|
|
|
}
|
|
|
|
|
2014-07-26 00:27:48 -04:00
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
assert(thread != nullptr);
|
|
|
|
if(thread == nullptr)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(thread->waitSemaphore != 0)
|
|
|
|
{
|
|
|
|
auto semaphore = m_semaphores[thread->waitSemaphore];
|
|
|
|
if(semaphore != nullptr)
|
|
|
|
{
|
|
|
|
assert(semaphore->waitCount > 0);
|
|
|
|
semaphore->waitCount--;
|
|
|
|
}
|
|
|
|
thread->waitSemaphore = 0;
|
|
|
|
}
|
|
|
|
thread->status = THREAD_STATUS_DORMANT;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2016-10-30 21:46:26 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2014-07-26 00:27:48 -04:00
|
|
|
}
|
|
|
|
|
2016-10-30 21:47:02 -04:00
|
|
|
int32 CIopBios::DelayThread(uint32 delay)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%i: DelayThread(delay = %i);\r\n",
|
|
|
|
m_currentThreadId.Get(), delay);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2015-11-04 01:03:32 -05:00
|
|
|
THREAD* thread = GetThread(m_currentThreadId);
|
2012-03-30 05:41:11 +00:00
|
|
|
thread->nextActivateTime = GetCurrentTime() + MicroSecToClock(delay);
|
2015-10-04 18:30:33 -04:00
|
|
|
//TODO: Add a proper wait state to allow thread to be relinked
|
|
|
|
//at the end of the queue at the right moment
|
|
|
|
UnlinkThread(thread->id);
|
|
|
|
LinkThread(thread->id);
|
2012-03-30 05:41:11 +00:00
|
|
|
m_rescheduleNeeded = true;
|
2016-10-30 21:47:02 -04:00
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2012-10-21 02:37:23 +00:00
|
|
|
void CIopBios::DelayThreadTicks(uint32 delay)
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
auto thread = GetThread(m_currentThreadId);
|
2012-10-21 02:37:23 +00:00
|
|
|
thread->nextActivateTime = GetCurrentTime() + delay;
|
2015-10-04 18:30:33 -04:00
|
|
|
//TODO: Add a proper wait state to allow thread to be relinked
|
|
|
|
//at the end of the queue at the right moment
|
|
|
|
UnlinkThread(thread->id);
|
|
|
|
LinkThread(thread->id);
|
2012-10-21 02:37:23 +00:00
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::SetAlarm(uint32 timePtr, uint32 alarmFunction, uint32 param)
|
|
|
|
{
|
2014-06-04 03:17:57 -04:00
|
|
|
uint32 alarmThreadId = -1;
|
|
|
|
|
|
|
|
//Find a thread we could recycle for a new alarm
|
2015-08-05 23:38:40 -04:00
|
|
|
for(auto thread : m_threads)
|
2014-06-04 03:17:57 -04:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
if(!thread) continue;
|
2014-06-04 03:17:57 -04:00
|
|
|
if(thread->threadProc != m_alarmThreadProcAddress) continue;
|
|
|
|
if(thread->status == THREAD_STATUS_DORMANT)
|
|
|
|
{
|
|
|
|
alarmThreadId = thread->id;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//If no threads are available, create a new one
|
|
|
|
if(alarmThreadId == -1)
|
|
|
|
{
|
2016-07-09 18:02:47 -04:00
|
|
|
alarmThreadId = CreateThread(m_alarmThreadProcAddress, 1, DEFAULT_STACKSIZE, 0, 0);
|
2014-06-04 03:17:57 -04:00
|
|
|
}
|
|
|
|
|
2015-05-15 19:53:13 -04:00
|
|
|
StartThread(alarmThreadId, 0);
|
2012-10-21 02:37:23 +00:00
|
|
|
|
|
|
|
auto thread = GetThread(alarmThreadId);
|
|
|
|
thread->context.gpr[CMIPS::SP] -= 0x20;
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2012-10-21 02:37:23 +00:00
|
|
|
uint32* delay = reinterpret_cast<uint32*>(m_ram + timePtr);
|
|
|
|
assert(delay[1] == 0);
|
|
|
|
|
|
|
|
*reinterpret_cast<uint32*>(m_ram + thread->context.gpr[CMIPS::SP] + 0x00) = alarmFunction;
|
|
|
|
*reinterpret_cast<uint32*>(m_ram + thread->context.gpr[CMIPS::SP] + 0x04) = param;
|
|
|
|
*reinterpret_cast<uint32*>(m_ram + thread->context.gpr[CMIPS::SP] + 0x08) = delay[0];
|
|
|
|
|
|
|
|
thread->context.gpr[CMIPS::A0] = thread->context.gpr[CMIPS::SP];
|
2019-09-18 07:52:51 -04:00
|
|
|
thread->optionData = alarmFunction;
|
2012-10-21 02:37:23 +00:00
|
|
|
|
2014-06-04 03:16:24 -04:00
|
|
|
//Returns negative value on failure
|
|
|
|
return 0;
|
2012-10-21 02:37:23 +00:00
|
|
|
}
|
|
|
|
|
2023-12-22 11:29:52 -05:00
|
|
|
uint32 CIopBios::CancelAlarm(uint32 alarmFunction, uint32 param, bool inInterrupt)
|
2014-06-04 03:18:31 -04:00
|
|
|
{
|
|
|
|
//TODO: This needs to garantee that the alarm handler function won't be called after the cancel
|
2014-11-30 18:54:28 -05:00
|
|
|
|
|
|
|
uint32 alarmThreadId = -1;
|
|
|
|
|
2015-08-05 23:38:40 -04:00
|
|
|
for(auto thread : m_threads)
|
2014-11-30 18:54:28 -05:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
if(!thread) continue;
|
2019-09-18 07:52:51 -04:00
|
|
|
if(thread->status == THREAD_STATUS_DORMANT) continue;
|
|
|
|
if(thread->optionData != alarmFunction) continue;
|
2014-11-30 18:54:28 -05:00
|
|
|
if(thread->threadProc == m_alarmThreadProcAddress)
|
|
|
|
{
|
|
|
|
alarmThreadId = thread->id;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(alarmThreadId == -1)
|
|
|
|
{
|
|
|
|
// handler not registered
|
2015-10-11 19:10:42 -04:00
|
|
|
return KERNEL_RESULT_ERROR_NOTFOUND_HANDLER;
|
2014-11-30 18:54:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
TerminateThread(alarmThreadId);
|
2014-06-04 03:18:31 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-12 15:11:10 -05:00
|
|
|
int32 CIopBios::ChangeThreadPriority(uint32 threadId, uint32 newPrio)
|
2011-10-22 22:36:49 +00:00
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ChangeThreadPriority(threadId = %d, newPrio = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId, newPrio);
|
2011-10-22 22:36:49 +00:00
|
|
|
#endif
|
2012-06-30 02:31:22 +00:00
|
|
|
|
2022-07-15 17:04:10 -04:00
|
|
|
//Priority needs to be between [1, 126]
|
|
|
|
if((newPrio < 1) || (newPrio > 126))
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_PRIORITY;
|
|
|
|
}
|
|
|
|
|
2012-06-30 02:31:22 +00:00
|
|
|
if(threadId == 0)
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
threadId = m_currentThreadId;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
2016-11-12 15:11:10 -05:00
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
assert(thread);
|
|
|
|
if(!thread)
|
2011-10-22 22:36:49 +00:00
|
|
|
{
|
2016-11-12 15:11:10 -05:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_THID;
|
2011-10-22 22:36:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
thread->priority = newPrio;
|
2016-11-12 15:12:08 -05:00
|
|
|
if(thread->status == THREAD_STATUS_RUNNING)
|
|
|
|
{
|
|
|
|
UnlinkThread(threadId);
|
|
|
|
LinkThread(threadId);
|
|
|
|
}
|
|
|
|
|
2011-10-22 22:36:49 +00:00
|
|
|
m_rescheduleNeeded = true;
|
2016-11-12 15:11:10 -05:00
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
2011-10-22 22:36:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-02 01:02:20 -05:00
|
|
|
uint32 CIopBios::ReferThreadStatus(uint32 threadId, uint32 statusPtr, bool inInterrupt)
|
2012-09-12 04:03:41 +00:00
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ReferThreadStatus(threadId = %d, statusPtr = 0x%08X, inInterrupt = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId, statusPtr, inInterrupt);
|
2012-09-12 04:03:41 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if(threadId == 0)
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
threadId = m_currentThreadId;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
|
2015-10-12 00:17:39 -04:00
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
assert(thread);
|
|
|
|
if(!thread)
|
2012-09-12 04:03:41 +00:00
|
|
|
{
|
2015-10-12 00:17:39 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_THID;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 threadStatus = 0;
|
2015-10-12 20:20:55 -04:00
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_STATUS_DORMANT:
|
|
|
|
threadStatus = 0x10;
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_SLEEPING:
|
|
|
|
case THREAD_STATUS_WAITING_MESSAGEBOX:
|
|
|
|
case THREAD_STATUS_WAITING_EVENTFLAG:
|
|
|
|
case THREAD_STATUS_WAITING_SEMAPHORE:
|
|
|
|
case THREAD_STATUS_WAIT_VBLANK_START:
|
|
|
|
case THREAD_STATUS_WAIT_VBLANK_END:
|
|
|
|
threadStatus = 0x04;
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_RUNNING:
|
2015-11-04 01:03:32 -05:00
|
|
|
if(threadId == m_currentThreadId)
|
2015-10-12 20:20:55 -04:00
|
|
|
{
|
|
|
|
threadStatus = 0x01;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
threadStatus = 0x02;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
threadStatus = 0;
|
|
|
|
break;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
|
2015-10-25 22:04:05 -04:00
|
|
|
uint32 waitType = 0;
|
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_STATUS_SLEEPING:
|
|
|
|
waitType = 1;
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_SEMAPHORE:
|
|
|
|
waitType = 3;
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_EVENTFLAG:
|
|
|
|
waitType = 4;
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_MESSAGEBOX:
|
|
|
|
waitType = 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
waitType = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-10-25 22:03:15 -04:00
|
|
|
auto threadInfo = reinterpret_cast<THREAD_INFO*>(m_ram + statusPtr);
|
2018-04-30 21:01:23 +01:00
|
|
|
threadInfo->option = thread->optionData;
|
|
|
|
threadInfo->attributes = thread->attributes;
|
|
|
|
threadInfo->status = threadStatus;
|
|
|
|
threadInfo->entryPoint = thread->threadProc;
|
|
|
|
threadInfo->stackAddr = thread->stackBase;
|
|
|
|
threadInfo->stackSize = thread->stackSize;
|
|
|
|
threadInfo->initPriority = thread->initPriority;
|
2015-10-25 22:03:15 -04:00
|
|
|
threadInfo->currentPriority = thread->priority;
|
2018-04-30 21:01:23 +01:00
|
|
|
threadInfo->waitType = waitType;
|
2012-09-12 04:03:41 +00:00
|
|
|
|
2015-10-12 00:17:39 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
|
2016-05-07 20:30:46 -04:00
|
|
|
int32 CIopBios::SleepThread()
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%i: SleepThread();\r\n",
|
|
|
|
m_currentThreadId.Get());
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2015-11-04 01:03:32 -05:00
|
|
|
THREAD* thread = GetThread(m_currentThreadId);
|
2012-03-30 05:41:11 +00:00
|
|
|
if(thread->status != THREAD_STATUS_RUNNING)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Thread isn't running.");
|
|
|
|
}
|
|
|
|
if(thread->wakeupCount == 0)
|
|
|
|
{
|
|
|
|
thread->status = THREAD_STATUS_SLEEPING;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2012-03-30 05:41:11 +00:00
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thread->wakeupCount--;
|
|
|
|
}
|
2016-05-07 20:30:46 -04:00
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2008-11-07 23:50:47 +00:00
|
|
|
uint32 CIopBios::WakeupThread(uint32 threadId, bool inInterrupt)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: WakeupThread(threadId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2020-11-03 14:21:07 -05:00
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
if(!thread)
|
|
|
|
{
|
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%d: Trying to wakeup a non-existing thread (%d).\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_THID;
|
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
if(thread->status == THREAD_STATUS_SLEEPING)
|
|
|
|
{
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
2015-10-04 18:30:33 -04:00
|
|
|
LinkThread(threadId);
|
2008-11-07 23:50:47 +00:00
|
|
|
if(!inInterrupt)
|
|
|
|
{
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thread->wakeupCount++;
|
|
|
|
}
|
|
|
|
return thread->wakeupCount;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 20:54:49 -04:00
|
|
|
int32 CIopBios::CancelWakeupThread(uint32 threadId, bool inInterrupt)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: CancelWakeupThread(threadId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2016-11-04 20:54:49 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if(threadId == 0)
|
|
|
|
{
|
|
|
|
threadId = m_currentThreadId;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
if(!thread)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_THID;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 result = thread->wakeupCount;
|
|
|
|
thread->wakeupCount = 0;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-05-07 16:34:42 -04:00
|
|
|
int32 CIopBios::RotateThreadReadyQueue(uint32 priority)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: RotateThreadReadyQueue(priority = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), priority);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if(priority == 0)
|
|
|
|
{
|
|
|
|
auto thread = GetThread(m_currentThreadId);
|
|
|
|
priority = thread->priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 nextThreadId = ThreadLinkHead();
|
|
|
|
while(nextThreadId != 0)
|
|
|
|
{
|
|
|
|
auto nextThread = m_threads[nextThreadId];
|
|
|
|
if(nextThread->priority == priority)
|
|
|
|
{
|
|
|
|
UnlinkThread(nextThreadId);
|
|
|
|
LinkThread(nextThreadId);
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
break;
|
|
|
|
}
|
2018-05-10 09:27:20 -04:00
|
|
|
nextThreadId = nextThread->nextThreadId;
|
2018-05-07 16:34:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2017-05-01 23:08:30 -04:00
|
|
|
int32 CIopBios::ReleaseWaitThread(uint32 threadId, bool inInterrupt)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ReleaseWaitThread(threadId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), threadId);
|
2017-05-01 23:08:30 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if(threadId == 0)
|
|
|
|
{
|
|
|
|
threadId = m_currentThreadId;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(threadId == m_currentThreadId)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_THID;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
if(!thread)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_THID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(
|
2018-04-30 21:01:23 +01:00
|
|
|
(thread->status == THREAD_STATUS_RUNNING) ||
|
|
|
|
(thread->status == THREAD_STATUS_DORMANT))
|
2017-05-01 23:08:30 -04:00
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_NOT_WAIT;
|
|
|
|
}
|
|
|
|
|
2018-05-07 17:07:40 -04:00
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_STATUS_SLEEPING:
|
|
|
|
//Nothing special to do
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_SEMAPHORE:
|
2018-05-08 10:02:13 -04:00
|
|
|
{
|
|
|
|
auto semaphore = m_semaphores[thread->waitSemaphore];
|
|
|
|
assert(semaphore);
|
|
|
|
assert(semaphore->waitCount != 0);
|
|
|
|
semaphore->waitCount--;
|
|
|
|
thread->waitSemaphore = 0;
|
|
|
|
}
|
|
|
|
break;
|
2023-01-23 09:12:18 -05:00
|
|
|
case THREAD_STATUS_WAITING_EVENTFLAG:
|
|
|
|
thread->waitEventFlag = 0;
|
|
|
|
thread->waitEventFlagResultPtr = 0;
|
|
|
|
break;
|
2018-05-07 17:07:40 -04:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Update return value for waiting thread
|
|
|
|
thread->context.gpr[CMIPS::V0] = KERNEL_RESULT_ERROR_RELEASE_WAIT;
|
2017-05-01 23:08:30 -04:00
|
|
|
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
|
|
|
LinkThread(threadId);
|
|
|
|
if(!inInterrupt)
|
|
|
|
{
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2018-07-17 12:41:13 -04:00
|
|
|
int32 CIopBios::RegisterVblankHandler(uint32 startEnd, uint32 priority, uint32 handlerPtr, uint32 handlerParam)
|
|
|
|
{
|
|
|
|
assert((startEnd == 0) || (startEnd == 1));
|
|
|
|
|
|
|
|
//Make sure interrupt handler is registered
|
|
|
|
{
|
|
|
|
uint32 intrLine = startEnd ? Iop::CIntc::LINE_EVBLANK : Iop::CIntc::LINE_VBLANK;
|
|
|
|
uint32 intrHandlerId = FindIntrHandler(intrLine);
|
|
|
|
if(intrHandlerId == -1)
|
|
|
|
{
|
|
|
|
RegisterIntrHandler(intrLine, 0, m_vblankHandlerAddress, startEnd);
|
|
|
|
|
|
|
|
uint32 mask = m_cpu.m_pMemoryMap->GetWord(Iop::CIntc::MASK0);
|
|
|
|
mask |= (1 << intrLine);
|
|
|
|
m_cpu.m_pMemoryMap->SetWord(Iop::CIntc::MASK0, mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-22 19:25:51 +02:00
|
|
|
// Check if the exact handler is already registered
|
|
|
|
if(FindVblankHandlerByLineAndPtr(startEnd, handlerPtr) != -1)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_FOUND_HANDLER;
|
|
|
|
}
|
|
|
|
|
2018-07-17 12:41:13 -04:00
|
|
|
uint32 handlerId = m_vblankHandlers.Allocate();
|
|
|
|
if(handlerId == -1)
|
|
|
|
{
|
2020-04-22 19:25:51 +02:00
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
2018-07-17 12:41:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
auto handler = m_vblankHandlers[handlerId];
|
|
|
|
assert(handler);
|
|
|
|
handler->handler = handlerPtr;
|
|
|
|
handler->arg = handlerParam;
|
|
|
|
handler->type = startEnd;
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2020-04-22 19:46:46 +02:00
|
|
|
int32 CIopBios::ReleaseVblankHandler(uint32 startEnd, uint32 handlerPtr)
|
|
|
|
{
|
|
|
|
assert((startEnd == 0) || (startEnd == 1));
|
|
|
|
|
|
|
|
uint32 handlerId = FindVblankHandlerByLineAndPtr(startEnd, handlerPtr);
|
|
|
|
if(handlerId == -1)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_NOTFOUND_HANDLER;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_vblankHandlers.Free(handlerId);
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 CIopBios::FindVblankHandlerByLineAndPtr(uint32 startEnd, uint32 handlerPtr)
|
|
|
|
{
|
|
|
|
uint32 handlerId = -1;
|
|
|
|
for(unsigned int i = 0; i < MAX_VBLANKHANDLER; i++)
|
|
|
|
{
|
|
|
|
if(m_vblankHandlers[i] != nullptr && m_vblankHandlers[i]->handler == handlerPtr && m_vblankHandlers[i]->type == startEnd)
|
|
|
|
{
|
|
|
|
handlerId = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return handlerId;
|
|
|
|
}
|
|
|
|
|
2011-05-05 04:28:11 +00:00
|
|
|
void CIopBios::SleepThreadTillVBlankStart()
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
THREAD* thread = GetThread(m_currentThreadId);
|
2011-05-05 04:28:11 +00:00
|
|
|
thread->status = THREAD_STATUS_WAIT_VBLANK_START;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2011-05-05 04:28:11 +00:00
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::SleepThreadTillVBlankEnd()
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
THREAD* thread = GetThread(m_currentThreadId);
|
2011-05-05 04:28:11 +00:00
|
|
|
thread->status = THREAD_STATUS_WAIT_VBLANK_END;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2011-05-05 04:28:11 +00:00
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
void CIopBios::LoadThreadContext(uint32 threadId)
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
THREAD* thread = GetThread(threadId);
|
|
|
|
for(unsigned int i = 0; i < 32; i++)
|
|
|
|
{
|
2008-01-15 20:27:44 +00:00
|
|
|
if(i == CMIPS::R0) continue;
|
|
|
|
if(i == CMIPS::K0) continue;
|
|
|
|
if(i == CMIPS::K1) continue;
|
2012-03-30 05:41:11 +00:00
|
|
|
m_cpu.m_State.nGPR[i].nD0 = static_cast<int32>(thread->context.gpr[i]);
|
|
|
|
}
|
2020-11-13 09:31:12 -05:00
|
|
|
m_cpu.m_State.nLO[0] = thread->context.gpr[CMIPS::K0];
|
|
|
|
m_cpu.m_State.nHI[0] = thread->context.gpr[CMIPS::K1];
|
2012-03-30 05:41:11 +00:00
|
|
|
m_cpu.m_State.nPC = thread->context.epc;
|
|
|
|
m_cpu.m_State.nDelayedJumpAddr = thread->context.delayJump;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::SaveThreadContext(uint32 threadId)
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
THREAD* thread = GetThread(threadId);
|
|
|
|
for(unsigned int i = 0; i < 32; i++)
|
|
|
|
{
|
2008-01-15 20:27:44 +00:00
|
|
|
if(i == CMIPS::R0) continue;
|
|
|
|
if(i == CMIPS::K0) continue;
|
|
|
|
if(i == CMIPS::K1) continue;
|
2012-03-30 05:41:11 +00:00
|
|
|
thread->context.gpr[i] = m_cpu.m_State.nGPR[i].nV0;
|
|
|
|
}
|
2020-11-13 09:31:12 -05:00
|
|
|
thread->context.gpr[CMIPS::K0] = m_cpu.m_State.nLO[0];
|
|
|
|
thread->context.gpr[CMIPS::K1] = m_cpu.m_State.nHI[0];
|
2012-03-30 05:41:11 +00:00
|
|
|
thread->context.epc = m_cpu.m_State.nPC;
|
|
|
|
thread->context.delayJump = m_cpu.m_State.nDelayedJumpAddr;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2009-01-28 23:06:08 +00:00
|
|
|
void CIopBios::LinkThread(uint32 threadId)
|
|
|
|
{
|
2017-02-26 13:14:43 -05:00
|
|
|
auto thread = m_threads[threadId];
|
|
|
|
auto nextThreadId = &ThreadLinkHead();
|
2009-01-28 23:06:08 +00:00
|
|
|
while(1)
|
|
|
|
{
|
2017-02-26 13:14:43 -05:00
|
|
|
assert((*nextThreadId) < MAX_THREAD);
|
2009-01-28 23:06:08 +00:00
|
|
|
if((*nextThreadId) == 0)
|
|
|
|
{
|
|
|
|
(*nextThreadId) = threadId;
|
|
|
|
thread->nextThreadId = 0;
|
|
|
|
break;
|
|
|
|
}
|
2017-02-26 13:14:43 -05:00
|
|
|
auto currentThread = m_threads[(*nextThreadId)];
|
2012-05-27 00:54:16 +00:00
|
|
|
if(currentThread->priority > thread->priority)
|
2009-01-28 23:06:08 +00:00
|
|
|
{
|
|
|
|
thread->nextThreadId = (*nextThreadId);
|
|
|
|
(*nextThreadId) = threadId;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nextThreadId = ¤tThread->nextThreadId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::UnlinkThread(uint32 threadId)
|
|
|
|
{
|
|
|
|
THREAD* thread = m_threads[threadId];
|
|
|
|
uint32* nextThreadId = &ThreadLinkHead();
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
if((*nextThreadId) == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
THREAD* currentThread = m_threads[(*nextThreadId)];
|
|
|
|
if((*nextThreadId) == threadId)
|
|
|
|
{
|
|
|
|
(*nextThreadId) = thread->nextThreadId;
|
|
|
|
thread->nextThreadId = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nextThreadId = ¤tThread->nextThreadId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
void CIopBios::Reschedule()
|
|
|
|
{
|
2012-06-24 19:06:02 +00:00
|
|
|
if((m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_EXL) != 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-10 18:08:59 -04:00
|
|
|
//Don't switch if interrupts are disabled
|
|
|
|
if((m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_IE) != CMIPS::STATUS_IE)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-04 01:03:32 -05:00
|
|
|
if(m_currentThreadId != -1)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
SaveThreadContext(m_currentThreadId);
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2008-01-15 20:27:44 +00:00
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
uint32 nextThreadId = GetNextReadyThread();
|
|
|
|
if(nextThreadId == -1)
|
|
|
|
{
|
2008-10-28 21:32:18 +00:00
|
|
|
m_cpu.m_State.nPC = m_idleFunctionAddress;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2008-10-28 21:32:18 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
LoadThreadContext(nextThreadId);
|
|
|
|
}
|
2008-11-14 17:10:21 +00:00
|
|
|
#ifdef _DEBUG
|
2015-11-04 01:03:32 -05:00
|
|
|
if(nextThreadId != m_currentThreadId)
|
2008-11-14 17:10:21 +00:00
|
|
|
{
|
|
|
|
CLog::GetInstance().Print(LOGNAME, "Switched over to thread %i.\r\n", nextThreadId);
|
|
|
|
}
|
|
|
|
#endif
|
2015-11-04 01:03:32 -05:00
|
|
|
m_currentThreadId = nextThreadId;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2009-01-28 23:06:08 +00:00
|
|
|
uint32 CIopBios::GetNextReadyThread()
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2009-01-28 23:06:08 +00:00
|
|
|
uint32 nextThreadId = ThreadLinkHead();
|
|
|
|
while(nextThreadId != 0)
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
THREAD* nextThread = m_threads[nextThreadId];
|
2009-01-28 23:06:08 +00:00
|
|
|
nextThreadId = nextThread->nextThreadId;
|
2012-03-30 05:41:11 +00:00
|
|
|
if(GetCurrentTime() <= nextThread->nextActivateTime) continue;
|
2015-10-04 18:30:33 -04:00
|
|
|
assert(nextThread->status == THREAD_STATUS_RUNNING);
|
|
|
|
return nextThread->id;
|
2009-01-28 23:06:08 +00:00
|
|
|
}
|
|
|
|
return -1;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 21:06:16 -04:00
|
|
|
uint64 CIopBios::GetCurrentTime() const
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2009-01-26 02:53:10 +00:00
|
|
|
return CurrentTime();
|
2008-11-07 23:50:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64 CIopBios::MilliSecToClock(uint32 value)
|
|
|
|
{
|
2012-05-29 01:01:02 +00:00
|
|
|
return (static_cast<uint64>(value) * static_cast<uint64>(PS2::IOP_CLOCK_OVER_FREQ)) / 1000;
|
2008-11-07 23:50:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64 CIopBios::MicroSecToClock(uint32 value)
|
|
|
|
{
|
2012-05-29 01:01:02 +00:00
|
|
|
return (static_cast<uint64>(value) * static_cast<uint64>(PS2::IOP_CLOCK_OVER_FREQ)) / 1000000;
|
2008-11-07 23:50:47 +00:00
|
|
|
}
|
|
|
|
|
2008-11-21 23:15:53 +00:00
|
|
|
uint64 CIopBios::ClockToMicroSec(uint64 clock)
|
|
|
|
{
|
2012-05-29 01:01:02 +00:00
|
|
|
return (clock * 1000000) / static_cast<uint64>(PS2::IOP_CLOCK_OVER_FREQ);
|
2008-11-21 23:15:53 +00:00
|
|
|
}
|
|
|
|
|
2008-11-07 23:50:47 +00:00
|
|
|
void CIopBios::CountTicks(uint32 ticks)
|
|
|
|
{
|
2009-01-26 02:53:10 +00:00
|
|
|
CurrentTime() += ticks;
|
2024-09-11 16:57:32 -04:00
|
|
|
m_sifMan->CountTicks(ticks);
|
2020-07-14 18:30:03 -04:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2023-02-12 17:55:55 -05:00
|
|
|
m_cdvdman->CountTicks(ticks);
|
2025-03-14 14:38:18 -04:00
|
|
|
m_cdvdfsv->CountTicks(ticks);
|
2020-07-14 18:30:03 -04:00
|
|
|
m_mcserv->CountTicks(ticks, m_sifMan.get());
|
2024-05-13 16:09:41 -04:00
|
|
|
m_usbd->CountTicks(ticks);
|
2020-07-14 18:30:03 -04:00
|
|
|
#endif
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2011-05-05 04:28:11 +00:00
|
|
|
void CIopBios::NotifyVBlankStart()
|
|
|
|
{
|
2015-10-04 18:30:33 -04:00
|
|
|
for(auto thread : m_threads)
|
2011-05-05 04:28:11 +00:00
|
|
|
{
|
2015-10-04 18:30:33 -04:00
|
|
|
if(!thread) continue;
|
|
|
|
if(thread->status == THREAD_STATUS_WAIT_VBLANK_START)
|
2011-05-05 04:28:11 +00:00
|
|
|
{
|
2015-10-04 18:30:33 -04:00
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
|
|
|
LinkThread(thread->id);
|
2011-05-05 04:28:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::NotifyVBlankEnd()
|
|
|
|
{
|
2015-10-04 18:30:33 -04:00
|
|
|
for(auto thread : m_threads)
|
2011-05-05 04:28:11 +00:00
|
|
|
{
|
2015-10-04 18:30:33 -04:00
|
|
|
if(!thread) continue;
|
|
|
|
if(thread->status == THREAD_STATUS_WAIT_VBLANK_END)
|
2011-05-05 04:28:11 +00:00
|
|
|
{
|
2015-10-04 18:30:33 -04:00
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
|
|
|
LinkThread(thread->id);
|
2011-05-05 04:28:11 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-16 03:33:59 -04:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2019-08-01 12:52:43 -04:00
|
|
|
m_fileIo->ProcessCommands(m_sifMan.get());
|
2014-10-16 03:33:59 -04:00
|
|
|
#endif
|
2011-05-05 04:28:11 +00:00
|
|
|
}
|
|
|
|
|
2021-11-19 17:25:42 -05:00
|
|
|
uint32 CIopBios::CreateSemaphore(uint32 initialCount, uint32 maxCount, uint32 optionData, uint32 attributes)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2021-11-19 17:25:42 -05:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%i: CreateSemaphore(initialCount = %i, maxCount = %i, optionData = 0x%08X, attributes = 0x%08X);\r\n",
|
|
|
|
m_currentThreadId.Get(), initialCount, maxCount, optionData, attributes);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2009-01-30 22:10:15 +00:00
|
|
|
uint32 semaphoreId = m_semaphores.Allocate();
|
|
|
|
assert(semaphoreId != -1);
|
|
|
|
if(semaphoreId == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-11-19 17:25:42 -05:00
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
2018-04-30 21:01:23 +01:00
|
|
|
semaphore->count = initialCount;
|
|
|
|
semaphore->maxCount = maxCount;
|
|
|
|
semaphore->id = semaphoreId;
|
|
|
|
semaphore->waitCount = 0;
|
2021-11-19 17:25:42 -05:00
|
|
|
semaphore->attrib = attributes;
|
|
|
|
semaphore->option = optionData;
|
2009-01-30 22:10:15 +00:00
|
|
|
|
|
|
|
return semaphore->id;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2009-02-09 05:27:03 +00:00
|
|
|
uint32 CIopBios::DeleteSemaphore(uint32 semaphoreId)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2012-03-30 05:41:11 +00:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%i: DeleteSemaphore(semaphoreId = %i);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), semaphoreId);
|
2009-02-09 05:27:03 +00:00
|
|
|
#endif
|
|
|
|
|
2018-05-07 16:40:15 -04:00
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
|
|
|
if(!semaphore)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%i: Warning, trying to access invalid semaphore with id %i.\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId);
|
2018-05-07 16:40:15 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_SEMAID;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2009-02-09 05:27:03 +00:00
|
|
|
|
2018-05-07 17:03:59 -04:00
|
|
|
if(semaphore->waitCount != 0)
|
|
|
|
{
|
|
|
|
while(semaphore->waitCount != 0)
|
|
|
|
{
|
|
|
|
bool changed = SemaReleaseSingleThread(semaphoreId, true);
|
|
|
|
if(!changed) break;
|
|
|
|
}
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
m_semaphores.Free(semaphoreId);
|
2009-02-09 05:27:03 +00:00
|
|
|
|
2018-05-07 16:40:15 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2009-02-09 05:27:03 +00:00
|
|
|
}
|
|
|
|
|
2008-10-28 21:32:18 +00:00
|
|
|
uint32 CIopBios::SignalSemaphore(uint32 semaphoreId, bool inInterrupt)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: SignalSemaphore(semaphoreId = %d, inInterrupt = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId, inInterrupt);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2018-05-07 17:01:19 -04:00
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
|
|
|
if(!semaphore)
|
2009-01-30 22:10:15 +00:00
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%d: Warning, trying to access invalid semaphore with id %d.\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId);
|
2018-05-07 17:01:19 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_SEMAID;
|
2009-01-30 22:10:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
if(semaphore->waitCount != 0)
|
|
|
|
{
|
2018-05-07 17:05:29 -04:00
|
|
|
SemaReleaseSingleThread(semaphoreId, false);
|
|
|
|
if(!inInterrupt)
|
2009-01-28 23:06:08 +00:00
|
|
|
{
|
2018-05-07 17:05:29 -04:00
|
|
|
m_rescheduleNeeded = true;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-13 19:17:35 -04:00
|
|
|
if(semaphore->count == semaphore->maxCount)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_SEMA_OVF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
semaphore->count++;
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2018-05-07 17:01:19 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::WaitSemaphore(uint32 semaphoreId)
|
|
|
|
{
|
2008-10-23 02:21:42 +00:00
|
|
|
#ifdef _DEBUG
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: WaitSemaphore(semaphoreId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId);
|
2008-10-23 02:21:42 +00:00
|
|
|
#endif
|
|
|
|
|
2018-05-05 12:17:41 -04:00
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
|
|
|
if(!semaphore)
|
2009-01-30 22:10:15 +00:00
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%d: Warning, trying to access invalid semaphore with id %d.\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId);
|
2018-05-05 12:17:41 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_SEMAID;
|
2009-01-30 22:10:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
if(semaphore->count == 0)
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
uint32 threadId = m_currentThreadId;
|
2015-10-04 18:30:33 -04:00
|
|
|
THREAD* thread = GetThread(threadId);
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->status = THREAD_STATUS_WAITING_SEMAPHORE;
|
|
|
|
thread->waitSemaphore = semaphoreId;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(threadId);
|
2012-03-30 05:41:11 +00:00
|
|
|
semaphore->waitCount++;
|
2012-04-12 04:52:26 +00:00
|
|
|
m_rescheduleNeeded = true;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
semaphore->count--;
|
|
|
|
}
|
2018-05-03 16:32:54 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2012-04-12 04:52:26 +00:00
|
|
|
}
|
|
|
|
|
2015-03-08 22:11:36 -04:00
|
|
|
uint32 CIopBios::PollSemaphore(uint32 semaphoreId)
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: PollSemaphore(semaphoreId = %d);\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId);
|
2015-03-08 22:11:36 -04:00
|
|
|
|
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
2018-05-05 12:17:41 -04:00
|
|
|
if(!semaphore)
|
2015-03-08 22:11:36 -04:00
|
|
|
{
|
2018-05-05 12:17:41 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_SEMAID;
|
2015-03-08 22:11:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if(semaphore->count == 0)
|
|
|
|
{
|
2015-11-28 21:04:37 -05:00
|
|
|
return KERNEL_RESULT_ERROR_SEMA_ZERO;
|
2015-03-08 22:11:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
semaphore->count--;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::ReferSemaphoreStatus(uint32 semaphoreId, uint32 statusPtr)
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ReferSemaphoreStatus(semaphoreId = %d, statusPtr = 0x%08X);\r\n",
|
|
|
|
m_currentThreadId.Get(), semaphoreId, statusPtr);
|
2015-03-08 22:11:36 -04:00
|
|
|
|
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
|
|
|
if(semaphore == nullptr)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto status = reinterpret_cast<SEMAPHORE_STATUS*>(m_ram + statusPtr);
|
2021-11-19 17:25:42 -05:00
|
|
|
status->attrib = semaphore->attrib;
|
|
|
|
status->option = semaphore->option;
|
2018-04-30 21:01:23 +01:00
|
|
|
status->initCount = 0;
|
|
|
|
status->maxCount = semaphore->maxCount;
|
|
|
|
status->currentCount = semaphore->count;
|
|
|
|
status->numWaitThreads = semaphore->waitCount;
|
2015-03-08 22:11:36 -04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-07 17:03:59 -04:00
|
|
|
bool CIopBios::SemaReleaseSingleThread(uint32 semaphoreId, bool deleted)
|
|
|
|
{
|
|
|
|
auto semaphore = m_semaphores[semaphoreId];
|
|
|
|
assert(semaphore);
|
|
|
|
assert(semaphore->waitCount != 0);
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
for(auto thread : m_threads)
|
|
|
|
{
|
|
|
|
if(!thread) continue;
|
|
|
|
if(thread->waitSemaphore == semaphoreId)
|
|
|
|
{
|
|
|
|
assert(thread->status == THREAD_STATUS_WAITING_SEMAPHORE);
|
|
|
|
thread->context.gpr[CMIPS::V0] = deleted ? KERNEL_RESULT_ERROR_WAIT_DELETE : KERNEL_RESULT_OK;
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
|
|
|
LinkThread(thread->id);
|
|
|
|
thread->waitSemaphore = 0;
|
|
|
|
semaphore->waitCount--;
|
|
|
|
changed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Something went wrong if nothing changed
|
|
|
|
assert(changed);
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2012-04-12 04:52:26 +00:00
|
|
|
uint32 CIopBios::CreateEventFlag(uint32 attributes, uint32 options, uint32 initValue)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: CreateEventFlag(attr = 0x%08X, opt = 0x%08X, initValue = 0x%08X);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), attributes, options, initValue);
|
2012-04-12 04:52:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32 eventId = m_eventFlags.Allocate();
|
|
|
|
assert(eventId != -1);
|
|
|
|
if(eventId == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVENTFLAG* eventFlag = m_eventFlags[eventId];
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
eventFlag->id = eventId;
|
|
|
|
eventFlag->value = initValue;
|
|
|
|
eventFlag->options = options;
|
|
|
|
eventFlag->attributes = attributes;
|
2012-04-12 04:52:26 +00:00
|
|
|
|
|
|
|
return eventFlag->id;
|
|
|
|
}
|
|
|
|
|
2016-10-15 21:20:46 -04:00
|
|
|
uint32 CIopBios::DeleteEventFlag(uint32 eventId)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: DeleteEventFlag(eventId = %d);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), eventId);
|
2016-10-15 21:20:46 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
auto eventFlag = m_eventFlags[eventId];
|
|
|
|
if(!eventFlag)
|
|
|
|
{
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%d: Warning, trying to access invalid event flag with id %d.\r\n",
|
|
|
|
m_currentThreadId.Get(), eventId);
|
2016-10-15 21:20:46 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_EVFID;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_eventFlags.Free(eventId);
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2012-04-12 04:52:26 +00:00
|
|
|
uint32 CIopBios::SetEventFlag(uint32 eventId, uint32 value, bool inInterrupt)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: SetEventFlag(eventId = %d, value = 0x%08X, inInterrupt = %d);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), eventId, value, inInterrupt);
|
2012-04-12 04:52:26 +00:00
|
|
|
#endif
|
|
|
|
|
2015-05-13 01:51:03 -04:00
|
|
|
auto eventFlag = m_eventFlags[eventId];
|
|
|
|
if(eventFlag == nullptr)
|
2012-04-12 04:52:26 +00:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
eventFlag->value |= value;
|
|
|
|
|
|
|
|
//Check all threads waiting for this event
|
2015-08-05 23:38:40 -04:00
|
|
|
for(auto thread : m_threads)
|
2012-04-12 04:52:26 +00:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
if(!thread) continue;
|
2012-06-30 02:31:22 +00:00
|
|
|
if(thread->status != THREAD_STATUS_WAITING_EVENTFLAG) continue;
|
2012-04-12 04:52:26 +00:00
|
|
|
if(thread->waitEventFlag == eventId)
|
|
|
|
{
|
2018-04-30 21:01:23 +01:00
|
|
|
bool success = ProcessEventFlag(thread->waitEventFlagMode, eventFlag->value, thread->waitEventFlagMask,
|
|
|
|
(thread->waitEventFlagResultPtr != 0) ? reinterpret_cast<uint32*>(m_ram + thread->waitEventFlagResultPtr) : nullptr);
|
2012-04-12 04:52:26 +00:00
|
|
|
if(success)
|
|
|
|
{
|
|
|
|
thread->waitEventFlag = 0;
|
|
|
|
thread->waitEventFlagResultPtr = 0;
|
|
|
|
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
2015-10-04 18:30:33 -04:00
|
|
|
LinkThread(thread->id);
|
|
|
|
|
2012-04-12 04:52:26 +00:00
|
|
|
if(!inInterrupt)
|
|
|
|
{
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::ClearEventFlag(uint32 eventId, uint32 value)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ClearEventFlag(eventId = %d, value = 0x%08X);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), eventId, value);
|
2012-04-12 04:52:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
EVENTFLAG* eventFlag = m_eventFlags[eventId];
|
|
|
|
if(eventFlag == NULL)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
eventFlag->value &= value;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::WaitEventFlag(uint32 eventId, uint32 value, uint32 mode, uint32 resultPtr)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: WaitEventFlag(eventId = %d, value = 0x%08X, mode = 0x%02X, resultPtr = 0x%08X);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), eventId, value, mode, resultPtr);
|
2012-04-12 04:52:26 +00:00
|
|
|
#endif
|
|
|
|
|
2015-05-13 01:51:03 -04:00
|
|
|
auto eventFlag = m_eventFlags[eventId];
|
|
|
|
if(eventFlag == nullptr)
|
2012-04-12 04:52:26 +00:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
bool success = ProcessEventFlag(mode, eventFlag->value, value,
|
|
|
|
(resultPtr != 0) ? reinterpret_cast<uint32*>(m_ram + resultPtr) : nullptr);
|
2012-09-12 04:03:41 +00:00
|
|
|
if(!success)
|
2012-04-12 04:52:26 +00:00
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
auto thread = GetThread(m_currentThreadId);
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->status = THREAD_STATUS_WAITING_EVENTFLAG;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->waitEventFlag = eventId;
|
|
|
|
thread->waitEventFlagMode = mode;
|
|
|
|
thread->waitEventFlagMask = value;
|
|
|
|
thread->waitEventFlagResultPtr = resultPtr;
|
2012-04-12 04:52:26 +00:00
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
2012-04-26 05:50:37 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-16 17:21:31 -04:00
|
|
|
uint32 CIopBios::PollEventFlag(uint32 eventId, uint32 value, uint32 mode, uint32 resultPtr)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: PollEventFlag(eventId = %d, value = 0x%08X, mode = 0x%02X, resultPtr = 0x%08X);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), eventId, value, mode, resultPtr);
|
2016-10-16 17:21:31 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
auto eventFlag = m_eventFlags[eventId];
|
|
|
|
if(!eventFlag)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_EVFID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(value == 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_EVF_ILLEGAL_PAT;
|
|
|
|
}
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
bool success = ProcessEventFlag(mode, eventFlag->value, value,
|
|
|
|
(resultPtr != 0) ? reinterpret_cast<uint32*>(m_ram + resultPtr) : nullptr);
|
2016-10-16 17:21:31 -04:00
|
|
|
if(!success)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_EVF_CONDITION;
|
|
|
|
}
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2012-04-26 05:50:37 +00:00
|
|
|
uint32 CIopBios::ReferEventFlagStatus(uint32 eventId, uint32 infoPtr)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ReferEventFlagStatus(eventId = %d, infoPtr = 0x%08X);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), eventId, infoPtr);
|
2012-04-26 05:50:37 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
EVENTFLAG* eventFlag = m_eventFlags[eventId];
|
|
|
|
if(eventFlag == NULL)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(infoPtr == 0)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVENTFLAGINFO* eventFlagInfo(reinterpret_cast<EVENTFLAGINFO*>(m_ram + infoPtr));
|
2018-04-30 21:01:23 +01:00
|
|
|
eventFlagInfo->attributes = eventFlag->attributes;
|
|
|
|
eventFlagInfo->options = eventFlag->options;
|
|
|
|
eventFlagInfo->initBits = 0;
|
|
|
|
eventFlagInfo->currBits = eventFlag->value;
|
|
|
|
eventFlagInfo->numThreads = 0;
|
2012-04-26 05:50:37 +00:00
|
|
|
|
2012-04-12 04:52:26 +00:00
|
|
|
return 0;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2012-09-12 04:03:41 +00:00
|
|
|
bool CIopBios::ProcessEventFlag(uint32 mode, uint32& value, uint32 mask, uint32* resultPtr)
|
|
|
|
{
|
|
|
|
bool success = false;
|
|
|
|
uint32 maskResult = value & mask;
|
|
|
|
|
|
|
|
if(mode & WEF_OR)
|
|
|
|
{
|
|
|
|
success = (maskResult != 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
success = (maskResult == mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(success)
|
|
|
|
{
|
2016-10-16 17:28:16 -04:00
|
|
|
if(resultPtr)
|
2012-09-12 04:03:41 +00:00
|
|
|
{
|
2016-10-16 17:28:16 -04:00
|
|
|
*resultPtr = value;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
|
2016-10-16 17:28:16 -04:00
|
|
|
if(mode & WEF_CLEAR)
|
2012-09-12 04:03:41 +00:00
|
|
|
{
|
2016-10-16 17:32:49 -04:00
|
|
|
value = 0;
|
2012-09-12 04:03:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2012-06-30 02:31:22 +00:00
|
|
|
uint32 CIopBios::CreateMessageBox()
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: CreateMessageBox();\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get());
|
2012-06-30 02:31:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32 boxId = m_messageBoxes.Allocate();
|
|
|
|
assert(boxId != -1);
|
|
|
|
if(boxId == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-11-01 23:09:06 -05:00
|
|
|
auto box = m_messageBoxes[boxId];
|
2012-06-30 02:31:22 +00:00
|
|
|
box->nextMsgPtr = 0;
|
2015-11-01 23:09:06 -05:00
|
|
|
box->numMessage = 0;
|
2012-06-30 02:31:22 +00:00
|
|
|
|
|
|
|
return boxId;
|
|
|
|
}
|
|
|
|
|
2015-11-01 16:21:07 -05:00
|
|
|
uint32 CIopBios::DeleteMessageBox(uint32 boxId)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: DeleteMessageBox(boxId = %d);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), boxId);
|
2015-11-01 16:21:07 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
auto box = m_messageBoxes[boxId];
|
|
|
|
if(!box)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MBXID;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_messageBoxes.Free(boxId);
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2016-10-20 21:09:41 -04:00
|
|
|
uint32 CIopBios::SendMessageBox(uint32 boxId, uint32 messagePtr, bool inInterrupt)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: SendMessageBox(boxId = %d, messagePtr = 0x%08X, inInterrupt = %d);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), boxId, messagePtr, inInterrupt);
|
2012-06-30 02:31:22 +00:00
|
|
|
#endif
|
|
|
|
|
2015-11-01 23:09:06 -05:00
|
|
|
auto box = m_messageBoxes[boxId];
|
|
|
|
if(!box)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2015-11-01 23:09:06 -05:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MBXID;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//Check if there's a thread waiting for a message first
|
2015-08-05 23:38:40 -04:00
|
|
|
for(auto thread : m_threads)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
if(!thread) continue;
|
2012-06-30 02:31:22 +00:00
|
|
|
if(thread->status != THREAD_STATUS_WAITING_MESSAGEBOX) continue;
|
|
|
|
if(thread->waitMessageBox == boxId)
|
|
|
|
{
|
|
|
|
if(thread->waitMessageBoxResultPtr != 0)
|
|
|
|
{
|
|
|
|
uint32* result = reinterpret_cast<uint32*>(m_ram + thread->waitMessageBoxResultPtr);
|
|
|
|
*result = messagePtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread->waitMessageBox = 0;
|
|
|
|
thread->waitMessageBoxResultPtr = 0;
|
|
|
|
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
2015-10-04 18:30:33 -04:00
|
|
|
LinkThread(thread->id);
|
2012-06-30 02:31:22 +00:00
|
|
|
if(!inInterrupt)
|
|
|
|
{
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2015-11-01 23:09:06 -05:00
|
|
|
return KERNEL_RESULT_OK;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-29 11:41:51 -05:00
|
|
|
//Check if message already exists in the linked list
|
2024-01-31 16:52:51 -05:00
|
|
|
if(box->numMessage != 0)
|
2024-01-29 11:41:51 -05:00
|
|
|
{
|
|
|
|
auto msgPtr = &box->nextMsgPtr;
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
if((*msgPtr) == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
msgPtr = reinterpret_cast<uint32*>(m_ram + *msgPtr);
|
|
|
|
if((*msgPtr) == messagePtr)
|
|
|
|
{
|
|
|
|
//Message already queued in the message box.
|
|
|
|
//Space Invaders: Invasion Day will trigger this and will end up corrupting the linked list.
|
|
|
|
//We return an error here, but this is not confirmed if this is the proper behavior.
|
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Failed to send message: message already queued (boxId = %d, messagePtr = 0x%08X).\r\n",
|
|
|
|
boxId, messagePtr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-01 23:09:06 -05:00
|
|
|
auto header = reinterpret_cast<MESSAGE_HEADER*>(m_ram + messagePtr);
|
|
|
|
header->nextMsgPtr = 0;
|
|
|
|
|
|
|
|
auto msgPtr = &box->nextMsgPtr;
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
if((*msgPtr) == 0)
|
|
|
|
{
|
|
|
|
(*msgPtr) = messagePtr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
msgPtr = reinterpret_cast<uint32*>(m_ram + *msgPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
box->numMessage++;
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::ReceiveMessageBox(uint32 messagePtr, uint32 boxId)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ReceiveMessageBox(messagePtr = 0x%08X, boxId = %d);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), messagePtr, boxId);
|
2012-06-30 02:31:22 +00:00
|
|
|
#endif
|
|
|
|
|
2015-11-01 23:09:06 -05:00
|
|
|
auto box = m_messageBoxes[boxId];
|
|
|
|
if(!box)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2015-11-01 23:09:06 -05:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MBXID;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 12:40:25 -04:00
|
|
|
if(box->numMessage != 0)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2015-11-01 23:09:06 -05:00
|
|
|
uint32* message = reinterpret_cast<uint32*>(m_ram + messagePtr);
|
|
|
|
(*message) = box->nextMsgPtr;
|
|
|
|
|
|
|
|
//Unlink message
|
|
|
|
auto header = reinterpret_cast<MESSAGE_HEADER*>(m_ram + box->nextMsgPtr);
|
|
|
|
box->nextMsgPtr = header->nextMsgPtr;
|
|
|
|
box->numMessage--;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
THREAD* thread = GetThread(m_currentThreadId);
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->status = THREAD_STATUS_WAITING_MESSAGEBOX;
|
2015-10-04 18:30:33 -04:00
|
|
|
UnlinkThread(thread->id);
|
2018-04-30 21:01:23 +01:00
|
|
|
thread->waitMessageBox = boxId;
|
|
|
|
thread->waitMessageBoxResultPtr = messagePtr;
|
2012-06-30 02:31:22 +00:00
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
2015-11-01 23:09:06 -05:00
|
|
|
return KERNEL_RESULT_OK;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::PollMessageBox(uint32 messagePtr, uint32 boxId)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: PollMessageBox(messagePtr = 0x%08X, boxId = %d);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), messagePtr, boxId);
|
2012-06-30 02:31:22 +00:00
|
|
|
#endif
|
|
|
|
|
2016-06-27 20:09:33 -04:00
|
|
|
auto box = m_messageBoxes[boxId];
|
|
|
|
if(!box)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2016-06-27 20:09:33 -04:00
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MBXID;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 12:40:25 -04:00
|
|
|
if(box->numMessage == 0)
|
2012-06-30 02:31:22 +00:00
|
|
|
{
|
2016-06-27 20:09:33 -04:00
|
|
|
return KERNEL_RESULT_ERROR_MBX_NOMSG;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
2016-06-27 20:09:33 -04:00
|
|
|
auto message = reinterpret_cast<uint32*>(m_ram + messagePtr);
|
|
|
|
(*message) = box->nextMsgPtr;
|
|
|
|
|
|
|
|
//Unlink message
|
|
|
|
auto header = reinterpret_cast<MESSAGE_HEADER*>(m_ram + box->nextMsgPtr);
|
|
|
|
box->nextMsgPtr = header->nextMsgPtr;
|
|
|
|
box->numMessage--;
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
2012-06-30 02:31:22 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 23:43:25 -05:00
|
|
|
uint32 CIopBios::ReferMessageBoxStatus(uint32 boxId, uint32 statusPtr)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2017-05-29 06:01:32 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "%d: ReferMessageBox(boxId = %d, statusPtr = 0x%08X);\r\n",
|
2018-04-30 21:01:23 +01:00
|
|
|
m_currentThreadId.Get(), boxId, statusPtr);
|
2015-11-01 23:43:25 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
auto box = m_messageBoxes[boxId];
|
|
|
|
if(!box)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_MBXID;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto status = reinterpret_cast<MESSAGEBOX_STATUS*>(m_ram + statusPtr);
|
2018-04-30 21:01:23 +01:00
|
|
|
status->attr = 0;
|
|
|
|
status->option = 0;
|
|
|
|
status->numMessage = box->numMessage;
|
2015-11-01 23:43:25 -05:00
|
|
|
status->numWaitThread = 0;
|
2018-04-30 21:01:23 +01:00
|
|
|
status->messagePtr = box->nextMsgPtr;
|
2015-11-01 23:43:25 -05:00
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2019-04-28 18:07:48 -04:00
|
|
|
uint32 CIopBios::CreateFpl(uint32 paramPtr)
|
|
|
|
{
|
|
|
|
auto param = reinterpret_cast<FPL_PARAM*>(m_ram + paramPtr);
|
|
|
|
if((param->attr & ~FPL_ATTR_VALID_MASK) != 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_ATTR;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto fplId = m_fpls.Allocate();
|
|
|
|
assert(fplId != FplList::INVALID_ID);
|
|
|
|
if(fplId == FplList::INVALID_ID)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 bitmapSize = (param->blockCount + 7) / 8;
|
|
|
|
uint32 totalSize = (param->blockCount * param->blockSize) + bitmapSize;
|
|
|
|
|
|
|
|
uint32 poolPtr = m_sysmem->AllocateMemory(totalSize, 0, 0);
|
|
|
|
if(poolPtr == 0)
|
|
|
|
{
|
|
|
|
m_fpls.Free(fplId);
|
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto fpl = m_fpls[fplId];
|
|
|
|
fpl->attr = param->attr;
|
|
|
|
fpl->option = param->option;
|
|
|
|
fpl->blockCount = param->blockCount;
|
|
|
|
fpl->blockSize = param->blockSize;
|
|
|
|
fpl->poolPtr = poolPtr;
|
|
|
|
|
|
|
|
return fplId;
|
|
|
|
}
|
|
|
|
|
2022-08-22 20:00:33 -04:00
|
|
|
uint32 CIopBios::DeleteFpl(uint32 fplId)
|
|
|
|
{
|
|
|
|
auto fpl = m_fpls[fplId];
|
|
|
|
if(!fpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_FPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_sysmem->FreeMemory(fpl->poolPtr);
|
|
|
|
m_fpls.Free(fplId);
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2019-04-28 18:07:48 -04:00
|
|
|
uint32 CIopBios::AllocateFpl(uint32 fplId)
|
|
|
|
{
|
|
|
|
uint32 result = pAllocateFpl(fplId);
|
|
|
|
if(result == KERNEL_RESULT_ERROR_NO_MEMORY)
|
|
|
|
{
|
|
|
|
CLog::GetInstance().Warn(LOGNAME, "No memory left while calling AllocateFpl, should be waiting. (not implemented)");
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::pAllocateFpl(uint32 fplId)
|
|
|
|
{
|
|
|
|
auto fpl = m_fpls[fplId];
|
|
|
|
if(!fpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_FPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 bitmapPtr = (fpl->blockCount * fpl->blockSize);
|
|
|
|
auto bitmap = m_ram + fpl->poolPtr + bitmapPtr;
|
|
|
|
|
|
|
|
for(uint32 blockIdx = 0; blockIdx < fpl->blockCount; blockIdx++)
|
|
|
|
{
|
|
|
|
uint32 bitmapByteIdx = blockIdx / 8;
|
|
|
|
uint32 bitmapBitMask = 1 << (blockIdx % 8);
|
|
|
|
uint8& bitmapByte = bitmap[bitmapByteIdx];
|
|
|
|
if(bitmapByte & bitmapBitMask) continue;
|
|
|
|
bitmapByte |= bitmapBitMask;
|
|
|
|
return fpl->poolPtr + (blockIdx * fpl->blockSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::FreeFpl(uint32 fplId, uint32 blockPtr)
|
|
|
|
{
|
|
|
|
auto fpl = m_fpls[fplId];
|
|
|
|
if(!fpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_FPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(blockPtr < fpl->poolPtr)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_MEMBLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 blockIdx = (blockPtr - fpl->poolPtr) / fpl->blockSize;
|
|
|
|
if(blockIdx >= fpl->blockCount)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_MEMBLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 bitmapPtr = (fpl->blockCount * fpl->blockSize);
|
|
|
|
auto bitmap = m_ram + fpl->poolPtr + bitmapPtr;
|
|
|
|
uint32 bitmapByteIdx = blockIdx / 8;
|
|
|
|
uint32 bitmapBitMask = 1 << (blockIdx % 8);
|
|
|
|
|
|
|
|
bitmap[bitmapByteIdx] &= ~bitmapBitMask;
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2016-02-28 15:55:45 -05:00
|
|
|
uint32 CIopBios::CreateVpl(uint32 paramPtr)
|
|
|
|
{
|
|
|
|
auto param = reinterpret_cast<VPL_PARAM*>(m_ram + paramPtr);
|
|
|
|
if((param->attr & ~VPL_ATTR_VALID_MASK) != 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_ATTR;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto vplId = m_vpls.Allocate();
|
|
|
|
assert(vplId != VplList::INVALID_ID);
|
|
|
|
if(vplId == VplList::INVALID_ID)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto headBlockId = m_memoryBlocks.Allocate();
|
|
|
|
assert(headBlockId != MemoryBlockList::INVALID_ID);
|
|
|
|
if(headBlockId == MemoryBlockList::INVALID_ID)
|
|
|
|
{
|
|
|
|
m_vpls.Free(vplId);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 poolPtr = m_sysmem->AllocateMemory(param->size, 0, 0);
|
|
|
|
if(poolPtr == 0)
|
|
|
|
{
|
|
|
|
//This seems to work on actual hardware (maybe fixed in later revisions)
|
|
|
|
m_memoryBlocks.Free(headBlockId);
|
|
|
|
m_vpls.Free(vplId);
|
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto vpl = m_vpls[vplId];
|
2018-04-30 21:01:23 +01:00
|
|
|
vpl->attr = param->attr;
|
|
|
|
vpl->option = param->option;
|
|
|
|
vpl->poolPtr = poolPtr;
|
|
|
|
vpl->size = param->size;
|
2016-02-28 15:55:45 -05:00
|
|
|
vpl->headBlockId = headBlockId;
|
|
|
|
|
|
|
|
auto headBlock = m_memoryBlocks[headBlockId];
|
|
|
|
headBlock->nextBlockId = MemoryBlockList::INVALID_ID;
|
2018-04-30 21:01:23 +01:00
|
|
|
headBlock->address = vpl->size;
|
|
|
|
headBlock->size = 0;
|
2016-02-28 15:55:45 -05:00
|
|
|
|
|
|
|
return vplId;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::DeleteVpl(uint32 vplId)
|
|
|
|
{
|
|
|
|
auto vpl = m_vpls[vplId];
|
|
|
|
if(!vpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_VPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_sysmem->FreeMemory(vpl->poolPtr);
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2016-02-28 15:55:45 -05:00
|
|
|
//Free blocks
|
|
|
|
auto nextBlockId = vpl->headBlockId;
|
|
|
|
auto nextBlock = m_memoryBlocks[nextBlockId];
|
|
|
|
while(nextBlock != nullptr)
|
|
|
|
{
|
|
|
|
uint32 currentBlockId = nextBlockId;
|
|
|
|
nextBlockId = nextBlock->nextBlockId;
|
|
|
|
nextBlock = m_memoryBlocks[nextBlockId];
|
|
|
|
m_memoryBlocks.Free(currentBlockId);
|
|
|
|
}
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2016-02-28 15:55:45 -05:00
|
|
|
m_vpls.Free(vplId);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-11-11 12:17:33 -05:00
|
|
|
uint32 CIopBios::AllocateVpl(uint32 vplId, uint32 size)
|
|
|
|
{
|
|
|
|
uint32 result = pAllocateVpl(vplId, size);
|
|
|
|
if(result == KERNEL_RESULT_ERROR_NO_MEMORY)
|
|
|
|
{
|
|
|
|
CLog::GetInstance().Warn(LOGNAME, "No memory left while calling AllocateVpl, should be waiting. (not implemented)");
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-02-28 15:55:45 -05:00
|
|
|
uint32 CIopBios::pAllocateVpl(uint32 vplId, uint32 size)
|
|
|
|
{
|
|
|
|
auto vpl = m_vpls[vplId];
|
|
|
|
if(!vpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_VPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Aligned to 8 bytes
|
|
|
|
int32 allocSize = (size + 7) & ~0x07;
|
|
|
|
if(allocSize < 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 freeSize = GetVplFreeSize(vplId);
|
|
|
|
if(allocSize > freeSize)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 begin = 0;
|
|
|
|
auto nextBlockId = &vpl->headBlockId;
|
|
|
|
auto nextBlock = m_memoryBlocks[*nextBlockId];
|
|
|
|
while(nextBlock != nullptr)
|
|
|
|
{
|
|
|
|
uint32 end = nextBlock->address;
|
|
|
|
if((end - begin) >= allocSize)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
begin = nextBlock->address + nextBlock->size;
|
|
|
|
nextBlockId = &nextBlock->nextBlockId;
|
|
|
|
nextBlock = m_memoryBlocks[*nextBlockId];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(nextBlock != nullptr)
|
|
|
|
{
|
|
|
|
uint32 newBlockId = m_memoryBlocks.Allocate();
|
|
|
|
assert(newBlockId != MemoryBlockList::INVALID_ID);
|
|
|
|
if(newBlockId == MemoryBlockList::INVALID_ID)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
auto newBlock = m_memoryBlocks[newBlockId];
|
2018-04-30 21:01:23 +01:00
|
|
|
newBlock->address = begin;
|
|
|
|
newBlock->size = allocSize;
|
2016-02-28 15:55:45 -05:00
|
|
|
newBlock->nextBlockId = *nextBlockId;
|
|
|
|
*nextBlockId = newBlockId;
|
|
|
|
return begin + vpl->poolPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_MEMSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::FreeVpl(uint32 vplId, uint32 ptr)
|
|
|
|
{
|
|
|
|
auto vpl = m_vpls[vplId];
|
|
|
|
if(!vpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_VPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr -= vpl->poolPtr;
|
|
|
|
//Search for block pointing at the address
|
|
|
|
auto nextBlockId = &vpl->headBlockId;
|
|
|
|
auto nextBlock = m_memoryBlocks[*nextBlockId];
|
|
|
|
while(nextBlock != nullptr)
|
|
|
|
{
|
|
|
|
if(nextBlock->address == ptr)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nextBlockId = &nextBlock->nextBlockId;
|
|
|
|
nextBlock = m_memoryBlocks[*nextBlockId];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(nextBlock != nullptr)
|
|
|
|
{
|
|
|
|
m_memoryBlocks.Free(*nextBlockId);
|
|
|
|
*nextBlockId = nextBlock->nextBlockId;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::ReferVplStatus(uint32 vplId, uint32 statPtr)
|
|
|
|
{
|
|
|
|
auto vpl = m_vpls[vplId];
|
|
|
|
if(!vpl)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_UNKNOWN_VPLID;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 size = vpl->size - 40;
|
|
|
|
uint32 freeSize = GetVplFreeSize(vplId);
|
|
|
|
|
|
|
|
auto stat = reinterpret_cast<VPL_STATUS*>(m_ram + statPtr);
|
2018-04-30 21:01:23 +01:00
|
|
|
stat->attr = vpl->attr;
|
|
|
|
stat->option = vpl->option;
|
|
|
|
stat->size = size;
|
2016-02-28 15:55:45 -05:00
|
|
|
stat->freeSize = freeSize;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::GetVplFreeSize(uint32 vplId)
|
|
|
|
{
|
|
|
|
auto vpl = m_vpls[vplId];
|
|
|
|
assert(vpl != nullptr);
|
|
|
|
if(!vpl)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 size = vpl->size - 40;
|
|
|
|
|
|
|
|
uint32 freeSize = size;
|
|
|
|
auto nextBlockId = vpl->headBlockId;
|
|
|
|
auto nextBlock = m_memoryBlocks[nextBlockId];
|
|
|
|
while(nextBlock != nullptr)
|
|
|
|
{
|
|
|
|
if(nextBlock->nextBlockId == MemoryBlockList::INVALID_ID)
|
|
|
|
{
|
|
|
|
assert(nextBlock->address == vpl->size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
freeSize -= nextBlock->size;
|
|
|
|
freeSize -= 8;
|
|
|
|
nextBlockId = nextBlock->nextBlockId;
|
|
|
|
nextBlock = m_memoryBlocks[nextBlockId];
|
|
|
|
}
|
|
|
|
|
|
|
|
return freeSize;
|
|
|
|
}
|
|
|
|
|
2020-11-13 12:00:51 -05:00
|
|
|
void CIopBios::WaitCdSync()
|
|
|
|
{
|
|
|
|
uint32 threadId = m_currentThreadId;
|
|
|
|
auto thread = GetThread(threadId);
|
|
|
|
thread->status = THREAD_STATUS_WAIT_CDSYNC;
|
|
|
|
UnlinkThread(threadId);
|
|
|
|
m_rescheduleNeeded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::ReleaseWaitCdSync()
|
|
|
|
{
|
|
|
|
for(auto thread : m_threads)
|
|
|
|
{
|
|
|
|
if(!thread) continue;
|
|
|
|
if(thread->status != THREAD_STATUS_WAIT_CDSYNC) continue;
|
|
|
|
|
|
|
|
thread->status = THREAD_STATUS_RUNNING;
|
|
|
|
LinkThread(thread->id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-15 15:45:48 -04:00
|
|
|
Iop::CSysmem* CIopBios::GetSysmem()
|
|
|
|
{
|
|
|
|
return m_sysmem.get();
|
|
|
|
}
|
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
Iop::CIoman* CIopBios::GetIoman()
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
return m_ioman.get();
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 18:46:38 -04:00
|
|
|
Iop::CSifMan* CIopBios::GetSifman()
|
|
|
|
{
|
|
|
|
return m_sifMan.get();
|
|
|
|
}
|
|
|
|
|
2023-06-28 09:16:55 -04:00
|
|
|
Iop::CSifCmd* CIopBios::GetSifcmd()
|
|
|
|
{
|
|
|
|
return m_sifCmd.get();
|
|
|
|
}
|
|
|
|
|
2009-04-28 01:20:03 +00:00
|
|
|
Iop::CCdvdman* CIopBios::GetCdvdman()
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
return m_cdvdman.get();
|
2009-04-28 01:20:03 +00:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:39:29 +00:00
|
|
|
Iop::CLoadcore* CIopBios::GetLoadcore()
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
return m_loadcore.get();
|
2012-10-24 06:39:29 +00:00
|
|
|
}
|
|
|
|
|
2008-10-20 04:14:13 +00:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
|
|
|
|
2008-03-03 00:38:28 +00:00
|
|
|
Iop::CPadMan* CIopBios::GetPadman()
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
return m_padman.get();
|
2008-03-03 00:38:28 +00:00
|
|
|
}
|
|
|
|
|
2008-11-10 01:46:02 +00:00
|
|
|
Iop::CCdvdfsv* CIopBios::GetCdvdfsv()
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
return m_cdvdfsv.get();
|
2008-11-10 01:46:02 +00:00
|
|
|
}
|
|
|
|
|
2018-12-20 12:37:47 -05:00
|
|
|
Iop::CMcServ* CIopBios::GetMcServ()
|
|
|
|
{
|
|
|
|
return static_cast<Iop::CMcServ*>(m_mcserv.get());
|
|
|
|
}
|
|
|
|
|
2024-05-13 16:09:41 -04:00
|
|
|
Iop::CUsbd* CIopBios::GetUsbd()
|
|
|
|
{
|
|
|
|
return static_cast<Iop::CUsbd*>(m_usbd.get());
|
|
|
|
}
|
|
|
|
|
2008-10-20 04:14:13 +00:00
|
|
|
#endif
|
|
|
|
|
2015-10-11 18:52:49 -04:00
|
|
|
int32 CIopBios::RegisterIntrHandler(uint32 line, uint32 mode, uint32 handler, uint32 arg)
|
2008-10-28 01:48:21 +00:00
|
|
|
{
|
2009-01-30 22:10:15 +00:00
|
|
|
assert(FindIntrHandler(line) == -1);
|
2015-10-11 18:52:49 -04:00
|
|
|
if(FindIntrHandler(line) != -1)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_FOUND_HANDLER;
|
|
|
|
}
|
|
|
|
|
2015-10-17 01:27:25 -04:00
|
|
|
if(line >= Iop::CIntc::LINES_MAX)
|
2015-10-11 18:52:49 -04:00
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_INTRCODE;
|
|
|
|
}
|
2009-01-30 22:10:15 +00:00
|
|
|
|
2015-10-19 23:38:03 -04:00
|
|
|
//Registering a null handler is a no-op
|
|
|
|
if(handler == 0)
|
|
|
|
{
|
|
|
|
return KERNEL_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2009-01-30 22:10:15 +00:00
|
|
|
uint32 handlerId = m_intrHandlers.Allocate();
|
|
|
|
assert(handlerId != -1);
|
|
|
|
if(handlerId == -1)
|
|
|
|
{
|
2015-10-11 18:52:49 -04:00
|
|
|
return KERNEL_RESULT_ERROR;
|
2009-01-30 22:10:15 +00:00
|
|
|
}
|
|
|
|
|
2015-10-11 18:52:49 -04:00
|
|
|
auto intrHandler = m_intrHandlers[handlerId];
|
2018-04-30 21:01:23 +01:00
|
|
|
intrHandler->line = line;
|
|
|
|
intrHandler->mode = mode;
|
|
|
|
intrHandler->handler = handler;
|
|
|
|
intrHandler->arg = arg;
|
2009-01-30 22:10:15 +00:00
|
|
|
|
2015-10-11 18:52:49 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2008-10-28 01:48:21 +00:00
|
|
|
}
|
|
|
|
|
2015-10-11 19:11:48 -04:00
|
|
|
int32 CIopBios::ReleaseIntrHandler(uint32 line)
|
2008-10-28 01:48:21 +00:00
|
|
|
{
|
2015-10-17 01:27:25 -04:00
|
|
|
if(line >= Iop::CIntc::LINES_MAX)
|
2015-10-11 19:11:48 -04:00
|
|
|
{
|
|
|
|
return KERNEL_RESULT_ERROR_ILLEGAL_INTRCODE;
|
|
|
|
}
|
|
|
|
|
2009-01-30 22:10:15 +00:00
|
|
|
uint32 handlerId = FindIntrHandler(line);
|
|
|
|
if(handlerId == -1)
|
|
|
|
{
|
2015-10-11 19:11:48 -04:00
|
|
|
return KERNEL_RESULT_ERROR_NOTFOUND_HANDLER;
|
2009-01-30 22:10:15 +00:00
|
|
|
}
|
2015-10-11 19:11:48 -04:00
|
|
|
|
2009-01-30 22:10:15 +00:00
|
|
|
m_intrHandlers.Free(handlerId);
|
2015-10-11 19:11:48 -04:00
|
|
|
return KERNEL_RESULT_OK;
|
2008-10-28 01:48:21 +00:00
|
|
|
}
|
|
|
|
|
2022-06-22 18:36:53 -04:00
|
|
|
int32 CIopBios::FindIntrHandler(uint32 line)
|
2009-01-30 22:10:15 +00:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
for(auto handlerIterator = std::begin(m_intrHandlers); handlerIterator != std::end(m_intrHandlers); handlerIterator++)
|
2009-01-30 22:10:15 +00:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
auto handler = m_intrHandlers[handlerIterator];
|
|
|
|
if(!handler) continue;
|
2009-01-30 22:10:15 +00:00
|
|
|
if(handler->line == line) return handlerIterator;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
uint32 CIopBios::AssembleThreadFinish(CMIPSAssembler& assembler)
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
uint32 address = BIOS_HANDLERS_BASE + assembler.GetProgramSize() * 4;
|
2012-09-18 03:21:43 +00:00
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_EXITTHREAD);
|
2012-03-30 05:41:11 +00:00
|
|
|
assembler.SYSCALL();
|
|
|
|
return address;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2008-10-28 21:32:18 +00:00
|
|
|
uint32 CIopBios::AssembleReturnFromException(CMIPSAssembler& assembler)
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
uint32 address = BIOS_HANDLERS_BASE + assembler.GetProgramSize() * 4;
|
2012-09-18 03:21:43 +00:00
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_RETURNFROMEXCEPTION);
|
2012-03-30 05:41:11 +00:00
|
|
|
assembler.SYSCALL();
|
|
|
|
return address;
|
2008-10-28 21:32:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CIopBios::AssembleIdleFunction(CMIPSAssembler& assembler)
|
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
uint32 address = BIOS_HANDLERS_BASE + assembler.GetProgramSize() * 4;
|
2012-09-18 03:21:43 +00:00
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_RESCHEDULE);
|
|
|
|
assembler.SYSCALL();
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2022-02-24 11:37:55 -05:00
|
|
|
uint32 CIopBios::AssembleModuleStarterProc(CMIPSAssembler& assembler)
|
2012-09-18 03:21:43 +00:00
|
|
|
{
|
|
|
|
uint32 address = BIOS_HANDLERS_BASE + assembler.GetProgramSize() * 4;
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2022-10-27 13:12:09 -04:00
|
|
|
//PROCESSMODULESTART sets some state in S0, S1, S2, S3 that is required by FINISHMODULESTART
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_PROCESSMODULESTART);
|
2012-03-30 05:41:11 +00:00
|
|
|
assembler.SYSCALL();
|
2022-10-27 13:12:09 -04:00
|
|
|
|
|
|
|
//Save module start/stop result
|
|
|
|
assembler.ADDIU(CMIPS::S4, CMIPS::V0, CMIPS::R0);
|
|
|
|
|
|
|
|
//Wait a little bit before reporting result. If we initialize/load modules too fast,
|
|
|
|
//there's a chance the current module won't have time to complete its init (through threads)
|
|
|
|
//properly before another module starts. Capcom vs SNK2 has this problem where multiple
|
|
|
|
//modules will call SdInit, but the order in which they are called is important.
|
2023-05-05 14:58:15 -04:00
|
|
|
//Shin Megami Tensei: Nocturne is also sensitive to this delay.
|
|
|
|
assembler.LI(CMIPS::A0, 0x4000);
|
2022-10-27 13:12:09 -04:00
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_DELAYTHREADTICKS);
|
|
|
|
assembler.SYSCALL();
|
|
|
|
|
|
|
|
//Restore module start/stop result
|
|
|
|
assembler.ADDU(CMIPS::A0, CMIPS::S4, CMIPS::R0);
|
|
|
|
|
2015-06-27 00:38:30 -04:00
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_FINISHMODULESTART);
|
2014-07-10 23:43:29 -04:00
|
|
|
assembler.SYSCALL();
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
return address;
|
2008-10-28 21:32:18 +00:00
|
|
|
}
|
|
|
|
|
2012-10-21 02:37:23 +00:00
|
|
|
uint32 CIopBios::AssembleAlarmThreadProc(CMIPSAssembler& assembler)
|
|
|
|
{
|
|
|
|
uint32 address = BIOS_HANDLERS_BASE + assembler.GetProgramSize() * 4;
|
|
|
|
auto delayThreadLabel = assembler.CreateLabel();
|
|
|
|
|
|
|
|
//Prolog
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0xFF80);
|
|
|
|
assembler.SW(CMIPS::RA, 0x10, CMIPS::SP);
|
|
|
|
assembler.SW(CMIPS::S0, 0x14, CMIPS::SP);
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
assembler.MOV(CMIPS::S0, CMIPS::A0); //S0 has the info struct ptr
|
2012-10-21 02:37:23 +00:00
|
|
|
|
|
|
|
//Delay thread
|
|
|
|
assembler.MarkLabel(delayThreadLabel);
|
|
|
|
assembler.LW(CMIPS::A0, 0x08, CMIPS::S0);
|
|
|
|
assembler.ADDIU(CMIPS::V0, CMIPS::R0, SYSCALL_DELAYTHREADTICKS);
|
|
|
|
assembler.SYSCALL();
|
|
|
|
|
|
|
|
//Call handler
|
|
|
|
assembler.LW(CMIPS::V0, 0x00, CMIPS::S0);
|
|
|
|
assembler.JALR(CMIPS::V0);
|
|
|
|
assembler.LW(CMIPS::A0, 0x04, CMIPS::S0);
|
|
|
|
|
|
|
|
assembler.BNE(CMIPS::V0, CMIPS::R0, delayThreadLabel);
|
|
|
|
assembler.SW(CMIPS::V0, 0x08, CMIPS::S0);
|
|
|
|
|
|
|
|
//Epilog
|
|
|
|
assembler.LW(CMIPS::S0, 0x14, CMIPS::SP);
|
|
|
|
assembler.LW(CMIPS::RA, 0x10, CMIPS::SP);
|
|
|
|
|
|
|
|
assembler.JR(CMIPS::RA);
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, 0x0080);
|
|
|
|
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2018-07-17 12:41:13 -04:00
|
|
|
uint32 CIopBios::AssembleVblankHandler(CMIPSAssembler& assembler)
|
|
|
|
{
|
|
|
|
uint32 address = BIOS_HANDLERS_BASE + assembler.GetProgramSize() * 4;
|
|
|
|
auto checkHandlerLabel = assembler.CreateLabel();
|
|
|
|
auto moveToNextHandlerLabel = assembler.CreateLabel();
|
|
|
|
|
|
|
|
int16 stackAlloc = 0x80;
|
|
|
|
|
|
|
|
//Prolog
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, -stackAlloc);
|
|
|
|
assembler.SW(CMIPS::RA, 0x10, CMIPS::SP);
|
|
|
|
assembler.SW(CMIPS::S0, 0x14, CMIPS::SP);
|
|
|
|
|
|
|
|
assembler.MOV(CMIPS::S0, CMIPS::A0); //S0 = type
|
|
|
|
assembler.MOV(CMIPS::S1, CMIPS::R0); //S1 = counter
|
|
|
|
|
|
|
|
assembler.MarkLabel(checkHandlerLabel);
|
|
|
|
assembler.LI(CMIPS::T0, BIOS_VBLANKHANDLER_BASE);
|
2018-08-03 12:37:25 -04:00
|
|
|
assembler.SLL(CMIPS::T1, CMIPS::S1, 4); //Multiples of 0x10 bytes
|
2018-07-17 12:41:13 -04:00
|
|
|
assembler.ADDU(CMIPS::T0, CMIPS::T0, CMIPS::T1);
|
|
|
|
|
|
|
|
//Check isValid
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(VBLANKHANDLER, isValid), CMIPS::T0);
|
|
|
|
assembler.BEQ(CMIPS::T1, CMIPS::R0, moveToNextHandlerLabel);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Check type
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(VBLANKHANDLER, type), CMIPS::T0);
|
|
|
|
assembler.BNE(CMIPS::T1, CMIPS::S0, moveToNextHandlerLabel);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
assembler.LW(CMIPS::T1, offsetof(VBLANKHANDLER, handler), CMIPS::T0);
|
|
|
|
assembler.JALR(CMIPS::T1);
|
|
|
|
assembler.LW(CMIPS::A0, offsetof(VBLANKHANDLER, arg), CMIPS::T0);
|
|
|
|
|
|
|
|
//TODO: Check return value
|
|
|
|
|
|
|
|
assembler.MarkLabel(moveToNextHandlerLabel);
|
|
|
|
assembler.ADDIU(CMIPS::S1, CMIPS::S1, 1);
|
|
|
|
assembler.SLTIU(CMIPS::T0, CMIPS::S1, MAX_VBLANKHANDLER);
|
|
|
|
|
|
|
|
assembler.BNE(CMIPS::T0, CMIPS::R0, checkHandlerLabel);
|
|
|
|
assembler.NOP();
|
|
|
|
|
|
|
|
//Epilog
|
|
|
|
assembler.LW(CMIPS::S0, 0x14, CMIPS::SP);
|
|
|
|
assembler.LW(CMIPS::RA, 0x10, CMIPS::SP);
|
|
|
|
|
|
|
|
assembler.JR(CMIPS::RA);
|
|
|
|
assembler.ADDIU(CMIPS::SP, CMIPS::SP, stackAlloc);
|
|
|
|
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2008-10-28 01:48:21 +00:00
|
|
|
void CIopBios::HandleException()
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2014-06-04 23:37:40 -04:00
|
|
|
assert(m_cpu.m_State.nHasException == MIPS_EXCEPTION_SYSCALL);
|
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
m_rescheduleNeeded = false;
|
2008-12-01 04:00:36 +00:00
|
|
|
|
2022-10-21 13:38:52 +01:00
|
|
|
uint32 searchAddress = m_cpu.m_pAddrTranslator(&m_cpu, m_cpu.m_State.nCOP0[CCOP_SCU::EPC]);
|
2024-07-17 09:04:31 -04:00
|
|
|
|
|
|
|
const auto* memoryMapElem = m_cpu.m_pMemoryMap->GetReadMap(searchAddress);
|
|
|
|
assert(memoryMapElem != nullptr);
|
|
|
|
assert(memoryMapElem->nType == CMemoryMap::MEMORYMAP_TYPE_MEMORY);
|
|
|
|
auto memory = reinterpret_cast<const uint32*>(reinterpret_cast<uint8*>(memoryMapElem->pPointer) + (searchAddress - memoryMapElem->nStart));
|
|
|
|
|
|
|
|
uint32 callInstruction = memory[0];
|
2012-03-30 05:41:11 +00:00
|
|
|
if(callInstruction == 0x0000000C)
|
|
|
|
{
|
|
|
|
switch(m_cpu.m_State.nGPR[CMIPS::V0].nV0)
|
|
|
|
{
|
2012-09-18 03:21:43 +00:00
|
|
|
case SYSCALL_EXITTHREAD:
|
2012-09-12 04:03:41 +00:00
|
|
|
ExitThread();
|
2008-10-28 21:32:18 +00:00
|
|
|
break;
|
2012-09-18 03:21:43 +00:00
|
|
|
case SYSCALL_RETURNFROMEXCEPTION:
|
2008-10-28 21:32:18 +00:00
|
|
|
ReturnFromException();
|
|
|
|
break;
|
2012-09-18 03:21:43 +00:00
|
|
|
case SYSCALL_RESCHEDULE:
|
2008-10-28 21:32:18 +00:00
|
|
|
Reschedule();
|
|
|
|
break;
|
2012-09-18 03:21:43 +00:00
|
|
|
case SYSCALL_SLEEPTHREAD:
|
|
|
|
SleepThread();
|
|
|
|
break;
|
2015-06-27 00:38:30 -04:00
|
|
|
case SYSCALL_PROCESSMODULESTART:
|
|
|
|
ProcessModuleStart();
|
2012-09-18 03:21:43 +00:00
|
|
|
break;
|
2015-06-27 00:38:30 -04:00
|
|
|
case SYSCALL_FINISHMODULESTART:
|
|
|
|
FinishModuleStart();
|
2014-07-10 23:43:29 -04:00
|
|
|
break;
|
2012-10-21 02:37:23 +00:00
|
|
|
case SYSCALL_DELAYTHREADTICKS:
|
|
|
|
DelayThreadTicks(m_cpu.m_State.nGPR[CMIPS::A0].nV0);
|
|
|
|
break;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Search for the import record
|
|
|
|
uint32 instruction = callInstruction;
|
|
|
|
while(instruction != 0x41E00000)
|
|
|
|
{
|
2024-07-17 09:04:31 -04:00
|
|
|
memory--;
|
|
|
|
instruction = memory[0];
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
uint32 functionId = callInstruction & 0xFFFF;
|
2024-07-17 09:04:31 -04:00
|
|
|
FRAMEWORK_MAYBE_UNUSED uint32 version = memory[2];
|
|
|
|
auto moduleName = ReadModuleName(reinterpret_cast<const uint8*>(memory + 3));
|
2008-01-15 20:27:44 +00:00
|
|
|
|
2015-03-29 23:57:21 -04:00
|
|
|
#ifdef _DEBUG
|
|
|
|
if(moduleName == "libsd")
|
|
|
|
{
|
|
|
|
Iop::CLibSd::TraceCall(m_cpu, functionId);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-11-06 19:28:55 -05:00
|
|
|
auto module(m_modules.find(moduleName));
|
2012-03-30 05:41:11 +00:00
|
|
|
if(module != m_modules.end())
|
|
|
|
{
|
|
|
|
module->second->Invoke(m_cpu, functionId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-15 20:27:44 +00:00
|
|
|
#ifdef _DEBUG
|
2019-06-12 20:04:39 -04:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "%08X: Trying to call a function from non-existing module (%s, %d).\r\n",
|
2024-02-23 16:52:17 -05:00
|
|
|
m_cpu.m_State.nPC, std::string(moduleName).c_str(), functionId);
|
2008-01-15 20:27:44 +00:00
|
|
|
#endif
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
2008-01-15 20:27:44 +00:00
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
if(m_rescheduleNeeded)
|
|
|
|
{
|
2008-10-28 21:32:18 +00:00
|
|
|
assert((m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] & CMIPS::STATUS_EXL) == 0);
|
2012-03-30 05:41:11 +00:00
|
|
|
m_rescheduleNeeded = false;
|
|
|
|
Reschedule();
|
|
|
|
}
|
2008-01-15 20:27:44 +00:00
|
|
|
|
2012-03-30 05:41:11 +00:00
|
|
|
m_cpu.m_State.nHasException = 0;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2008-10-28 01:48:21 +00:00
|
|
|
void CIopBios::HandleInterrupt()
|
|
|
|
{
|
2008-10-28 21:32:18 +00:00
|
|
|
if(m_cpu.GenerateInterrupt(m_cpu.m_State.nPC))
|
2008-10-28 01:48:21 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
//Find first concerned interrupt
|
|
|
|
UNION64_32 status(
|
2018-04-30 21:01:23 +01:00
|
|
|
m_cpu.m_pMemoryMap->GetWord(Iop::CIntc::STATUS0),
|
|
|
|
m_cpu.m_pMemoryMap->GetWord(Iop::CIntc::STATUS1));
|
2008-10-28 21:32:18 +00:00
|
|
|
UNION64_32 mask(
|
2018-04-30 21:01:23 +01:00
|
|
|
m_cpu.m_pMemoryMap->GetWord(Iop::CIntc::MASK0),
|
|
|
|
m_cpu.m_pMemoryMap->GetWord(Iop::CIntc::MASK1));
|
2008-10-28 21:32:18 +00:00
|
|
|
status.f &= mask.f;
|
2020-12-28 12:59:50 -05:00
|
|
|
assert(status.f != 0);
|
|
|
|
if(status.f == 0)
|
2008-10-28 21:32:18 +00:00
|
|
|
{
|
|
|
|
ReturnFromException();
|
|
|
|
return;
|
|
|
|
}
|
2020-12-28 12:59:50 -05:00
|
|
|
uint32 line = __builtin_ctzll(status.f);
|
|
|
|
status.f = ~(1ULL << line);
|
2008-10-28 21:32:18 +00:00
|
|
|
m_cpu.m_pMemoryMap->SetWord(Iop::CIntc::STATUS0, status.h0);
|
|
|
|
m_cpu.m_pMemoryMap->SetWord(Iop::CIntc::STATUS1, status.h1);
|
|
|
|
//Check if there's an handler to call
|
|
|
|
{
|
2009-01-30 22:10:15 +00:00
|
|
|
uint32 handlerId = FindIntrHandler(line);
|
|
|
|
if(handlerId == -1)
|
2008-10-28 21:32:18 +00:00
|
|
|
{
|
|
|
|
ReturnFromException();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Snap out of current thread
|
2015-11-04 01:03:32 -05:00
|
|
|
if(m_currentThreadId != -1)
|
2008-10-28 21:32:18 +00:00
|
|
|
{
|
2015-11-04 01:03:32 -05:00
|
|
|
SaveThreadContext(m_currentThreadId);
|
2008-10-28 21:32:18 +00:00
|
|
|
}
|
2015-11-04 01:03:32 -05:00
|
|
|
m_currentThreadId = -1;
|
2022-01-25 15:15:42 -05:00
|
|
|
auto handler = m_intrHandlers[handlerId];
|
2009-01-30 22:10:15 +00:00
|
|
|
m_cpu.m_State.nPC = handler->handler;
|
2022-01-25 15:15:42 -05:00
|
|
|
m_cpu.m_State.nGPR[CMIPS::SP].nD0 = (BIOS_INTSTACK_BASE + BIOS_INTSTACK_SIZE) - STACK_FRAME_RESERVE_SIZE;
|
2009-01-30 22:10:15 +00:00
|
|
|
m_cpu.m_State.nGPR[CMIPS::A0].nD0 = static_cast<int32>(handler->arg);
|
2008-10-28 21:32:18 +00:00
|
|
|
m_cpu.m_State.nGPR[CMIPS::RA].nD0 = static_cast<int32>(m_returnFromExceptionAddress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::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;
|
2008-10-28 01:48:21 +00:00
|
|
|
}
|
2008-10-28 21:32:18 +00:00
|
|
|
Reschedule();
|
2008-10-28 01:48:21 +00:00
|
|
|
}
|
|
|
|
|
2008-01-15 20:27:44 +00:00
|
|
|
void CIopBios::DeleteModules()
|
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
m_modules.clear();
|
2012-03-30 05:41:11 +00:00
|
|
|
|
2008-11-25 02:00:42 +00:00
|
|
|
#ifdef _IOP_EMULATE_MODULES
|
2016-01-13 22:18:10 -05:00
|
|
|
m_padman.reset();
|
2018-02-06 19:09:48 -05:00
|
|
|
m_mtapman.reset();
|
2018-02-06 19:07:57 -05:00
|
|
|
m_mcserv.reset();
|
2016-01-13 22:18:10 -05:00
|
|
|
m_cdvdfsv.reset();
|
|
|
|
m_fileIo.reset();
|
2018-09-11 17:53:00 -04:00
|
|
|
m_hleModules.clear();
|
2008-11-25 02:00:42 +00:00
|
|
|
#endif
|
2019-09-15 13:31:24 -04:00
|
|
|
m_sifCmd.reset();
|
|
|
|
m_sifMan.reset();
|
|
|
|
m_libsd.reset();
|
|
|
|
m_stdio.reset();
|
|
|
|
m_ioman.reset();
|
|
|
|
m_sysmem.reset();
|
|
|
|
m_modload.reset();
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-30 21:46:14 -04:00
|
|
|
void CIopBios::UnloadUserComponents()
|
|
|
|
{
|
|
|
|
//This will attempt to get rid of most things a game might have loaded
|
|
|
|
//This also adds some constraints that could be annoying, like the inability
|
|
|
|
//for HLE modules to create threads or semaphores
|
|
|
|
//This won't get rid of some stuff such as memory allocations
|
|
|
|
assert(m_currentThreadId == -1);
|
|
|
|
for(auto thread : m_threads)
|
|
|
|
{
|
|
|
|
if(!thread) continue;
|
|
|
|
TerminateThread(thread->id);
|
|
|
|
DeleteThread(thread->id);
|
|
|
|
}
|
|
|
|
for(auto loadedModuleIterator = std::begin(m_loadedModules);
|
2022-12-20 15:39:16 -05:00
|
|
|
loadedModuleIterator != std::end(m_loadedModules); loadedModuleIterator++)
|
2022-10-30 21:46:14 -04:00
|
|
|
{
|
|
|
|
auto loadedModule = *loadedModuleIterator;
|
|
|
|
if(!loadedModule) continue;
|
|
|
|
if(loadedModule->state == MODULE_STATE::STARTED)
|
|
|
|
{
|
|
|
|
loadedModule->state = MODULE_STATE::STOPPED;
|
|
|
|
}
|
|
|
|
UnloadModule(loadedModuleIterator);
|
|
|
|
}
|
|
|
|
std::experimental::erase_if(m_modules, [](const auto& modulePair) { return std::dynamic_pointer_cast<Iop::CDynamic>(modulePair.second); });
|
|
|
|
m_intrHandlers.FreeAll();
|
|
|
|
m_semaphores.FreeAll();
|
|
|
|
m_sifCmd->ClearServers();
|
|
|
|
}
|
|
|
|
|
2016-01-16 20:46:14 -05:00
|
|
|
int32 CIopBios::LoadHleModule(const Iop::ModulePtr& module)
|
|
|
|
{
|
|
|
|
auto loadedModuleId = SearchModuleByName(module->GetId().c_str());
|
2021-02-19 17:45:43 -05:00
|
|
|
if(loadedModuleId >= 0)
|
2016-01-16 20:46:14 -05:00
|
|
|
{
|
|
|
|
return loadedModuleId;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadedModuleId = m_loadedModules.Allocate();
|
|
|
|
assert(loadedModuleId != -1);
|
|
|
|
if(loadedModuleId == -1) return -1;
|
|
|
|
|
|
|
|
auto loadedModule = m_loadedModules[loadedModuleId];
|
|
|
|
strncpy(loadedModule->name, module->GetId().c_str(), LOADEDMODULE::MAX_NAME_SIZE);
|
|
|
|
loadedModule->state = MODULE_STATE::HLE;
|
|
|
|
|
|
|
|
//Register entries as if the module initialized itself
|
2018-09-11 17:53:00 -04:00
|
|
|
RegisterHleModule(module);
|
|
|
|
|
|
|
|
return loadedModuleId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::RegisterHleModule(const Iop::ModulePtr& module)
|
|
|
|
{
|
2016-01-16 20:46:14 -05:00
|
|
|
RegisterModule(module);
|
|
|
|
if(auto sifModuleProvider = std::dynamic_pointer_cast<Iop::CSifModuleProvider>(module))
|
|
|
|
{
|
|
|
|
sifModuleProvider->RegisterSifModules(*m_sifMan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-23 18:02:40 -04:00
|
|
|
CIopBios::ModuleSet CIopBios::GetBuiltInModules() const
|
|
|
|
{
|
|
|
|
//This gathers all built-in modules
|
|
|
|
//We don't have a centralised place for them at the moment
|
|
|
|
//and we only need this for save/load state
|
|
|
|
ModuleSet modules;
|
|
|
|
for(const auto& modulePair : m_modules)
|
|
|
|
{
|
|
|
|
//Exclude dynamic modules
|
|
|
|
if(std::dynamic_pointer_cast<Iop::CDynamic>(modulePair.second))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
modules.insert(modulePair.second.get());
|
|
|
|
}
|
|
|
|
for(const auto& modulePair : m_hleModules)
|
|
|
|
{
|
|
|
|
modules.insert(modulePair.second.get());
|
|
|
|
}
|
|
|
|
return modules;
|
|
|
|
}
|
|
|
|
|
2024-07-17 09:04:31 -04:00
|
|
|
std::string_view CIopBios::ReadModuleName(const uint8* memory)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2024-02-23 16:52:17 -05:00
|
|
|
auto currChar = memory;
|
|
|
|
uint32 size = 0;
|
|
|
|
while(size < 8)
|
2011-01-16 21:47:01 +00:00
|
|
|
{
|
2024-02-23 16:52:17 -05:00
|
|
|
uint8 character = *(currChar++);
|
|
|
|
if(character < 0x10) break;
|
|
|
|
size++;
|
2011-01-16 21:47:01 +00:00
|
|
|
}
|
2024-07-17 09:04:31 -04:00
|
|
|
return std::string_view(reinterpret_cast<const char*>(memory), size);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string_view CIopBios::ReadModuleName(uint32 address)
|
|
|
|
{
|
|
|
|
const auto* memoryMapElem = m_cpu.m_pMemoryMap->GetReadMap(address);
|
|
|
|
assert(memoryMapElem != nullptr);
|
|
|
|
assert(memoryMapElem->nType == CMemoryMap::MEMORYMAP_TYPE_MEMORY);
|
|
|
|
auto memory = reinterpret_cast<const uint8*>(memoryMapElem->pPointer) + (address - memoryMapElem->nStart);
|
|
|
|
return ReadModuleName(memory);
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 00:36:04 -05:00
|
|
|
bool CIopBios::RegisterModule(const Iop::ModulePtr& module)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2016-01-13 22:18:10 -05:00
|
|
|
bool registered = (m_modules.find(module->GetId()) != std::end(m_modules));
|
2016-01-15 00:36:04 -05:00
|
|
|
if(registered) return false;
|
2012-03-30 05:41:11 +00:00
|
|
|
m_modules[module->GetId()] = module;
|
2016-01-15 00:36:04 -05:00
|
|
|
return true;
|
2009-02-06 05:10:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 20:50:57 -05:00
|
|
|
bool CIopBios::ReleaseModule(const std::string& moduleName)
|
|
|
|
{
|
|
|
|
auto moduleIterator = m_modules.find(moduleName);
|
|
|
|
if(moduleIterator == std::end(m_modules)) return false;
|
|
|
|
m_modules.erase(moduleIterator);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-15 11:41:38 -04:00
|
|
|
void CIopBios::RegisterHleModuleReplacement(const std::string& path, const Iop::ModulePtr& module)
|
|
|
|
{
|
|
|
|
//Not definitive function, needs to support filtering by module name
|
2023-06-11 10:04:40 -04:00
|
|
|
//Can also screw up some things with saved states
|
|
|
|
m_hleModules[path] = module;
|
2022-10-15 11:41:38 -04:00
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
uint32 CIopBios::LoadExecutable(CELF32& elf, ExecutableRange& executableRange, uint32 baseAddress)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
unsigned int programHeaderIndex = GetElfProgramToLoad(elf);
|
|
|
|
if(programHeaderIndex == -1)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("No program to load.");
|
|
|
|
}
|
2022-03-03 11:21:46 -05:00
|
|
|
auto programHeader = elf.GetProgram(programHeaderIndex);
|
2022-02-22 13:26:35 -05:00
|
|
|
if(baseAddress == ~0U)
|
|
|
|
{
|
|
|
|
baseAddress = m_sysmem->AllocateMemory(programHeader->nMemorySize, 0, 0);
|
|
|
|
}
|
2012-03-30 05:41:11 +00:00
|
|
|
memcpy(
|
2018-04-30 21:01:23 +01:00
|
|
|
m_ram + baseAddress,
|
|
|
|
elf.GetContent() + programHeader->nOffset,
|
|
|
|
programHeader->nFileSize);
|
2022-03-03 11:21:46 -05:00
|
|
|
RelocateElf(elf, baseAddress, programHeader->nFileSize);
|
2012-03-30 05:41:11 +00:00
|
|
|
|
|
|
|
executableRange.first = baseAddress;
|
|
|
|
executableRange.second = baseAddress + programHeader->nMemorySize;
|
|
|
|
|
|
|
|
return baseAddress + elf.GetHeader().nEntryPoint;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
unsigned int CIopBios::GetElfProgramToLoad(CELF32& elf)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2012-03-30 05:41:11 +00:00
|
|
|
unsigned int program = -1;
|
2022-03-13 16:04:09 -04:00
|
|
|
const auto& header = elf.GetHeader();
|
2012-03-30 05:41:11 +00:00
|
|
|
for(unsigned int i = 0; i < header.nProgHeaderCount; i++)
|
|
|
|
{
|
2022-03-13 16:04:09 -04:00
|
|
|
auto programHeader = elf.GetProgram(i);
|
2022-07-22 17:01:59 -04:00
|
|
|
if(programHeader != NULL && programHeader->nType == ELF::PT_LOAD)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
|
|
|
if(program != -1)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Multiple loadable program headers found.");
|
|
|
|
}
|
|
|
|
program = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return program;
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
void CIopBios::RelocateElf(CELF32& elf, uint32 programBaseAddress, uint32 programSize)
|
2008-01-15 20:27:44 +00:00
|
|
|
{
|
2017-05-05 22:34:44 -04:00
|
|
|
//The IOP's ELF loader doesn't seem to follow the ELF standard completely
|
|
|
|
//when it comes to relocations. The relocation function seems to use the
|
2022-03-03 11:21:46 -05:00
|
|
|
//loaded program's base address for all adjustments. Using information from the
|
2017-05-05 22:34:44 -04:00
|
|
|
//section headers (either the section's start address or info field) will yield
|
2022-03-03 11:21:46 -05:00
|
|
|
//an incorrect result in some cases.
|
|
|
|
//Examples:
|
|
|
|
//- RWA.IRX module from Burnout 3 and Burnout Revenge
|
|
|
|
//- Modules from Twinkle Star Sprites (stripped section name string table)
|
2016-06-28 17:08:39 -04:00
|
|
|
const auto& header = elf.GetHeader();
|
|
|
|
bool isVersion2 = (header.nType == ET_SCE_IOPRELEXEC2);
|
2022-03-03 11:21:46 -05:00
|
|
|
auto programData = m_ram + programBaseAddress;
|
2012-03-30 05:41:11 +00:00
|
|
|
for(unsigned int i = 0; i < header.nSectHeaderCount; i++)
|
|
|
|
{
|
2016-06-28 17:08:39 -04:00
|
|
|
const auto* sectionHeader = elf.GetSection(i);
|
2022-07-22 17:01:59 -04:00
|
|
|
if(sectionHeader != nullptr && sectionHeader->nType == ELF::SHT_REL)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2022-03-28 17:41:27 -04:00
|
|
|
int32 lastHi16 = -1;
|
2012-03-30 05:41:11 +00:00
|
|
|
uint32 instructionHi16 = -1;
|
|
|
|
unsigned int recordCount = sectionHeader->nSize / 8;
|
2016-06-28 17:08:39 -04:00
|
|
|
auto relocationRecord = reinterpret_cast<const uint32*>(elf.GetSectionData(i));
|
2017-05-05 22:34:44 -04:00
|
|
|
uint32 sectionBase = 0;
|
2012-03-30 05:41:11 +00:00
|
|
|
for(unsigned int record = 0; record < recordCount; record++)
|
|
|
|
{
|
2022-04-04 14:15:27 -04:00
|
|
|
//Helper to make sure we don't read/write things out of the module's memory area
|
|
|
|
uint32 oobInstruction = 0;
|
|
|
|
const auto& getInstructionRef = [&](int32 offset) -> uint32& {
|
|
|
|
if((offset < 0) || (offset >= static_cast<int32>(programSize)))
|
|
|
|
{
|
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Relocation %d accessing location out of bounds: %d.\r\n", record, offset);
|
|
|
|
oobInstruction = 0;
|
|
|
|
return oobInstruction;
|
|
|
|
}
|
|
|
|
return *reinterpret_cast<uint32*>(programData + offset);
|
|
|
|
};
|
|
|
|
|
2022-03-28 17:41:27 -04:00
|
|
|
//Some games have negative relocation addresses (Hitman 2: Silent Assassin)
|
|
|
|
//Doesn't seem to be an issue, but we need offsets to be signed
|
|
|
|
int32 relocationAddress = relocationRecord[0] - sectionBase;
|
2012-03-30 05:41:11 +00:00
|
|
|
uint32 relocationType = relocationRecord[1] & 0xFF;
|
|
|
|
{
|
2022-04-04 14:15:27 -04:00
|
|
|
uint32& instruction = getInstructionRef(relocationAddress);
|
2012-03-30 05:41:11 +00:00
|
|
|
switch(relocationType)
|
|
|
|
{
|
2022-07-22 17:01:59 -04:00
|
|
|
case ELF::R_MIPS_32:
|
2018-04-30 21:01:23 +01:00
|
|
|
{
|
2022-03-03 11:21:46 -05:00
|
|
|
instruction += programBaseAddress;
|
2018-04-30 21:01:23 +01:00
|
|
|
}
|
|
|
|
break;
|
2022-07-22 17:01:59 -04:00
|
|
|
case ELF::R_MIPS_26:
|
2018-04-30 21:01:23 +01:00
|
|
|
{
|
2022-03-03 11:21:46 -05:00
|
|
|
uint32 offset = (instruction & 0x03FFFFFF) + (programBaseAddress >> 2);
|
2018-04-30 21:01:23 +01:00
|
|
|
instruction &= ~0x03FFFFFF;
|
|
|
|
instruction |= offset;
|
|
|
|
}
|
|
|
|
break;
|
2022-07-22 17:01:59 -04:00
|
|
|
case ELF::R_MIPS_HI16:
|
2016-06-28 17:08:39 -04:00
|
|
|
if(isVersion2)
|
|
|
|
{
|
|
|
|
assert((record + 1) != recordCount);
|
2022-07-22 17:01:59 -04:00
|
|
|
assert((relocationRecord[3] & 0xFF) == ELF::R_MIPS_LO16);
|
2022-04-04 14:15:27 -04:00
|
|
|
int32 nextRelocationAddress = relocationRecord[2] - sectionBase;
|
|
|
|
uint32 nextInstruction = getInstructionRef(nextRelocationAddress);
|
2016-06-28 17:08:39 -04:00
|
|
|
uint32 offset = static_cast<int16>(nextInstruction) + (instruction << 16);
|
2022-03-03 11:21:46 -05:00
|
|
|
offset += programBaseAddress;
|
2016-06-28 17:08:39 -04:00
|
|
|
if(offset & 0x8000) offset += 0x10000;
|
|
|
|
instruction &= ~0xFFFF;
|
|
|
|
instruction |= offset >> 16;
|
|
|
|
}
|
|
|
|
else
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2016-06-28 17:08:39 -04:00
|
|
|
assert(lastHi16 == -1);
|
2012-03-30 05:41:11 +00:00
|
|
|
lastHi16 = relocationAddress;
|
|
|
|
instructionHi16 = instruction;
|
|
|
|
}
|
|
|
|
break;
|
2022-07-22 17:01:59 -04:00
|
|
|
case ELF::R_MIPS_LO16:
|
2016-06-28 17:08:39 -04:00
|
|
|
if(isVersion2)
|
|
|
|
{
|
|
|
|
uint32 offset = static_cast<int16>(instruction);
|
2022-03-03 11:21:46 -05:00
|
|
|
offset += programBaseAddress;
|
2016-06-28 17:08:39 -04:00
|
|
|
instruction &= ~0xFFFF;
|
|
|
|
instruction |= offset & 0xFFFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(lastHi16 != -1);
|
|
|
|
|
|
|
|
uint32 offset = static_cast<int16>(instruction) + (instructionHi16 << 16);
|
2022-03-03 11:21:46 -05:00
|
|
|
offset += programBaseAddress;
|
2016-06-28 17:08:39 -04:00
|
|
|
instruction &= ~0xFFFF;
|
|
|
|
instruction |= offset & 0xFFFF;
|
|
|
|
|
2022-04-04 14:15:27 -04:00
|
|
|
uint32& prevInstruction = getInstructionRef(lastHi16);
|
2016-06-28 17:08:39 -04:00
|
|
|
prevInstruction &= ~0xFFFF;
|
|
|
|
if(offset & 0x8000) offset += 0x10000;
|
|
|
|
prevInstruction |= offset >> 16;
|
|
|
|
lastHi16 = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case R_MIPSSCE_MHI16:
|
2018-04-30 21:01:23 +01:00
|
|
|
{
|
|
|
|
assert(isVersion2);
|
|
|
|
assert((record + 1) != recordCount);
|
|
|
|
assert((relocationRecord[3] & 0xFF) == R_MIPSSCE_ADDEND);
|
2022-03-03 11:21:46 -05:00
|
|
|
uint32 offset = relocationRecord[2] + programBaseAddress;
|
2018-04-30 21:01:23 +01:00
|
|
|
if(offset & 0x8000) offset += 0x10000;
|
|
|
|
offset >>= 16;
|
|
|
|
while(1)
|
2012-03-30 05:41:11 +00:00
|
|
|
{
|
2022-04-04 14:15:27 -04:00
|
|
|
uint32& prevInstruction = getInstructionRef(relocationAddress);
|
2016-06-28 17:08:39 -04:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
int32 mhiOffset = static_cast<int16>(prevInstruction);
|
|
|
|
mhiOffset *= 4;
|
2012-03-30 05:41:11 +00:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
prevInstruction &= ~0xFFFF;
|
|
|
|
prevInstruction |= offset;
|
2016-06-28 17:08:39 -04:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
if(mhiOffset == 0) break;
|
|
|
|
relocationAddress += mhiOffset;
|
2012-03-30 05:41:11 +00:00
|
|
|
}
|
2018-04-30 21:01:23 +01:00
|
|
|
}
|
|
|
|
break;
|
2012-03-30 05:41:11 +00:00
|
|
|
default:
|
2018-04-25 10:57:15 -04:00
|
|
|
//Some games use relocation types that might not be supported by the IOP's ELF loader
|
|
|
|
//- R_MIPS_GPREL16: Used by Sega Ages 2500 Volume 8: Virtua Racing
|
2024-03-09 20:24:16 -05:00
|
|
|
CLog::GetInstance().Warn(LOGNAME, "Unsupported ELF relocation type encountered (%d).\r\n",
|
|
|
|
relocationType);
|
2018-04-25 10:57:15 -04:00
|
|
|
assert(false);
|
2012-03-30 05:41:11 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
relocationRecord += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-15 20:27:44 +00:00
|
|
|
}
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2022-02-24 11:37:55 -05:00
|
|
|
int32 CIopBios::TriggerCallback(uint32 address, uint32 arg0, uint32 arg1, uint32 arg2, uint32 arg3)
|
2014-12-12 20:20:51 +00:00
|
|
|
{
|
|
|
|
// Call the addres on a callback thread with A0 set to arg0
|
|
|
|
uint32 callbackThreadId = -1;
|
|
|
|
|
|
|
|
//Find a thread we could recycle for a new callback
|
2015-08-05 23:38:40 -04:00
|
|
|
for(auto thread : m_threads)
|
2014-12-12 20:20:51 +00:00
|
|
|
{
|
2015-08-05 23:38:40 -04:00
|
|
|
if(!thread) continue;
|
2014-12-12 20:20:51 +00:00
|
|
|
if(thread->threadProc != address) continue;
|
|
|
|
if(thread->status == THREAD_STATUS_DORMANT)
|
|
|
|
{
|
|
|
|
callbackThreadId = thread->id;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//If no threads are available, create a new one
|
|
|
|
if(callbackThreadId == -1)
|
|
|
|
{
|
2016-07-09 18:02:47 -04:00
|
|
|
callbackThreadId = CreateThread(address, DEFAULT_PRIORITY, DEFAULT_STACKSIZE, 0, 0);
|
2014-12-12 20:20:51 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 19:53:13 -04:00
|
|
|
StartThread(callbackThreadId, 0);
|
2016-01-09 18:54:39 -05:00
|
|
|
ChangeThreadPriority(callbackThreadId, 1);
|
2014-12-12 20:20:51 +00:00
|
|
|
|
|
|
|
auto thread = GetThread(callbackThreadId);
|
|
|
|
thread->context.gpr[CMIPS::A0] = arg0;
|
|
|
|
thread->context.gpr[CMIPS::A1] = arg1;
|
2018-12-19 19:24:04 -05:00
|
|
|
thread->context.gpr[CMIPS::A2] = arg2;
|
|
|
|
thread->context.gpr[CMIPS::A3] = arg3;
|
2022-02-24 11:37:55 -05:00
|
|
|
|
|
|
|
return callbackThreadId;
|
2014-12-12 20:20:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 13:19:31 +00:00
|
|
|
void CIopBios::PopulateSystemIntcHandlers()
|
|
|
|
{
|
|
|
|
auto intrHandlerTable = reinterpret_cast<CIopBios::SYSTEM_INTRHANDLER*>(&m_ram[BIOS_SYSTEM_INTRHANDLER_TABLE_BASE]);
|
|
|
|
|
|
|
|
// homebrews expect this to be non-zero, to modify and use it as a callback during shadown
|
|
|
|
// https://github.com/ps2dev/ps2sdk/blob/8b7579979db87ace4b0aa5693a8a560d15224a96/iop/dev9/poweroff/src/poweroff.c#L240
|
|
|
|
auto cdvdIntrHandler = &intrHandlerTable[Iop::CIntc::LINES::LINE_CDROM];
|
|
|
|
cdvdIntrHandler->handler = 1;
|
|
|
|
}
|
|
|
|
|
2014-07-04 01:55:09 -04:00
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Debug Stuff
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
#define TAGS_SECTION_IOP_MODULES ("modules")
|
|
|
|
#define TAGS_SECTION_IOP_MODULES_MODULE ("module")
|
|
|
|
#define TAGS_SECTION_IOP_MODULES_MODULE_BEGINADDRESS ("beginAddress")
|
|
|
|
#define TAGS_SECTION_IOP_MODULES_MODULE_ENDADDRESS ("endAddress")
|
|
|
|
#define TAGS_SECTION_IOP_MODULES_MODULE_NAME ("name")
|
2014-07-04 01:55:09 -04:00
|
|
|
|
|
|
|
void CIopBios::LoadDebugTags(Framework::Xml::CNode* root)
|
|
|
|
{
|
|
|
|
auto moduleSection = root->Select(TAGS_SECTION_IOP_MODULES);
|
|
|
|
if(moduleSection == NULL) return;
|
|
|
|
|
|
|
|
for(Framework::Xml::CFilteringNodeIterator nodeIterator(moduleSection, TAGS_SECTION_IOP_MODULES_MODULE);
|
2018-04-30 21:01:23 +01:00
|
|
|
!nodeIterator.IsEnd(); nodeIterator++)
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
|
|
|
auto moduleNode(*nodeIterator);
|
2018-04-30 21:01:23 +01:00
|
|
|
const char* moduleName = moduleNode->GetAttribute(TAGS_SECTION_IOP_MODULES_MODULE_NAME);
|
|
|
|
const char* beginAddress = moduleNode->GetAttribute(TAGS_SECTION_IOP_MODULES_MODULE_BEGINADDRESS);
|
|
|
|
const char* endAddress = moduleNode->GetAttribute(TAGS_SECTION_IOP_MODULES_MODULE_ENDADDRESS);
|
2014-07-04 01:55:09 -04:00
|
|
|
if(!moduleName || !beginAddress || !endAddress) continue;
|
|
|
|
if(FindModuleDebugInfo(moduleName) != std::end(m_moduleTags)) continue;
|
|
|
|
|
|
|
|
BIOS_DEBUG_MODULE_INFO module;
|
2018-04-30 21:01:23 +01:00
|
|
|
module.name = moduleName;
|
|
|
|
module.begin = lexical_cast_hex<std::string>(beginAddress);
|
|
|
|
module.end = lexical_cast_hex<std::string>(endAddress);
|
|
|
|
module.param = NULL;
|
2014-07-04 01:55:09 -04:00
|
|
|
m_moduleTags.push_back(module);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CIopBios::SaveDebugTags(Framework::Xml::CNode* root)
|
|
|
|
{
|
2023-11-14 17:42:31 -05:00
|
|
|
auto moduleSection = std::make_unique<Framework::Xml::CNode>(TAGS_SECTION_IOP_MODULES, true);
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2017-01-22 14:15:50 -05:00
|
|
|
for(const auto& module : m_moduleTags)
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
2023-11-14 17:42:31 -05:00
|
|
|
auto moduleNode = std::make_unique<Framework::Xml::CNode>(TAGS_SECTION_IOP_MODULES_MODULE, true);
|
2018-04-30 21:01:23 +01:00
|
|
|
moduleNode->InsertAttribute(TAGS_SECTION_IOP_MODULES_MODULE_BEGINADDRESS, lexical_cast_hex<std::string>(module.begin, 8).c_str());
|
|
|
|
moduleNode->InsertAttribute(TAGS_SECTION_IOP_MODULES_MODULE_ENDADDRESS, lexical_cast_hex<std::string>(module.end, 8).c_str());
|
|
|
|
moduleNode->InsertAttribute(TAGS_SECTION_IOP_MODULES_MODULE_NAME, module.name.c_str());
|
2023-11-14 17:42:31 -05:00
|
|
|
moduleSection->InsertNode(std::move(moduleNode));
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
2023-11-14 17:42:31 -05:00
|
|
|
root->InsertNode(std::move(moduleSection));
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
2014-07-04 02:18:14 -04:00
|
|
|
BiosDebugModuleInfoArray CIopBios::GetModulesDebugInfo() const
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
|
|
|
return m_moduleTags;
|
|
|
|
}
|
|
|
|
|
2023-01-11 16:25:49 -05:00
|
|
|
enum IOP_BIOS_DEBUG_OBJECT_TYPE
|
|
|
|
{
|
|
|
|
IOP_BIOS_DEBUG_OBJECT_TYPE_SIFRPCSERVER = BIOS_DEBUG_OBJECT_TYPE_CUSTOM_START,
|
|
|
|
};
|
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
BiosDebugObjectInfoMap CIopBios::GetBiosObjectsDebugInfo() const
|
|
|
|
{
|
2022-09-10 12:47:16 -04:00
|
|
|
static BiosDebugObjectInfoMap objectDebugInfo = [] {
|
2022-09-07 19:58:01 -04:00
|
|
|
BiosDebugObjectInfoMap result;
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
2022-09-07 19:58:01 -04:00
|
|
|
BIOS_DEBUG_OBJECT_INFO info;
|
|
|
|
info.name = "Threads";
|
|
|
|
info.selectionAction = BIOS_DEBUG_OBJECT_ACTION::SHOW_STACK_OR_LOCATION;
|
|
|
|
info.fields =
|
2022-09-10 12:47:16 -04:00
|
|
|
{
|
|
|
|
{"Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::IDENTIFIER},
|
|
|
|
{"Priority", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32},
|
|
|
|
{"Location", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::LOCATION | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::TEXT_ADDRESS},
|
|
|
|
{"RA", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::RETURN_ADDRESS | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::HIDDEN},
|
|
|
|
{"SP", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::STACK_POINTER | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::HIDDEN},
|
|
|
|
{"State", BIOS_DEBUG_OBJECT_FIELD_TYPE::STRING},
|
|
|
|
};
|
2022-09-08 19:17:13 -04:00
|
|
|
result.emplace(std::make_pair(BIOS_DEBUG_OBJECT_TYPE_THREAD, std::move(info)));
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
2023-01-11 16:25:49 -05:00
|
|
|
{
|
|
|
|
BIOS_DEBUG_OBJECT_INFO info;
|
|
|
|
info.name = "SIF RPC Servers";
|
|
|
|
info.selectionAction = BIOS_DEBUG_OBJECT_ACTION::SHOW_LOCATION;
|
|
|
|
info.fields =
|
|
|
|
{
|
|
|
|
{"Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::IDENTIFIER | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::DATA_ADDRESS},
|
|
|
|
{"Handler", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::LOCATION | BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::TEXT_ADDRESS},
|
|
|
|
{"Queue Thread Id", BIOS_DEBUG_OBJECT_FIELD_TYPE::UINT32, BIOS_DEBUG_OBJECT_FIELD_ATTRIBUTE::NONE},
|
|
|
|
};
|
|
|
|
result.emplace(std::make_pair(IOP_BIOS_DEBUG_OBJECT_TYPE_SIFRPCSERVER, std::move(info)));
|
|
|
|
}
|
2022-09-07 19:58:01 -04:00
|
|
|
return result;
|
|
|
|
}();
|
|
|
|
return objectDebugInfo;
|
|
|
|
}
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
BiosDebugObjectArray CIopBios::GetBiosObjects(uint32 typeId) const
|
|
|
|
{
|
|
|
|
BiosDebugObjectArray result;
|
|
|
|
switch(typeId)
|
|
|
|
{
|
2022-09-08 19:17:13 -04:00
|
|
|
case BIOS_DEBUG_OBJECT_TYPE_THREAD:
|
2022-09-07 19:58:01 -04:00
|
|
|
for(auto it = std::begin(m_threads); it != std::end(m_threads); it++)
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
2022-09-07 19:58:01 -04:00
|
|
|
auto thread = *it;
|
|
|
|
if(!thread) continue;
|
2022-09-10 12:47:16 -04:00
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
uint32 pc = 0;
|
|
|
|
uint32 ra = 0;
|
|
|
|
uint32 sp = 0;
|
2022-09-10 12:47:16 -04:00
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
if(m_currentThreadId == it)
|
2018-07-11 21:06:16 -04:00
|
|
|
{
|
2022-09-07 19:58:01 -04:00
|
|
|
pc = m_cpu.m_State.nPC;
|
|
|
|
ra = m_cpu.m_State.nGPR[CMIPS::RA].nV0;
|
|
|
|
sp = m_cpu.m_State.nGPR[CMIPS::SP].nV0;
|
2018-07-11 21:06:16 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-07 19:58:01 -04:00
|
|
|
pc = thread->context.epc;
|
|
|
|
ra = thread->context.gpr[CMIPS::RA];
|
|
|
|
sp = thread->context.gpr[CMIPS::SP];
|
2018-07-11 21:06:16 -04:00
|
|
|
}
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
std::string stateDescription;
|
|
|
|
int64 deltaTime = thread->nextActivateTime - GetCurrentTime();
|
|
|
|
|
|
|
|
switch(thread->status)
|
|
|
|
{
|
|
|
|
case THREAD_STATUS_DORMANT:
|
|
|
|
stateDescription = "Dormant";
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_RUNNING:
|
|
|
|
if(deltaTime <= 0)
|
|
|
|
{
|
|
|
|
stateDescription = "Running";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stateDescription = string_format("Delayed (%ld ticks)", deltaTime);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_SLEEPING:
|
|
|
|
stateDescription = "Sleeping";
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_SEMAPHORE:
|
|
|
|
stateDescription = string_format("Waiting (Semaphore: %d)", thread->waitSemaphore);
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_EVENTFLAG:
|
|
|
|
stateDescription = string_format("Waiting (Event Flag: %d)", thread->waitEventFlag);
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAITING_MESSAGEBOX:
|
|
|
|
stateDescription = string_format("Waiting (Message Box: %d)", thread->waitMessageBox);
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAIT_VBLANK_START:
|
|
|
|
stateDescription = "Waiting (Vblank Start)";
|
|
|
|
break;
|
|
|
|
case THREAD_STATUS_WAIT_VBLANK_END:
|
|
|
|
stateDescription = "Waiting (Vblank End)";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
stateDescription = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2022-09-07 19:58:01 -04:00
|
|
|
BIOS_DEBUG_OBJECT obj;
|
|
|
|
obj.fields = {
|
2022-09-10 12:47:16 -04:00
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, it),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, thread->priority),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, pc),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, ra),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, sp),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<std::string>, stateDescription),
|
2022-09-07 19:58:01 -04:00
|
|
|
};
|
|
|
|
result.push_back(obj);
|
|
|
|
}
|
|
|
|
break;
|
2023-01-11 16:25:49 -05:00
|
|
|
case IOP_BIOS_DEBUG_OBJECT_TYPE_SIFRPCSERVER:
|
|
|
|
{
|
|
|
|
const auto& servers = m_sifCmd->GetServers();
|
|
|
|
for(const auto& server : servers)
|
|
|
|
{
|
|
|
|
uint32 serverDataAddr = server->GetServerDataAddress();
|
|
|
|
uint32 queueThreadId = -1;
|
|
|
|
auto serverData = reinterpret_cast<const Iop::CSifCmd::SIFRPCSERVERDATA*>(m_ram + serverDataAddr);
|
|
|
|
if(serverData->queueAddr != 0)
|
|
|
|
{
|
|
|
|
auto queueData = reinterpret_cast<const Iop::CSifCmd::SIFRPCQUEUEDATA*>(m_ram + serverData->queueAddr);
|
|
|
|
queueThreadId = queueData->threadId;
|
|
|
|
}
|
|
|
|
BIOS_DEBUG_OBJECT obj;
|
|
|
|
obj.fields = {
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, serverData->serverId),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, serverData->function),
|
|
|
|
BIOS_DEBUG_OBJECT_FIELD(std::in_place_type<uint32>, queueThreadId),
|
|
|
|
};
|
|
|
|
result.push_back(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2022-09-07 19:58:01 -04:00
|
|
|
}
|
|
|
|
return result;
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
2022-07-22 17:01:59 -04:00
|
|
|
void CIopBios::PrepareModuleDebugInfo(CELF32& elf, const ExecutableRange& moduleRange, const std::string& moduleName, const std::string& modulePath)
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
|
|
|
//Update module tag
|
|
|
|
{
|
|
|
|
auto moduleIterator(FindModuleDebugInfo(moduleRange.first, moduleRange.second));
|
|
|
|
if(moduleIterator == std::end(m_moduleTags))
|
|
|
|
{
|
|
|
|
moduleIterator = FindModuleDebugInfo(moduleName);
|
|
|
|
if(moduleIterator == std::end(m_moduleTags))
|
|
|
|
{
|
|
|
|
moduleIterator = m_moduleTags.insert(std::end(m_moduleTags), BIOS_DEBUG_MODULE_INFO());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& module(*moduleIterator);
|
2018-04-30 21:01:23 +01:00
|
|
|
module.name = moduleName;
|
|
|
|
module.begin = moduleRange.first;
|
|
|
|
module.end = moduleRange.second;
|
|
|
|
module.param = NULL;
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
2014-08-16 21:42:19 -04:00
|
|
|
m_cpu.m_analysis->Analyse(moduleRange.first, moduleRange.second);
|
2014-07-04 01:55:09 -04:00
|
|
|
|
|
|
|
bool functionAdded = false;
|
2023-08-12 10:31:28 -04:00
|
|
|
bool variableAdded = false;
|
|
|
|
|
2014-07-04 01:55:09 -04:00
|
|
|
//Look for import tables
|
|
|
|
for(uint32 address = moduleRange.first; address < moduleRange.second; address += 4)
|
|
|
|
{
|
|
|
|
if(m_cpu.m_pMemoryMap->GetWord(address) == 0x41E00000)
|
|
|
|
{
|
|
|
|
if(m_cpu.m_pMemoryMap->GetWord(address + 4) != 0) continue;
|
2018-04-30 21:01:23 +01:00
|
|
|
|
2014-07-04 01:55:09 -04:00
|
|
|
uint32 version = m_cpu.m_pMemoryMap->GetWord(address + 8);
|
2024-02-23 16:52:17 -05:00
|
|
|
auto moduleName = ReadModuleName(address + 0xC);
|
|
|
|
auto module(m_modules.find(moduleName));
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2024-05-09 17:21:54 -04:00
|
|
|
uint32 entryAddress = address + 0x14;
|
2014-07-04 01:55:09 -04:00
|
|
|
while(m_cpu.m_pMemoryMap->GetWord(entryAddress) == 0x03E00008)
|
|
|
|
{
|
|
|
|
uint32 target = m_cpu.m_pMemoryMap->GetWord(entryAddress + 4);
|
|
|
|
uint32 functionId = target & 0xFFFF;
|
|
|
|
std::string functionName;
|
2015-02-07 23:06:08 -05:00
|
|
|
if(moduleName == m_libsd->GetId())
|
|
|
|
{
|
|
|
|
functionName = m_libsd->GetFunctionName(functionId);
|
|
|
|
}
|
|
|
|
else if(module != m_modules.end())
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
|
|
|
functionName = (module->second)->GetFunctionName(functionId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char functionNameTemp[256];
|
2017-05-29 06:01:32 +01:00
|
|
|
sprintf(functionNameTemp, "unknown_%04X", functionId);
|
2014-07-04 01:55:09 -04:00
|
|
|
functionName = functionNameTemp;
|
|
|
|
}
|
2023-11-29 16:00:54 -05:00
|
|
|
if(!m_cpu.m_Functions.Find(address))
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
2023-11-29 16:00:54 -05:00
|
|
|
auto fullFunctionName = std::string(moduleName) + "_" + functionName;
|
|
|
|
m_cpu.m_Functions.InsertTag(entryAddress, std::move(fullFunctionName));
|
2014-07-04 01:55:09 -04:00
|
|
|
functionAdded = true;
|
|
|
|
}
|
|
|
|
entryAddress += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Look also for symbol tables
|
2023-07-31 18:21:18 -04:00
|
|
|
elf.EnumerateSymbols([&](const ELF::ELFSYMBOL32& symbol, uint8 type, uint8 binding, const char* name) {
|
|
|
|
if(type == ELF::STT_FUNC)
|
2014-07-04 01:55:09 -04:00
|
|
|
{
|
2023-07-31 18:21:18 -04:00
|
|
|
m_cpu.m_Functions.InsertTag(symbol.nValue + moduleRange.first, name);
|
|
|
|
functionAdded = true;
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
2023-08-12 10:31:28 -04:00
|
|
|
else if((type == ELF::STT_OBJECT) && (binding == ELF::STB_GLOBAL))
|
|
|
|
{
|
|
|
|
m_cpu.m_Variables.InsertTag(symbol.nValue + moduleRange.first, name);
|
|
|
|
variableAdded = true;
|
|
|
|
}
|
2023-07-31 18:21:18 -04:00
|
|
|
});
|
2014-07-04 01:55:09 -04:00
|
|
|
|
|
|
|
if(functionAdded)
|
|
|
|
{
|
|
|
|
m_cpu.m_Functions.OnTagListChange();
|
|
|
|
}
|
2023-08-12 10:31:28 -04:00
|
|
|
if(variableAdded)
|
|
|
|
{
|
|
|
|
m_cpu.m_Variables.OnTagListChange();
|
|
|
|
}
|
2014-07-04 01:55:09 -04:00
|
|
|
|
2018-04-30 21:01:23 +01:00
|
|
|
CLog::GetInstance().Print(LOGNAME, "Loaded IOP module '%s' @ 0x%08X.\r\n",
|
|
|
|
modulePath.c_str(), moduleRange.first);
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
BiosDebugModuleInfoIterator CIopBios::FindModuleDebugInfo(const std::string& name)
|
|
|
|
{
|
|
|
|
return std::find_if(std::begin(m_moduleTags), std::end(m_moduleTags),
|
2018-04-30 21:01:23 +01:00
|
|
|
[=](const BIOS_DEBUG_MODULE_INFO& module) {
|
|
|
|
return name == module.name;
|
|
|
|
});
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
BiosDebugModuleInfoIterator CIopBios::FindModuleDebugInfo(uint32 beginAddress, uint32 endAddress)
|
|
|
|
{
|
|
|
|
return std::find_if(std::begin(m_moduleTags), std::end(m_moduleTags),
|
2018-04-30 21:01:23 +01:00
|
|
|
[=](const BIOS_DEBUG_MODULE_INFO& module) {
|
|
|
|
return (beginAddress == module.begin) && (endAddress == module.end);
|
|
|
|
});
|
2014-07-04 01:55:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|