- Added function-based interface Audio for OSD sound output, together with SDL implementation

- Added object interface CThread for OSD threading and synchronization, together with SDL implementation
- Added multi-threading to CModel3 so that separate CPUs (PPC of main board, 68K of sound board and Z80 of drive board) can be run in separate threads if requested to improve performance on multi-core PCs
- Added -multi-threaded command line option (default is to run single-threaded still)
This commit is contained in:
Nik Henson 2011-07-20 21:14:00 +00:00
parent 1f73280282
commit 03fa9532eb
10 changed files with 1012 additions and 39 deletions

View file

@ -191,7 +191,6 @@
#include <string.h>
#include "Supermodel.h"
/******************************************************************************
Model 3 Inputs
@ -1226,7 +1225,7 @@ void CModel3::Write16(UINT32 addr, UINT16 data)
break;
}
DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
DebugLog("PC=%08X\twrite16 : %08X=%04X\n", ppc_get_pc(), addr, data);
break;
// MPC105/106
@ -1238,7 +1237,7 @@ void CModel3::Write16(UINT32 addr, UINT16 data)
// Unknown
default:
DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data);
DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data);
break;
}
}
@ -1865,6 +1864,244 @@ void CModel3::ClearNVRAM(void)
void CModel3::RunFrame(void)
{
// See if currently running multi-threaded
if (multiThreaded)
{
// If so, check all threads are up and running
if (!StartThreads())
goto ThreadError;
// Wake sound board and drive board threads so they can process a frame
#ifdef SUPERMODEL_DRIVEBOARD
if (!sndBrdThreadSync->Post() || !drvBrdThreadSync->Post())
#else
if (!sndBrdThreadSync->Post())
#endif
goto ThreadError;
// At the same time, process a single frame for main board (PPC) in this thread
RunMainBoardFrame();
// Enter notify wait critical section
if (!notifyLock->Lock())
goto ThreadError;
// Wait for sound board and drive board threads to finish their work (if they haven't done so already)
#ifdef SUPERMODEL_DRIVEBOARD
while (!sndBrdThreadDone || !drvBrdThreadDone)
#else
while (!sndBrdThreadDone)
#endif
{
if (!notifySync->Wait(notifyLock))
goto ThreadError;
}
sndBrdThreadDone = false;
#ifdef SUPERMODEL_DRIVEBOARD
drvBrdThreadDone = false;
#endif
// Leave notify wait critical section
if (!notifyLock->Unlock())
goto ThreadError;
}
else
{
// If not multi-threaded, then just process a single frame for main board, sound board and drive board in turn in this thread
RunMainBoardFrame();
SoundBoard.RunFrame();
#ifdef SUPERMODEL_DRIVEBOARD
DriveBoard.RunFrame();
#endif
}
// End frame
GPU.EndFrame();
TileGen.EndFrame();
IRQ.Assert(0x0D);
return;
ThreadError:
ErrorLog("Threading error in CModel3::RunFrame: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
multiThreaded = false;
}
bool CModel3::StartThreads()
{
if (startedThreads)
return true;
// Create synchronization objects
sndBrdThreadSync = CThread::CreateSemaphore(1);
if (sndBrdThreadSync == NULL)
goto ThreadError;
#ifdef SUPERMODEL_DRIVEBOARD
drvBrdThreadSync = CThread::CreateSemaphore(1);
if (drvBrdThreadSync == NULL)
goto ThreadError;
#endif
notifyLock = CThread::CreateMutex();
if (notifyLock == NULL)
goto ThreadError;
notifySync = CThread::CreateCondVar();
if (notifySync == NULL)
goto ThreadError;
// Create sound board thread
sndBrdThread = CThread::CreateThread(StartSoundBoardThread, this);
if (sndBrdThread == NULL)
goto ThreadError;
#ifdef SUPERMODEL_DRIVEBOARD
// Create drive board thread
drvBrdThread = CThread::CreateThread(StartDriveBoardThread, this);
if (drvBrdThread == NULL)
goto ThreadError;
#endif
startedThreads = true;
return true;
ThreadError:
ErrorLog("Unable to create threads and/or synchronization objects: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
DeleteThreadObjects();
multiThreaded = false;
return false;
}
void CModel3::StopThreads()
{
if (!startedThreads)
return;
DeleteThreadObjects();
startedThreads = false;
}
void CModel3::DeleteThreadObjects()
{
// Delete (which in turn kills) sound board and drive board threads
// Note that can do so here safely because threads will always be waiting on their semaphores when this method is called
if (sndBrdThread != NULL)
{
delete sndBrdThread;
sndBrdThread = NULL;
}
#ifdef SUPERMODEL_DRIVEBOARD
if (drvBrdThread != NULL)
{
delete drvBrdThread;
drvBrdThread = NULL;
}
#endif
// Delete synchronization objects
if (sndBrdThreadSync != NULL)
{
delete sndBrdThreadSync;
sndBrdThreadSync = NULL;
}
#ifdef SUPERMODEL_DRIVEBOARD
if (drvBrdThreadSync != NULL)
{
delete drvBrdThreadSync;
drvBrdThreadSync = NULL;
}
#endif
if (notifyLock != NULL)
{
delete notifyLock;
notifyLock = NULL;
}
if (notifySync != NULL)
{
delete notifySync;
notifySync = NULL;
}
}
int CModel3::StartSoundBoardThread(void *data)
{
// Call sound board thread method on CModel3
CModel3 *model3 = (CModel3*)data;
model3->RunSoundBoardThread();
return 0;
}
#ifdef SUPERMODEL_DRIVEBOARD
int CModel3::StartDriveBoardThread(void *data)
{
// Call drive board thread method on CModel3
CModel3 *model3 = (CModel3*)data;
model3->RunDriveBoardThread();
return 0;
}
#endif
void CModel3::RunSoundBoardThread()
{
for (;;)
{
// Wait on sound board thread semaphore
if (!sndBrdThreadSync->Wait())
goto ThreadError;
// Process a single frame for sound board
SoundBoard.RunFrame();
// Enter notify critical section
if (!notifyLock->Lock())
goto ThreadError;
// Let main thread know processing has finished
sndBrdThreadDone = true;
if (!notifySync->Signal())
goto ThreadError;
// Leave notify critical section
if (!notifyLock->Unlock())
goto ThreadError;
}
ThreadError:
ErrorLog("Threading error in sound board thread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
multiThreaded = false;
}
#ifdef SUPERMODEL_DRIVEBOARD
void CModel3::RunDriveBoardThread()
{
for (;;)
{
// Wait on drive board thread semaphore
if (!drvBrdThreadSync->Wait())
goto ThreadError;
// Process a single frame for drive board
//DriveBoard.RunFrame();
// Enter notify critical section
if (!notifyLock->Lock())
goto ThreadError;
// Let main thread know processing has finished
drvBrdThreadDone = true;
if (!notifySync->Signal())
goto ThreadError;
// Leave notify critical section
if (!notifyLock->Unlock())
goto ThreadError;
}
ThreadError:
ErrorLog("Threading error in drive board thread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
multiThreaded = false;
}
#endif
void CModel3::RunMainBoardFrame(void)
{
// Run the PowerPC for a frame
ppc_execute(ppcFrequency/60-10000);
@ -1891,14 +2128,7 @@ void CModel3::RunFrame(void)
break;
}
}
IRQ.Deassert(0x40);
SoundBoard.RunFrame();
// End frame
GPU.EndFrame();
TileGen.EndFrame();
IRQ.Assert(0x0D);
}
void CModel3::Reset(void)
@ -1932,6 +2162,9 @@ void CModel3::Reset(void)
TileGen.Reset();
GPU.Reset();
SoundBoard.Reset();
#ifdef SUPERMODEL_DRIVEBOARD
DriveBoard.Reset();
#endif
DebugLog("Model 3 reset\n");
}
@ -2289,7 +2522,7 @@ void CModel3::AttachInputs(CInputs *InputsPtr)
DebugLog("Model 3 attached inputs\n");
}
BOOL CModel3::Init(unsigned ppcFrequencyParam)
BOOL CModel3::Init(unsigned ppcFrequencyParam, BOOL multiThreadedParam)
{
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
@ -2297,6 +2530,7 @@ BOOL CModel3::Init(unsigned ppcFrequencyParam)
ppcFrequency = ppcFrequencyParam;
if (ppcFrequency < 1000000)
ppcFrequency = 1000000;
multiThreaded = !!multiThreadedParam;
// Allocate all memory for ROMs and PPC RAM
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
@ -2326,6 +2560,9 @@ BOOL CModel3::Init(unsigned ppcFrequencyParam)
return FAIL;
if (OKAY != SoundBoard.Init(soundROM,sampleROM,&IRQ,0x40))
return FAIL;
#ifdef SUPERMODEL_DRIVEBOARD
DriveBoard.Init();
#endif
PCIBridge.AttachPCIBus(&PCIBus);
PCIBus.AttachDevice(13,&GPU);
@ -2333,6 +2570,7 @@ BOOL CModel3::Init(unsigned ppcFrequencyParam)
PCIBus.AttachDevice(16,this);
DebugLog("Initialized Model 3 (allocated %1.1f MB)\n", memSizeMB);
return OKAY;
}
@ -2354,6 +2592,23 @@ CModel3::CModel3(void)
securityPtr = 0;
multiThreaded = true;
startedThreads = false;
sndBrdThread = NULL;
#ifdef SUPERMODEL_DRIVEBOARD
drvBrdThread = NULL;
#endif
sndBrdThreadDone = false;
#ifdef SUPERMODEL_DRIVEBOARD
drvBrdThreadDone = false;
#endif
sndBrdThreadSync = NULL;
#ifdef SUPERMODEL_DRIVEBOARD
drvBrdThreadSync = NULL;
#endif
notifyLock = NULL;
notifySync = NULL;
DebugLog("Built Model 3\n");
}
@ -2369,6 +2624,9 @@ CModel3::~CModel3(void)
//Dump("sampleROM", sampleROM, 0x800000, FALSE, TRUE);
#endif
// Stop all threads
StopThreads();
if (memoryPool != NULL)
{
delete [] memoryPool;

View file

@ -171,7 +171,7 @@ public:
* Runs one frame (assuming 60 Hz video refresh rate).
*/
void RunFrame(void);
/*
* Reset(void):
*
@ -224,7 +224,7 @@ public:
void AttachInputs(CInputs *InputsPtr);
/*
* Init(ppcFrequencyParam):
* Init(ppcFrequencyParam, multiThreadedParam):
*
* One-time initialization of the context. Must be called prior to all
* other members. Allocates memory and initializes device states.
@ -237,7 +237,7 @@ public:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Prints own error messages.
*/
BOOL Init(unsigned ppcFrequencyParam);
BOOL Init(unsigned ppcFrequencyParam, BOOL multiThreadedParam);
/*
* CModel3(void):
@ -250,13 +250,12 @@ public:
*/
CModel3(void);
~CModel3(void);
/*
* Private Property.
* Tresspassers will be shot! ;)
*/
private:
// Private member functions
UINT8 ReadInputs(unsigned reg);
void WriteInputs(unsigned reg, UINT8 data);
@ -266,12 +265,27 @@ private:
UINT8 ReadSystemRegister(unsigned reg);
void WriteSystemRegister(unsigned reg, UINT8 data);
void Patch(void);
void RunMainBoardFrame(); // Runs the main board (PPC) for a frame
bool StartThreads(); // Starts all threads
void StopThreads(); // Stops all threads
void DeleteThreadObjects(); // Deletes all threads and synchronization objects
static int StartSoundBoardThread(void *data); // Callback to start sound board thread
#ifdef SUPERMODEL_DRIVEBOARD
static int StartDriveBoardThread(void *data); // Callback to start drive board thread
#endif
void RunSoundBoardThread(); // Runs sound board thread
#ifdef SUPERMODEL_DRIVEBOARD
void RunDriveBoardThread(); // Runs drive board thread
#endif
// Game and hardware information
const struct GameInfo *Game;
// Game inputs
CInputs *Inputs;
CInputs *Inputs;
// Input registers (game controls)
UINT8 inputBank;
@ -302,6 +316,26 @@ private:
// PowerPC
PPC_FETCH_REGION PPCFetchRegions[3];
unsigned ppcFrequency; // clock frequency (Hz)
// Multiple threading
bool multiThreaded; // True if should run CPUs in multiple threads, otherwise everything is run in a single thread
bool startedThreads; // True if threads have been created and started
CThread *sndBrdThread; // Sound board thread
#ifdef SUPERMODEL_DRIVEBOARD
CThread *drvBrdThread; // Drive board thread
#endif
bool sndBrdThreadDone; // Flag to indicate sound board thread has finished processing for current frame
#ifdef SUPERMODEL_DRIVEBOARD
bool drvBrdThreadDone; // Flag to indicate drive board thread has finished processing for current frame
#endif
// Thread synchronization objects
CSemaphore *sndBrdThreadSync;
#ifdef SUPERMODEL_DRIVEBOARD
CSemaphore *drvBrdThreadSync;
#endif
CMutex *notifyLock;
CCondVar *notifySync;
// Other devices
CIRQ IRQ; // Model 3 IRQ controller
@ -312,7 +346,10 @@ private:
C93C46 EEPROM; // 93C46 EEPROM
CTileGen TileGen; // Sega 2D tile generator
CReal3D GPU; // Real3D graphics hardware
CSoundBoard SoundBoard; // sound board
CSoundBoard SoundBoard; // Sound board
#ifdef SUPERMODEL_DRIVEBOARD
CDriveBoard DriveBoard; // Drive board
#endif
};

