mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-06 21:47:44 +00:00
Rewrite audio line output. Now deterministic (from the game's POV) and smoother.
This commit is contained in:
parent
ba171c8360
commit
5a48578a12
@ -25,68 +25,83 @@
|
||||
|
||||
// Not fully featured, no safety checking yet. Add features as needed.
|
||||
|
||||
// TODO: "inline" storage?
|
||||
|
||||
template <class T, int N>
|
||||
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_
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "StdMutex.h"
|
||||
#include "CoreTiming.h"
|
||||
#include "Core.h"
|
||||
#include "HLE/sceKernelThread.h"
|
||||
|
||||
int CPU_HZ = 222000000;
|
||||
|
||||
|
@ -178,6 +178,10 @@ template<u32 func(u32, u32, u32, u32, u32)> void WrapU_UUUUU() {
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<void func(u32, u32, u32, u32, u32)> void WrapV_UUUUU() {
|
||||
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
||||
}
|
||||
|
||||
template<int func(const char *, int, u32, int, u32)> void WrapU_CIUIU() {
|
||||
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
||||
RETURN(retval);
|
||||
|
@ -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[] =
|
||||
|
@ -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<s16, hwBlockSize * 8> 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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>, "sceAudioSetFrequency"},
|
||||
{0xE0727056, 0, "sceAudioSRCOutputBlocking"},
|
||||
{0xE926D3FB, 0, "sceAudioInputInitEx"},
|
||||
{0x8c1009b2, 0, "sceAudioOutput"},
|
||||
{0x8c1009b2, WrapU_UUU<sceAudioOutput>, "sceAudioOutput"},
|
||||
{0x136CAF51, WrapV_UUU<sceAudioOutputBlocking>, "sceAudioOutputBlocking"},
|
||||
{0xE2D56B2D, WrapU_UUUU<sceAudioOutputPanned>, "sceAudioOutputPanned"},
|
||||
{0x13F592BC, WrapV_UUUU<sceAudioOutputPannedBlocking>, "sceAudioOutputPannedBlocking"}, //(u32, u32, u32, void *)Output sound, blocking
|
||||
{0x5EC81C55, WrapU_UUU<sceAudioChReserve>, "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>, "sceAudioChRelease"}, //(long handle)Terminate channel and deallocate buffer //free buffer?
|
||||
{0xE9D97901, sceAudioGetChannelRestLen, "sceAudioGetChannelRestLen"},
|
||||
{0xCB2E439E, WrapU_UU<sceAudioSetChannelDataLen>, "sceAudioSetChannelDataLen"}, //(u32, u32)
|
||||
{0xE9D97901, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLen"},
|
||||
{0xB011922F, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLength"}, // Is there a difference between this and sceAudioGetChannelRestLen?
|
||||
{0xCB2E439E, WrapU_UU<sceAudioSetChannelDataLen>, "sceAudioSetChannelDataLen"}, //(u32, u32)
|
||||
{0x95FD0C2D, WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig"},
|
||||
{0xB7E1D8E7, WrapU_UUU<sceAudioChangeChannelVolume>, "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"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <queue>
|
||||
|
||||
#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<s16, 16384> sampleQueue;
|
||||
FixedSizeQueue<s16, 32768> sampleQueue;
|
||||
|
||||
|
||||
void clear() {
|
||||
reserved = false;
|
||||
running = false;
|
||||
waitingThread = 0;
|
||||
leftVolume = 0;
|
||||
rightVolume = 0;
|
||||
format = 0;
|
||||
sampleAddress = 0;
|
||||
sampleCount = 0;
|
||||
sampleQueue.clear();
|
||||
}
|
||||
};
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#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>(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>(module,error);
|
||||
if (m)
|
||||
Module *module = kernelObjects.Get<Module>(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<Module>(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>,"sceKernelLoadModule"},
|
||||
{0xb7f46618,0,"sceKernelLoadModuleByID"},
|
||||
{0x50F0C1EC,&sceKernelStartModule,"sceKernelStartModule"},
|
||||
{0xb7f46618,&WrapU_U<sceKernelLoadModuleByID>,"sceKernelLoadModuleByID"},
|
||||
{0x50F0C1EC,&WrapV_UUUUU<sceKernelStartModule>,"sceKernelStartModule"},
|
||||
{0xD675EBB8,&sceKernelExitGame,"sceKernelSelfStopUnloadModule"}, //HACK
|
||||
{0xd1ff982a,&sceKernelStopModule,"sceKernelStopModule"},
|
||||
{0xd1ff982a,&WrapV_UUUUU<sceKernelStopModule>,"sceKernelStopModule"},
|
||||
{0x2e0911aa,&sceKernelUnloadModule,"sceKernelUnloadModule"},
|
||||
{0x710F61B5,0,"sceKernelLoadModuleMs"},
|
||||
{0xF9275D98,0,"sceKernelLoadModuleBufferUsbWlan"}, ///???
|
||||
|
@ -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; i<args; i++)
|
||||
Memory::Write_U8(argp[i], location+i);
|
||||
for (int i = 0; i < args; i++)
|
||||
Memory::Write_U8(argp[i], location + i);
|
||||
}
|
||||
|
||||
|
||||
@ -1072,6 +1073,12 @@ SceUID __KernelGetCurThread()
|
||||
return currentThread->GetUID();
|
||||
}
|
||||
|
||||
SceUID __KernelGetCurThreadModuleId()
|
||||
{
|
||||
return currentThread->moduleId;
|
||||
}
|
||||
|
||||
|
||||
void sceKernelGetThreadId()
|
||||
{
|
||||
u32 retVal = currentThread->GetUID();
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user