SPURS: Fix some issues

This commit is contained in:
S Gopal Rajagopal
2015-02-03 02:13:32 +05:30
parent 2c70f5168a
commit 67342781b7
2 changed files with 95 additions and 39 deletions
+64 -10
View File
@@ -2588,7 +2588,7 @@ s64 spursCreateTaskset(vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> tasks
taskset->m.size = size; taskset->m.size = size;
vm::var<CellSpursWorkloadAttribute> wkl_attr; vm::var<CellSpursWorkloadAttribute> wkl_attr;
_cellSpursWorkloadAttributeInitialize(wkl_attr, 1 /*revision*/, 0x33 /*sdk_version*/, vm::ptr<const void>::make(16) /*pm*/, 0x1E40 /*pm_size*/, _cellSpursWorkloadAttributeInitialize(wkl_attr, 1 /*revision*/, 0x33 /*sdk_version*/, vm::ptr<const void>::make(SPURS_IMG_ADDR_TASKSET_PM), 0x1E40 /*pm_size*/,
taskset.addr(), priority, 8 /*min_contention*/, max_contention); taskset.addr(), priority, 8 /*min_contention*/, max_contention);
// TODO: Check return code // TODO: Check return code
@@ -2768,14 +2768,16 @@ s64 spursCreateTask(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> task_id, vm:
if (ls_pattern.addr() != 0) if (ls_pattern.addr() != 0)
{ {
u32 ls_blocks = 0; u32 ls_blocks = 0;
for (u32 i = 0; i < 2; i++) for (auto i = 0; i < 64; i++)
{ {
for (u32 j = 0; j < 64; j++) if (ls_pattern->u64[0] & ((u64)1 << i))
{
if (ls_pattern->u64[0] & ((u64)1 << j))
{ {
ls_blocks++; ls_blocks++;
} }
if (ls_pattern->u64[1] & ((u64)1 << i))
{
ls_blocks++;
} }
} }
@@ -2803,7 +2805,9 @@ s64 spursCreateTask(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> task_id, vm:
{ {
if (!taskset->m.enabled.value()._bit[tmp_task_id]) if (!taskset->m.enabled.value()._bit[tmp_task_id])
{ {
taskset->m.enabled.value()._bit[tmp_task_id] = true; auto enabled = taskset->m.enabled.value();
enabled._bit[tmp_task_id] = true;
taskset->m.enabled = enabled;
break; break;
} }
} }
@@ -2818,23 +2822,73 @@ s64 spursCreateTask(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> task_id, vm:
for (u32 i = 0; i < 2; i++) for (u32 i = 0; i < 2; i++)
{ {
taskset->m.task_info[tmp_task_id].args.u64[i] = arg != 0 ? arg->u64[i] : 0; taskset->m.task_info[tmp_task_id].args.u64[i] = arg != 0 ? arg->u64[i] : 0;
taskset->m.task_info[tmp_task_id].ls_pattern.u64[i] = ls_pattern != 0 ? ls_pattern->u64[i] : 0; if (ls_pattern.addr())
{
taskset->m.task_info[tmp_task_id].ls_pattern.u64[i] = ls_pattern->u64[i];
}
} }
*task_id = tmp_task_id; *task_id = tmp_task_id;
return CELL_OK; return CELL_OK;
} }
s64 cellSpursCreateTask(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> taskID, u32 elf_addr, u32 context_addr, u32 context_size, vm::ptr<CellSpursTaskLsPattern> lsPattern, s64 spursTaskStart(vm::ptr<CellSpursTaskset> taskset, u32 taskId)
{
auto pendingReady = taskset->m.pending_ready.value();
pendingReady._bit[taskId] = true;
taskset->m.pending_ready = pendingReady;
cellSpursSendWorkloadSignal(vm::ptr<CellSpurs>::make((u32)taskset->m.spurs.addr()), taskset->m.wid);
auto rc = cellSpursWakeUp(GetCurrentPPUThread(), vm::ptr<CellSpurs>::make((u32)taskset->m.spurs.addr()));
if (rc != CELL_OK)
{
if (rc == CELL_SPURS_POLICY_MODULE_ERROR_STAT)
{
rc = CELL_SPURS_TASK_ERROR_STAT;
}
else
{
assert(0);
}
}
return rc;
}
s64 cellSpursCreateTask(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> taskId, u32 elf_addr, u32 context_addr, u32 context_size, vm::ptr<CellSpursTaskLsPattern> lsPattern,
vm::ptr<CellSpursTaskArgument> argument) vm::ptr<CellSpursTaskArgument> argument)
{ {
cellSpurs->Warning("cellSpursCreateTask(taskset_addr=0x%x, taskID_addr=0x%x, elf_addr_addr=0x%x, context_addr_addr=0x%x, context_size=%d, lsPattern_addr=0x%x, argument_addr=0x%x)", cellSpurs->Warning("cellSpursCreateTask(taskset_addr=0x%x, taskID_addr=0x%x, elf_addr_addr=0x%x, context_addr_addr=0x%x, context_size=%d, lsPattern_addr=0x%x, argument_addr=0x%x)",
taskset.addr(), taskID.addr(), elf_addr, context_addr, context_size, lsPattern.addr(), argument.addr()); taskset.addr(), taskId.addr(), elf_addr, context_addr, context_size, lsPattern.addr(), argument.addr());
#ifdef PRX_DEBUG #ifdef PRX_DEBUG
return GetCurrentPPUThread().FastCall2(libsre + 0x12414, libsre_rtoc); return GetCurrentPPUThread().FastCall2(libsre + 0x12414, libsre_rtoc);
#else #else
return spursCreateTask(taskset, taskID, vm::ptr<u32>::make(elf_addr), vm::ptr<u32>::make(context_addr), context_size, lsPattern, argument); if (!taskset)
{
return CELL_SPURS_TASK_ERROR_NULL_POINTER;
}
if (taskset.addr() % CellSpursTaskset::align)
{
return CELL_SPURS_TASK_ERROR_ALIGN;
}
vm::var<u32> tmpTaskId;
auto rc = spursCreateTask(taskset, tmpTaskId, vm::ptr<u32>::make(elf_addr), vm::ptr<u32>::make(context_addr), context_size, lsPattern, argument);
if (rc != CELL_OK)
{
return rc;
}
rc = spursTaskStart(taskset, tmpTaskId);
if (rc != CELL_OK)
{
return rc;
}
*taskId = tmpTaskId;
return CELL_OK;
#endif #endif
} }
+26 -24
View File
@@ -116,7 +116,9 @@ bool spursDma(SPUThread & spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag) {
u128 rv; u128 rv;
spu.ReadChannel(rv, MFC_RdAtomicStat); spu.ReadChannel(rv, MFC_RdAtomicStat);
return rv._u32[3] ? true : false; auto success = rv._u32[3] ? true : false;
success = cmd == MFC_PUTLLC_CMD ? !success : success;
return success;
} }
return true; return true;
@@ -442,11 +444,11 @@ bool spursKernel2SelectWorkload(SPUThread & spu) {
/// SPURS kernel dispatch workload /// SPURS kernel dispatch workload
void spursKernelDispatchWorkload(SPUThread & spu, u64 widAndPollStatus) { void spursKernelDispatchWorkload(SPUThread & spu, u64 widAndPollStatus) {
SpursKernelContext * ctxt = vm::get_ptr<SpursKernelContext>(spu.ls_offset + 0x100); auto ctxt = vm::get_ptr<SpursKernelContext>(spu.ls_offset + 0x100);
auto isKernel2 = ctxt->spurs->m.flags1 & SF1_32_WORKLOADS ? true : false; auto isKernel2 = ctxt->spurs->m.flags1 & SF1_32_WORKLOADS ? true : false;
auto pollStatus = (u32)(spu.GPR[3]._u64[1]); auto pollStatus = (u32)widAndPollStatus;
auto wid = (u32)(spu.GPR[3]._u64[1] >> 32); auto wid = (u32)(widAndPollStatus >> 32);
// DMA in the workload info for the selected workload // DMA in the workload info for the selected workload
auto wklInfoOffset = wid < CELL_SPURS_MAX_WORKLOAD ? offsetof(CellSpurs, m.wklInfo1[wid]) : auto wklInfoOffset = wid < CELL_SPURS_MAX_WORKLOAD ? offsetof(CellSpurs, m.wklInfo1[wid]) :
@@ -491,7 +493,7 @@ void spursKernelDispatchWorkload(SPUThread & spu, u64 widAndPollStatus) {
/// SPURS kernel workload exit /// SPURS kernel workload exit
bool spursKernelWorkloadExit(SPUThread & spu) { bool spursKernelWorkloadExit(SPUThread & spu) {
SpursKernelContext * ctxt = vm::get_ptr<SpursKernelContext>(spu.ls_offset + 0x100); auto ctxt = vm::get_ptr<SpursKernelContext>(spu.ls_offset + 0x100);
auto isKernel2 = ctxt->spurs->m.flags1 & SF1_32_WORKLOADS ? true : false; auto isKernel2 = ctxt->spurs->m.flags1 & SF1_32_WORKLOADS ? true : false;
// Select next workload to run // Select next workload to run
@@ -508,7 +510,8 @@ bool spursKernelWorkloadExit(SPUThread & spu) {
/// SPURS kernel entry point /// SPURS kernel entry point
bool spursKernelEntry(SPUThread & spu) { bool spursKernelEntry(SPUThread & spu) {
SpursKernelContext * ctxt = vm::get_ptr<SpursKernelContext>(spu.ls_offset + 0x100); auto ctxt = vm::get_ptr<SpursKernelContext>(spu.ls_offset + 0x100);
memset(ctxt, 0, sizeof(SpursKernelContext));
// Save arguments // Save arguments
ctxt->spuNum = spu.GPR[3]._u32[3]; ctxt->spuNum = spu.GPR[3]._u32[3];
@@ -516,8 +519,6 @@ bool spursKernelEntry(SPUThread & spu) {
auto isKernel2 = ctxt->spurs->m.flags1 & SF1_32_WORKLOADS ? true : false; auto isKernel2 = ctxt->spurs->m.flags1 & SF1_32_WORKLOADS ? true : false;
memset(ctxt, 0, sizeof(SpursKernelContext));
// Initialise the SPURS context to its initial values // Initialise the SPURS context to its initial values
ctxt->dmaTagId = CELL_SPURS_KERNEL_DMA_TAG_ID; ctxt->dmaTagId = CELL_SPURS_KERNEL_DMA_TAG_ID;
ctxt->wklCurrentUniqueId = 0x20; ctxt->wklCurrentUniqueId = 0x20;
@@ -566,6 +567,7 @@ bool spursSysServiceEntry(SPUThread & spu) {
// system workload. Need to implement this. // system workload. Need to implement this.
} }
cellSpursModuleExit(spu);
return false; return false;
} }
@@ -599,11 +601,11 @@ void spursSysServiceIdleHandler(SPUThread & spu, SpursKernelContext * ctxt) {
if (spurs->m.flags1 & SF1_32_WORKLOADS) { if (spurs->m.flags1 & SF1_32_WORKLOADS) {
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD2; i++) { for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD2; i++) {
u32 j = i & 0x0F; u32 j = i & 0x0F;
u8 runnable = i < CELL_SPURS_MAX_WORKLOAD ? ctxt->wklRunnable1 & (0x8000 >> j) : ctxt->wklRunnable2 & (0x8000 >> j); u16 runnable = i < CELL_SPURS_MAX_WORKLOAD ? ctxt->wklRunnable1 & (0x8000 >> j) : ctxt->wklRunnable2 & (0x8000 >> j);
u8 priority = i < CELL_SPURS_MAX_WORKLOAD ? ctxt->priority[j] & 0x0F : ctxt->priority[j] >> 4; u8 priority = i < CELL_SPURS_MAX_WORKLOAD ? ctxt->priority[j] & 0x0F : ctxt->priority[j] >> 4;
u8 maxContention = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklMaxContention[j].read_relaxed() & 0x0F : spurs->m.wklMaxContention[j].read_relaxed() >> 4; u8 maxContention = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklMaxContention[j].read_relaxed() & 0x0F : spurs->m.wklMaxContention[j].read_relaxed() >> 4;
u8 contention = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklCurrentContention[j] & 0x0F : spurs->m.wklCurrentContention[j] >> 4; u8 contention = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklCurrentContention[j] & 0x0F : spurs->m.wklCurrentContention[j] >> 4;
u8 wklSignal = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklSignal1.read_relaxed() & (0x8000 >> j) : spurs->m.wklSignal2.read_relaxed() & (0x8000 >> j); u16 wklSignal = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklSignal1.read_relaxed() & (0x8000 >> j) : spurs->m.wklSignal2.read_relaxed() & (0x8000 >> j);
u8 wklFlag = spurs->m.wklFlag.flag.read_relaxed() == 0 ? spurs->m.wklFlagReceiver.read_relaxed() == i ? 1 : 0 : 0; u8 wklFlag = spurs->m.wklFlag.flag.read_relaxed() == 0 ? spurs->m.wklFlagReceiver.read_relaxed() == i ? 1 : 0 : 0;
u8 readyCount = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklReadyCount1[j].read_relaxed() : spurs->m.wklIdleSpuCountOrReadyCount2[j].read_relaxed(); u8 readyCount = i < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklReadyCount1[j].read_relaxed() : spurs->m.wklIdleSpuCountOrReadyCount2[j].read_relaxed();
@@ -616,8 +618,8 @@ void spursSysServiceIdleHandler(SPUThread & spu, SpursKernelContext * ctxt) {
} }
} else { } else {
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) { for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) {
u8 runnable = ctxt->wklRunnable1 & (0x8000 >> i); u16 runnable = ctxt->wklRunnable1 & (0x8000 >> i);
u8 wklSignal = spurs->m.wklSignal1.read_relaxed() & (0x8000 >> i); u16 wklSignal = spurs->m.wklSignal1.read_relaxed() & (0x8000 >> i);
u8 wklFlag = spurs->m.wklFlag.flag.read_relaxed() == 0 ? spurs->m.wklFlagReceiver.read_relaxed() == i ? 1 : 0 : 0; u8 wklFlag = spurs->m.wklFlag.flag.read_relaxed() == 0 ? spurs->m.wklFlagReceiver.read_relaxed() == i ? 1 : 0 : 0;
u8 readyCount = spurs->m.wklReadyCount1[i].read_relaxed() > CELL_SPURS_MAX_SPU ? CELL_SPURS_MAX_SPU : spurs->m.wklReadyCount1[i].read_relaxed(); u8 readyCount = spurs->m.wklReadyCount1[i].read_relaxed() > CELL_SPURS_MAX_SPU ? CELL_SPURS_MAX_SPU : spurs->m.wklReadyCount1[i].read_relaxed();
u8 idleSpuCount = spurs->m.wklIdleSpuCountOrReadyCount2[i].read_relaxed() > CELL_SPURS_MAX_SPU ? CELL_SPURS_MAX_SPU : spurs->m.wklIdleSpuCountOrReadyCount2[i].read_relaxed(); u8 idleSpuCount = spurs->m.wklIdleSpuCountOrReadyCount2[i].read_relaxed() > CELL_SPURS_MAX_SPU ? CELL_SPURS_MAX_SPU : spurs->m.wklIdleSpuCountOrReadyCount2[i].read_relaxed();
@@ -676,7 +678,7 @@ void spursSysServiceMain(SPUThread & spu, u32 pollStatus) {
do { do {
spursDma(spu, MFC_GETLLAR_CMD, ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1), 0x2D80/*LSA*/, 0x80/*size*/, 0/*tag*/); spursDma(spu, MFC_GETLLAR_CMD, ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1), 0x2D80/*LSA*/, 0x80/*size*/, 0/*tag*/);
CellSpurs * spurs = vm::get_ptr<CellSpurs>(spu.ls_offset + 0x2D80 - offsetof(CellSpurs, m.wklState1)); auto spurs = vm::get_ptr<CellSpurs>(spu.ls_offset + 0x2D80 - offsetof(CellSpurs, m.wklState1));
// Halt if already initialised // Halt if already initialised
if (spurs->m.sysSrvOnSpu & (1 << ctxt->spuNum)) { if (spurs->m.sysSrvOnSpu & (1 << ctxt->spuNum)) {
@@ -1167,7 +1169,7 @@ s32 spursTasksetProcessRequest(SPUThread & spu, s32 request, u32 * taskId, u32 *
waiting._bit[ctxt->taskId] = true; waiting._bit[ctxt->taskId] = true;
break; break;
case SPURS_TASKSET_REQUEST_WAIT_SIGNAL: case SPURS_TASKSET_REQUEST_WAIT_SIGNAL:
if (signalled._bit[ctxt->taskId]) { if (signalled._bit[ctxt->taskId] == false) {
numNewlyReadyTasks--; numNewlyReadyTasks--;
running._bit[ctxt->taskId] = false; running._bit[ctxt->taskId] = false;
waiting._bit[ctxt->taskId] = true; waiting._bit[ctxt->taskId] = true;
@@ -1229,8 +1231,8 @@ s32 spursTasksetProcessRequest(SPUThread & spu, s32 request, u32 * taskId, u32 *
*isWaiting = waiting._bit[selectedTaskId < CELL_SPURS_MAX_TASK ? selectedTaskId : 0] ? 1 : 0; *isWaiting = waiting._bit[selectedTaskId < CELL_SPURS_MAX_TASK ? selectedTaskId : 0] ? 1 : 0;
if (selectedTaskId != CELL_SPURS_MAX_TASK) { if (selectedTaskId != CELL_SPURS_MAX_TASK) {
taskset->m.last_scheduled_task = selectedTaskId; taskset->m.last_scheduled_task = selectedTaskId;
running._bit[ctxt->taskId] = true; running._bit[selectedTaskId] = true;
waiting._bit[ctxt->taskId] = false; waiting._bit[selectedTaskId] = false;
} }
break; break;
case SPURS_TASKSET_REQUEST_RECV_WKL_FLAG: case SPURS_TASKSET_REQUEST_RECV_WKL_FLAG:
@@ -1343,7 +1345,7 @@ s32 spursTasketSaveTaskContext(SPUThread & spu) {
u32 allocLsBlocks = taskInfo->context_save_storage_and_alloc_ls_blocks & 0x7F; u32 allocLsBlocks = taskInfo->context_save_storage_and_alloc_ls_blocks & 0x7F;
u32 lsBlocks = 0; u32 lsBlocks = 0;
for (auto i = 0; i < 128; i++) { for (auto i = 0; i < 128; i++) {
if (taskInfo->ls_pattern.u64[i < 64 ? 1 : 0] & (0x8000000000000000ull >> i)) { if (taskInfo->ls_pattern.u64[i < 64 ? 0 : 1] & (0x8000000000000000ull >> i)) {
lsBlocks++; lsBlocks++;
} }
} }
@@ -1354,7 +1356,7 @@ s32 spursTasketSaveTaskContext(SPUThread & spu) {
// Make sure the stack is area is specified in the ls pattern // Make sure the stack is area is specified in the ls pattern
for (auto i = (ctxt->savedContextSp.value()._u32[3]) >> 11; i < 128; i++) { for (auto i = (ctxt->savedContextSp.value()._u32[3]) >> 11; i < 128; i++) {
if ((taskInfo->ls_pattern.u64[i < 64 ? 1 : 0] & (0x8000000000000000ull >> i)) == 0) { if ((taskInfo->ls_pattern.u64[i < 64 ? 0 : 1] & (0x8000000000000000ull >> i)) == 0) {
return CELL_SPURS_TASK_ERROR_STAT; return CELL_SPURS_TASK_ERROR_STAT;
} }
} }
@@ -1374,7 +1376,7 @@ s32 spursTasketSaveTaskContext(SPUThread & spu) {
// Save LS context // Save LS context
for (auto i = 6; i < 128; i++) { for (auto i = 6; i < 128; i++) {
bool shouldStore = taskInfo->ls_pattern.u64[i < 64 ? 1 : 0] & (0x8000000000000000ull >> i) ? true : false; bool shouldStore = taskInfo->ls_pattern.u64[i < 64 ? 0 : 1] & (0x8000000000000000ull >> i) ? true : false;
if (shouldStore) { if (shouldStore) {
// TODO: Combine DMA requests for consecutive blocks into a single request // TODO: Combine DMA requests for consecutive blocks into a single request
spursDma(spu, MFC_PUT_CMD, contextSaveStorage + 0x400 + ((i - 6) << 11), CELL_SPURS_TASK_TOP + ((i - 6) << 11), 0x800/*size*/, ctxt->dmaTagId); spursDma(spu, MFC_PUT_CMD, contextSaveStorage + 0x400 + ((i - 6) << 11), CELL_SPURS_TASK_TOP + ((i - 6) << 11), 0x800/*size*/, ctxt->dmaTagId);
@@ -1417,6 +1419,7 @@ void spursTasksetDispatch(SPUThread & spu) {
if (isWaiting == 0) { if (isWaiting == 0) {
// If we reach here it means that the task is being started and not being resumed // If we reach here it means that the task is being started and not being resumed
memset(vm::get_ptr<void>(spu.ls_offset + CELL_SPURS_TASK_TOP), 0, CELL_SPURS_TASK_BOTTOM - CELL_SPURS_TASK_TOP);
ctxt->guidAddr = CELL_SPURS_TASK_TOP; ctxt->guidAddr = CELL_SPURS_TASK_TOP;
u32 entryPoint; u32 entryPoint;
@@ -1428,7 +1431,7 @@ void spursTasksetDispatch(SPUThread & spu) {
spursDmaWaitForCompletion(spu, 1 << ctxt->dmaTagId); spursDmaWaitForCompletion(spu, 1 << ctxt->dmaTagId);
ctxt->savedContextLr.value()._u32[3] = entryPoint; ctxt->savedContextLr = u128::from32r(entryPoint);
ctxt->guidAddr = lowestLoadAddr; ctxt->guidAddr = lowestLoadAddr;
ctxt->tasksetMgmtAddr = 0x2700; ctxt->tasksetMgmtAddr = 0x2700;
ctxt->x2FC0 = 0; ctxt->x2FC0 = 0;
@@ -1512,12 +1515,11 @@ s32 spursTasksetProcessSyscall(SPUThread & spu, u32 syscallNum, u32 args) {
// for DMA completion // for DMA completion
if ((syscallNum & 0x10) == 0) { if ((syscallNum & 0x10) == 0) {
spursDmaWaitForCompletion(spu, 0xFFFFFFFF); spursDmaWaitForCompletion(spu, 0xFFFFFFFF);
syscallNum &= 0x0F;
} }
s32 rc = 0; s32 rc = 0;
u32 incident = 0; u32 incident = 0;
switch (syscallNum) { switch (syscallNum & 0x0F) {
case CELL_SPURS_TASK_SYSCALL_EXIT: case CELL_SPURS_TASK_SYSCALL_EXIT:
if (ctxt->x2FD4 == 4 || (ctxt->x2FC0 & 0xFFFFFFFF) != 0) { // TODO: Figure this out if (ctxt->x2FD4 == 4 || (ctxt->x2FC0 & 0xFFFFFFFF) != 0) { // TODO: Figure this out
if (ctxt->x2FD4 != 4) { if (ctxt->x2FD4 != 4) {
@@ -1587,10 +1589,10 @@ s32 spursTasksetProcessSyscall(SPUThread & spu, u32 syscallNum, u32 args) {
if (spursTasksetPollStatus(spu)) { if (spursTasksetPollStatus(spu)) {
spursTasksetExit(spu); spursTasksetExit(spu);
} } else {
spursTasksetDispatch(spu); spursTasksetDispatch(spu);
} }
}
return rc; return rc;
} }