View file

@ -346,7 +346,10 @@ void CSoundBoard::RunFrame(void)
{
#ifdef SUPERMODEL_SOUND
SCSP_Update();
// Output the audio buffers
OutputAudio(44100/60, leftBuffer, rightBuffer);
// Output to binary file
INT16 s;
for (int i = 0; i < 44100/60; i++)

31
Src/OSD/Audio.h Executable file
View file

@ -0,0 +1,31 @@
#ifndef INCLUDED_AUDIO_H
#define INCLUDED_AUDIO_H
/*
* Audio.h
*
* Function-based interface for audio output.
*/
/*
* OpenAudio()
*
* Initializes the audio system.
*/
extern BOOL OpenAudio();
/*
* OutputAudio(unsigned numSamples, *INT16 leftBuffer, *INT16 rightBuffer)
*
* Sends a chunk of two-channel audio with the given number of samples to the audio system.
*/
extern void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer);
/*
* CloseAudio()
*
* Shuts down the audio system.
*/
extern void CloseAudio();
#endif // INCLUDED_AUDIO_H

294
Src/OSD/SDL/Audio.cpp Executable file
View file

@ -0,0 +1,294 @@
#include "Supermodel.h"
#ifdef SUPERMODEL_OSX
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#else
#include <SDL.h>
#include <SDL_audio.h>
#endif
#include <math.h>
// Model3 audio output is 44.1KHz 2-channel sound and frame rate is 60fps
#define SAMPLE_RATE 44100
#define NUM_CHANNELS 2
#define SUPERMODEL_FPS 60
#define BYTES_PER_SAMPLE (NUM_CHANNELS * sizeof(INT16))
#define SAMPLES_PER_FRAME (SAMPLE_RATE / SUPERMODEL_FPS)
#define BYTES_PER_FRAME (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE)
#define MAX_LATENCY 100
static bool enabled = true; // True if sound output is enabled
static unsigned latency = 20; // Audio latency to use (ie size of audio buffer) as percentage of max buffer size
static unsigned playSamples = 512; // Size (in samples) of callback play buffer
static UINT32 audioBufferSize = 0; // Size (in bytes) of audio buffer
static INT8 *audioBuffer = NULL; // Audio buffer
static UINT32 writePos = 0; // Current position at which writing into buffer
static UINT32 playPos = 0; // Current position at which playing data in buffer via callback
static bool writeWrapped = false; // True if write position has wrapped around at end of buffer but play position has not done so yet
static unsigned underRuns = 0; // Number of buffer under-runs that have occured
static unsigned overRuns = 0; // Number of buffer over-runs that have occured
static void PlayCallback(void *data, Uint8 *stream, int len)
{
//printf("PlayCallback(%d)\n", len);
// Get current write position and adjust it if write has wrapped
UINT32 adjWritePos = writePos;
if (writeWrapped)
adjWritePos += audioBufferSize;
// Check if play position overlaps write position (ie buffer under-run)
if (adjWritePos < playPos + len)
{
// If so, just copy silence to audio output stream and exit
memset(stream, 0, len);
//printf("Audio buffer under-run in PlayCallback\n");
underRuns++;
return;
}
INT8* src1;
INT8* src2;
UINT32 len1;
UINT32 len2;
// Check if play region extends past end of buffer
if (playPos + len > audioBufferSize)
{
// If so, split play region into two
src1 = audioBuffer + playPos;
src2 = audioBuffer;
len1 = audioBufferSize - playPos;
len2 = len - len1;
}
else
{
// Otherwise, just copy whole region
src1 = audioBuffer + playPos;
src2 = 0;
len1 = len;
len2 = 0;
}
// Check if audio is enabled
if (enabled)
{
// If so, copy play region into audio output stream and blank region out afterwards
memcpy(stream, src1, len1);
memset(src1, 0, len1);
if (len2)
{
// If region was split into two, copy second half into audio output stream as well and blank region out afterwards
memcpy(stream + len1, src2, len2);
memset(src2, 0, len2);
}
}
else
// Otherwise, just copy silence to audio output stream
memset(stream, 0, len);
// Move play position forward for next time
playPos += len;
// Check if play position has moved past end of buffer
if (playPos >= audioBufferSize)
{
// If so, wrap it around to beginning again and reset write wrap flag
playPos -= audioBufferSize;
writeWrapped = false;
}
}
static void MixChannels(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer, void *dest)
{
INT16 *p = (INT16*)dest;
for (unsigned i = 0; i < numSamples; i++)
{
#if (NUM_CHANNELS == 1)
*p++ = leftBuffer[i] + rightBuffer[i];
#else
*p++ = rightBuffer[i];
*p++ = leftBuffer[i];
#endif
}
}
BOOL OpenAudio()
{
// Initialize SDL audio sub-system
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
{
ErrorLog("Unable to initialize SDL audio sub-system: %s\n", SDL_GetError());
return FAIL;
}
// Set up audio specification
SDL_AudioSpec fmt;
memset(&fmt, 0, sizeof(SDL_AudioSpec));
fmt.freq = SAMPLE_RATE;
fmt.channels = NUM_CHANNELS;
fmt.format = AUDIO_S16SYS;
fmt.samples = playSamples;
fmt.callback = PlayCallback;
// Try opening SDL audio output with that specification
SDL_AudioSpec obtained;
if (SDL_OpenAudio(&fmt, &obtained) < 0)
{
ErrorLog("Unable to open 44.1KHz 2-channel audio with SDL: %s\n", SDL_GetError());
return FAIL;
}
// Check what buffer sample size was actually obtained, and use that
playSamples = obtained.samples;
// Create audio buffer
audioBufferSize = SAMPLE_RATE * BYTES_PER_SAMPLE * latency / MAX_LATENCY;
int roundBuffer = 2 * playSamples;
audioBufferSize = max<int>(roundBuffer, (audioBufferSize / roundBuffer) * roundBuffer);
audioBuffer = new INT8[audioBufferSize];
memset(audioBuffer, 0, sizeof(INT8) * audioBufferSize);
// Set initial play position to be beginning of buffer and initial write position to be half-way into buffer
playPos = 0;
writePos = (BYTES_PER_FRAME + audioBufferSize) / 2;
writeWrapped = false;
// Reset counters
underRuns = 0;
overRuns = 0;
// Start audio playing
SDL_PauseAudio(0);
return OKAY;
}
void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer)
{
//printf("OutputAudio(%u)\n", numSamples);
// Number of samples should never be more than max number of samples per frame
if (numSamples > SAMPLES_PER_FRAME)
numSamples = SAMPLES_PER_FRAME;
// Mix together left and right channels into single chunk of data
INT16 mixBuffer[NUM_CHANNELS * SAMPLES_PER_FRAME];
MixChannels(numSamples, leftBuffer, rightBuffer, mixBuffer);
// Lock SDL audio callback so that it doesn't interfere with following code
SDL_LockAudio();
// Calculate number of bytes for current sound chunk
UINT32 numBytes = numSamples * BYTES_PER_SAMPLE;
// Get end of current play region (writing must occur past this point)
UINT32 playEndPos = playPos + BYTES_PER_FRAME;
// Undo any wrap-around of the write position that may have occured to create following ordering: playPos < playEndPos < writePos
if (writePos < playEndPos && writeWrapped)
writePos += audioBufferSize;
// If play region has caught up with write position and now overlaps it (ie buffer under-run), then bump write position forward in chunks
// until it is past end of play region
while (writePos < playEndPos)
{
//printf("Audio buffer under-run in OutputAudio\n");
underRuns++;
writePos += numBytes;
}
// If write position has caught up with play region and now overlaps it (ie buffer over-run), then discard current chunk of data
if (writePos + numBytes > playPos + audioBufferSize)
{
//printf("Audio buffer over-run in OutputAudio\n");
overRuns++;
goto Finish;
}
// Check if write position has moved past end of buffer
if (writePos >= audioBufferSize)
{
// If so, wrap it around to beginning again and set write wrap flag
writePos -= audioBufferSize;
writeWrapped = true;
}
INT16 *src = mixBuffer;
INT8 *dst1;
INT8 *dst2;
UINT32 len1;
UINT32 len2;
// Check if write region extends past end of buffer
if (writePos + numBytes > audioBufferSize)
{
// If so, split write region into two
dst1 = audioBuffer + writePos;
dst2 = audioBuffer;
len1 = audioBufferSize - writePos;
len2 = numBytes - len1;
}
else
{
// Otherwise, just copy whole region
dst1 = audioBuffer + writePos;
dst2 = 0;
len1 = numBytes;
len2 = 0;
}
// Copy chunk to write position in buffer
UINT32 bytesRemaining = numBytes;
UINT32 bytesToCopy = (bytesRemaining > len1 ? len1 : bytesRemaining);
memcpy(dst1, src, bytesToCopy);
// Adjust for number of bytes copied
bytesRemaining -= bytesToCopy;
src = (INT16*)((UINT8*)src + bytesToCopy);
if (bytesRemaining)
{
// If write region was split into two, copy second half of chunk into buffer as well
bytesToCopy = (bytesRemaining > len2 ? len2 : bytesRemaining);
memcpy(dst2, src, bytesToCopy);
}
// Move write position forward for next time
writePos += numBytes;
// Check if write position has moved past end of buffer
if (writePos >= audioBufferSize)
{
// If so wrap it around to beginning again and set write wrap flag
writePos -= audioBufferSize;
writeWrapped = true;
}
Finish:
// Unlock SDL audio callback
SDL_UnlockAudio();
}
void CloseAudio()
{
// Close SDL audio output
SDL_CloseAudio();
// Delete audio buffer
if (audioBuffer != NULL)
{
delete[] audioBuffer;
audioBuffer = NULL;
}
}

