diff --git a/Common/FixedSizeQueue.h b/Common/FixedSizeQueue.h index 7cbc6aee69..4f1b0b0602 100644 --- a/Common/FixedSizeQueue.h +++ b/Common/FixedSizeQueue.h @@ -25,68 +25,83 @@ // Not fully featured, no safety checking yet. Add features as needed. -// TODO: "inline" storage? - template -class FixedSizeQueue -{ - T *storage; - int head; - int tail; - int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future. - - // Make copy constructor private for now. - FixedSizeQueue(FixedSizeQueue &other) { } - +class FixedSizeQueue { public: - FixedSizeQueue() - { - storage = new T[N]; + FixedSizeQueue() { + storage_ = new T[N]; clear(); } - ~FixedSizeQueue() - { - delete [] storage; + ~FixedSizeQueue() { + delete [] storage_; } void clear() { - head = 0; - tail = 0; - count = 0; + head_ = 0; + tail_ = 0; + count_ = 0; } void push(T t) { - storage[tail] = t; - tail++; - if (tail == N) - tail = 0; - count++; + storage_[tail_] = t; + tail_++; + if (tail_ == N) + tail_ = 0; + count_++; + } + + void push_array(const T *ptr, size_t num) { + // TODO: memcpy + for (size_t i = 0; i < num; i++) { + push(ptr[i]); + } } void pop() { - head++; - if (head == N) - head = 0; - count--; + head_++; + if (head_ == N) + head_ = 0; + count_--; + } + + void pop_array(T *outptr, size_t num) { + for (size_t i = 0; i < num) { + outptr[i] = front(); + pop(); + } } T pop_front() { - const T &temp = storage[head]; + const T &temp = storage_[head_]; pop(); return temp; } - T &front() { return storage[head]; } - const T &front() const { return storage[head]; } + T &front() { return storage_[head_]; } + + const T &front() const { return storage_[head_]; } size_t size() const { - return count; + return count_; + } + + int room() const { + return N - count_; } bool empty() { - return count; + return count_; } + +private: + T *storage_; + int head_; + int tail_; + int count_; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future. + + // Make copy constructor private for now. + FixedSizeQueue(FixedSizeQueue &other) { } }; #endif // _FIXED_SIZE_QUEUE_H_ diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index c01e293461..db686e885f 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -23,6 +23,7 @@ #include "StdMutex.h" #include "CoreTiming.h" #include "Core.h" +#include "HLE/sceKernelThread.h" int CPU_HZ = 222000000; diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 4a21ba13c5..b5d2d7cfb0 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -178,6 +178,10 @@ template void WrapU_UUUUU() { RETURN(retval); } +template void WrapV_UUUUU() { + func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); +} + template void WrapU_CIUIU() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index c5a8646832..d434d337ec 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -60,7 +60,7 @@ const HLEFunction FakeSysCalls[] = {NID_THREADRETURN, __KernelReturnFromThread, "__KernelReturnFromThread"}, {NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall"}, {NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt"}, - {NID_IDLE, _sceKernelIdle, "_sceKernelIdle"}, + {NID_IDLE, __KernelIdle, "_sceKernelIdle"}, }; const HLEFunction UtilsForUser[] = diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 3f47266594..3386524d2f 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -24,6 +24,9 @@ #include "../CoreTiming.h" #include "../MemMap.h" #include "../Host.h" +#include "../Config.h" +#include "FixedSizeQueue.h" +#include "Common/Thread.h" // While buffers == MAX_BUFFERS, block on blocking write // non-blocking writes will return busy, I guess @@ -34,46 +37,52 @@ std::recursive_mutex section; int eventAudioUpdate = -1; +int eventHostAudioUpdate = -1; +int mixFrequency = 44100; +const int hwSampleRate = 44100; +const int hwBlockSize = 480; +const int hostAttemptBlockSize = 64; +const int audioIntervalUs = (int)(1000000ULL * hwBlockSize / hwSampleRate); +const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSampleRate); + +// High and low watermarks, basically. +const int chanQueueMaxSizeFactor = 4; +const int chanQueueMinSizeFactor = 2; + +// A whole second, should be enough for anything. +FixedSizeQueue outAudioQueue; -const int audioIntervalMs = 20; void hleAudioUpdate(u64 userdata, int cyclesLate) { - host->UpdateSound(); __AudioUpdate(); - CoreTiming::ScheduleEvent(msToCycles(audioIntervalMs), eventAudioUpdate, 0); + CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0); +} + +void hleHostAudioUpdate(u64 userdata, int cyclesLate) +{ + host->UpdateSound(); + CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0); } void __AudioInit() { - eventAudioUpdate = CoreTiming::RegisterEvent("AudioUpdate", &hleAudioUpdate); + mixFrequency = 44100; - CoreTiming::ScheduleEvent(msToCycles(1), eventAudioUpdate, 0); + eventAudioUpdate = CoreTiming::RegisterEvent("AudioUpdate", &hleAudioUpdate); + eventHostAudioUpdate = CoreTiming::RegisterEvent("AudioUpdateHost", &hleHostAudioUpdate); + + CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0); + CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0); for (int i = 0; i < 8; i++) chans[i].clear(); } void __AudioShutdown() { -} - -void __AudioUpdate() -{ - // DEBUG_LOG(HLE, "Updating audio"); - section.lock(); - for (int i = 0; i < MAX_CHANNEL; i++) - { - if (chans[i].triggered) - { - chans[i].triggered = false; - - // Instead of looping through all threads, which this does, we should keep track of which threads are waiting and just - // resume them. - __KernelTriggerWait(WAITTYPE_AUDIOCHANNEL, (SceUID)i, true); - } - } - section.unlock(); + for (int i = 0; i < 8; i++) + chans[i].clear(); } u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) @@ -81,15 +90,18 @@ 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) { + 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); section.unlock(); return 0; } else { + chan.waitingThread = 0; return SCE_ERROR_AUDIO_CHANNEL_BUSY; } } @@ -114,49 +126,122 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) return 0; } - -int __AudioMix(short *outstereo, int numSamples) +// Mix samples from the various audio channels into a single sample queue. +// This single sample queue is where __AudioMix should read from. If the sample queue is full, we should +// just sleep the main emulator thread a little. +void __AudioUpdate() { - memset(outstereo, 0, numSamples*sizeof(short)*2); + NOTICE_LOG(HLE, "__AudioUpdate"); +#ifdef _WIN32 + // HACK - TODO: Remove + bool noThrottle = GetAsyncKeyState(VK_TAB) != 0; +#else + bool noThrottle = false; +#endif + // Sleep here until the host audio hardware is ready to receive samples. + // This will effectively throttle the frame rate. - // Disable Audio for now. - // return numSamples; + if (!noThrottle) { + while (true) + { + if (outAudioQueue.size() < hwBlockSize * 4) + break; // room can only increase without us pushing, so there's no race condition between here and section.lock() + Common::SleepCurrentThread(0); + } + } section.lock(); - int numActiveChans = 0; - - for (int j = 0; j < MAX_CHANNEL; j++) - { - numActiveChans += chans[j].running ? 1 : 0; - } - - if (!numActiveChans) - { - section.unlock(); - return numSamples; - } + s32 mixBuffer[hwBlockSize * 2]; + memset(mixBuffer, 0, sizeof(mixBuffer)); for (int i = 0; i < MAX_CHANNEL; i++) { - for (int s = 0; s < numSamples; s++) + if (!chans[i].reserved) + continue; + if (!chans[i].sampleQueue.size()) { + DEBUG_LOG(HLE, "No queued samples, skipping channel %i", i); + continue; + } + + for (int s = 0; s < hwBlockSize; s++) { if (chans[i].sampleQueue.size() >= 2) { - s16 sample1 = chans[i].sampleQueue.front(); + s16 sampleL = chans[i].sampleQueue.front(); + s16 sampleR = chans[i].sampleQueue.front(); chans[i].sampleQueue.pop(); - s16 sample2 = chans[i].sampleQueue.front(); chans[i].sampleQueue.pop(); - - outstereo[s*2] += sample1 / 4;//(sample * chans[i].vol1) >> 8; - outstereo[s*2+1] += sample2 / 4;//.(sample * chans[i].vol2) >> 8; + mixBuffer[s * 2] += sampleL; + mixBuffer[s * 2 + 1] += sampleR; + } + else + { + ERROR_LOG(HLE, "channel %i buffer underrun at %i of %i", i, s, hwBlockSize); + break; } } - if (chans[i].sampleQueue.size() < chans[i].sampleCount) + + if (chans[i].sampleQueue.size() < chans[i].sampleCount * 2 * chanQueueMinSizeFactor) { - chans[i].triggered = true; + // Ask the thread to send more samples until next time, queue is being drained. + if (chans[i].waitingThread) { + SceUID waitingThread = chans[i].waitingThread; + chans[i].waitingThread = 0; + // DEBUG_LOG(HLE, "Woke thread %i for some buffer filling", waitingThread); + __KernelResumeThreadFromWait(waitingThread); + } } } + + if (!noThrottle && g_Config.bEnableSound) { + // Push the mixed samples onto the output audio queue. + for (int i = 0; i < hwBlockSize; i++) { + s32 sampleL = mixBuffer[i * 2] >> 2; // TODO - what factor? + s32 sampleR = mixBuffer[i * 2 + 1] >> 2; + + outAudioQueue.push((s16)sampleL); + outAudioQueue.push((s16)sampleR); + } + } + section.unlock(); - return numSamples; +} + +void __AudioSetOutputFrequency(int freq) +{ + mixFrequency = freq; +} + +// numFrames is number of stereo frames. +int __AudioMix(short *outstereo, int numFrames) +{ + // TODO: if mixFrequency != the actual output frequency, resample! + + section.lock(); + int underrun = -1; + s16 sampleL = 0; + s16 sampleR = 0; + for (size_t i = 0; i < numFrames; i++) { + if (outAudioQueue.size() >= 2) + { + sampleL = outAudioQueue.front(); + outAudioQueue.pop(); + sampleR = outAudioQueue.front(); + outAudioQueue.pop(); + outstereo[i * 2] = sampleL; + outstereo[i * 2 + 1] = sampleR; + } else { + underrun = i; + outstereo[i * 2] = sampleL; // repeat last sample, can reduce clicking + outstereo[i * 2 + 1] = sampleR; // repeat last sample, can reduce clicking + } + } + if (underrun >= 0) { + ERROR_LOG(HLE, "audio out buffer UNDERRUN at %i of %i", underrun, numFrames); + } else { + // DEBUG_LOG(HLE, "No underrun, mixed %i samples fine", numFrames); + } + section.unlock(); + return numFrames; } diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index 11af38ed87..196c16a7b8 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -24,6 +24,7 @@ void __AudioInit(); void __AudioUpdate(); void __AudioShutdown(); +void __AudioSetOutputFrequency(int freq); // May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking); diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 7acca0edad..40ba1f3657 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -35,6 +35,39 @@ AudioChannel chans[8]; // Not sure about the range of volume, I often see 0x800 so that might be either // max or 50%? + +void sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr) +{ + if (samplePtr == 0) + { + ERROR_LOG(HLE, "sceAudioOutputBlocking - Sample pointer null"); + RETURN(0); + } + if (chan < 0 || chan >= MAX_CHANNEL) + { + ERROR_LOG(HLE,"sceAudioOutputBlocking() - BAD CHANNEL"); + RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL); + } + else if (!chans[chan].reserved) + { + ERROR_LOG(HLE,"sceAudioOutputBlocking() - channel not reserved"); + RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED); + } + else + { + DEBUG_LOG(HLE, "sceAudioOutputBlocking(%d, %d, %08x )",chan,vol,samplePtr); + chans[chan].leftVolume = vol; + chans[chan].rightVolume = vol; + chans[chan].sampleAddress = samplePtr; + RETURN(0); + int retval = __AudioEnqueue(chans[chan], chan, true); + if (retval != 0) { + // There was an error and didn't block (block always returns 0 here). Fine to RETURN. + RETURN(retval); + } + } +} + void sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 samplePtr) { if (samplePtr == 0) @@ -54,74 +87,77 @@ void sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 sample } else { - DEBUG_LOG(HLE,"sceAudioOutputPannedBlocking(%d,%d,%d, %08x )", chan, volume1, volume2, samplePtr); - chans[chan].running = true; + DEBUG_LOG(HLE, "sceAudioOutputPannedBlocking(%d,%d,%d, %08x )", chan, volume1, volume2, samplePtr); chans[chan].leftVolume = volume1; chans[chan].rightVolume = volume2; chans[chan].sampleAddress = samplePtr; RETURN(0); - __AudioEnqueue(chans[chan], chan, true); + int retval = __AudioEnqueue(chans[chan], chan, true); + if (retval != 0) { + // There was an error and didn't block (block always returns 0 here). Fine to RETURN. + RETURN(retval); + } } } -void sceAudioGetChannelRestLen() +u32 sceAudioOutput(u32 chan, u32 vol, u32 samplePtr) { - ERROR_LOG(HLE,"UNIMPL sceAudioGetChannelRestLen(%i)", PARAM(0)); - // Remaining samples in that channel buffer. - RETURN(0); + if (chan < 0 || chan >= MAX_CHANNEL) + { + ERROR_LOG(HLE,"sceAudioOutput() - BAD CHANNEL"); + return SCE_ERROR_AUDIO_INVALID_CHANNEL; + } + else if (!chans[chan].reserved) + { + ERROR_LOG(HLE,"sceAudioOutput(%d, %d, %08x) - channel not reserved", chan, vol, samplePtr); + return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; + } + else + { + chans[chan].leftVolume = vol; + chans[chan].rightVolume = vol; + chans[chan].sampleAddress = samplePtr; + u32 retval = __AudioEnqueue(chans[chan], chan, false); + DEBUG_LOG(HLE, "%08x=sceAudioOutputPanned(%d, %d, %08x)", retval, chan, vol, samplePtr); + return retval; + } } u32 sceAudioOutputPanned(u32 chan, u32 leftVol, u32 rightVol, u32 samplePtr) { - if (chan < 0 || chan >= MAX_CHANNEL) - { - ERROR_LOG(HLE,"sceAudioOutputPanned() - BAD CHANNEL"); - return SCE_ERROR_AUDIO_INVALID_CHANNEL; - } - else if (!chans[chan].reserved) - { - ERROR_LOG(HLE,"sceAudioOutputPanned(%d, %d, %d, %08x) - channel not reserved", chan,leftVol,rightVol,samplePtr); - return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; - } - else - { - chans[chan].running = true; + if (chan < 0 || chan >= MAX_CHANNEL) + { + ERROR_LOG(HLE,"sceAudioOutputPanned() - BAD CHANNEL"); + return SCE_ERROR_AUDIO_INVALID_CHANNEL; + } + else if (!chans[chan].reserved) + { + ERROR_LOG(HLE,"sceAudioOutputPanned(%d, %d, %d, %08x) - channel not reserved", chan, leftVol, rightVol, samplePtr); + return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; + } + else + { chans[chan].leftVolume = leftVol; chans[chan].rightVolume = rightVol; chans[chan].sampleAddress = samplePtr; - u32 retval = __AudioEnqueue(chans[chan], chan, false); - DEBUG_LOG(HLE,"%08x=sceAudioOutputPanned(%d, %d, %d, %08x)",retval,chan,leftVol,rightVol,samplePtr); - return retval; + u32 retval = __AudioEnqueue(chans[chan], chan, false); + DEBUG_LOG(HLE, "%08x=sceAudioOutputPanned(%d, %d, %d, %08x)", retval, chan, leftVol, rightVol, samplePtr); + return retval; } } -void sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr) +int sceAudioGetChannelRestLen(u32 chan) { - if (samplePtr == 0) - { - ERROR_LOG(HLE, "sceAudioOutputBlocking - Sample pointer null"); - RETURN(0); - } - if (chan < 0 || chan >= MAX_CHANNEL) - { - ERROR_LOG(HLE,"sceAudioOutputBlocking() - BAD CHANNEL"); - RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL); - } - else if (!chans[chan].reserved) - { - ERROR_LOG(HLE,"sceAudioOutputBlocking() - channel not reserved"); - RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED); - } - else - { - DEBUG_LOG(HLE,"sceAudioOutputPanned(%d, %d, %08x )",chan,vol,samplePtr); - chans[chan].running = true; - chans[chan].leftVolume = vol; - chans[chan].rightVolume = vol; - chans[chan].sampleAddress = samplePtr; - RETURN(0); - __AudioEnqueue(chans[chan], chan, true); - } + int sz = (int)chans[chan].sampleQueue.size() / 2; + DEBUG_LOG(HLE,"UNTESTED %i = sceAudioGetChannelRestLen(%i)", PARAM(0)); + return sz; +} + +int sceAudioGetChannelRestLength(u32 chan) +{ + int sz = (int)chans[chan].sampleQueue.size() / 2; + DEBUG_LOG(HLE,"UNTESTED %i = sceAudioGetChannelRestLen(%i)", PARAM(0)); + return sz; } static int GetFreeChannel() @@ -158,7 +194,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun { WARN_LOG(HLE, "WARNING: Reserving already reserved channel. Error?"); } - DEBUG_LOG(HLE,"%i = sceAudioChReserve(%i, %i, %i)", channel, PARAM(0), sampleCount, format); + DEBUG_LOG(HLE, "%i = sceAudioChReserve(%i, %i, %i)", channel, PARAM(0), sampleCount, format); chans[channel].sampleCount = sampleCount; chans[channel].reserved = true; @@ -172,11 +208,9 @@ u32 sceAudioChRelease(u32 chan) ERROR_LOG(HLE,"sceAudioChRelease(%i): channel not reserved", chan); return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; } - chans[chan].triggered = false; - chans[chan].running = false; chans[chan].reserved = false; - DEBUG_LOG(HLE,"sceAudioChRelease(%i)", chan); + DEBUG_LOG(HLE, "sceAudioChRelease(%i)", chan); return 1; } @@ -194,8 +228,8 @@ u32 sceAudioSetChannelDataLen(u32 chan, u32 len) } else { - DEBUG_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i)", chan, len); - chans[chan].sampleCount = len; + DEBUG_LOG(HLE, "sceAudioSetChannelDataLen(%i, %i)", chan, len); + chans[chan].dataLen = len; return 0; } } @@ -214,7 +248,7 @@ u32 sceAudioChangeChannelConfig(u32 chan, u32 format) } else { - DEBUG_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i)", chan, format); + DEBUG_LOG(HLE, "sceAudioChangeChannelConfig(%i, %i)", chan, format); chans[chan].format = format; return 0; } @@ -234,7 +268,7 @@ u32 sceAudioChangeChannelVolume(u32 chan, u32 lvolume, u32 rvolume) } else { - DEBUG_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i)", chan, lvolume, rvolume); + DEBUG_LOG(HLE, "sceAudioChangeChannelVolume(%i, %i, %i)", chan, lvolume, rvolume); chans[chan].leftVolume = lvolume; chans[chan].rightVolume = rvolume; return 0; @@ -262,7 +296,6 @@ void sceAudioOutput2OutputBlocking() u32 dataPtr = PARAM(1); WARN_LOG(HLE,"FAKE sceAudioOutput2OutputBlocking(%i, %08x)", vol, dataPtr); - chans[0].running = true; chans[0].leftVolume = vol; chans[0].rightVolume = vol; chans[0].sampleAddress = dataPtr; @@ -289,6 +322,17 @@ void sceAudioOutput2Release() RETURN(0); } +u32 sceAudioSetFrequency(u32 freq) { + if (freq == 44100 || freq == 48000) { + INFO_LOG(HLE, "sceAudioSetFrequency(%i)", freq); + __AudioSetOutputFrequency(freq); + return 0; + } else { + ERROR_LOG(HLE, "sceAudioSetFrequency(%i) - invalid frequency (must be 44.1 or 48 khz)", freq); + return -1; + } +} + const HLEFunction sceAudio[] = { {0x01562ba3, sceAudioOutput2Reserve, "sceAudioOutput2Reserve"}, // N+, Super Stardust Portable uses these @@ -302,29 +346,28 @@ const HLEFunction sceAudio[] = {0x5C37C0AE, 0, "sceAudioSRCChRelease"}, {0x80F1F7E0, sceAudioInit, "sceAudioInit"}, {0x927AC32B, 0, "sceAudioSetVolumeOffset"}, - {0xA2BEAA6C, 0, "sceAudioSetFrequency"}, - {0xA633048E, 0, "sceAudioPollInputEnd"}, - {0xB011922F, 0, "sceAudioGetChannelRestLength"}, - {0xB61595C0, 0, "sceAudioLoopbackTest"}, + {0xA2BEAA6C, WrapU_U, "sceAudioSetFrequency"}, {0xE0727056, 0, "sceAudioSRCOutputBlocking"}, - {0xE926D3FB, 0, "sceAudioInputInitEx"}, - {0x8c1009b2, 0, "sceAudioOutput"}, + {0x8c1009b2, WrapU_UUU, "sceAudioOutput"}, {0x136CAF51, WrapV_UUU, "sceAudioOutputBlocking"}, {0xE2D56B2D, WrapU_UUUU, "sceAudioOutputPanned"}, {0x13F592BC, WrapV_UUUU, "sceAudioOutputPannedBlocking"}, //(u32, u32, u32, void *)Output sound, blocking {0x5EC81C55, WrapU_UUU, "sceAudioChReserve"}, //(u32, u32 samplecount, u32) Initialize channel and allocate buffer long, long samplecount, long);//init buffer? returns handle, minus if error {0x6FC46853, WrapU_U, "sceAudioChRelease"}, //(long handle)Terminate channel and deallocate buffer //free buffer? - {0xE9D97901, sceAudioGetChannelRestLen, "sceAudioGetChannelRestLen"}, - {0xCB2E439E, WrapU_UU, "sceAudioSetChannelDataLen"}, //(u32, u32) + {0xE9D97901, WrapI_U, "sceAudioGetChannelRestLen"}, + {0xB011922F, WrapI_U, "sceAudioGetChannelRestLength"}, // Is there a difference between this and sceAudioGetChannelRestLen? + {0xCB2E439E, WrapU_UU, "sceAudioSetChannelDataLen"}, //(u32, u32) {0x95FD0C2D, WrapU_UU, "sceAudioChangeChannelConfig"}, {0xB7E1D8E7, WrapU_UUU, "sceAudioChangeChannelVolume"}, - {0x41efade7, 0, "sceAudioOneshotOutput"}, - {0x086e5895, 0, "sceAudioInputBlocking"}, - {0x6d4bec68, 0, "sceAudioInput"}, + {0x41efade7, 0, "sceAudioOneshotOutput"}, + {0xB61595C0, 0, "sceAudioLoopbackTest"}, + {0x7de61688, 0, "sceAudioInputInit"}, + {0xE926D3FB, 0, "sceAudioInputInitEx"}, + {0x6d4bec68, 0, "sceAudioInput"}, + {0x086e5895, 0, "sceAudioInputBlocking"}, {0xa708c6a6, 0, "sceAudioGetInputLength"}, - {0x87b2e651, 0, "sceAudioWaitInputEnd"}, - {0x7de61688, 0, "sceAudioInputInit"}, - {0xb011922f, 0, "sceAudioGetChannelRestLength"}, + {0xA633048E, 0, "sceAudioPollInputEnd"}, + {0x87b2e651, 0, "sceAudioWaitInputEnd"}, }; diff --git a/Core/HLE/sceAudio.h b/Core/HLE/sceAudio.h index 48ee8900a2..804400505f 100644 --- a/Core/HLE/sceAudio.h +++ b/Core/HLE/sceAudio.h @@ -20,6 +20,7 @@ #include #include "CommonTypes.h" +#include "sceKernel.h" #include "FixedSizeQueue.h" enum PspAudioFormats { PSP_AUDIO_FORMAT_STEREO = 0, PSP_AUDIO_FORMAT_MONO = 0x10 }; @@ -39,7 +40,9 @@ enum PspAudioFrequencies { PSP_AUDIO_FREQ_44K = 44100, PSP_AUDIO_FREQ_48K = 48 struct AudioChannel { - AudioChannel() {clear();} + AudioChannel() { + clear(); + } // PSP side @@ -48,26 +51,30 @@ struct AudioChannel // last sample address u32 sampleAddress; u32 sampleCount; // Number of samples written in each OutputBlocking - - int running; - int triggered; + int dataLen; // Probably internal queue size. Not currently used. int leftVolume; int rightVolume; int format; + SceUID waitingThread; // PC side - should probably split out // We copy samples as they are written into this simple ring buffer. // Might try something more efficient later. - FixedSizeQueue sampleQueue; + FixedSizeQueue sampleQueue; void clear() { reserved = false; - running = false; + waitingThread = 0; + leftVolume = 0; + rightVolume = 0; + format = 0; + sampleAddress = 0; + sampleCount = 0; sampleQueue.clear(); } }; diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 8515c27aea..ea6e980ff7 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -19,7 +19,7 @@ #include #include "HLE.h" - +#include "Common/Action.h" #include "Common/FileUtil.h" #include "../Host.h" #include "../MIPS/MIPS.h" @@ -57,37 +57,71 @@ static const char *blacklistedModules[] = { "sceNet_Library", }; -struct Module : public KernelObject +struct NativeModule { + u32 next; + u16 attribute; + u8 version[2]; + char name[28]; + u32 status; + u32 unk1; + u32 usermod_thid; + u32 memid; + u32 mpidtext; + u32 mpiddata; + u32 ent_top; + u32 ent_size; + u32 stub_top; + u32 stub_size; + u32 module_start_func; + u32 module_stop_func; + u32 module_bootstart_func; + u32 module_reboot_before_func; + u32 module_reboot_phase_func; + u32 entry_addr; + u32 gp_value; + u32 text_addr; + u32 text_size; + u32 data_size; + u32 bss_size; + u32 nsegment; + u32 segmentaddr[4]; + u32 segmentsize[4]; + u32 module_start_thread_priority; + u32 module_start_thread_stacksize; + u32 module_start_thread_attr; + u32 module_stop_thread_priority; + u32 module_stop_thread_stacksize; + u32 module_stop_thread_attr; + u32 module_reboot_before_thread_priority; + u32 module_reboot_before_thread_stacksize; + u32 module_reboot_before_thread_attr; +}; + +class Module : public KernelObject { - const char *GetName() {return name;} +public: + Module() : memoryBlockAddr(0) {} + ~Module() { + if (memoryBlockAddr) { + userMemory.Free(memoryBlockAddr); + } + } + const char *GetName() {return nm.name;} const char *GetTypeName() {return "Module";} void GetQuickInfo(char *ptr, int size) { // ignore size sprintf(ptr, "name=%s gp=%08x entry=%08x", - name, - gp_value, - entry_addr); + nm.name, + nm.gp_value, + nm.entry_addr); } static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MODULE; } int GetIDType() const { return 0; } - SceSize size; - char nsegment; - char reserved[3]; - int segmentaddr[4]; - int segmentsize[4]; - unsigned int entry_addr; - unsigned int gp_value; - unsigned int text_addr; - unsigned int text_size; - unsigned int data_size; - unsigned int bss_size; - // The following is only available in the v1.5 firmware and above, - // but as sceKernelQueryModuleInfo is broken in v1.0 it doesn't matter. - unsigned short attribute; - unsigned char version[2]; - char name[28]; + NativeModule nm; + + u32 memoryBlockAddr; }; ////////////////////////////////////////////////////////////////////////// @@ -183,6 +217,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro kernelObjects.Destroy(module->GetUID()); return 0; } + module->memoryBlockAddr = reader.GetVaddr(); struct libent { @@ -256,8 +291,8 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro } } - module->gp_value = modinfo->gp; - strncpy(module->name, modinfo->name, 28); + module->nm.gp_value = modinfo->gp; + strncpy(module->nm.name, modinfo->name, 28); INFO_LOG(LOADER,"Module %s: %08x %08x %08x", modinfo->name, modinfo->gp, modinfo->libent,modinfo->libstub); @@ -341,7 +376,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro if (ent->name == 0) { // ? - name = module->name; + name = module->nm.name; } else { @@ -368,7 +403,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro } } - module->entry_addr = reader.GetEntryPoint(); + module->nm.entry_addr = reader.GetEntryPoint(); if (newptr) { @@ -418,7 +453,7 @@ bool __KernelLoadPBP(const char *filename, std::string *error_string) Module *module = __KernelLoadELFFromPtr(temp, PSP_GetDefaultLoadAddress(), error_string); if (!module) return false; - mipsr4k.pc = module->entry_addr; + mipsr4k.pc = module->nm.entry_addr; delete [] temp; } in.close(); @@ -460,13 +495,13 @@ void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOptio } -u32 __KernelGetModuleGP(SceUID module) +u32 __KernelGetModuleGP(SceUID uid) { u32 error; - Module *m = kernelObjects.Get(module,error); - if (m) + Module *module = kernelObjects.Get(uid, error); + if (module) { - return m->gp_value; + return module->nm.gp_value; } else { @@ -490,14 +525,14 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std:: pspFileSystem.ReadFile(handle, temp, (size_t)info.size); - Module *m = __KernelLoadModule(temp, 0, error_string); + Module *module = __KernelLoadModule(temp, 0, error_string); - if (!m) { + if (!module) { ERROR_LOG(LOADER, "Failed to load module %s", filename); return false; } - mipsr4k.pc = m->entry_addr; + mipsr4k.pc = module->nm.entry_addr; INFO_LOG(LOADER, "Module entry: %08x", mipsr4k.pc); @@ -512,7 +547,7 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std:: option.priority = 0x20; option.stacksize = 0x40000; // crazy? but seems to be the truth - __KernelStartModule(m, (u32)strlen(filename) + 1, filename, &option); + __KernelStartModule(module, (u32)strlen(filename) + 1, filename, &option); __KernelStartIdleThreads(); return true; @@ -578,6 +613,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags) if (PARAM(2)) { SceKernelLMOption *lmoption = (SceKernelLMOption *)Memory::GetPointer(PARAM(2)); + } Module *module = 0; @@ -608,23 +644,66 @@ u32 sceKernelLoadModule(const char *name, u32 flags) return module->GetUID(); } -void sceKernelStartModule() -{ - int id = PARAM(0); - int argsize = PARAM(1); - u32 argptr = PARAM(2); - u32 ptrReturn = PARAM(3); - if (PARAM(4)) { - SceKernelSMOption *smoption = (SceKernelSMOption*)Memory::GetPointer(PARAM(4)); - } - ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,...)", - id,argsize,argptr,ptrReturn); - RETURN(0); +class AfterModuleEntryCall : public Action { +public: + AfterModuleEntryCall() {} + Module *module_; + u32 retValAddr; + virtual void run(); +}; + +void AfterModuleEntryCall::run() { + Memory::Write_U32(retValAddr, currentMIPS->r[2]); } -void sceKernelStopModule() +void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) { - ERROR_LOG(HLE,"UNIMPL sceKernelStopModule"); + // Dunno what these three defaults should be... + u32 priority = 0x20; + u32 stacksize = 0x40000; + u32 attr = 0; + int stackPartition = 0; + if (optionAddr) { + SceKernelSMOption smoption; + Memory::ReadStruct(optionAddr, &smoption);; + priority = smoption.priority; + attr = smoption.attribute; + stacksize = smoption.stacksize; + stackPartition = smoption.mpidstack; + } + u32 error; + Module *module = kernelObjects.Get(moduleId, error); + if (!module) { + // TODO: Try not to lie so much. + /* + RETURN(error); + return; + */ + } else { + u32 entryAddr = module->nm.entry_addr; + if (entryAddr == -1) { + entryAddr = module->nm.module_start_func; + // attr = module->nm + } + } + + //SceUID threadId; + //__KernelCreateThread(threadId, moduleId, module->nm.name, module->nm.entry_addr, priority, stacksize, attr); + + ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)", + moduleId,argsize,argAddr,returnValueAddr,optionAddr); + + // Apparently, we need to call the entry point directly and insert the return value afterwards. This calls + // for a MipsCall and an Action. TODO + RETURN(0); // TODO: Delete +} + +void sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) +{ + ERROR_LOG(HLE,"UNIMPL sceKernelStopModule(%i, %i, %08x, %08x, %08x)", + moduleId, argSize, argAddr, returnValueAddr, optionAddr); + + // We should call the "stop" entry point and return the value in returnValueAddr. See StartModule. RETURN(0); } @@ -655,8 +734,8 @@ void sceKernelGetModuleIdByAddress() void sceKernelGetModuleId() { - ERROR_LOG(HLE,"UNIMPL sceKernelGetModuleId"); - RETURN(0); + ERROR_LOG(HLE,"sceKernelGetModuleId()"); + RETURN(__KernelGetCurThreadModuleId()); } void sceKernelFindModuleByName() @@ -665,15 +744,19 @@ void sceKernelFindModuleByName() RETURN(1); } - +u32 sceKernelLoadModuleByID(u32 id) { + ERROR_LOG(HLE,"UNIMPL sceKernelLoadModuleById(%08x)", id); + // Apparenty, ID is a sceIo File UID. So this shouldn't be too hard when needed. + return 0; +} const HLEFunction ModuleMgrForUser[] = { {0x977DE386,&WrapU_CU,"sceKernelLoadModule"}, - {0xb7f46618,0,"sceKernelLoadModuleByID"}, - {0x50F0C1EC,&sceKernelStartModule,"sceKernelStartModule"}, + {0xb7f46618,&WrapU_U,"sceKernelLoadModuleByID"}, + {0x50F0C1EC,&WrapV_UUUUU,"sceKernelStartModule"}, {0xD675EBB8,&sceKernelExitGame,"sceKernelSelfStopUnloadModule"}, //HACK - {0xd1ff982a,&sceKernelStopModule,"sceKernelStopModule"}, + {0xd1ff982a,&WrapV_UUUUU,"sceKernelStopModule"}, {0x2e0911aa,&sceKernelUnloadModule,"sceKernelUnloadModule"}, {0x710F61B5,0,"sceKernelLoadModuleMs"}, {0xF9275D98,0,"sceKernelLoadModuleBufferUsbWlan"}, ///??? diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index df4e7796f5..7770bb0676 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -242,6 +242,7 @@ public: ThreadWaitInfo waitInfo; bool sleeping; + SceUID moduleId; bool isProcessingCallbacks; @@ -354,12 +355,11 @@ void __KernelStartIdleThreads() } } -void _sceKernelIdle() +void __KernelIdle() { CoreTiming::Idle(); // Advance must happen between Idle and Reschedule, so that threads that were waiting for something // that was triggered at the end of the Idle period must get a chance to be scheduled. - // get a chance to be rescheduled. CoreTiming::Advance(); // In Advance, we might trigger an interrupt such as vblank. @@ -749,7 +749,7 @@ void ThreadContext::reset() lo = 0; } -Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr) +Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr) { Thread *t = new Thread; id = kernelObjects.Create(t); @@ -780,10 +780,11 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 t->nt.runForClocks.hi = 0; t->nt.wakeupCount = 0; t->isProcessingCallbacks = false; - if (moduleID) - t->nt.gpreg = __KernelGetModuleGP(moduleID); + if (moduleId) + t->nt.gpreg = __KernelGetModuleGP(moduleId); else t->nt.gpreg = 0; // sceKernelStartThread will take care of this. + t->moduleId = moduleId; strncpy(t->nt.name, name, 32); t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix @@ -806,8 +807,8 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr mipsr4k.r[MIPS_REG_SP] -= 256; u32 location = mipsr4k.r[MIPS_REG_SP]; mipsr4k.r[MIPS_REG_A1] = location; - for (int i=0; iGetUID(); } +SceUID __KernelGetCurThreadModuleId() +{ + return currentThread->moduleId; +} + + void sceKernelGetThreadId() { u32 retVal = currentThread->GetUID(); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 53a66b329c..0a93b52c51 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -102,7 +102,7 @@ void __KernelLoadContext(ThreadContext *ctx); // TODO: Replace this with __KernelResumeThread over time as it's misguided. bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch = false); -u32 __KernelResumeThread(SceUID threadID); // can return an error value +u32 __KernelResumeThreadFromWait(SceUID threadID); // can return an error value u32 __KernelGetWaitValue(SceUID threadID, u32 &error); void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, int timeout, bool processCallbacks); @@ -131,12 +131,13 @@ u32 __KernelUnregisterCallback(RegisteredCallbackType type, SceUID cbId); u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg); SceUID __KernelGetCurThread(); +SceUID __KernelGetCurThreadModuleId(); void __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution void __KernelStartIdleThreads(); void __KernelReturnFromThread(); // Called as HLE function u32 __KernelGetThreadPrio(SceUID id); -void _sceKernelIdle(); +void __KernelIdle(); u32 __KernelMipsCallReturnAddress(); u32 __KernelInterruptReturnAddress(); // TODO: remove @@ -159,7 +160,6 @@ bool __KernelInCallback(); bool __KernelCheckCallbacks(); class Thread; void __KernelSwitchContext(Thread *target, const char *reason); -u32 __KernelResumeThreadFromWait(SceUID threadID); bool __KernelExecutePendingMipsCalls(); void __KernelNotifyCallback(RegisteredCallbackType type, SceUID threadId, SceUID cbId, int notifyArg);