u32 hleDelayResult(u32 result, const char *reason, int usec) { if (__KernelIsDispatchEnabled()) { CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, __KernelGetCurThread()); __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason); } else WARN_LOG(HLE, "Dispatch disabled, not delaying HLE result (right thing to do?)"); return result; }
int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr) { DEBUG_LOG(HLE, "sceKernelWaitEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr); if ((wait & ~PSP_EVENT_WAITKNOWN) != 0) { WARN_LOG_REPORT(HLE, "sceKernelWaitEventFlag(%i) invalid mode parameter: %08x", id, wait); return SCE_KERNEL_ERROR_ILLEGAL_MODE; } // Can't wait on 0, that's guaranteed to wait forever. if (bits == 0) return SCE_KERNEL_ERROR_EVF_ILPAT; if (!__KernelIsDispatchEnabled()) return SCE_KERNEL_ERROR_CAN_NOT_WAIT; u32 error; EventFlag *e = kernelObjects.Get<EventFlag>(id, error); if (e) { EventFlagTh th; if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr)) { // If this thread was left in waitingThreads after a timeout, remove it. // Otherwise we might write the outBitsPtr in the wrong place. __KernelEventFlagRemoveThread(e, __KernelGetCurThread()); u32 timeout = 0xFFFFFFFF; if (Memory::IsValidAddress(timeoutPtr)) timeout = Memory::Read_U32(timeoutPtr); // Do we allow more than one thread to wait? if (e->waitingThreads.size() > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0) return SCE_KERNEL_ERROR_EVF_MULTI; // No match - must wait. th.tid = __KernelGetCurThread(); th.bits = bits; th.wait = wait; // If < 5ms, sometimes hardware doesn't write this, but it's unpredictable. th.outAddr = timeout == 0 ? 0 : outBitsPtr; e->waitingThreads.push_back(th); __KernelSetEventFlagTimeout(e, timeoutPtr); __KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, false, "event flag waited"); } return 0; } else { return error; } }
u32 sceDisplayWaitVblankCB() { if (!isVblank) { DEBUG_LOG(HLE,"sceDisplayWaitVblankCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true, "vblank waited"); return 0; } else { DEBUG_LOG(HLE,"sceDisplayWaitVblank() - not waiting since in vBlank"); return 1; } }
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) { section.lock(); if (chan.sampleAddress == 0) return SCE_ERROR_AUDIO_NOT_OUTPUT; if (chan.sampleQueue.size() > chan.sampleCount*2*chanQueueMaxSizeFactor) { // Block! if (blocking) { chan.waitingThread = __KernelGetCurThread(); // WARNING: This changes currentThread so must grab waitingThread before (line above). __KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum, 0, 0, false, "blocking audio waited"); // Fall through to the sample queueing, don't want to lose the samples even though // we're getting full. } else { chan.waitingThread = 0; return SCE_ERROR_AUDIO_CHANNEL_BUSY; } } if (chan.format == PSP_AUDIO_FORMAT_STEREO) { const u32 totalSamples = chan.sampleCount * 2; if (IS_LITTLE_ENDIAN) { s16 *sampleData = (s16 *) Memory::GetPointer(chan.sampleAddress); // Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr. if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16))) { for (u32 i = 0; i < totalSamples; i++) chan.sampleQueue.push(*sampleData++); } } else { for (u32 i = 0; i < totalSamples; i++) chan.sampleQueue.push((s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i)); } } else if (chan.format == PSP_AUDIO_FORMAT_MONO) { for (u32 i = 0; i < chan.sampleCount; i++) { // Expand to stereo s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i); chan.sampleQueue.push(sample); chan.sampleQueue.push(sample); } } section.unlock(); return 0; }
u32 sceDisplayWaitVblankCB() { if (!isVblank) { VERBOSE_LOG(SCEDISPLAY,"sceDisplayWaitVblankCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 1, 0, 0, true, "vblank waited"); return 0; } else { DEBUG_LOG(SCEDISPLAY,"sceDisplayWaitVblankCB() - not waiting since in vBlank"); hleEatCycles(1110); return 1; } }
/** * Wait for a drive to reach a certain state * * @param stat - The drive stat to wait for. * @return < 0 on error * */ int sceUmdWaitDriveStat(u32 stat) { DEBUG_LOG(HLE,"0=sceUmdWaitDriveStat(stat = %08x)", stat); if ((stat & __KernelUmdGetState()) == 0) { umdWaitingThreads.push_back(UmdWaitingThread::Make(__KernelGetCurThread(), stat)); __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited"); } return 0; }
int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { u32 error = 0; // If dispatch is disabled or in an interrupt, don't check, just return an error. // But still write the addr and size (some games require this to work, and it's testably true.) if (!__KernelIsDispatchEnabled()) { error = SCE_KERNEL_ERROR_CAN_NOT_WAIT; } else if (__IsInInterrupt()) { error = SCE_KERNEL_ERROR_ILLEGAL_CONTEXT; } else { error = __KernelVolatileMemLock(type, paddr, psize); } switch (error) { case 0: DEBUG_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - success", type, paddr, psize); break; case ERROR_POWER_VMEM_IN_USE: { WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - already locked, waiting", type, paddr, psize); const VolatileWaitingThread waitInfo = { __KernelGetCurThread(), paddr, psize }; volatileWaitingThreads.push_back(waitInfo); __KernelWaitCurThread(WAITTYPE_VMEM, 1, 0, 0, false, "volatile mem waited"); } break; case SCE_KERNEL_ERROR_CAN_NOT_WAIT: { WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x): dispatch disabled", type, paddr, psize); Memory::Write_U32(0x08400000, paddr); Memory::Write_U32(0x00400000, psize); } break; case SCE_KERNEL_ERROR_ILLEGAL_CONTEXT: { WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x): in interrupt", type, paddr, psize); Memory::Write_U32(0x08400000, paddr); Memory::Write_U32(0x00400000, psize); } break; default: ERROR_LOG_REPORT(HLE, "%08x=sceKernelVolatileMemLock(%i, %08x, %08x) - error", type, paddr, psize, error); break; } return error; }
int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) { DEBUG_LOG(HLE,"0=sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d)", stat, timeout); if ((stat & __KernelUmdGetState()) == 0) { __UmdWaitStat(timeout); __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0); } else hleReSchedule("umd stat waited with timer"); return 0; }
u32 sceDisplayWaitVblankStartMultiCB(int vblanks) { if (vblanks <= 0) { WARN_LOG(SCEDISPLAY, "sceDisplayWaitVblankStartMultiCB(%d): invalid number of vblanks", vblanks); return SCE_KERNEL_ERROR_INVALID_VALUE; } VERBOSE_LOG(SCEDISPLAY,"sceDisplayWaitVblankStartMultiCB(%d)", vblanks); if (!__KernelIsDispatchEnabled()) return SCE_KERNEL_ERROR_CAN_NOT_WAIT; if (__IsInInterrupt()) return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT; vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks)); __KernelWaitCurThread(WAITTYPE_VBLANK, 1, 0, 0, true, "vblank start multi waited"); return 0; }
static int DisplayWaitForVblanks(const char *reason, int vblanks, bool callbacks = false) { const s64 ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; const s64 cyclesToNextVblank = msToCycles(frameMs) - ticksIntoFrame; // These syscalls take about 115 us, so if the next vblank is before then, we're waiting extra. // At least, on real firmware a wait >= 16500 into the frame will wait two. if (cyclesToNextVblank <= usToCycles(115)) { ++vblanks; } vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks)); __KernelWaitCurThread(WAITTYPE_VBLANK, 1, 0, 0, callbacks, reason); return hleLogSuccessVerboseI(SCEDISPLAY, 0, "waiting for %d vblanks", vblanks); }
void sceCtrlReadBufferNegative(u32 ctrlDataPtr, u32 nBufs) { int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, false); if (done != 0) { RETURN(done); DEBUG_LOG(SCECTRL, "%d=sceCtrlReadBufferNegative(%08x, %i)", done, ctrlDataPtr, nBufs); } else { waitingThreads.push_back(__KernelGetCurThread()); __KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_NEGATIVE, ctrlDataPtr, 0, false, "ctrl buffer waited"); DEBUG_LOG(SCECTRL, "sceCtrlReadBufferNegative(%08x, %i) - waiting", ctrlDataPtr, nBufs); } }
int sceKernelAllocateTls(SceUID uid) { // TODO: Allocate downward if PSP_TLS_ATTR_HIGHMEM? DEBUG_LOG(HLE, "sceKernelAllocateTls(%08x)", uid); u32 error; TLS *tls = kernelObjects.Get<TLS>(uid, error); if (tls) { SceUID threadID = __KernelGetCurThread(); int allocBlock = -1; // If the thread already has one, return it. for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i) { if (tls->usage[i] == threadID) allocBlock = (int) i; } if (allocBlock == -1) { for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i) { // The PSP doesn't give the same block out twice in a row, even if freed. if (tls->usage[tls->next] == 0) allocBlock = tls->next; tls->next = (tls->next + 1) % tls->ntls.totalBlocks; } if (allocBlock != -1) { tls->usage[allocBlock] = threadID; --tls->ntls.freeBlocks; } } if (allocBlock == -1) { tls->waitingThreads.push_back(threadID); __KernelWaitCurThread(WAITTYPE_TLS, uid, 1, 0, false, "allocate tls"); return -1; } return tls->address + allocBlock * tls->ntls.blockSize; } else return error; }
int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, bool processCallbacks) { hleEatCycles(900); if (wantedCount <= 0) return SCE_KERNEL_ERROR_ILLEGAL_COUNT; hleEatCycles(500); u32 error; Semaphore *s = kernelObjects.Get<Semaphore>(id, error); if (s) { if (wantedCount > s->ns.maxCount) return SCE_KERNEL_ERROR_ILLEGAL_COUNT; // If there are any callbacks, we always wait, and wake after the callbacks. bool hasCallbacks = processCallbacks && __KernelCurHasReadyCallbacks(); if (s->ns.currentCount >= wantedCount && s->waitingThreads.size() == 0 && !hasCallbacks) { if (hasCallbacks) { // Might actually end up having to wait, so set the timeout. __KernelSetSemaTimeout(s, timeoutPtr); __KernelWaitCallbacksCurThread(WAITTYPE_SEMA, id, wantedCount, timeoutPtr); } else s->ns.currentCount -= wantedCount; } else { SceUID threadID = __KernelGetCurThread(); // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. if (std::find(s->waitingThreads.begin(), s->waitingThreads.end(), threadID) == s->waitingThreads.end()) s->waitingThreads.push_back(threadID); __KernelSetSemaTimeout(s, timeoutPtr); __KernelWaitCurThread(WAITTYPE_SEMA, id, wantedCount, timeoutPtr, processCallbacks, "sema waited"); } return 0; } else return error; }
static int __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll) { bool needsResched = false; bool needsWait = false; int result = __KernelReceiveMsgPipe(m, receiveBufAddr, receiveSize, waitMode, resultAddr, timeoutPtr, cbEnabled, poll, needsResched, needsWait); if (needsResched) hleReSchedule(cbEnabled, "msgpipe data received"); if (needsWait) { if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, m->GetUID(), MSGPIPE_WAIT_VALUE_RECV, timeoutPtr, cbEnabled, "msgpipe receive waited"); else return SCE_KERNEL_ERROR_WAIT_TIMEOUT; } return result; }
int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) { DEBUG_LOG(HLE,"0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout); hleCheckCurrentCallbacks(); if ((stat & __KernelUmdGetState()) == 0) { if (timeout == 0) timeout = 8000; __UmdWaitStat(timeout); umdWaitingThreads.push_back(UmdWaitingThread::Make(__KernelGetCurThread(), stat)); __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, true, "umd stat waited"); } else hleReSchedule("umd stat waited"); return 0; }
// int sceKernelLockMutex(SceUID id, int count, int *timeout) // void because it changes threads. void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) { DEBUG_LOG(HLE,"sceKernelLockMutex(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get<Mutex>(id, error); if (__KernelLockMutex(mutex, count, error)) { RETURN(0); __KernelReSchedule("mutex locked"); } else if (error) RETURN(error); else { mutex->waitingThreads.push_back(__KernelGetCurThread()); __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, false); } }
static int __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll) { hleEatCycles(2400); bool needsResched = false; bool needsWait = false; int result = __KernelSendMsgPipe(m, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr, cbEnabled, poll, needsResched, needsWait); if (needsResched) hleReSchedule(cbEnabled, "msgpipe data sent"); if (needsWait) { if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, m->GetUID(), MSGPIPE_WAIT_VALUE_SEND, timeoutPtr, cbEnabled, "msgpipe send waited"); else result = SCE_KERNEL_ERROR_WAIT_TIMEOUT; } return result; }
int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr) { u32 error, ignore; if (__KernelAllocateVpl(uid, size, addrPtr, error, __FUNCTION__)) { VPL *vpl = kernelObjects.Get<VPL>(uid, ignore); if (error == SCE_KERNEL_ERROR_NO_MEMORY) { if (vpl) { SceUID threadID = __KernelGetCurThread(); __KernelVplRemoveThread(vpl, threadID); VplWaitingThread waiting = {threadID, addrPtr}; vpl->waitingThreads.push_back(waiting); } __KernelSetVplTimeout(timeoutPtr); __KernelWaitCurThread(WAITTYPE_VPL, uid, size, timeoutPtr, false, "vpl waited"); } } return error; }
// int sceKernelLockMutex(SceUID id, int count, int *timeout) int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) { DEBUG_LOG(HLE, "sceKernelLockMutex(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get<Mutex>(id, error); if (__KernelLockMutex(mutex, count, error)) return 0; else if (error) return error; else { SceUID threadID = __KernelGetCurThread(); // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) mutex->waitingThreads.push_back(threadID); __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, false, "mutex waited"); // Return value will be overwritten by wait. return 0; } }
int GPUCommon::ListSync(int listid, int mode) { if (listid < 0 || listid >= DisplayListMaxCount) return SCE_KERNEL_ERROR_INVALID_ID; if (mode < 0 || mode > 1) return SCE_KERNEL_ERROR_INVALID_MODE; DisplayList& dl = dls[listid]; if (mode == 1) { switch (dl.state) { case PSP_GE_DL_STATE_QUEUED: if (dl.interrupted) return PSP_GE_LIST_PAUSED; return PSP_GE_LIST_QUEUED; case PSP_GE_DL_STATE_RUNNING: if (dl.pc == dl.stall) return PSP_GE_LIST_STALLING; return PSP_GE_LIST_DRAWING; case PSP_GE_DL_STATE_COMPLETED: return PSP_GE_LIST_COMPLETED; case PSP_GE_DL_STATE_PAUSED: return PSP_GE_LIST_PAUSED; default: return SCE_KERNEL_ERROR_INVALID_ID; } } if (dl.shouldWait) { __KernelWaitCurThread(WAITTYPE_GELISTSYNC, listid, 0, 0, false, "GeListSync"); } return PSP_GE_LIST_COMPLETED; }
//int sceKernelWaitEventFlagCB(SceUID evid, u32 bits, u32 wait, u32 *outBits, SceUInt *timeout); void sceKernelWaitEventFlagCB() { SceUID id = PARAM(0); u32 bits = PARAM(1); u32 wait = PARAM(2); u32 outBitsPtr = PARAM(3); u32 timeoutPtr = PARAM(4); DEBUG_LOG(HLE,"sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr); u32 error; EventFlag *e = kernelObjects.Get<EventFlag>(id, error); if (e) { EventFlagTh th; if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr)) { // No match - must wait. e->nef.numWaitThreads++; th.tid = __KernelGetCurThread(); th.bits = bits; th.wait = wait; th.outAddr = outBitsPtr; e->waitingThreads.push_back(th); u32 timeout; if (Memory::IsValidAddress(timeoutPtr)) timeout = Memory::Read_U32(timeoutPtr); __KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, true); } RETURN(0); } else { RETURN(error); } }
// int sceKernelLockMutexCB(SceUID id, int count, int *timeout) int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) { DEBUG_LOG(HLE, "sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get<Mutex>(id, error); if (!__KernelLockMutexCheck(mutex, count, error)) { if (error) return error; SceUID threadID = __KernelGetCurThread(); // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) mutex->waitingThreads.push_back(threadID); __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, true, "mutex waited"); // Return value will be overwritten by wait. return 0; } else { if (__KernelCurHasReadyCallbacks()) { // Might actually end up having to wait, so set the timeout. __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCallbacksCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr); // Return value will be written to callback's v0, but... that's probably fine? } else __KernelLockMutex(mutex, count, error); return 0; } }
u32 sceDisplayWaitVblankStartCB() { VERBOSE_LOG(SCEDISPLAY,"sceDisplayWaitVblankStartCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 1, 0, 0, true, "vblank start waited"); return 0; }
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) { u32 ret = chan.sampleCount; if (chan.sampleAddress == 0) { // For some reason, multichannel audio lies and returns the sample count here. if (chanNum == PSP_AUDIO_CHANNEL_SRC || chanNum == PSP_AUDIO_CHANNEL_OUTPUT2) { ret = 0; } } // If there's anything on the queue at all, it should be busy, but we try to be a bit lax. if (chan.sampleQueue.size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) { if (blocking) { // TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync? int blockSamples = chan.sampleQueue.size() / 2 / chanQueueMinSizeFactor; AudioChannelWaitInfo waitInfo = {__KernelGetCurThread(), blockSamples}; chan.waitingThreads.push_back(waitInfo); // Also remember the value to return in the waitValue. __KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum + 1, ret, 0, false, "blocking audio waited"); // Fall through to the sample queueing, don't want to lose the samples even though // we're getting full. The PSP would enqueue after blocking. } else { // Non-blocking doesn't even enqueue, but it's not commonly used. return SCE_ERROR_AUDIO_CHANNEL_BUSY; } } if (chan.sampleAddress == 0) { return ret; } if (chan.format == PSP_AUDIO_FORMAT_STEREO) { const u32 totalSamples = chan.sampleCount * 2; if (IS_LITTLE_ENDIAN) { s16 *sampleData = (s16 *) Memory::GetPointer(chan.sampleAddress); // Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr. if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16))) { for (u32 i = 0; i < totalSamples; i++) chan.sampleQueue.push(*sampleData++); } } else { for (u32 i = 0; i < totalSamples; i++) chan.sampleQueue.push((s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i)); } } else if (chan.format == PSP_AUDIO_FORMAT_MONO) { for (u32 i = 0; i < chan.sampleCount; i++) { // Expand to stereo s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i); chan.sampleQueue.push(sample); chan.sampleQueue.push(sample); } } return ret; }
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) { u32 ret = chan.sampleCount; if (chan.sampleAddress == 0) { // For some reason, multichannel audio lies and returns the sample count here. if (chanNum == PSP_AUDIO_CHANNEL_SRC || chanNum == PSP_AUDIO_CHANNEL_OUTPUT2) { ret = 0; } } // If there's anything on the queue at all, it should be busy, but we try to be a bit lax. //if (chan.sampleQueue.size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) { if (chan.sampleQueue.size() > 0) { if (blocking) { // TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync? int blockSamples = (int)chan.sampleQueue.size() / 2 / chanQueueMinSizeFactor; if (__KernelIsDispatchEnabled()) { AudioChannelWaitInfo waitInfo = {__KernelGetCurThread(), blockSamples}; chan.waitingThreads.push_back(waitInfo); // Also remember the value to return in the waitValue. __KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum + 1, ret, 0, false, "blocking audio"); } else { // TODO: Maybe we shouldn't take this audio after all? ret = SCE_KERNEL_ERROR_CAN_NOT_WAIT; } // Fall through to the sample queueing, don't want to lose the samples even though // we're getting full. The PSP would enqueue after blocking. } else { // Non-blocking doesn't even enqueue, but it's not commonly used. return SCE_ERROR_AUDIO_CHANNEL_BUSY; } } if (chan.sampleAddress == 0) { return ret; } if (chan.format == PSP_AUDIO_FORMAT_STEREO) { const u32 totalSamples = chan.sampleCount * 2; if (IS_LITTLE_ENDIAN) { s16 *sampleData = (s16 *) Memory::GetPointer(chan.sampleAddress); // Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr. if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16))) { #if 0 for (u32 i = 0; i < totalSamples; i += 2) { chan.sampleQueue.push(adjustvolume(*sampleData++, chan.leftVolume)); chan.sampleQueue.push(adjustvolume(*sampleData++, chan.rightVolume)); } #else s16 *buf1 = 0, *buf2 = 0; size_t sz1, sz2; chan.sampleQueue.pushPointers(totalSamples, &buf1, &sz1, &buf2, &sz2); int leftVol = chan.leftVolume; int rightVol = chan.rightVolume; // TODO: SSE/NEON implementations for (u32 i = 0; i < sz1; i += 2) { buf1[i] = adjustvolume(sampleData[i], leftVol); buf1[i + 1] = adjustvolume(sampleData[i + 1], rightVol); } if (buf2) { sampleData += sz1; for (u32 i = 0; i < sz2; i += 2) { buf2[i] = adjustvolume(sampleData[i], leftVol); buf2[i + 1] = adjustvolume(sampleData[i + 1], rightVol); } } #endif } } else { for (u32 i = 0; i < totalSamples; i++) { s16 sampleL = (s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i); sampleL = adjustvolume(sampleL, chan.leftVolume); chan.sampleQueue.push(sampleL); i++; s16 sampleR = (s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i); sampleR = adjustvolume(sampleR, chan.rightVolume); chan.sampleQueue.push(sampleR); } } } else if (chan.format == PSP_AUDIO_FORMAT_MONO) { for (u32 i = 0; i < chan.sampleCount; i++) { // Expand to stereo s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i); chan.sampleQueue.push(adjustvolume(sample, chan.leftVolume)); chan.sampleQueue.push(adjustvolume(sample, chan.rightVolume)); } } return ret; }
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) { u32 ret = chan.sampleCount; if (chan.sampleAddress == 0) { // For some reason, multichannel audio lies and returns the sample count here. if (chanNum == PSP_AUDIO_CHANNEL_SRC || chanNum == PSP_AUDIO_CHANNEL_OUTPUT2) { ret = 0; } } // If there's anything on the queue at all, it should be busy, but we try to be a bit lax. //if (chan.sampleQueue.size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) { if (chan.sampleQueue.size() > 0) { if (blocking) { // TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync? int blockSamples = (int)chan.sampleQueue.size() / 2 / chanQueueMinSizeFactor; if (__KernelIsDispatchEnabled()) { AudioChannelWaitInfo waitInfo = {__KernelGetCurThread(), blockSamples}; chan.waitingThreads.push_back(waitInfo); // Also remember the value to return in the waitValue. __KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum + 1, ret, 0, false, "blocking audio"); } else { // TODO: Maybe we shouldn't take this audio after all? ret = SCE_KERNEL_ERROR_CAN_NOT_WAIT; } // Fall through to the sample queueing, don't want to lose the samples even though // we're getting full. The PSP would enqueue after blocking. } else { // Non-blocking doesn't even enqueue, but it's not commonly used. return SCE_ERROR_AUDIO_CHANNEL_BUSY; } } if (chan.sampleAddress == 0) { return ret; } int leftVol = chan.leftVolume; int rightVol = chan.rightVolume; if (leftVol == (1 << 15) && rightVol == (1 << 15) && chan.format == PSP_AUDIO_FORMAT_STEREO && IS_LITTLE_ENDIAN) { // TODO: Add mono->stereo conversion to this path. // Good news: the volume doesn't affect the values at all. // We can just do a direct memory copy. const u32 totalSamples = chan.sampleCount * (chan.format == PSP_AUDIO_FORMAT_STEREO ? 2 : 1); s16 *buf1 = 0, *buf2 = 0; size_t sz1, sz2; chan.sampleQueue.pushPointers(totalSamples, &buf1, &sz1, &buf2, &sz2); if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16_le))) { Memory::Memcpy(buf1, chan.sampleAddress, (u32)sz1 * sizeof(s16)); if (buf2) Memory::Memcpy(buf2, chan.sampleAddress + (u32)sz1 * sizeof(s16), (u32)sz2 * sizeof(s16)); } } else { // Remember that maximum volume allowed is 0xFFFFF so left shift is no issue. // This way we can optimally shift by 16. leftVol <<=1; rightVol <<=1; if (chan.format == PSP_AUDIO_FORMAT_STEREO) { const u32 totalSamples = chan.sampleCount * 2; s16_le *sampleData = (s16_le *) Memory::GetPointer(chan.sampleAddress); // Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr. if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16_le))) { s16 *buf1 = 0, *buf2 = 0; size_t sz1, sz2; chan.sampleQueue.pushPointers(totalSamples, &buf1, &sz1, &buf2, &sz2); AdjustVolumeBlock(buf1, sampleData, sz1, leftVol, rightVol); if (buf2) { AdjustVolumeBlock(buf2, sampleData + sz1, sz2, leftVol, rightVol); } } } else if (chan.format == PSP_AUDIO_FORMAT_MONO) { // Rare, so unoptimized. Expands to stereo. for (u32 i = 0; i < chan.sampleCount; i++) { s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i); chan.sampleQueue.push(ApplySampleVolume(sample, leftVol)); chan.sampleQueue.push(ApplySampleVolume(sample, rightVol)); } } } return ret; }
void sceDisplayWaitVblankStartCB() { DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB()"); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true); __KernelCheckCallbacks(); }
void sceDisplayWaitVblank() { DEBUG_LOG(HLE,"sceDisplayWaitVblank()"); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false); }
u32 sceDisplayWaitVblankStartMultiCB(int vblanks) { VERBOSE_LOG(HLE,"sceDisplayWaitVblankStartMultiCB()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks)); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true, "vblank start multi waited"); return 0; }
u32 sceDisplayWaitVblankStart() { VERBOSE_LOG(HLE,"sceDisplayWaitVblankStart()"); vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread())); __KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false, "vblank start waited"); return 0; }