View file

@ -54,7 +54,7 @@
#include "Supermodel.h"
#include "SDLInputSystem.h"
#ifdef SUPERMODEL_WIN32
#include "OSD/Windows/DirectInputSystem.h"
#include "DirectInputSystem.h"
#endif
CLogger *GetLogger();
@ -361,12 +361,12 @@ static void LoadNVRAM(CModel3 *Model3)
******************************************************************************/
#ifdef SUPERMODEL_DEBUGGER
int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, unsigned ppcFrequency,
int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, unsigned ppcFrequency, BOOL multiThreaded,
unsigned xResParam, unsigned yResParam, BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS,
const char *vsFile, const char *fsFile)
{
#else
int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, unsigned xResParam, unsigned yResParam,
int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL multiThreaded, unsigned xResParam, unsigned yResParam,
BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS, const char *vsFile, const char *fsFile)
{
CModel3 *Model3 = new CModel3();
@ -386,7 +386,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, unsi
InfoLog("Frame rate limiting: %s", noThrottle?"Disabled":"Enabled");
// Initialize and load ROMs
Model3->Init(ppcFrequency);
Model3->Init(ppcFrequency, multiThreaded);
if (OKAY != Model3->LoadROMSet(Model3GameList, zipFile))
return 1;
@ -399,7 +399,11 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, unsi
sprintf(titleStr, "Supermodel - %s", Model3->GetGameInfo()->title);
if (OKAY != CreateGLScreen(titleStr,&xOffset,&yOffset,&xRes,&yRes,keepAspectRatio,fullScreen))
return 1;
// Initialize audio system
if (OKAY != OpenAudio())
return 1;
// Hide mouse if fullscreen
Inputs->GetInputSystem()->SetMouseVisibility(!fullScreen);
@ -606,6 +610,9 @@ int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, unsi
// Save NVRAM
SaveNVRAM(Model3);
// Close audio
CloseAudio();
// Shut down
#ifndef SUPERMODEL_DEBUGGER
delete Model3;
@ -1002,6 +1009,7 @@ static void Help(void)
puts("");
puts("Emulation Options:");
puts(" -ppc-frequency=<f> Set PowerPC frequency in MHz [Default: 25]");
puts(" -multi-threaded Enable multi-threading");
#ifdef SUPERMODEL_DEBUGGER
puts(" -disable-debugger Completely disable debugger functionality");
puts(" -enter-debugger Enter debugger at start of emulation");
@ -1055,7 +1063,7 @@ static void PrintGameList(void)
int main(int argc, char **argv)
{
int i, ret;
int cmd=0, fileIdx=0, cmdFullScreen=0, cmdNoThrottle=0, cmdShowFPS=0, cmdPrintInputs=0, cmdConfigInputs=0, cmdPrintGames=0, cmdDis=0, cmdPrintGLInfo=0;
int cmd=0, fileIdx=0, cmdMultiThreaded=0, cmdFullScreen=0, cmdNoThrottle=0, cmdShowFPS=0, cmdPrintInputs=0, cmdConfigInputs=0, cmdPrintGames=0, cmdDis=0, cmdPrintGLInfo=0;
#ifdef SUPERMODEL_DEBUGGER
int cmdDisableDebugger = 0, cmdEnterDebugger=0;
#endif // SUPERMODEL_DEBUGGER
@ -1099,6 +1107,8 @@ int main(int argc, char **argv)
ppcFrequency = f*1000000;
}
}
else if (!strncmp(argv[i],"-multi-threaded", 16))
cmd = cmdMultiThreaded = 1;
#ifdef SUPERMODEL_DEBUGGER
else if (!strncmp(argv[i],"-disable-debugger",17))
cmd = cmdDisableDebugger = 1;
@ -1268,13 +1278,13 @@ int main(int argc, char **argv)
Debugger->ForceBreak(true);
}
// Fire up Supermodel with debugger
exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,ppcFrequency,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
if (Debugger != NULL)
delete Debugger;
delete Model3;
#else
// Fire up Supermodel
exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
#endif // SUPERMODEL_DEBUGGER
Exit:

