daedalus/Source/OSHLE/patch_thread_hle.inl
2021-10-23 21:48:59 +11:00

802 lines
26 KiB
C++

#define TEST_DISABLE_THREAD_FUNCS DAEDALUS_PROFILE(__FUNCTION__);
//*****************************************************************************
//
//*****************************************************************************
u32 Patch_osCreateThread_Mario()
{
TEST_DISABLE_THREAD_FUNCS
u32 thread = gGPR[REG_a0]._u32_0;
u32 id = gGPR[REG_a1]._u32_0;
u32 func = gGPR[REG_a2]._u32_0;
u32 arg = gGPR[REG_a3]._u32_0;
// Other variables are on the stack - dig them out!
u8 * pStackBase = (u8 *)ReadAddress(gGPR[REG_sp]._u32_0);
u8 * pThreadListBase = (u8 *)ReadAddress(VAR_ADDRESS(osGlobalThreadList));
u8 * pThreadBase = (u8 *)ReadAddress(thread);
// Stack is arg 4
u32 stack = QuickRead32Bits(pStackBase, 4*4);
// Pri is arg 5
u32 pri = QuickRead32Bits(pStackBase, 4*5);
#ifdef DAEDALUS_DEBUG_CONSOLE
DBGConsole_Msg(0, "[WosCreateThread](0x%08x, %d, 0x%08x(), 0x%08x, 0x%08x, %d)",
thread, id, func, arg, stack, pri );
#endif
// fp used - we now HLE the Cop1 Unusable exception and set this
// when the thread first accesses the FP unit
QuickWrite32Bits(pThreadBase, offsetof(OSThread, fp), 0); // pThread->fp
QuickWrite16Bits(pThreadBase, offsetof(OSThread, state), OS_STATE_STOPPED); // pThread->state
QuickWrite16Bits(pThreadBase, offsetof(OSThread, flags), 0); // pThread->flags
QuickWrite32Bits(pThreadBase, offsetof(OSThread, id), id);
QuickWrite32Bits(pThreadBase, offsetof(OSThread, priority), pri);
QuickWrite32Bits(pThreadBase, offsetof(OSThread, next), 0); // pThread->next
QuickWrite32Bits(pThreadBase, offsetof(OSThread, queue), 0); // Queue
QuickWrite32Bits(pThreadBase, offsetof(OSThread, context.pc), func); // state.pc
s64 sArg = (s64)(s32)arg;
QuickWrite64Bits(pThreadBase, offsetof(OSThread, context.a0), sArg); // a0
s64 sStack = (s64)(s32)stack;
QuickWrite64Bits(pThreadBase, offsetof(OSThread, context.sp), sStack - 16); // sp (sub 16 for a0 arg etc)
s64 ra = (s64)(s32)VAR_ADDRESS(osThreadDieRA);
QuickWrite64Bits(pThreadBase, offsetof(OSThread, context.ra), ra); // ra
QuickWrite32Bits(pThreadBase, offsetof(OSThread, context.sr), (SR_IMASK|SR_EXL|SR_IE)); // state.sr
QuickWrite32Bits(pThreadBase, offsetof(OSThread, context.rcp), (OS_IM_ALL & RCP_IMASK)>>RCP_IMASKSHIFT); // state.rcp
QuickWrite32Bits(pThreadBase, offsetof(OSThread, context.fpcsr), (FPCSR_FS|FPCSR_EV)); // state.fpcsr
// Set us as head of global list
u32 NextThread = QuickRead32Bits(pThreadListBase, 0x0);
QuickWrite32Bits(pThreadBase, offsetof(OSThread, tlnext), NextThread); // pThread->next
QuickWrite32Bits(pThreadListBase, 0x0, thread);
return PATCH_RET_JR_RA;
}
//*****************************************************************************
//
//*****************************************************************************
// Identical to Mario code - just more optimised
u32 Patch_osCreateThread_Rugrats()
{
TEST_DISABLE_THREAD_FUNCS
return Patch_osCreateThread_Mario();
}
//*****************************************************************************
//
//*****************************************************************************
// ToDo : Implement me
u32 Patch_osSetThreadPri()
{
TEST_DISABLE_THREAD_FUNCS
u32 thread = gGPR[REG_a0]._u32_0;
// u32 pri = gGPR[REG_a1]._u32_0;
u32 ActiveThread = Read32Bits(VAR_ADDRESS(osActiveThread));
if (thread == 0x00000000)
{
thread = ActiveThread;
}
//DBGConsole_Msg(0, "[WosSetThreadPri](0x%08x, %d) 0x%08x", thread, pri, ActiveThread);
return PATCH_RET_NOT_PROCESSED;
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch_osGetThreadPri()
{
TEST_DISABLE_THREAD_FUNCS
u32 thread = gGPR[REG_a0]._u32_0;
u32 pri;
if (thread == 0)
{
thread = Read32Bits(VAR_ADDRESS(osActiveThread));
}
pri = Read32Bits(thread + offsetof(OSThread, priority));
gGPR[REG_v0]._s64 = (s64)(s32)pri;
return PATCH_RET_JR_RA;
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch___osDequeueThread()
{
TEST_DISABLE_THREAD_FUNCS
u32 queue = gGPR[REG_a0]._u32_0;
u32 thread = gGPR[REG_a1]._u32_0;
//DBGConsole_Msg(0, "Dequeuing Thread");
u32 CurThread = Read32Bits(queue + 0x0);
while (CurThread != 0)
{
if (CurThread == thread)
{
// Set the next pointer of the previous thread
// to the next pointer of this thread
Write32Bits(queue, Read32Bits(thread + offsetof(OSThread, next)));
break;
}
else
{
// Set queue pointer to next in list
queue = CurThread;
CurThread = Read32Bits(queue + 0x0);
}
}
return PATCH_RET_JR_RA;
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch___osDispatchThread_Mario()
{
TEST_DISABLE_THREAD_FUNCS
// First pop the first thread off the stack (copy of osPopThread code):
u32 thread = Read32Bits(VAR_ADDRESS(osThreadQueue));
u8 * pThreadBase = (u8 *)ReadAddress(thread);
// Update queue to point to next thread:
Write32Bits(VAR_ADDRESS(osThreadQueue), QuickRead32Bits(pThreadBase, offsetof(OSThread, next)));
// Set the current active thread:
Write32Bits(VAR_ADDRESS(osActiveThread), thread);
// Set the current thread's status to OS_STATE_RUNNING:
QuickWrite16Bits(pThreadBase, offsetof(OSThread, state), OS_STATE_RUNNING);
#if 1 //1->better cache efficiency //Corn
// CPU regs
for(u32 Reg = 1; Reg < 26; Reg++) //AT -> T9
{
gGPR[Reg]._u64 = QuickRead64Bits(pThreadBase, 0x0018 + (Reg << 3));
}
gGPR[REG_gp]._u64 = QuickRead64Bits(pThreadBase, 0x00e8);
gGPR[REG_sp]._u64 = QuickRead64Bits(pThreadBase, 0x00f0);
gGPR[REG_s8]._u64 = QuickRead64Bits(pThreadBase, 0x00f8);
gGPR[REG_ra]._u64 = QuickRead64Bits(pThreadBase, 0x0100);
#else
// Restore all registers:
// For speed, we cache the base pointer!!!
gGPR[REG_at]._u64 = QuickRead64Bits(pThreadBase, 0x0020);
gGPR[REG_v0]._u64 = QuickRead64Bits(pThreadBase, 0x0028);
gGPR[REG_v1]._u64 = QuickRead64Bits(pThreadBase, 0x0030);
gGPR[REG_a0]._u64 = QuickRead64Bits(pThreadBase, 0x0038);
gGPR[REG_a1]._u64 = QuickRead64Bits(pThreadBase, 0x0040);
gGPR[REG_a2]._u64 = QuickRead64Bits(pThreadBase, 0x0048);
gGPR[REG_a3]._u64 = QuickRead64Bits(pThreadBase, 0x0050);
gGPR[REG_t0]._u64 = QuickRead64Bits(pThreadBase, 0x0058);
gGPR[REG_t1]._u64 = QuickRead64Bits(pThreadBase, 0x0060);
gGPR[REG_t2]._u64 = QuickRead64Bits(pThreadBase, 0x0068);
gGPR[REG_t3]._u64 = QuickRead64Bits(pThreadBase, 0x0070);
gGPR[REG_t4]._u64 = QuickRead64Bits(pThreadBase, 0x0078);
gGPR[REG_t5]._u64 = QuickRead64Bits(pThreadBase, 0x0080);
gGPR[REG_t6]._u64 = QuickRead64Bits(pThreadBase, 0x0088);
gGPR[REG_t7]._u64 = QuickRead64Bits(pThreadBase, 0x0090);
gGPR[REG_s0]._u64 = QuickRead64Bits(pThreadBase, 0x0098);
gGPR[REG_s1]._u64 = QuickRead64Bits(pThreadBase, 0x00a0);
gGPR[REG_s2]._u64 = QuickRead64Bits(pThreadBase, 0x00a8);
gGPR[REG_s3]._u64 = QuickRead64Bits(pThreadBase, 0x00b0);
gGPR[REG_s4]._u64 = QuickRead64Bits(pThreadBase, 0x00b8);
gGPR[REG_s5]._u64 = QuickRead64Bits(pThreadBase, 0x00c0);
gGPR[REG_s6]._u64 = QuickRead64Bits(pThreadBase, 0x00c8);
gGPR[REG_s7]._u64 = QuickRead64Bits(pThreadBase, 0x00d0);
gGPR[REG_t8]._u64 = QuickRead64Bits(pThreadBase, 0x00d8);
gGPR[REG_t9]._u64 = QuickRead64Bits(pThreadBase, 0x00e0);
gGPR[REG_gp]._u64 = QuickRead64Bits(pThreadBase, 0x00e8);
gGPR[REG_sp]._u64 = QuickRead64Bits(pThreadBase, 0x00f0);
gGPR[REG_s8]._u64 = QuickRead64Bits(pThreadBase, 0x00f8);
gGPR[REG_ra]._u64 = QuickRead64Bits(pThreadBase, 0x0100);
#endif
gCPUState.MultLo._u64 = QuickRead64Bits(pThreadBase, offsetof(OSThread, context.lo));
gCPUState.MultHi._u64 = QuickRead64Bits(pThreadBase, offsetof(OSThread, context.hi));
// Set the EPC
gCPUState.CPUControl[C0_EPC]._u32 = QuickRead32Bits(pThreadBase, offsetof(OSThread, context.pc));
// Set the STATUS register. Normally this would trigger a
// Check for pending interrupts, but we're running in kernel mode
// So SR_ERL or SR_EXL is probably set. Don't think that a check is
// necessary
u32 NewSR = QuickRead32Bits(pThreadBase, offsetof(OSThread, context.sr));
R4300_SetSR(NewSR);
// Don't restore CAUSE
// Check if the FP unit was used
u32 RestoreFP = QuickRead32Bits(pThreadBase, offsetof(OSThread, fp));
if (RestoreFP != 0)
{
// Restore control reg
gCPUState.FPUControl[31]._u32 = QuickRead32Bits(pThreadBase, offsetof(OSThread, context.fpcsr));
// Floats - can probably optimise this to eliminate 64 bits reads...
for (u32 FPReg = 0; FPReg < 16; FPReg++)
{
gCPUState.FPU[(FPReg*2)+1]._u32 = QuickRead32Bits(pThreadBase, 0x0130 + (FPReg << 3));
gCPUState.FPU[(FPReg*2)+0]._u32 = QuickRead32Bits(pThreadBase, 0x0134 + (FPReg << 3));
}
}
// Set interrupt mask...does this do anything???
u32 rcp = QuickRead32Bits(pThreadBase, 0x0128);
u16 TempVal = Read16Bits(VAR_ADDRESS(osDispatchThreadRCPThingamy) + (rcp*2));
MemoryUpdateMI( (u32)TempVal ); // MI_INTR_MASK_REG
// Done - when we exit we should ERET
return PATCH_RET_ERET;
}
//*****************************************************************************
//
//*****************************************************************************
// Neither of these are correct- they ignore the interrupt mask thing
u32 Patch___osDispatchThread_MarioKart()
{
TEST_DISABLE_THREAD_FUNCS
return Patch___osDispatchThread_Mario();
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch___osDispatchThread_Rugrats()
{
TEST_DISABLE_THREAD_FUNCS
u32 thread = Read32Bits(VAR_ADDRESS(osThreadQueue));
u8 * pThreadBase = (u8 *)ReadAddress(thread);
// Update queue to point to next thread:
Write32Bits(VAR_ADDRESS(osThreadQueue), QuickRead32Bits(pThreadBase, offsetof(OSThread, next)));
// Set the current active thread:
Write32Bits(VAR_ADDRESS(osActiveThread), thread);
// Set the current thread's status to OS_STATE_RUNNING:
QuickWrite16Bits(pThreadBase, offsetof(OSThread, state), OS_STATE_RUNNING);
/*
0x80051ad0: <0x0040d021> ADDU k0 = v0 + r0
0x80051ad4: <0x8f5b0118> LW k1 <- 0x0118(k0)*/
u32 k1 = QuickRead32Bits(pThreadBase, offsetof(OSThread, context.sr));
/*
0x80051ad8: <0x3c088006> LUI t0 = 0x80060000
0x80051adc: <0x25081880> ADDIU t0 = t0 + 0x1880
0x80051ae0: <0x8d080000> LW t0 <- 0x0000(t0)*/
u32 t0 = Read32Bits(VAR_ADDRESS(osInterruptMaskThingy));
/*
0x80051ae4: <0x3108ff00> ANDI t0 = t0 & 0xff00
0x80051ae8: <0x3369ff00> ANDI t1 = k1 & 0xff00
0x80051aec: <0x01284824> AND t1 = t1 & t0
0x80051af0: <0x3c01ffff> LUI at = 0xffff0000
0x80051af4: <0x342100ff> ORI at = at | 0x00ff
0x80051af8: <0x0361d824> AND k1 = k1 & at
0x80051afc: <0x0369d825> OR k1 = k1 | t1
0x80051b00: <0x409b6000> MTC0 k1 -> Status*/
t0 &= 0xFF00;
u32 t1 = k1 & t0 & 0xFF00;
k1 &= 0xFFFF00FF;
k1 = k1 | t1;
R4300_SetSR(k1);
#if 1 //1->better cache efficiency //Corn
// CPU regs
for(u32 Reg = 1; Reg < 26; Reg++) //AT -> T9
{
gGPR[Reg]._u64 = QuickRead64Bits(pThreadBase, 0x0018 + (Reg << 3));
}
gGPR[REG_gp]._u64 = QuickRead64Bits(pThreadBase, 0x00e8);
gGPR[REG_sp]._u64 = QuickRead64Bits(pThreadBase, 0x00f0);
gGPR[REG_s8]._u64 = QuickRead64Bits(pThreadBase, 0x00f8);
gGPR[REG_ra]._u64 = QuickRead64Bits(pThreadBase, 0x0100);
#else
// Restore all registers:
// For speed, we cache the base pointer!!!
gGPR[REG_at]._u64 = QuickRead64Bits(pThreadBase, 0x0020);
gGPR[REG_v0]._u64 = QuickRead64Bits(pThreadBase, 0x0028);
gGPR[REG_v1]._u64 = QuickRead64Bits(pThreadBase, 0x0030);
gGPR[REG_a0]._u64 = QuickRead64Bits(pThreadBase, 0x0038);
gGPR[REG_a1]._u64 = QuickRead64Bits(pThreadBase, 0x0040);
gGPR[REG_a2]._u64 = QuickRead64Bits(pThreadBase, 0x0048);
gGPR[REG_a3]._u64 = QuickRead64Bits(pThreadBase, 0x0050);
gGPR[REG_t0]._u64 = QuickRead64Bits(pThreadBase, 0x0058);
gGPR[REG_t1]._u64 = QuickRead64Bits(pThreadBase, 0x0060);
gGPR[REG_t2]._u64 = QuickRead64Bits(pThreadBase, 0x0068);
gGPR[REG_t3]._u64 = QuickRead64Bits(pThreadBase, 0x0070);
gGPR[REG_t4]._u64 = QuickRead64Bits(pThreadBase, 0x0078);
gGPR[REG_t5]._u64 = QuickRead64Bits(pThreadBase, 0x0080);
gGPR[REG_t6]._u64 = QuickRead64Bits(pThreadBase, 0x0088);
gGPR[REG_t7]._u64 = QuickRead64Bits(pThreadBase, 0x0090);
gGPR[REG_s0]._u64 = QuickRead64Bits(pThreadBase, 0x0098);
gGPR[REG_s1]._u64 = QuickRead64Bits(pThreadBase, 0x00a0);
gGPR[REG_s2]._u64 = QuickRead64Bits(pThreadBase, 0x00a8);
gGPR[REG_s3]._u64 = QuickRead64Bits(pThreadBase, 0x00b0);
gGPR[REG_s4]._u64 = QuickRead64Bits(pThreadBase, 0x00b8);
gGPR[REG_s5]._u64 = QuickRead64Bits(pThreadBase, 0x00c0);
gGPR[REG_s6]._u64 = QuickRead64Bits(pThreadBase, 0x00c8);
gGPR[REG_s7]._u64 = QuickRead64Bits(pThreadBase, 0x00d0);
gGPR[REG_t8]._u64 = QuickRead64Bits(pThreadBase, 0x00d8);
gGPR[REG_t9]._u64 = QuickRead64Bits(pThreadBase, 0x00e0);
gGPR[REG_gp]._u64 = QuickRead64Bits(pThreadBase, 0x00e8);
gGPR[REG_sp]._u64 = QuickRead64Bits(pThreadBase, 0x00f0);
gGPR[REG_s8]._u64 = QuickRead64Bits(pThreadBase, 0x00f8);
gGPR[REG_ra]._u64 = QuickRead64Bits(pThreadBase, 0x0100);
#endif
gCPUState.MultLo._u64 = QuickRead64Bits(pThreadBase, offsetof(OSThread, context.lo));
gCPUState.MultHi._u64 = QuickRead64Bits(pThreadBase, offsetof(OSThread, context.hi));
// Set the EPC
gCPUState.CPUControl[C0_EPC]._u32 = QuickRead32Bits(pThreadBase, offsetof(OSThread, context.pc));
// Check if the FP unit was used
u32 RestoreFP = QuickRead32Bits(pThreadBase, offsetof(OSThread, fp));
if (RestoreFP != 0)
{
// Restore control reg
gCPUState.FPUControl[31]._u32 = QuickRead32Bits(pThreadBase, offsetof(OSThread, context.fpcsr));
// Floats - can probably optimise this to eliminate 64 bits reads...
for (u32 FPReg = 0; FPReg < 16; FPReg++)
{
gCPUState.FPU[(FPReg*2)+1]._u32 = QuickRead32Bits(pThreadBase, 0x0130 + (FPReg << 3));
gCPUState.FPU[(FPReg*2)+0]._u32 = QuickRead32Bits(pThreadBase, 0x0134 + (FPReg << 3));
}
}
/*
0x80051be4: <0x8f5b0128> LW k1 <- 0x0128(k0)
0x80051be8: <0x3c1a8006> LUI k0 = 0x80060000
0x80051bec: <0x275a1880> ADDIU k0 = k0 + 0x1880
0x80051bf0: <0x8f5a0000> LW k0 <- 0x0000(k0)
*/
// Set interrupt mask...does this do anything???
u32 rcp = QuickRead32Bits(pThreadBase, 0x0128);
u32 IntMask = Read32Bits(VAR_ADDRESS(osInterruptMaskThingy));
/*
0x80051bf4: <0x001ad402> SRL k0 = k0 >> 0x0010
0x80051bf8: <0x037ad824> AND k1 = k1 & k0
0x80051bfc: <0x001bd840> SLL k1 = k1 << 0x0001
0x80051c00: <0x3c1a8006> LUI k0 = 0x80060000
0x80051c04: <0x275a6ab0> ADDIU k0 = k0 + 0x6ab0
0x80051c08: <0x037ad821> ADDU k1 = k1 + k0
0x80051c0c: <0x977b0000> LHU k1 <- 0x0000(k1)*/
IntMask >>= 0x10;
rcp = rcp & IntMask;
u16 TempVal = Read16Bits(VAR_ADDRESS(osDispatchThreadRCPThingamy) + (rcp*2));
/*
0x80051c10: <0x3c1aa430> LUI k0 = 0xa4300000
0x80051c14: <0x375a000c> ORI k0 = k0 | 0x000c
0x80051c18: <0xaf5b0000> SW k1 -> 0x0000(k0)
0x80051c1c: <0x00000000> NOP
0x80051c20: <0x00000000> NOP
0x80051c24: <0x00000000> NOP
0x80051c28: <0x00000000> NOP
0x80051c2c: <0x42000018> ERET*/
MemoryUpdateMI( (u32)TempVal ); // MI_INTR_MASK_REG
// Done - when we exit we should ERET
return PATCH_RET_ERET;
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch_osDestroyThread_Mario()
{
TEST_DISABLE_THREAD_FUNCS
u32 thread = gGPR[REG_a0]._u32_0;
u32 CurrThread;
u32 NextThread;
u32 ActiveThread;
u16 state;
ActiveThread = Read32Bits(VAR_ADDRESS(osActiveThread));
if (thread == 0)
{
thread = ActiveThread;
}
#ifdef DAEDALUS_DEBUG_CONSOLE
DBGConsole_Msg(0, "osDestroyThread(0x%08x)", thread);
#endif
state = Read16Bits(thread + offsetof(OSThread, state));
if (state != OS_STATE_STOPPED)
{
u32 queue = Read32Bits(thread + offsetof(OSThread, queue));
gGPR[REG_a0]._s64 = (s64)(s32)queue;
gGPR[REG_a1]._s64 = (s64)(s32)thread;
g___osDequeueThread_s.Function();
}
CurrThread = Read32Bits(VAR_ADDRESS(osGlobalThreadList));
NextThread = Read32Bits(CurrThread + offsetof(OSThread, tlnext));
if (thread == CurrThread)
{
Write32Bits(VAR_ADDRESS(osGlobalThreadList), NextThread);
}
else
{
while (NextThread != 0)
{
if (thread == NextThread)
{
Write32Bits(CurrThread + offsetof(OSThread, tlnext),
Read32Bits(thread + offsetof(OSThread, tlnext)));
break;
}
CurrThread = NextThread;
NextThread = Read32Bits(CurrThread + offsetof(OSThread, tlnext));
}
}
// If we're destorying the active thread, dispatch the next thread
// Otherwise, just return control to the caller
if (thread == ActiveThread)
{
return CALL_PATCHED_FUNCTION(__osDispatchThread);
}
else
{
return PATCH_RET_JR_RA;
}
}
//*****************************************************************************
//
//*****************************************************************************
// ToDo : Implement me
u32 Patch_osDestroyThread_Zelda()
{
TEST_DISABLE_THREAD_FUNCS
#ifdef DAEDALUS_DEBUG_CONSOLE
DBGConsole_Msg(0, "osDestroyThread_Zelda not implemented (0x%08x)", gGPR[REG_a0]._u32_0);
#endif
return PATCH_RET_NOT_PROCESSED0(osDestroyThread);
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch___osEnqueueThread_Mario()
{
TEST_DISABLE_THREAD_FUNCS
u32 queue = gGPR[REG_a0]._u32_0;
u32 thread = gGPR[REG_a1]._u32_0;
u32 ThreadPri = Read32Bits(thread + 0x4);
//DBGConsole_Msg(0, "osEnqueueThread(queue = 0x%08x, thread = 0x%08x)", queue, thread);
//DBGConsole_Msg(0, " thread->priority = 0x%08x", ThreadPri);
u32 t9 = queue;
u32 CurThread = Read32Bits(t9);
u32 CurThreadPri = Read32Bits(CurThread + 0x4);
//DBGConsole_Msg(0, curthread = 0x%08x, curthread->priority = 0x%08x", CurThread, CurThreadPri);
while ((s32)CurThreadPri >= (s32)ThreadPri)
{
t9 = CurThread;
CurThread = Read32Bits(CurThread + 0x0); // Get next thread
// Check if CurThread is null there?
CurThreadPri = Read32Bits(CurThread + 0x4);
//DBGConsole_Msg(0, " curthread = 0x%08x, curthread->priority = 0x%08x", CurThread, CurThreadPri);
}
CurThread = Read32Bits(t9);
Write32Bits(thread + 0x0, CurThread); // Set thread->next
Write32Bits(thread + 0x8, queue); // Set thread->queue
Write32Bits(t9, thread); // Set prevthread->next
return PATCH_RET_JR_RA;
}
//*****************************************************************************
//
//*****************************************************************************
// Identical - just different compilation
u32 Patch___osEnqueueThread_Rugrats()
{
TEST_DISABLE_THREAD_FUNCS
u32 queue = gGPR[REG_a0]._u32_0;
u32 thread = gGPR[REG_a1]._u32_0;
u32 ThreadPri = Read32Bits(thread + 0x4);
//DBGConsole_Msg(0, "osEnqueueThread(queue = 0x%08x, thread = 0x%08x)", queue, thread);
//DBGConsole_Msg(0, " thread->priority = 0x%08x", ThreadPri);
u32 t9 = queue;
u32 CurThread = Read32Bits(t9);
u32 CurThreadPri = Read32Bits(CurThread + 0x4);
//DBGConsole_Msg(0, curthread = 0x%08x, curthread->priority = 0x%08x", CurThread, CurThreadPri);
while ((s32)CurThreadPri >= (s32)ThreadPri)
{
t9 = CurThread;
CurThread = Read32Bits(CurThread + 0x0); // Get next thread
// Check if CurThread is null there?
CurThreadPri = Read32Bits(CurThread + 0x4);
//DBGConsole_Msg(0, " curthread = 0x%08x, curthread->priority = 0x%08x", CurThread, CurThreadPri);
}
CurThread = Read32Bits(t9);
Write32Bits(thread + 0x0, CurThread); // Set thread->next
Write32Bits(thread + 0x8, queue); // Set thread->queue
Write32Bits(t9, thread); // Set prevthread->next
return PATCH_RET_JR_RA;
}
//*****************************************************************************
//
//*****************************************************************************
// Gets active thread in a1. Adds to queue in a0 (if specified), dispatches
u32 Patch___osEnqueueAndYield_Mario()
{
TEST_DISABLE_THREAD_FUNCS
// Get the active thread
u32 thread = Read32Bits(VAR_ADDRESS(osActiveThread));
u8 * pThreadBase = (u8 *)ReadAddress(thread);
//DBGConsole_Msg(0, "EnqueueAndYield()");
// Store various registers:
// For speed, we cache the base pointer!!!
u32 status = gCPUState.CPUControl[C0_SR]._u32;
status |= SR_EXL;
QuickWrite32Bits(pThreadBase, 0x118, status);
QuickWrite64Bits(pThreadBase, 0x0098, gGPR[REG_s0]._u64);
QuickWrite64Bits(pThreadBase, 0x00a0, gGPR[REG_s1]._u64);
QuickWrite64Bits(pThreadBase, 0x00a8, gGPR[REG_s2]._u64);
QuickWrite64Bits(pThreadBase, 0x00b0, gGPR[REG_s3]._u64);
QuickWrite64Bits(pThreadBase, 0x00b8, gGPR[REG_s4]._u64);
QuickWrite64Bits(pThreadBase, 0x00c0, gGPR[REG_s5]._u64);
QuickWrite64Bits(pThreadBase, 0x00c8, gGPR[REG_s6]._u64);
QuickWrite64Bits(pThreadBase, 0x00d0, gGPR[REG_s7]._u64);
QuickWrite64Bits(pThreadBase, 0x00e8, gGPR[REG_gp]._u64);
QuickWrite64Bits(pThreadBase, 0x00f0, gGPR[REG_sp]._u64);
QuickWrite64Bits(pThreadBase, 0x00f8, gGPR[REG_s8]._u64);
QuickWrite64Bits(pThreadBase, 0x0100, gGPR[REG_ra]._u64);
QuickWrite32Bits(pThreadBase, 0x011c, gGPR[REG_ra]._u32_0);
// Check if the FP unit was used
u32 RestoreFP = QuickRead32Bits(pThreadBase, 0x0018);
if (RestoreFP != 0)
{
// Save control reg
QuickWrite32Bits(pThreadBase, 0x012c, gCPUState.FPUControl[31]._u32);
// Floats - can probably optimise this to eliminate 64 bits writes...
for (u32 FPReg = 0; FPReg < 16; FPReg++)
{
QuickWrite32Bits(pThreadBase, 0x0130 + (FPReg * 8), gCPUState.FPU[(FPReg*2)+1]._u32);
QuickWrite32Bits(pThreadBase, 0x0134 + (FPReg * 8), gCPUState.FPU[(FPReg*2)+0]._u32);
}
}
// Set interrupt mask...does this do anything???
u32 rcp = Memory_MI_GetRegister( MI_INTR_MASK_REG );
QuickWrite32Bits(pThreadBase, 0x128, rcp);
u32 queue = gGPR[REG_a0]._u32_0;
if (queue != 0) // Call EnqueueThread if queue is set
{
//a0 is set already
//Set a1 (necessary for osEnqueueThread)
gGPR[REG_a1]._u32_0 = thread;
CALL_PATCHED_FUNCTION(__osEnqueueThread);
}
return CALL_PATCHED_FUNCTION(__osDispatchThread);
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch___osEnqueueAndYield_MarioKart()
{
TEST_DISABLE_THREAD_FUNCS
return Patch___osEnqueueAndYield_Mario();
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch_osStartThread()
{
TEST_DISABLE_THREAD_FUNCS
u32 thread = gGPR[REG_a0]._u32_0;
u8 * pThreadBase = (u8 *)ReadAddress(thread);
// Disable interrupts
//DBGConsole_Msg(0, "osStartThread(0x%08x)", thread)
u32 ThreadQueue = VAR_ADDRESS(osThreadQueue);
u32 ThreadState = QuickRead16Bits(pThreadBase, 0x10);
if (ThreadState == OS_STATE_WAITING)
{
//DBGConsole_Msg(0, " Thread is WAITING");
QuickWrite16Bits(pThreadBase, 0x10, OS_STATE_RUNNABLE);
gGPR[REG_a0]._u32_0 = ThreadQueue;
gGPR[REG_a1]._u32_0 = thread;
g___osEnqueueThread_s.Function();
}
else if (ThreadState == OS_STATE_STOPPED)
{
//DBGConsole_Msg(0, " Thread is STOPPED");
u32 queue = QuickRead32Bits(pThreadBase, 0x08);
if (queue == 0 || queue == ThreadQueue)
{
//if (queue == NULL)
//DBGConsole_Msg(0, " Thread has NULL queue");
//else
//DBGConsole_Msg(0, " Thread's queue is VAR_ADDRESS(osThreadQueue)");
QuickWrite16Bits(pThreadBase, 0x10, OS_STATE_RUNNABLE);
gGPR[REG_a0]._u32_0 = ThreadQueue;
gGPR[REG_a1]._u32_0 = thread;
g___osEnqueueThread_s.Function();
}
else
{
//DBGConsole_Msg(0, " Thread has it's own queue");
QuickWrite16Bits(pThreadBase, 0x10, OS_STATE_WAITING);
gGPR[REG_a0]._u32_0 = queue;
gGPR[REG_a1]._u32_0 = thread;
g___osEnqueueThread_s.Function();
// Pop the highest priority thread from the queue
u32 NewThread = Read32Bits(queue + 0x0);
Write32Bits(queue, Read32Bits(NewThread + 0x0));
// Enqueue the next thread to run
gGPR[REG_a0]._u32_0 = ThreadQueue;
gGPR[REG_a1]._u32_0 = NewThread;
g___osEnqueueThread_s.Function();
}
}
else
{
#ifdef DAEDALUS_DEBUG_CONSOLE
DBGConsole_Msg(0, " Thread is neither WAITING nor STOPPED");
#endif
}
// At this point, we check the priority of the current
// thread and the highest priority thread on the thread queue. If
// the current thread has a higher priority, nothing happens, else
// the new thread is started
u32 ActiveThread = Read32Bits(VAR_ADDRESS(osActiveThread));
if (ActiveThread == 0)
{
// There is no currently active thread
//DBGConsole_Msg(0, " No active thread, dispatching");
return CALL_PATCHED_FUNCTION(__osDispatchThread);
}
else
{
// A thread is currently active
u32 QueueThread = Read32Bits(ThreadQueue);
u32 QueueThreadPri = Read32Bits(QueueThread + 0x4);
u32 ActiveThreadPri = Read32Bits(ActiveThread + 0x4);
if (ActiveThreadPri < QueueThreadPri)
{
//DBGConsole_Msg(0, " New thread has higher priority, enqueue/yield");
// Set the active thread's state to RUNNABLE
Write16Bits(ActiveThread + 0x10, OS_STATE_RUNNABLE);
gGPR[REG_a0]._u32_0 = ThreadQueue;
// Doing this is ok, because when the active thread is resumed, it will resume
// after the call to osStartThread(). We don't do any processing after this
// event (we don't bother with interrupts)
return CALL_PATCHED_FUNCTION(__osEnqueueAndYield);
}
else
{
//DBGConsole_Msg(0, " Thread has lower priority, continuing with active thread");
}
}
// Restore interrupts?
return PATCH_RET_JR_RA;
}
//*****************************************************************************
//
//*****************************************************************************
u32 Patch___osPopThread()
{
TEST_DISABLE_THREAD_FUNCS
u32 queue = gGPR[REG_a0]._u32_0;
u8 * pBase = (u8 *)ReadAddress(queue);
u32 thread = QuickRead32Bits(pBase, 0x0);
gGPR[REG_v0]._s64 = (s64)thread;
QuickWrite32Bits(pBase, Read32Bits(thread + 0x0));
//DBGConsole_Msg(0, "0x%08x = __osPopThread(0x%08x)", thread, queue);
return PATCH_RET_JR_RA;
}