143
Src/OSD/SDL/Thread.cpp Executable file
View file

@ -0,0 +1,143 @@
#include "Supermodel.h"
#ifdef SUPERMODEL_OSX
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#else
#include <SDL.h>
#include <SDL_thread.h>
#endif
CThread *CThread::CreateThread(ThreadStart start, void *startParam)
{
SDL_Thread *impl = SDL_CreateThread(start, startParam);
if (impl == NULL)
return NULL;
return new CThread(impl);
}
CSemaphore *CThread::CreateSemaphore(UINT32 initVal)
{
SDL_sem *impl = SDL_CreateSemaphore(initVal);
if (impl == NULL)
return NULL;
return new CSemaphore(impl);
}
CCondVar *CThread::CreateCondVar()
{
SDL_cond *impl = SDL_CreateCond();
if (impl == NULL)
return NULL;
return new CCondVar(impl);
}
CMutex *CThread::CreateMutex()
{
SDL_mutex *impl = SDL_CreateMutex();
if (impl == NULL)
return NULL;
return new CMutex(impl);
}
const char *CThread::GetLastError()
{
return SDL_GetError();
}
CThread::CThread(void *impl) : m_impl(impl)
{
//
}
CThread::~CThread()
{
Kill();
}
UINT32 CThread::GetId()
{
return SDL_GetThreadID((SDL_Thread*)m_impl);
}
void CThread::Kill()
{
if (m_impl != NULL)
SDL_KillThread((SDL_Thread*)m_impl);
m_impl = NULL;
}
int CThread::Wait()
{
int status;
if (m_impl == NULL)
return -1;
SDL_WaitThread((SDL_Thread*)m_impl, &status);
m_impl = NULL;
return status;
}
CSemaphore::CSemaphore(void *impl) : m_impl(impl)
{
//
}
CSemaphore::~CSemaphore()
{
SDL_DestroySemaphore((SDL_sem*)m_impl);
}
UINT32 CSemaphore::GetValue()
{
return SDL_SemValue((SDL_sem*)m_impl);
}
bool CSemaphore::Wait()
{
return SDL_SemWait((SDL_sem*)m_impl) == 0;
}
bool CSemaphore::Post()
{
return SDL_SemPost((SDL_sem*)m_impl) == 0;
}
CCondVar::CCondVar(void *impl) : m_impl(impl)
{
//
}
CCondVar::~CCondVar()
{
SDL_DestroyCond((SDL_cond*)m_impl);
}
bool CCondVar::Wait(CMutex *mutex)
{
return SDL_CondWait((SDL_cond*)m_impl, (SDL_mutex*)mutex->m_impl) == 0;
}
bool CCondVar::Signal()
{
return SDL_CondSignal((SDL_cond*)m_impl) == 0;
}
CMutex::CMutex(void *impl) : m_impl(impl)
{
//
}
CMutex::~CMutex()
{
SDL_DestroyMutex((SDL_mutex*)m_impl);
}
bool CMutex::Lock()
{
return SDL_mutexP((SDL_mutex*)m_impl) == 0;
}
bool CMutex::Unlock()
{
return SDL_mutexV((SDL_mutex*)m_impl) == 0;
}

196
Src/OSD/Thread.h Executable file
View file

@ -0,0 +1,196 @@
#ifndef INCLUDED_THREADS_H
#define INCLUDED_THREADS_H
class CSemaphore;
class CMutex;
class CCondVar;
typedef int (*ThreadStart)(void *startParam);
/*
* CThread
*
* Class that represents an O/S thread.
*/
class CThread
{
private:
void *m_impl;
CThread(void *impl);
public:
/*
* CreateThread
*
* Creates a new thread with the given ThreadStart callback and start parameter. The thread starts running immediately.
*/
static CThread *CreateThread(ThreadStart start, void *startParam);
/*
* CreateSemaphore
*
* Creates a new semaphore with the given initial starting value.
*/
static CSemaphore *CreateSemaphore(UINT32 initVal);
/*
* CreateCondVar
*
* Creates a new condition variable.
*/
static CCondVar *CreateCondVar();
/*
* CreateMutex
*
* Creates a new mutex.
*/
static CMutex *CreateMutex();
/*
* GetLastError
*
* Returns the error message for the last error.
*/
static const char *GetLastError();
/*
* Thread destructor.
*/
~CThread();
/*
* GetId
*
* Returns the id of this thread.
*/
UINT32 GetId();
/*
* Kill
*
* Kills this thread.
*/
void Kill();
/*
* Wait
*
* Waits until this thread has exited.
*/
int Wait();
};
/*
* CSemaphore
*
* Class that represents a semaphore.
*/
class CSemaphore
{
friend class CThread;
private:
void *m_impl;
CSemaphore(void *impl);
public:
~CSemaphore();
/*
* GetValue
*
* Returns the current value of this semaphore.
*/
UINT32 GetValue();
/*
* Wait
*
* Locks this semaphore and suspends the calling thread if its value is zero.
*/
bool Wait();
/*
* Post
*
* Unlocks this semaphore and resumes any threads that were blocked on it.
*/
bool Post();
};
/*
* CSemaphore
*
* Class that represents a condition variable.
*/
class CCondVar
{
friend class CThread;
private:
void *m_impl;
CCondVar(void *impl);
public:
~CCondVar();
/*
* Wait
*
* Waits on this condition variable and unlocks the provided mutex (which must be locked before calling this method).
*/
bool Wait(CMutex *mutex);
/*
* Signal
*
* Restarts a single thread that is waiting on this condition variable.
*/
bool Signal();
/*
* SignalAll
*
* Restarts all threads that are waiting on this condition variable.
*/
bool SignalAll();
};
/*
* CSemaphore
*
* Class that represents a mutex.
*/
class CMutex
{
friend class CThread;
friend class CCondVar;
private:
void *m_impl;
CMutex(void *impl);
public:
~CMutex();
/*
* Lock
*
* Locks this mutex. If it is already locked then the calling thread will suspend until it is unlocked.
*/
bool Lock();
/*
* Unlock
*
* Unlocks this mutex.
*/
bool Unlock();
};
#endif // INCLUDED_THREADS_H

View file

@ -173,23 +173,23 @@ bool IsXInputDevice(const GUID &devProdGUID)
bool isXInpDev = false;
HRESULT hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
if (FAILED(hr) || pIWbemLocator == NULL)
goto exit;
goto Finish;
if ((bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2")) == NULL) goto exit;
if ((bstrClassName = SysAllocString(L"Win32_PNPEntity")) == NULL) goto exit;
if ((bstrDeviceID = SysAllocString(L"DeviceID")) == NULL) goto exit;
if ((bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2")) == NULL) goto Finish;
if ((bstrClassName = SysAllocString(L"Win32_PNPEntity")) == NULL) goto Finish;
if ((bstrDeviceID = SysAllocString(L"DeviceID")) == NULL) goto Finish;
// Connect to WMI
hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices);
if (FAILED(hr) || pIWbemServices == NULL)
goto exit;
goto Finish;
// Switch security level to IMPERSONATE
CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices);
if (FAILED(hr) || pEnumDevices == NULL)
goto exit;
goto Finish;
// Loop over all devices
for (;;)
@ -198,7 +198,7 @@ bool IsXInputDevice(const GUID &devProdGUID)
DWORD uReturned;
hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned);
if (FAILED(hr) || uReturned == 0)
goto exit;
goto Finish;
for (unsigned devNum = 0; devNum < uReturned; devNum++)
{
@ -224,7 +224,7 @@ bool IsXInputDevice(const GUID &devProdGUID)
if (dwVidPid == devProdGUID.Data1)
{
isXInpDev = true;
goto exit;
goto Finish;
}
}
}
@ -236,7 +236,7 @@ bool IsXInputDevice(const GUID &devProdGUID)
}
}
exit:
Finish:
if (bstrNamespace)
SysFreeString(bstrNamespace);
if (bstrDeviceID)

View file

@ -70,6 +70,10 @@
*/
#include "Types.h"
// OSD Interfaces
#include "Thread.h"
#include "Audio.h"
/*
* Error and Debug Logging
*/
@ -139,9 +143,6 @@ extern void InfoLog(const char *fmt, ...);
#ifdef SUPERMODEL_DEBUGGER
#include "Debugger/SupermodelDebugger.h"
#include "Debugger/CPU/PPCDebug.h"
#ifdef SUPERMODEL_SOUND
#include "Debugger/CPU/68KDebug.h"
#endif // SUPERMODEL_SOUND
#endif // SUPERMODEL_DEBUGGER
#include "CPU/Bus.h"
#include "CPU/PowerPC/PPCDisasm.h"