Refactor speaker system to prevent audio glitches and to support CPU automatic speed switching

Squashed commit of the following:

    REFACTOR : fix comments, logging, and rename some variables
    REFACTOR : fix up some commentary, clean deadc0de
    REFACTOR : mostly fix all the audio glitches
        - amplitudes of samples are gradually shifted to zero when speaker has fallen silent
        - simplifies speaker state machine
        - fullspeed mode only enqueues quiet samples
    REFACTOR : fix up a number of other functions and comments
    REFACTOR : clean up code to submit normal speed wave buffer to OpenAL
    Move some initializations to the cpu_thread()
    REFACTOR : properly reset the speaker cycles access counter so we don't underflow and assert
    REFACTOR : should never get a split buffer from our soundcore implementation
    Fix warning from gcc ... static array size needs to be computed from integer values
    REFACTOR : gcc (but not clang) complains about these, so just make them preprocessor defines
    REFACTOR : use unsigned long long because we don't actually care that this counter is 64bit
    REFACTOR : remainder_buffer and miscellaneous tweaks
        - Adds implementation commentary to document remainder_buffer purpose
        - Also adds sample average for square wave boundary in case where remainder_buffer not used (whole-sample boundary)
        - Variable renaming and code shuffling
    REFACTOR : do not dynamically alloc remainder buffer
        - Never attribute to cache-coherancy bugs what is a simple thread race =P
    REFACTOR : comments and whitespace
    REFACTOR : rename public speaker API functions
    REFACTOR : clean up public speaker API
    REFACTOR : tabs to spaces
    REFACTOR : moar deadc0de clean up and renaming
    REFACTOR : remove deadc0de paths from soundcore
    REFACTOR : fully excise soundtype stuff now that we only support soundcard output
    Move a file static to function scope
    REFACTOR : rename more variables and remove deadc0de
    REFACTOR : samples_buffer naming and change to explict int16_t
    REFACTOR : removed deadc0de and shuffled code locations
    REFACTOR : remainder buffer naming and clarify type
    REFACTOR : move joystick timing to VM module and remove header visibility
    REFACTOR : clarify speaker variable name
    REFACTOR : clarify cycle counting codepaths
    REFACTOR : VBL/timing interfaces
        - eliminates passing around a common global
    REFACTOR : names and comments
    HACK around volume issue
    REFACTOR : rename speaker feedback variable
    REFACTOR : rename global total cycle count
    REFACTOR : rename a constant
    Fix test builds
    REFACTOR: rename to is_fullspeed
    REFACTOR : local variable naming changes
    REFACTOR : migrate cycle timing variables to correct location and remove header visibility
    Allow fullspeed codepath to update speaker
    REFACTOR : remove deadc0de paths in prep for cleanup
    REFACTOR : speaker now manages its own VM entry point
This commit is contained in:
Aaron Culliney 2015-01-31 13:57:10 -08:00
parent 780905155a
commit f234de116f
22 changed files with 861 additions and 2279 deletions

View File

@ -226,7 +226,7 @@ AC_ARG_ENABLE([audio], AS_HELP_STRING([--disable-audio], [Disable emulator audio
dnl found OpenAL ...
AC_DEFINE(AUDIO_OPENAL, 1, [Enable OpenAL audio output])
AC_DEFINE(AUDIO_ENABLED, 1, [Enable sound module])
AUDIO_GLUE_C="src/audio/mockingboard.c"
AUDIO_GLUE_C="src/audio/speaker.c src/audio/mockingboard.c"
AUDIO_O="src/audio/soundcore.o src/audio/soundcore-openal.o src/audio/speaker.o src/audio/win-shim.o src/audio/alhelpers.o src/audio/mockingboard.o src/audio/AY8910.o"
], [
AC_MSG_WARN([Could not find OpenAL libraries, sound will be disabled])

View File

@ -137,7 +137,7 @@ void CAY8910_init(CAY8910 *_this)
_this->env_first = 1;
_this->env_rev = 0;
_this->env_counter = 15;
//m_fCurrentCLK_AY8910 = g_fCurrentCLK6502; -- believe this is handled by an initial call to SetCLK()
//m_fCurrentCLK_AY8910 = cycles_persec_target; -- believe this is handled by an initial call to SetCLK()
};
@ -983,7 +983,7 @@ void SetCLK(double CLK)
// AY8910 interface
#ifndef APPLE2IX
#include "CPU.h" // For g_nCumulativeCycles
#include "CPU.h" // For cycles_count_total
#endif
static CAY8910 g_AY8910[MAX_8910];
@ -996,7 +996,7 @@ static unsigned __int64 g_uLastCumulativeCycles = 0;
void _AYWriteReg(int chip, int r, int v)
{
libspectrum_dword uOffset = (libspectrum_dword) (g_nCumulativeCycles - g_uLastCumulativeCycles);
libspectrum_dword uOffset = (libspectrum_dword) (cycles_count_total - g_uLastCumulativeCycles);
sound_ay_write(&g_AY8910[chip], r, v, uOffset);
}
@ -1008,7 +1008,7 @@ void AY8910_reset(int chip)
void AY8910UpdateSetCycles()
{
g_uLastCumulativeCycles = g_nCumulativeCycles;
g_uLastCumulativeCycles = cycles_count_total;
}
void AY8910Update(int chip, INT16** buffer, int nNumSamples)

View File

@ -822,13 +822,13 @@ static void MB_Update()
return;
#endif
if (g_bFullSpeed)
if (is_fullspeed)
{
// Keep AY reg writes relative to the current 'frame'
// - Required for Ultima3:
// . Tune ends
// . g_bFullSpeed:=true (disk-spinning) for ~50 frames
// . U3 sets AY_ENABLE:=0xFF (as a side-effect, this sets g_bFullSpeed:=false)
// . is_fullspeed:=true (disk-spinning) for ~50 frames
// . U3 sets AY_ENABLE:=0xFF (as a side-effect, this sets is_fullspeed:=false)
// o Without this, the write to AY_ENABLE gets ignored (since AY8910's /g_uLastCumulativeCycles/ was last set 50 frame ago)
AY8910UpdateSetCycles();
@ -844,12 +844,12 @@ static void MB_Update()
{
if(!g_nMB_InActiveCycleCount)
{
g_nMB_InActiveCycleCount = g_nCumulativeCycles;
g_nMB_InActiveCycleCount = cycles_count_total;
}
#ifdef APPLE2IX
else if(g_nCumulativeCycles - g_nMB_InActiveCycleCount > (uint64_t)g_fCurrentCLK6502/10)
else if(cycles_count_total - g_nMB_InActiveCycleCount > (uint64_t)cycles_persec_target/10)
#else
else if(g_nCumulativeCycles - g_nMB_InActiveCycleCount > (unsigned __int64)g_fCurrentCLK6502/10)
else if(cycles_count_total - g_nMB_InActiveCycleCount > (unsigned __int64)cycles_persec_target/10)
#endif
{
// After 0.1 sec of Apple time, assume MB is not active
@ -872,7 +872,7 @@ static void MB_Update()
const double n6522TimerPeriod = MB_GetFramePeriod();
const double nIrqFreq = g_fCurrentCLK6502 / n6522TimerPeriod + 0.5; // Round-up
const double nIrqFreq = cycles_persec_target / n6522TimerPeriod + 0.5; // Round-up
const int nNumSamplesPerPeriod = (int) ((double)SAMPLE_RATE / nIrqFreq); // Eg. For 60Hz this is 735
int nNumSamples = nNumSamplesPerPeriod + nNumSamplesError; // Apply correction
if(nNumSamples <= 0)
@ -968,7 +968,7 @@ static void MB_Update()
if (dbg_print != now)
{
dbg_print = now;
LOG("g_nCpuCyclesFeedback:%d nNumSamplesError:%d n6522TimerPeriod:%f nIrqFreq:%f nNumSamplesPerPeriod:%d nNumSamples:%d nBytesRemaining:%d ", g_nCpuCyclesFeedback, nNumSamplesError, n6522TimerPeriod, nIrqFreq, nNumSamplesPerPeriod, nNumSamples, nBytesRemaining);
LOG("cycles_speaker_feedback:%d nNumSamplesError:%d n6522TimerPeriod:%f nIrqFreq:%f nNumSamplesPerPeriod:%d nNumSamples:%d nBytesRemaining:%d ", cycles_speaker_feedback, nNumSamplesError, n6522TimerPeriod, nIrqFreq, nNumSamplesPerPeriod, nNumSamples, nBytesRemaining);
}
#endif
@ -1611,7 +1611,7 @@ void MB_Initialize()
#endif
}
AY8910_InitAll((int)g_fCurrentCLK6502, SAMPLE_RATE);
AY8910_InitAll((int)cycles_persec_target, SAMPLE_RATE);
LogFileOutput("MB_Initialize: AY8910_InitAll()\n");
for(i=0; i<NUM_AY8910; i++)
@ -1629,10 +1629,10 @@ void MB_Initialize()
//-----------------------------------------------------------------------------
// NB. Called when /g_fCurrentCLK6502/ changes
// NB. Called when /cycles_persec_target/ changes
void MB_Reinitialize()
{
AY8910_InitClock((int)g_fCurrentCLK6502);
AY8910_InitClock((int)cycles_persec_target);
}
//-----------------------------------------------------------------------------
@ -1694,14 +1694,13 @@ void MB_Reset()
#ifdef APPLE2IX
#define MemReadFloatingBus floating_bus
#define nCyclesLeft cpu65_cycle_count
#define nAddr ea
GLUE_C_READ(MB_Read)
#else
static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nCyclesLeft)
#endif
{
MB_UpdateCycles(nCyclesLeft);
MB_UpdateCycles();
#ifdef _DEBUG
if(!IS_APPLE2 && !MemCheckSLOTCXROM())
@ -1713,7 +1712,7 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
if(g_SoundcardType == CT_Empty)
{
_ASSERT(0); // Card unplugged, so IORead_Cxxx() returns the floating bus
return MemReadFloatingBus(nCyclesLeft);
return MemReadFloatingBus();
}
#endif
@ -1724,7 +1723,7 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
{
if(nMB != 0) // Slot4 only
{
return MemReadFloatingBus(nCyclesLeft);
return MemReadFloatingBus();
}
int CS;
@ -1749,7 +1748,7 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
bAccessedDevice = true;
}
return bAccessedDevice ? nRes : MemReadFloatingBus(nCyclesLeft);
return bAccessedDevice ? nRes : MemReadFloatingBus();
}
if(nOffset <= (SY6522A_Offset+0x0F))
@ -1766,7 +1765,7 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
}
else
{
return MemReadFloatingBus(nCyclesLeft);
return MemReadFloatingBus();
}
}
@ -1779,7 +1778,7 @@ GLUE_C_WRITE(MB_Write)
static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nCyclesLeft)
#endif
{
MB_UpdateCycles(nCyclesLeft);
MB_UpdateCycles();
#ifdef _DEBUG
if(!IS_APPLE2 && !MemCheckSLOTCXROM())
@ -1864,7 +1863,7 @@ static BYTE __stdcall PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
{
if(!g_bPhasorEnable)
{
return MemReadFloatingBus(nCyclesLeft);
return MemReadFloatingBus();
}
if(g_nPhasorMode < 2)
@ -1874,7 +1873,7 @@ static BYTE __stdcall PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
AY8910_InitClock((int)fCLK);
return MemReadFloatingBus(nCyclesLeft);
return MemReadFloatingBus();
}
//-----------------------------------------------------------------------------
@ -2002,7 +2001,7 @@ void MB_Demute()
// Called by CpuExecute() before doing CPU emulation
void MB_StartOfCpuExecute()
{
g_uLastCumulativeCycles = g_nCumulativeCycles;
g_uLastCumulativeCycles = cycles_count_total;
}
// Called by ContinueExecution() at the end of every video frame
@ -2018,14 +2017,14 @@ void MB_EndOfVideoFrame()
//-----------------------------------------------------------------------------
// Called by CpuExecute() after every N opcodes (N = ~1000 @ 1MHz)
void MB_UpdateCycles(ULONG uExecutedCycles)
void MB_UpdateCycles(void)
{
if(g_SoundcardType == CT_Empty)
return;
CpuCalcCycles(uExecutedCycles);
UINT64 uCycles = g_nCumulativeCycles - g_uLastCumulativeCycles;
g_uLastCumulativeCycles = g_nCumulativeCycles;
timing_checkpoint_cycles();
UINT64 uCycles = cycles_count_total - g_uLastCumulativeCycles;
g_uLastCumulativeCycles = cycles_count_total;
//_ASSERT(uCycles < 0x10000);
if (uCycles >= 0x10000) {
printf("OOPS!!! Mockingboard failed assert!\n");
@ -2137,6 +2136,11 @@ DWORD MB_GetVolume()
void MB_SetVolume(DWORD dwVolume, DWORD dwVolumeMax)
{
#ifdef APPLE2IX
#warning TODO FIXME ... why is OpenAL on my Linux box so damn loud?!
dwVolume >>= 2;
dwVolumeMax >>= 2;
#endif
MockingboardVoice.dwUserVolume = dwVolume;
MockingboardVoice.nVolume = NewVolume(dwVolume, dwVolumeMax);

View File

@ -105,7 +105,7 @@ void MB_Mute();
void MB_Demute();
void MB_StartOfCpuExecute();
void MB_EndOfVideoFrame();
void MB_UpdateCycles(ULONG uExecutedCycles);
void MB_UpdateCycles(void);
SS_CARDTYPE MB_GetSoundcardType();
void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType);
double MB_GetFramePeriod();

View File

@ -469,7 +469,10 @@ static long ALGetPosition(void *_this, unsigned long *bytes_queued, unsigned lon
{
ALVoice *voice = (ALVoice*)_this;
*bytes_queued = 0;
*unused_write_cursor = 0;
if (unused_write_cursor)
{
*unused_write_cursor = 0;
}
ALuint queued = 0;
int err = _ALProcessPlayBuffers(voice, &queued);
@ -537,7 +540,7 @@ static long ALBegin(void *_this, unsigned long unused, unsigned long write_bytes
static int _ALSubmitBufferToOpenAL(ALVoice *voice)
{
int err =0;
int err = 0;
ALPlayBuf *node = PlaylistEnqueue(voice, voice->index);
if (!node)

View File

@ -28,42 +28,22 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common.h"
#ifdef APPLE2IX
#include "audio/win-shim.h"
#else
#include "StdAfx.h"
#endif
//-----------------------------------------------------------------------------
#define MAX_SOUND_DEVICES 100
#ifdef APPLE2IX
static char **sound_devices = NULL;
#else
static char *sound_devices[MAX_SOUND_DEVICES];
static GUID sound_device_guid[MAX_SOUND_DEVICES];
#endif
static long num_sound_devices = 0;
#ifdef APPLE2IX
LPDIRECTSOUND g_lpDS = NULL;
#else
static LPDIRECTSOUND g_lpDS = NULL;
#endif
//-------------------------------------
// Used for muting & fading:
#ifdef APPLE2IX
#define uMAX_VOICES 66
#else
static const UINT uMAX_VOICES = 66; // 64 phonemes + spkr + mockingboard
#endif
static UINT g_uNumVoices = 0;
static VOICE* g_pVoices[uMAX_VOICES] = {NULL};
@ -73,7 +53,6 @@ static VOICE* g_pSpeakerVoice = NULL;
bool g_bDSAvailable = false;
#ifdef APPLE2IX
bool g_bDisableDirectSound = false;
FILE *g_fh = NULL;
@ -81,511 +60,209 @@ __attribute__((constructor))
static void _init_soundcore() {
g_fh = error_log;
}
#endif
//-----------------------------------------------------------------------------
#ifndef APPLE2IX
static BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext)
{
int i = num_sound_devices;
if(i == MAX_SOUND_DEVICES)
return TRUE;
if(lpGUID != NULL)
memcpy(&sound_device_guid[i], lpGUID, sizeof (GUID));
sound_devices[i] = _strdup(lpszDesc);
if(g_fh) fprintf(g_fh, "%d: %s - %s\n",i,lpszDesc,lpszDrvName);
num_sound_devices++;
return TRUE;
}
#endif
//-----------------------------------------------------------------------------
#ifdef _DEBUG
static char *DirectSound_ErrorText (HRESULT error)
{
switch( error )
{
case DSERR_ALLOCATED:
return "Allocated";
case DSERR_CONTROLUNAVAIL:
return "Control Unavailable";
case DSERR_INVALIDPARAM:
return "Invalid Parameter";
case DSERR_INVALIDCALL:
return "Invalid Call";
case DSERR_GENERIC:
return "Generic";
case DSERR_PRIOLEVELNEEDED:
return "Priority Level Needed";
case DSERR_OUTOFMEMORY:
return "Out of Memory";
case DSERR_BADFORMAT:
return "Bad Format";
case DSERR_UNSUPPORTED:
return "Unsupported";
case DSERR_NODRIVER:
return "No Driver";
case DSERR_ALREADYINITIALIZED:
return "Already Initialized";
case DSERR_NOAGGREGATION:
return "No Aggregation";
case DSERR_BUFFERLOST:
return "Buffer Lost";
case DSERR_OTHERAPPHASPRIO:
return "Other Application Has Priority";
case DSERR_UNINITIALIZED:
return "Uninitialized";
case DSERR_NOINTERFACE:
return "No Interface";
default:
return "Unknown";
}
}
#endif
//-----------------------------------------------------------------------------
bool DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
SHORT** ppDSLockedBuffer0, DWORD* pdwDSLockedBufferSize0,
SHORT** ppDSLockedBuffer1, DWORD* pdwDSLockedBufferSize1)
SHORT** ppDSLockedBuffer0, DWORD* pdwDSLockedBufferSize0,
SHORT** ppDSLockedBuffer1, DWORD* pdwDSLockedBufferSize1)
{
DWORD nStatus = 0;
#ifdef APPLE2IX
HRESULT hr = pVoice->GetStatus(pVoice->_this, &nStatus);
#else
HRESULT hr = pVoice->GetStatus(&nStatus);
#endif
if(hr != DS_OK)
return false;
DWORD nStatus = 0;
HRESULT hr = pVoice->GetStatus(pVoice->_this, &nStatus);
if(hr != DS_OK)
return false;
if(nStatus & DSBSTATUS_BUFFERLOST)
{
do
{
#ifdef APPLE2IX
hr = pVoice->Restore(pVoice->_this);
#else
hr = pVoice->Restore();
#endif
if(hr == DSERR_BUFFERLOST)
Sleep(10);
}
while(hr != DS_OK);
}
if(nStatus & DSBSTATUS_BUFFERLOST)
{
do
{
hr = pVoice->Restore(pVoice->_this);
if(hr == DSERR_BUFFERLOST)
Sleep(10);
}
while(hr != DS_OK);
}
// Get write only pointer(s) to sound buffer
if(dwBytes == 0)
{
#ifdef APPLE2IX
if(FAILED(hr = pVoice->Lock(pVoice->_this, 0, 0,
#else
if(FAILED(hr = pVoice->Lock(0, 0,
#endif
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
DSBLOCK_ENTIREBUFFER)))
return false;
}
else
{
#ifdef APPLE2IX
if(FAILED(hr = pVoice->Lock(pVoice->_this, dwOffset, dwBytes,
#else
if(FAILED(hr = pVoice->Lock(dwOffset, dwBytes,
#endif
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
0)))
return false;
}
// Get write only pointer(s) to sound buffer
if(dwBytes == 0)
{
if(FAILED(hr = pVoice->Lock(pVoice->_this, 0, 0,
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
DSBLOCK_ENTIREBUFFER)))
return false;
}
else
{
if(FAILED(hr = pVoice->Lock(pVoice->_this, dwOffset, dwBytes,
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
0)))
return false;
}
return true;
return true;
}
//-----------------------------------------------------------------------------
HRESULT DSGetSoundBuffer(VOICE* pVoice, DWORD dwFlags, DWORD dwBufferSize, DWORD nSampleRate, int nChannels)
{
WAVEFORMATEX wavfmt;
DSBUFFERDESC dsbdesc;
WAVEFORMATEX wavfmt;
DSBUFFERDESC dsbdesc;
wavfmt.wFormatTag = WAVE_FORMAT_PCM;
wavfmt.nChannels = nChannels;
wavfmt.nSamplesPerSec = nSampleRate;
wavfmt.wBitsPerSample = 16;
wavfmt.nBlockAlign = wavfmt.nChannels==1 ? 2 : 4;
wavfmt.nAvgBytesPerSec = wavfmt.nBlockAlign * wavfmt.nSamplesPerSec;
wavfmt.wFormatTag = WAVE_FORMAT_PCM;
wavfmt.nChannels = nChannels;
wavfmt.nSamplesPerSec = nSampleRate;
wavfmt.wBitsPerSample = 16;
wavfmt.nBlockAlign = wavfmt.nChannels==1 ? 2 : 4;
wavfmt.nAvgBytesPerSec = wavfmt.nBlockAlign * wavfmt.nSamplesPerSec;
memset (&dsbdesc, 0, sizeof (dsbdesc));
dsbdesc.dwSize = sizeof (dsbdesc);
dsbdesc.dwBufferBytes = dwBufferSize;
dsbdesc.lpwfxFormat = &wavfmt;
dsbdesc.dwFlags = dwFlags | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS;
memset (&dsbdesc, 0, sizeof (dsbdesc));
dsbdesc.dwSize = sizeof (dsbdesc);
dsbdesc.dwBufferBytes = dwBufferSize;
dsbdesc.lpwfxFormat = &wavfmt;
dsbdesc.dwFlags = dwFlags | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS;
// Are buffers released when g_lpDS OR pVoice->lpDSBvoice is released?
// . From DirectX doc:
// "Buffer objects are owned by the device object that created them. When the
// device object is released, all buffers created by that object are also released..."
#ifdef APPLE2IX
// Are buffers released when g_lpDS OR pVoice->lpDSBvoice is released?
// . From DirectX doc:
// "Buffer objects are owned by the device object that created them. When the
// device object is released, all buffers created by that object are also released..."
if (pVoice->lpDSBvoice)
{
g_lpDS->DestroySoundBuffer(&pVoice->lpDSBvoice);
//DSReleaseSoundBuffer(pVoice);
}
HRESULT hr = g_lpDS->CreateSoundBuffer(&dsbdesc, &pVoice->lpDSBvoice, g_lpDS);
#else
HRESULT hr = g_lpDS->CreateSoundBuffer(&dsbdesc, &pVoice->lpDSBvoice, NULL);
#endif
if(FAILED(hr))
return hr;
HRESULT hr = g_lpDS->CreateSoundBuffer(&dsbdesc, &pVoice->lpDSBvoice, g_lpDS);
if(FAILED(hr))
return hr;
//
//
_ASSERT(g_uNumVoices < uMAX_VOICES);
if(g_uNumVoices < uMAX_VOICES)
g_pVoices[g_uNumVoices++] = pVoice;
_ASSERT(g_uNumVoices < uMAX_VOICES);
if(g_uNumVoices < uMAX_VOICES)
g_pVoices[g_uNumVoices++] = pVoice;
if(pVoice->bIsSpeaker)
g_pSpeakerVoice = pVoice;
if(pVoice->bIsSpeaker)
g_pSpeakerVoice = pVoice;
return hr;
return hr;
}
void DSReleaseSoundBuffer(VOICE* pVoice)
{
if(pVoice->bIsSpeaker)
g_pSpeakerVoice = NULL;
if(pVoice->bIsSpeaker)
g_pSpeakerVoice = NULL;
for(UINT i=0; i<g_uNumVoices; i++)
{
if(g_pVoices[i] == pVoice)
{
g_pVoices[i] = g_pVoices[g_uNumVoices-1];
g_pVoices[g_uNumVoices-1] = NULL;
g_uNumVoices--;
break;
}
}
for(UINT i=0; i<g_uNumVoices; i++)
{
if(g_pVoices[i] == pVoice)
{
g_pVoices[i] = g_pVoices[g_uNumVoices-1];
g_pVoices[g_uNumVoices-1] = NULL;
g_uNumVoices--;
break;
}
}
#ifdef APPLE2IX
if (g_lpDS)
{
g_lpDS->DestroySoundBuffer(&pVoice->lpDSBvoice);
}
#else
SAFE_RELEASE(pVoice->lpDSBvoice);
#endif
}
//-----------------------------------------------------------------------------
bool DSZeroVoiceBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize)
{
DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
SHORT* pDSLockedBuffer;
DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
SHORT* pDSLockedBuffer;
#ifdef APPLE2IX
DWORD argX = 0;
HRESULT hr = Voice->lpDSBvoice->Stop(Voice->lpDSBvoice->_this);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSStop failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
hr = !DSGetLock(Voice->lpDSBvoice, 0, 0, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, &argX);
#else
HRESULT hr = Voice->lpDSBvoice->Stop(Voice->lpDSBvoice->_this);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSStop failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
hr = !DSGetLock(Voice->lpDSBvoice, 0, 0, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, &argX);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSGetLock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
hr = DSGetLock(Voice->lpDSBvoice, 0, 0, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, 0);
#endif
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSGetLock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
_ASSERT(dwDSLockedBufferSize == dwBufferSize);
memset(pDSLockedBuffer, 0x00, dwDSLockedBufferSize);
_ASSERT(dwDSLockedBufferSize == dwBufferSize);
memset(pDSLockedBuffer, 0x00, dwDSLockedBufferSize);
hr = Voice->lpDSBvoice->Unlock(Voice->lpDSBvoice->_this, (void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, argX);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSUnlock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
#ifdef APPLE2IX
hr = Voice->lpDSBvoice->Unlock(Voice->lpDSBvoice->_this, (void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, argX);
#else
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0);
#endif
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSUnlock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
hr = Voice->lpDSBvoice->Play(Voice->lpDSBvoice->_this,0,0,0);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSPlay failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
#ifdef APPLE2IX
hr = Voice->lpDSBvoice->Play(Voice->lpDSBvoice->_this,0,0,0);
#else
hr = Voice->lpDSBvoice->Play(0,0,DSBPLAY_LOOPING);
#endif
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSPlay failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
return true;
return true;
}
//-----------------------------------------------------------------------------
bool DSZeroVoiceWritableBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize)
{
DWORD dwDSLockedBufferSize0=0, dwDSLockedBufferSize1=0;
SHORT *pDSLockedBuffer0, *pDSLockedBuffer1;
DWORD dwDSLockedBufferSize0=0, dwDSLockedBufferSize1=0;
SHORT *pDSLockedBuffer0, *pDSLockedBuffer1;
HRESULT hr = DSGetLock(Voice->lpDSBvoice,
0, dwBufferSize,
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
#ifdef APPLE2IX
HRESULT hr = DSGetLock(Voice->lpDSBvoice,
0, dwBufferSize,
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
hr = !hr;
#endif
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSGetLock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSGetLock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
memset(pDSLockedBuffer0, 0x00, dwDSLockedBufferSize0);
if(pDSLockedBuffer1)
memset(pDSLockedBuffer1, 0x00, dwDSLockedBufferSize1);
memset(pDSLockedBuffer0, 0x00, dwDSLockedBufferSize0);
if(pDSLockedBuffer1)
memset(pDSLockedBuffer1, 0x00, dwDSLockedBufferSize1);
#ifdef APPLE2IX
hr = Voice->lpDSBvoice->Unlock(Voice->lpDSBvoice->_this, (void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#else
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#endif
(void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSUnlock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
hr = Voice->lpDSBvoice->Unlock(Voice->lpDSBvoice->_this, (void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
(void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSUnlock failed (%08X)\n",pszDevName,(unsigned int)hr);
return false;
}
return true;
return true;
}
//-----------------------------------------------------------------------------
static bool g_bTimerActive = false;
#ifndef APPLE2IX
static eFADE g_FadeType = FADE_NONE;
static UINT_PTR g_nTimerID = 0;
#endif
//-------------------------------------
#ifndef APPLE2IX
static VOID CALLBACK SoundCore_TimerFunc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
#endif
#ifndef APPLE2IX
static bool SoundCore_StartTimer()
{
if(g_bTimerActive)
return true;
g_nTimerID = SetTimer(NULL, 0, 1, SoundCore_TimerFunc); // 1ms interval
if(g_nTimerID == 0)
{
fprintf(stderr, "Error creating timer\n");
_ASSERT(0);
return false;
}
g_bTimerActive = true;
return true;
}
#endif
static void SoundCore_StopTimer()
{
#ifdef APPLE2IX
// using our own timing and nanosleep() ...
#else
if(!g_bTimerActive)
return;
if(KillTimer(NULL, g_nTimerID) == FALSE)
{
fprintf(stderr, "Error killing timer\n");
_ASSERT(0);
return;
}
g_bTimerActive = false;
#endif
}
bool SoundCore_GetTimerState()
{
return g_bTimerActive;
}
//-------------------------------------
// [OLD: Used to fade volume in/out]
// FADE_OUT : Just keep filling speaker soundbuffer with same value
// FADE_IN : Switch to FADE_NONE & StopTimer()
#ifndef APPLE2IX
static VOID CALLBACK SoundCore_TimerFunc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
if((g_pSpeakerVoice == NULL) || (g_pSpeakerVoice->bActive == false))
g_FadeType = FADE_NONE;
// Timer expired
if(g_FadeType == FADE_NONE)
{
SoundCore_StopTimer();
return;
}
//
#if 1
if(g_FadeType == FADE_IN)
g_FadeType = FADE_NONE;
else
SpkrUpdate_Timer();
#else
const LONG nFadeUnit_Fast = (DSBVOLUME_MAX - DSBVOLUME_MIN) / 10;
const LONG nFadeUnit_Slow = (DSBVOLUME_MAX - DSBVOLUME_MIN) / 1000; // Less noisy for 'silence'
LONG nFadeUnit = g_pSpeakerVoice->bRecentlyActive ? nFadeUnit_Fast : nFadeUnit_Slow;
LONG nFadeVolume = g_pSpeakerVoice->nFadeVolume;
if(g_FadeType == FADE_IN)
{
if(nFadeVolume == g_pSpeakerVoice->nVolume)
{
g_FadeType = FADE_NONE;
SoundCore_StopTimer();
return;
}
nFadeVolume += nFadeUnit;
if(nFadeVolume > g_pSpeakerVoice->nVolume)
nFadeVolume = g_pSpeakerVoice->nVolume;
}
else // FADE_OUT
{
if(nFadeVolume == DSBVOLUME_MIN)
{
g_FadeType = FADE_NONE;
SoundCore_StopTimer();
return;
}
nFadeVolume -= nFadeUnit;
if(nFadeVolume < DSBVOLUME_MIN)
nFadeVolume = DSBVOLUME_MIN;
}
g_pSpeakerVoice->nFadeVolume = nFadeVolume;
g_pSpeakerVoice->lpDSBvoice->SetVolume(nFadeVolume);
#endif
}
#endif
//-----------------------------------------------------------------------------
#ifndef APPLE2IX
void SoundCore_SetFade(eFADE FadeType)
{
static int nLastMode = -1;
if(g_nAppMode == MODE_DEBUG)
return;
// Fade in/out just for speaker, the others are demuted/muted
if(FadeType != FADE_NONE)
{
for(UINT i=0; i<g_uNumVoices; i++)
{
// Note: Kludge for fading speaker if curr/last g_nAppMode is/was MODE_LOGO:
// . Bug in DirectSound? SpeakerVoice.lpDSBvoice->SetVolume() doesn't work without this!
// . See SoundCore_TweakVolumes() - could be this?
if((g_pVoices[i]->bIsSpeaker) && (g_nAppMode != MODE_LOGO) && (nLastMode != MODE_LOGO))
{
g_pVoices[i]->lpDSBvoice->GetVolume(&g_pVoices[i]->nFadeVolume);
g_FadeType = FadeType;
SoundCore_StartTimer();
}
else if(FadeType == FADE_OUT)
{
g_pVoices[i]->lpDSBvoice->SetVolume(DSBVOLUME_MIN);
g_pVoices[i]->bMute = true;
}
else // FADE_IN
{
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume);
g_pVoices[i]->bMute = false;
}
}
}
else // FadeType == FADE_NONE
{
if( (g_FadeType != FADE_NONE) && // Currently fading-in/out
(g_pSpeakerVoice && g_pSpeakerVoice->bActive) )
{
g_FadeType = FADE_NONE; // TimerFunc will call StopTimer()
g_pSpeakerVoice->lpDSBvoice->SetVolume(g_pSpeakerVoice->nVolume);
}
}
nLastMode = g_nAppMode;
}
#endif
//-----------------------------------------------------------------------------
// If AppleWin started by double-clicking a .dsk, then our window won't have focus when volumes are set (so gets ignored).
// Subsequent setting (to the same volume) will get ignored, as DirectSound thinks that volume is already set.
#ifndef APPLE2IX
void SoundCore_TweakVolumes()
{
for (UINT i=0; i<g_uNumVoices; i++)
{
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume-1);
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume);
}
}
#endif
//-----------------------------------------------------------------------------
static UINT g_uDSInitRefCount = 0;
bool DSInit()
{
#ifdef APPLE2IX
if (!g_fh)
{
g_fh = stderr;
}
#endif
if(g_bDSAvailable)
{
g_uDSInitRefCount++;
return true; // Already initialised successfully
}
if(g_bDSAvailable)
{
g_uDSInitRefCount++;
return true; // Already initialised successfully
}
#ifdef APPLE2IX
if (sound_devices)
{
LOG("Destroying old device names...");
@ -598,261 +275,107 @@ bool DSInit()
FREE(sound_devices);
sound_devices = NULL;
}
num_sound_devices = SoundSystemEnumerate(&sound_devices, MAX_SOUND_DEVICES);
num_sound_devices = SoundSystemEnumerate(&sound_devices, MAX_SOUND_DEVICES);
HRESULT hr = (num_sound_devices <= 0);
#else
HRESULT hr = DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc, NULL);
#endif
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "DSEnumerate failed (%08X)\n",(unsigned int)hr);
return false;
}
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "DSEnumerate failed (%08X)\n",(unsigned int)hr);
return false;
}
if(g_fh)
{
#if !defined(APPLE2IX)
fprintf(g_fh, "Number of sound devices = %d\n",num_sound_devices);
#endif
}
if(g_fh)
{
fprintf(g_fh, "Number of sound devices = %ld\n",num_sound_devices);
}
bool bCreatedOK = false;
for(int x=0; x<num_sound_devices; x++)
{
#ifdef APPLE2IX
bool bCreatedOK = false;
for(int x=0; x<num_sound_devices; x++)
{
if (g_lpDS)
{
SoundSystemDestroy((SoundSystemStruct**)&g_lpDS);
}
hr = (int)SoundSystemCreate(sound_devices[x], (SoundSystemStruct**)&g_lpDS);
#else
hr = DirectSoundCreate(&sound_device_guid[x], &g_lpDS, NULL);
#endif
if(SUCCEEDED(hr))
{
#if !defined(APPLE2IX)
if(g_fh) fprintf(g_fh, "DSCreate succeeded for sound device #%d\n",x);
#endif
bCreatedOK = true;
break;
}
if(SUCCEEDED(hr))
{
bCreatedOK = true;
break;
}
if(g_fh) fprintf(g_fh, "DSCreate failed for sound device #%d (%08X)\n",x,(unsigned int)hr);
}
if(!bCreatedOK)
{
if(g_fh) fprintf(g_fh, "DSCreate failed for all sound devices\n");
return false;
}
if(g_fh) fprintf(g_fh, "DSCreate failed for sound device #%d (%08X)\n",x,(unsigned int)hr);
}
if(!bCreatedOK)
{
if(g_fh) fprintf(g_fh, "DSCreate failed for all sound devices\n");
return false;
}
#ifndef APPLE2IX
hr = g_lpDS->SetCooperativeLevel(g_hFrameWindow, DSSCL_NORMAL);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "SetCooperativeLevel failed (%08X)\n",hr);
return false;
}
g_bDSAvailable = true;
DSCAPS DSCaps;
ZeroMemory(&DSCaps, sizeof(DSCAPS));
DSCaps.dwSize = sizeof(DSCAPS);
hr = g_lpDS->GetCaps(&DSCaps);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "GetCaps failed (%08X)\n",hr);
// Not fatal: so continue...
}
#endif
g_uDSInitRefCount = 1;
g_bDSAvailable = true;
g_uDSInitRefCount = 1;
return true;
return true;
}
//-----------------------------------------------------------------------------
void DSUninit()
{
if(!g_bDSAvailable)
return;
if(!g_bDSAvailable)
return;
_ASSERT(g_uDSInitRefCount);
_ASSERT(g_uDSInitRefCount);
if(g_uDSInitRefCount == 0)
return;
if(g_uDSInitRefCount == 0)
return;
g_uDSInitRefCount--;
g_uDSInitRefCount--;
if(g_uDSInitRefCount)
return;
if(g_uDSInitRefCount)
return;
//
//
_ASSERT(g_uNumVoices == 0);
_ASSERT(g_uNumVoices == 0);
#ifdef APPLE2IX
SoundSystemDestroy((SoundSystemStruct**)&g_lpDS);
#else
SAFE_RELEASE(g_lpDS);
#endif
g_bDSAvailable = false;
SoundCore_StopTimer();
g_bDSAvailable = false;
}
//-----------------------------------------------------------------------------
LONG NewVolume(DWORD dwVolume, DWORD dwVolumeMax)
{
float fVol = (float) dwVolume / (float) dwVolumeMax; // 0.0=Max, 1.0=Min
float fVol = (float) dwVolume / (float) dwVolumeMax; // 0.0=Max, 1.0=Min
return (LONG) ((float) DSBVOLUME_MIN * fVol);
return (LONG) ((float) DSBVOLUME_MIN * fVol);
}
//=============================================================================
static int g_nErrorInc = 20; // Old: 1
static int g_nErrorMax = 200; // Old: 50
static int g_nErrorInc = 20; // Old: 1
static int g_nErrorMax = 200; // Old: 50
int SoundCore_GetErrorInc()
{
return g_nErrorInc;
return g_nErrorInc;
}
void SoundCore_SetErrorInc(const int nErrorInc)
{
g_nErrorInc = nErrorInc < g_nErrorMax ? nErrorInc : g_nErrorMax;
if(g_fh) fprintf(g_fh, "Speaker/MB Error Inc = %d\n", g_nErrorInc);
g_nErrorInc = nErrorInc < g_nErrorMax ? nErrorInc : g_nErrorMax;
if(g_fh) fprintf(g_fh, "Speaker/MB Error Inc = %d\n", g_nErrorInc);
}
int SoundCore_GetErrorMax()
{
return g_nErrorMax;
return g_nErrorMax;
}
void SoundCore_SetErrorMax(const int nErrorMax)
{
g_nErrorMax = nErrorMax < MAX_SAMPLES ? nErrorMax : MAX_SAMPLES;
if(g_fh) fprintf(g_fh, "Speaker/MB Error Max = %d\n", g_nErrorMax);
g_nErrorMax = nErrorMax < MAX_SAMPLES ? nErrorMax : MAX_SAMPLES;
if(g_fh) fprintf(g_fh, "Speaker/MB Error Max = %d\n", g_nErrorMax);
}
//=============================================================================
#ifndef APPLE2IX
static DWORD g_dwAdviseToken;
static IReferenceClock *g_pRefClock = NULL;
static HANDLE g_hSemaphore = NULL;
static bool g_bRefClockTimerActive = false;
static DWORD g_dwLastUsecPeriod = 0;
#endif
bool SysClk_InitTimer()
{
#ifdef APPLE2IX
// Not using timers ...
return false;
#else
g_hSemaphore = CreateSemaphore(NULL, 0, 1, NULL); // Max count = 1
if (g_hSemaphore == NULL)
{
fprintf(stderr, "Error creating semaphore\n");
return false;
}
if (CoCreateInstance(CLSID_SystemClock, NULL, CLSCTX_INPROC,
IID_IReferenceClock, (LPVOID*)&g_pRefClock) != S_OK)
{
fprintf(stderr, "Error initialising COM\n");
return false; // Fails for Win95!
}
return true;
#endif
}
void SysClk_UninitTimer()
{
#ifdef APPLE2IX
// Not using timers ...
#else
SysClk_StopTimer();
SAFE_RELEASE(g_pRefClock);
if (CloseHandle(g_hSemaphore) == 0)
fprintf(stderr, "Error closing semaphore handle\n");
#endif
}
//
void SysClk_WaitTimer()
{
#ifdef APPLE2IX
// Not using timers ...
#else
if(!g_bRefClockTimerActive)
return;
WaitForSingleObject(g_hSemaphore, INFINITE);
#endif
}
//
void SysClk_StartTimerUsec(DWORD dwUsecPeriod)
{
#ifdef APPLE2IX
// Not using timers ...
#else
if(g_bRefClockTimerActive && (g_dwLastUsecPeriod == dwUsecPeriod))
return;
SysClk_StopTimer();
REFERENCE_TIME rtPeriod = (REFERENCE_TIME) (dwUsecPeriod * 10); // In units of 100ns
REFERENCE_TIME rtNow;
HRESULT hr = g_pRefClock->GetTime(&rtNow);
// S_FALSE : Returned time is the same as the previous value
if ((hr != S_OK) && (hr != S_FALSE))
{
_ASSERT(0);
return;
}
if (g_pRefClock->AdvisePeriodic(rtNow, rtPeriod, g_hSemaphore, &g_dwAdviseToken) != S_OK)
{
fprintf(stderr, "Error creating timer\n");
_ASSERT(0);
return;
}
g_dwLastUsecPeriod = dwUsecPeriod;
g_bRefClockTimerActive = true;
#endif
}
void SysClk_StopTimer()
{
#ifdef APPLE2IX
// Not using timers ...
#else
if(!g_bRefClockTimerActive)
return;
if (g_pRefClock->Unadvise(g_dwAdviseToken) != S_OK)
{
fprintf(stderr, "Error deleting timer\n");
_ASSERT(0);
return;
}
g_bRefClockTimerActive = false;
#endif
}

View File

@ -21,41 +21,29 @@
#define MAX_SAMPLES (8*1024)
#if defined(APPLE2IX)
#define SAFE_RELEASE(p) FREE(p)
#else
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#endif
// Define max 1 of these:
//#define RIFF_SPKR
//#define RIFF_MB
#ifdef APPLE2IX
extern bool g_bDisableDirectSound;
#endif
typedef struct
{
LPDIRECTSOUNDBUFFER lpDSBvoice;
LPDIRECTSOUNDBUFFER lpDSBvoice;
#ifdef APPLE2IX
// apparently lpDSNotify isn't used...
#define LPDIRECTSOUNDNOTIFY void*
#endif
LPDIRECTSOUNDNOTIFY lpDSNotify;
bool bActive; // Playback is active
bool bMute;
LONG nVolume; // Current volume (as used by DirectSound)
LONG nFadeVolume; // Current fade volume (as used by DirectSound)
DWORD dwUserVolume; // Volume from slider on Property Sheet (0=Max)
bool bIsSpeaker;
bool bRecentlyActive; // (Speaker only) false after 0.2s of speaker inactivity
LPDIRECTSOUNDNOTIFY lpDSNotify;
bool bActive; // Playback is active
bool bMute;
LONG nVolume; // Current volume (as used by DirectSound)
LONG nFadeVolume; // Current fade volume (as used by DirectSound)
DWORD dwUserVolume; // Volume from slider on Property Sheet (0=Max)
bool bIsSpeaker;
bool bRecentlyActive; // (Speaker only) false after 0.2s of speaker inactivity
} VOICE, *PVOICE;
bool DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
SHORT** ppDSLockedBuffer0, DWORD* pdwDSLockedBufferSize0,
SHORT** ppDSLockedBuffer1, DWORD* pdwDSLockedBufferSize1);
SHORT** ppDSLockedBuffer0, DWORD* pdwDSLockedBufferSize0,
SHORT** ppDSLockedBuffer1, DWORD* pdwDSLockedBufferSize1);
HRESULT DSGetSoundBuffer(VOICE* pVoice, DWORD dwFlags, DWORD dwBufferSize, DWORD nSampleRate, int nChannels);
void DSReleaseSoundBuffer(VOICE* pVoice);
@ -63,14 +51,8 @@ void DSReleaseSoundBuffer(VOICE* pVoice);
bool DSZeroVoiceBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize);
bool DSZeroVoiceWritableBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize);
#if defined(APPLE2IX)
typedef enum eFADE {FADE_NONE, FADE_IN, FADE_OUT} eFADE;
#else
enum eFADE {FADE_NONE, FADE_IN, FADE_OUT};
#endif
void SoundCore_SetFade(eFADE FadeType);
bool SoundCore_GetTimerState();
void SoundCore_TweakVolumes();
int SoundCore_GetErrorInc();
void SoundCore_SetErrorInc(const int nErrorInc);

File diff suppressed because it is too large Load Diff

View File

@ -12,48 +12,21 @@
#ifndef _SPEAKER_H_
#define _SPEAKER_H_
#ifdef APPLE2IX
#include "audio/win-shim.h"
#endif
extern DWORD soundtype;
extern double g_fClksPerSpkrSample;
void SpkrDestroy ();
void SpkrInitialize ();
void SpkrReinitialize ();
void SpkrReset();
BOOL SpkrSetEmulationType (HWND,DWORD);
void SpkrUpdate (DWORD);
void SpkrUpdate_Timer();
void Spkr_SetErrorInc(const int nErrorInc);
void Spkr_SetErrorMax(const int nErrorMax);
DWORD SpkrGetVolume();
#ifdef APPLE2IX
#define SPKR_SAMPLE_RATE 22050
#define SPKR_DATA_INIT 0x4000
void SpkrSetVolume(short amplitude);
#else
void SpkrSetVolume(DWORD dwVolume, DWORD dwVolumeMax);
#endif
void Spkr_Mute();
void Spkr_Demute();
bool Spkr_IsActive();
bool Spkr_DSInit();
void Spkr_DSUninit();
#ifdef APPLE2IX
#define __int64
typedef struct {
unsigned __int64 g_nSpkrLastCycle;
} SS_IO_Speaker;
#endif
DWORD SpkrGetSnapshot(SS_IO_Speaker* pSS);
DWORD SpkrSetSnapshot(SS_IO_Speaker* pSS);
#ifdef APPLE2IX
void SpkrToggle();
#else
BYTE __stdcall SpkrToggle (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);
#endif
void speaker_init(void);
void speaker_destroy(void);
void speaker_reset(void);
void speaker_flush(void);
void speaker_set_volume(int16_t amplitude);
bool speaker_is_active(void);
/*
* returns the machine cycles per sample
* - for emulator running at normal speed: CLK_6502 / SPKR_SAMPLE_RATE == ~46
*/
double speaker_cycles_per_sample(void);
#endif /* whole file */

View File

@ -36,8 +36,6 @@ uint8_t cpu65_rw;
uint8_t cpu65_opcode;
uint8_t cpu65_opcycles;
int16_t cpu65_cycle_count = 0;
int16_t cpu65_cycles_to_execute = 0;
uint8_t cpu65__signal = 0;
static pthread_mutex_t irq_mutex = PTHREAD_MUTEX_INITIALIZER;
@ -831,7 +829,7 @@ GLUE_C_WRITE(cpu65_trace_epilogue)
void cpu65_trace_checkpoint(void) {
if (cpu_trace_fp) {
fprintf(cpu_trace_fp, "---TOTAL CYC:%lu\n",g_nCumulativeCycles);
fprintf(cpu_trace_fp, "---TOTAL CYC:%lu\n",cycles_count_total);
fflush(cpu_trace_fp);
}
}

View File

@ -58,7 +58,6 @@ extern unsigned char cpu65_flags_encode[256];
extern unsigned char cpu65_flags_decode[256];
extern int16_t cpu65_cycle_count;
extern int16_t cpu65_cycles_to_execute;
#if CPU_TRACING
void cpu65_trace_begin(const char *trace_file);

View File

@ -603,33 +603,33 @@ GLUE_C_READ(disk_read_phase)
#endif
}
return ea == 0xE0 ? 0xFF : floating_bus_hibit(1, cpu65_cycle_count);
return ea == 0xE0 ? 0xFF : floating_bus_hibit(1);
}
GLUE_C_READ(disk_read_motor_off)
{
clock_gettime(CLOCK_MONOTONIC, &disk6.motor_time);
disk6.motor_off = 1;
return floating_bus_hibit(1, cpu65_cycle_count);
return floating_bus_hibit(1);
}
GLUE_C_READ(disk_read_motor_on)
{
clock_gettime(CLOCK_MONOTONIC, &disk6.motor_time);
disk6.motor_off = 0;
return floating_bus_hibit(1, cpu65_cycle_count);
return floating_bus_hibit(1);
}
GLUE_C_READ(disk_read_select_a)
{
disk6.drive = 0;
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(disk_read_select_b)
{
disk6.drive = 1;
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(disk_read_latch)
@ -640,13 +640,13 @@ GLUE_C_READ(disk_read_latch)
GLUE_C_READ(disk_read_prepare_in)
{
disk6.ddrw = 0;
return floating_bus_hibit(disk6.disk[disk6.drive].is_protected, cpu65_cycle_count);
return floating_bus_hibit(disk6.disk[disk6.drive].is_protected);
}
GLUE_C_READ(disk_read_prepare_out)
{
disk6.ddrw = 1;
return floating_bus_hibit(1, cpu65_cycle_count);
return floating_bus_hibit(1);
}
GLUE_C_WRITE(disk_write_latch)

View File

@ -1151,8 +1151,8 @@ void video_redraw(void) {
// References to Jim Sather's books are given as eg:
// UTAIIe:5-7,P3 (Understanding the Apple IIe, chapter 5, page 7, Paragraph 3)
extern unsigned int CpuGetCyclesThisVideoFrame(const unsigned int nExecutedCycles);
uint16_t video_scanner_get_address(bool *vblBarOut, const unsigned int executedCycles) {
extern unsigned int CpuGetCyclesThisVideoFrame(void);
uint16_t video_scanner_get_address(bool *vblBarOut) {
const bool SW_HIRES = (softswitches & SS_HIRES);
const bool SW_TEXT = (softswitches & SS_TEXT);
const bool SW_PAGE2 = (softswitches & SS_PAGE2);
@ -1160,7 +1160,7 @@ uint16_t video_scanner_get_address(bool *vblBarOut, const unsigned int executedC
const bool SW_MIXED = (softswitches & SS_MIXED);
// get video scanner position
unsigned int nCycles = CpuGetCyclesThisVideoFrame(executedCycles);
unsigned int nCycles = CpuGetCyclesThisVideoFrame();
// machine state switches
int nHires = (SW_HIRES && !SW_TEXT) ? 1 : 0;
@ -1258,13 +1258,13 @@ uint16_t video_scanner_get_address(bool *vblBarOut, const unsigned int executedC
return (uint16_t)nAddress;
}
uint8_t floating_bus(const unsigned int executedCycles) {
uint16_t scanner_addr = video_scanner_get_address(NULL, executedCycles);
uint8_t floating_bus(void) {
uint16_t scanner_addr = video_scanner_get_address(NULL);
return apple_ii_64k[0][scanner_addr];
}
uint8_t floating_bus_hibit(const bool hibit, const unsigned int executedCycles) {
uint16_t scanner_addr = video_scanner_get_address(NULL, executedCycles);
uint8_t floating_bus_hibit(const bool hibit) {
uint16_t scanner_addr = video_scanner_get_address(NULL);
uint8_t b = apple_ii_64k[0][scanner_addr];
return (b & ~0x80) | (hibit ? 0x80 : 0);
}

View File

@ -1280,7 +1280,7 @@ void c_interface_parameters()
#ifdef __linux__
LOG("Back to Linux, w00t!\n");
#endif
SpkrDestroy();
speaker_destroy();
MB_Destroy();
video_shutdown();

View File

@ -1140,10 +1140,10 @@ static int begin_cpu_stepping() {
// kludgey set max CPU speed...
double saved_scale = cpu_scale_factor;
double saved_altscale = cpu_altscale_factor;
bool saved_fullspeed = g_bFullSpeed;
bool saved_fullspeed = is_fullspeed;
cpu_scale_factor = CPU_SCALE_FASTEST;
cpu_altscale_factor = CPU_SCALE_FASTEST;
g_bFullSpeed = true;
is_fullspeed = true;
unsigned int idx = 0;
size_t textlen = 0;
@ -1197,7 +1197,7 @@ static int begin_cpu_stepping() {
cpu_scale_factor = saved_scale;
cpu_altscale_factor = saved_altscale;
g_bFullSpeed = saved_fullspeed;
is_fullspeed = saved_fullspeed;
return ch;
}

View File

@ -520,7 +520,7 @@ void c_initialize_apple_ii_memory()
void c_initialize_sound_hooks()
{
#ifdef AUDIO_ENABLED
SpkrSetVolume(sound_volume * (SPKR_DATA_INIT/10));
speaker_set_volume(sound_volume * (SPKR_DATA_INIT/10));
#endif
for (int i = 0xC030; i < 0xC040; i++)
{
@ -581,8 +581,11 @@ void c_initialize_vm() {
void c_initialize_firsttime()
------------------------------------------------------------------------- */
void reinitialize(void)
{
void reinitialize(void) {
assert(pthread_self() == cpu_thread_id);
cycles_count_total = 0;
c_initialize_vm();
softswitches = SS_TEXT | SS_IOUDIS | SS_C3ROM | SS_LCWRT | SS_LCSEC;
@ -600,8 +603,7 @@ void reinitialize(void)
#endif
}
void c_initialize_firsttime()
{
void c_initialize_firsttime(void) {
#ifdef INTERFACE_CLASSIC
/* read in system files and calculate system defaults */
c_load_interface_font();
@ -610,18 +612,9 @@ void c_initialize_firsttime()
/* initialize the video system */
video_init();
// TODO FIXME : sound system never released ...
#ifdef AUDIO_ENABLED
DSInit();
SpkrInitialize();
MB_Initialize();
#endif
#ifdef DEBUGGER
c_debugger_init();
#endif
reinitialize();
}
#if !defined(TESTING) && !defined(__APPLE__)

View File

@ -41,7 +41,7 @@ uint8_t c_PhasorIO(uint16_t addr) {
return 0x0;
}
void SpkrToggle() {
void c_speaker_toggle(void) {
}
void c_interface_print(int x, int y, const int cs, const char *s) {
@ -119,7 +119,7 @@ void test_common_init(bool do_cputhread) {
// kludgey set max CPU speed...
cpu_scale_factor = CPU_SCALE_FASTEST;
cpu_altscale_factor = CPU_SCALE_FASTEST;
g_bFullSpeed = true;
is_fullspeed = true;
caps_lock = true;

View File

@ -52,6 +52,7 @@ static void testcpu_setup(void *arg) {
//reinitialize();
cpu65_uninterrupt(0xff);
extern int16_t cpu65_cycles_to_execute;
cpu65_cycles_to_execute = 1;
cpu65_pc = TEST_LOC;

View File

@ -10,9 +10,24 @@
*/
/*
* 65c02 CPU Timing Support. Some source derived from AppleWin.
* 65c02 CPU timing support. Source inspired/derived from AppleWin.
*
* Copyleft 2013 Aaron Culliney
* Simplified timing loop for each execution period:
*
* ..{...+....[....|..................|.........]....^....|....^....^....}......
* ti MBB CHK CHK MBE CHX SPK MBX tj ZZZ
*
* - ti : timing sample begin (lock out interface thread)
* - tj : timing sample end (unlock interface thread)
* - [ : cpu65_run()
* - ] : cpu65_run() finished
* - CHK : incoming timing_checkpoint_cycles() call from IO (bumps cycles_count_total)
* - CHX : update remainder of timing_checkpoint_cycles() for execution period
* - MBB : Mockingboard begin
* - MBE : Mockingboard end/flush (output)
* - MBX : Mockingboard end video frame (output)
* - SPK : Speaker output
* - ZZZ : housekeeping+sleep (or not)
*
*/
@ -29,31 +44,32 @@
#define DISK_MOTOR_QUIET_NSECS 2000000
static const unsigned int uCyclesPerLine = 65; // 25 cycles of HBL & 40 cycles of HBL'
static const unsigned int uVisibleLinesPerFrame = 64*3; // 192
static const unsigned int uLinesPerFrame = 262; // 64 in each third of the screen & 70 in VBL
static const unsigned int dwClksPerFrame = uCyclesPerLine * uLinesPerFrame; // 17030
// VBL constants?
#define uCyclesPerLine 65 // 25 cycles of HBL & 40 cycles of HBL'
#define uVisibleLinesPerFrame (64*3) // 192
#define uLinesPerFrame (262) // 64 in each third of the screen & 70 in VBL
#define dwClksPerFrame (uCyclesPerLine * uLinesPerFrame) // 17030
double g_fCurrentCLK6502 = CLK_6502;
bool g_bFullSpeed = false; // HACK TODO FIXME : prolly shouldn't be global anymore -- don't think it's necessary for speaker/soundcore/etc anymore ...
uint64_t g_nCumulativeCycles = 0; // cumulative cycles since emulator (re)start
int g_nCpuCyclesFeedback = 0;
// cycle counting
double cycles_persec_target = CLK_6502;
unsigned long long cycles_count_total = 0;
int cycles_speaker_feedback = 0;
int16_t cpu65_cycles_to_execute = 0; // cycles-to-execute by cpu65_run()
int16_t cpu65_cycle_count = 0; // cycles currently excuted by cpu65_run()
static int16_t cycles_checkpoint_count = 0;
static unsigned int g_dwCyclesThisFrame = 0;
static bool alt_speed_enabled = false;
// scaling and speed adjustments
static bool auto_adjust_speed = true;
double cpu_scale_factor = 1.0;
double cpu_altscale_factor = 1.0;
bool auto_adjust_speed = true;
int gc_cycles_timer_0 = 0;
int gc_cycles_timer_1 = 0;
bool is_fullspeed = false;
static bool alt_speed_enabled = false;
// misc
volatile uint8_t emul_reinitialize = 0;
pthread_t cpu_thread_id = 0;
static unsigned int g_nCyclesExecuted = 0; // # of cycles executed up to last IO access
// -----------------------------------------------------------------------------
struct timespec timespec_diff(struct timespec start, struct timespec end, bool *negative) {
@ -94,68 +110,59 @@ struct timespec timespec_diff(struct timespec start, struct timespec end, bool *
static inline struct timespec timespec_add(struct timespec start, unsigned long nsecs) {
start.tv_nsec += nsecs;
if (start.tv_nsec > NANOSECONDS)
if (start.tv_nsec > NANOSECONDS_PER_SECOND)
{
start.tv_sec += (start.tv_nsec / NANOSECONDS);
start.tv_nsec %= NANOSECONDS;
start.tv_sec += (start.tv_nsec / NANOSECONDS_PER_SECOND);
start.tv_nsec %= NANOSECONDS_PER_SECOND;
}
return start;
}
static void _timing_initialize(double scale)
{
if (g_bFullSpeed)
{
TIMING_LOG("timing_initialize() emulation at fullspeed ...");
return;
static void _timing_initialize(double scale) {
is_fullspeed = (scale >= CPU_SCALE_FASTEST);
if (!is_fullspeed) {
cycles_persec_target = CLK_6502 * scale;
}
g_fCurrentCLK6502 = CLK_6502 * scale;
// this is extracted out of SetClksPerSpkrSample -- speaker.c
#ifdef AUDIO_ENABLED
g_fClksPerSpkrSample = (double) (UINT) (g_fCurrentCLK6502 / (double)SPKR_SAMPLE_RATE);
SpkrReinitialize();
TIMING_LOG("timing_initialize() ... ClockRate:%0.2lf ClockCyclesPerSpeakerSample:%0.2lf", g_fCurrentCLK6502, g_fClksPerSpkrSample);
speaker_reset();
//TIMING_LOG("ClockRate:%0.2lf ClockCyclesPerSpeakerSample:%0.2lf", cycles_persec_target, speaker_cycles_per_sample());
#endif
}
static inline void _switch_to_fullspeed(void)
{
g_bFullSpeed = true;
void timing_initialize(void) {
_timing_initialize(alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor);
}
static inline void _switch_to_regular_speed(double scale)
{
g_bFullSpeed = false;
_timing_initialize(scale);
}
void timing_toggle_cpu_speed(void)
{
void timing_toggle_cpu_speed(void) {
alt_speed_enabled = !alt_speed_enabled;
double scale_factor = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor;
if (scale_factor >= CPU_SCALE_FASTEST)
{
_switch_to_fullspeed();
}
else
{
_switch_to_regular_speed(scale_factor);
}
timing_initialize();
}
void timing_initialize(void)
{
_timing_initialize(cpu_scale_factor);
void timing_set_auto_adjust_speed(bool auto_adjust) {
auto_adjust_speed = auto_adjust;
timing_initialize();
}
bool timing_should_auto_adjust_speed(void) {
double speed = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor;
return auto_adjust_speed && (speed < CPU_SCALE_FASTEST);
}
void *cpu_thread(void *dummyptr) {
assert(pthread_self() == cpu_thread_id);
#ifdef AUDIO_ENABLED
DSInit();
speaker_init();
MB_Initialize();
#endif
reinitialize();
struct timespec deltat;
struct timespec disk_motor_time;
struct timespec t0; // the target timer
struct timespec ti, tj; // actual time samples
bool negative = false;
@ -173,7 +180,6 @@ void *cpu_thread(void *dummyptr) {
do
{
g_nCumulativeCycles = 0;
LOG("cpu_thread : begin main loop ...");
clock_gettime(CLOCK_MONOTONIC, &t0);
@ -187,7 +193,9 @@ void *cpu_thread(void *dummyptr) {
deltat = timespec_diff(t0, ti, &negative);
if (deltat.tv_sec)
{
TIMING_LOG("NOTE : serious divergence from target time ...");
if (!is_fullspeed) {
TIMING_LOG("NOTE : serious divergence from target time ...");
}
t0 = ti;
deltat = timespec_diff(t0, ti, &negative);
}
@ -195,15 +203,15 @@ void *cpu_thread(void *dummyptr) {
drift_adj_nsecs = negative ? ~deltat.tv_nsec : deltat.tv_nsec;
// set up increment & decrement counters
cpu65_cycles_to_execute = (g_fCurrentCLK6502 / 1000); // g_fCurrentCLK6502 * EXECUTION_PERIOD_NSECS / NANOSECONDS
cpu65_cycles_to_execute += g_nCpuCyclesFeedback;
cpu65_cycles_to_execute = (cycles_persec_target / 1000); // cycles_persec_target * EXECUTION_PERIOD_NSECS / NANOSECONDS_PER_SECOND
if (!is_fullspeed) {
cpu65_cycles_to_execute += cycles_speaker_feedback;
}
if (cpu65_cycles_to_execute < 0)
{
cpu65_cycles_to_execute = 0;
}
g_nCyclesExecuted = 0;
#ifdef AUDIO_ENABLED
MB_StartOfCpuExecute();
#endif
@ -218,6 +226,7 @@ void *cpu_thread(void *dummyptr) {
}
cpu65_cycle_count = 0;
cycles_checkpoint_count = 0;
cpu65_run(); // run emulation for cpu65_cycles_to_execute cycles ...
if (is_debugging) {
@ -243,32 +252,23 @@ void *cpu_thread(void *dummyptr) {
#if DEBUG_TIMING
dbg_cycles_executed += cpu65_cycle_count;
#endif
unsigned int uActualCyclesExecuted = cpu65_cycle_count;
g_dwCyclesThisFrame += uActualCyclesExecuted;
g_dwCyclesThisFrame += cpu65_cycle_count;
#ifdef AUDIO_ENABLED
MB_UpdateCycles(uActualCyclesExecuted); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
MB_UpdateCycles(); // update 6522s (NOTE: do this before updating cycles_count_total)
#endif
// N.B.: IO calls that depend on accurate timing will update g_nCyclesExecuted
const int nRemainingCycles = uActualCyclesExecuted - g_nCyclesExecuted;
assert(nRemainingCycles >= 0);
g_nCumulativeCycles += nRemainingCycles;
timing_checkpoint_cycles();
#if CPU_TRACING
cpu65_trace_checkpoint();
#endif
if (!g_bFullSpeed)
{
#ifdef AUDIO_ENABLED
SpkrUpdate(uActualCyclesExecuted); // play audio
speaker_flush(); // play audio
#endif
}
if (g_dwCyclesThisFrame >= dwClksPerFrame)
{
if (g_dwCyclesThisFrame >= dwClksPerFrame) {
g_dwCyclesThisFrame -= dwClksPerFrame;
//VideoEndOfVideoFrame();
#ifdef AUDIO_ENABLED
MB_EndOfVideoFrame();
#endif
@ -278,29 +278,21 @@ void *cpu_thread(void *dummyptr) {
pthread_mutex_unlock(&interface_mutex);
// -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj
if (auto_adjust_speed) {
deltat = timespec_diff(disk6.motor_time, tj, &negative);
if (timing_should_auto_adjust_speed()) {
disk_motor_time = timespec_diff(disk6.motor_time, tj, &negative);
assert(!negative);
if (!g_bFullSpeed &&
if (!is_fullspeed &&
#ifdef AUDIO_ENABLED
!Spkr_IsActive() &&
!speaker_is_active() &&
#endif
!video_dirty() && (!disk6.motor_off && (deltat.tv_sec || deltat.tv_nsec > DISK_MOTOR_QUIET_NSECS)) )
!video_dirty() && (!disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS)) )
{
TIMING_LOG("auto switching to full speed");
_switch_to_fullspeed();
} else if (g_bFullSpeed && (
#ifdef AUDIO_ENABLED
Spkr_IsActive() ||
#endif
video_dirty() || (disk6.motor_off && (deltat.tv_sec || deltat.tv_nsec > DISK_MOTOR_QUIET_NSECS))) )
{
TIMING_LOG("auto switching to regular speed");
_switch_to_regular_speed(alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor);
_timing_initialize(CPU_SCALE_FASTEST);
}
}
if (!g_bFullSpeed) {
if (!is_fullspeed) {
deltat = timespec_diff(ti, tj, &negative);
assert(!negative);
long sleepfor = 0;
@ -328,19 +320,19 @@ void *cpu_thread(void *dummyptr) {
#if DEBUG_TIMING
// collect timing statistics
if (speaker_neg_feedback > g_nCpuCyclesFeedback)
if (speaker_neg_feedback > cycles_speaker_feedback)
{
speaker_neg_feedback = g_nCpuCyclesFeedback;
speaker_neg_feedback = cycles_speaker_feedback;
}
if (speaker_pos_feedback < g_nCpuCyclesFeedback)
if (speaker_pos_feedback < cycles_speaker_feedback)
{
speaker_pos_feedback = g_nCpuCyclesFeedback;
speaker_pos_feedback = cycles_speaker_feedback;
}
dbg_ticks += EXECUTION_PERIOD_NSECS;
if ((dbg_ticks % NANOSECONDS) == 0)
if ((dbg_ticks % NANOSECONDS_PER_SECOND) == 0)
{
LOG("tick:(%ld.%ld) real:(%ld.%ld) cycles exe: %d ... speaker feedback: %d/%d", t0.tv_sec, t0.tv_nsec, ti.tv_sec, ti.tv_nsec, dbg_cycles_executed, speaker_neg_feedback, speaker_pos_feedback);
TIMING_LOG("tick:(%ld.%ld) real:(%ld.%ld) cycles exe: %d ... speaker feedback: %d/%d", t0.tv_sec, t0.tv_nsec, ti.tv_sec, ti.tv_nsec, dbg_cycles_executed, speaker_neg_feedback, speaker_pos_feedback);
dbg_cycles_executed = 0;
dbg_ticks = 0;
speaker_neg_feedback = 0;
@ -348,6 +340,21 @@ void *cpu_thread(void *dummyptr) {
}
#endif
}
if (timing_should_auto_adjust_speed()) {
if (is_fullspeed && (
#ifdef AUDIO_ENABLED
speaker_is_active() ||
#endif
video_dirty() || (disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS))) )
{
double speed = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor;
if (speed < CPU_SCALE_FASTEST) {
TIMING_LOG("auto switching to configured speed");
_timing_initialize(speed);
}
}
}
} while (!emul_reinitialize);
reinitialize();
@ -356,23 +363,16 @@ void *cpu_thread(void *dummyptr) {
return NULL;
}
// From AppleWin...
unsigned int CpuGetCyclesThisVideoFrame(const unsigned int nExecutedCycles) {
CpuCalcCycles(nExecutedCycles);
return g_dwCyclesThisFrame + g_nCyclesExecuted;
unsigned int CpuGetCyclesThisVideoFrame(void) {
timing_checkpoint_cycles();
return g_dwCyclesThisFrame + cycles_checkpoint_count;
}
// Called when an IO-reg is accessed & accurate cycle info is needed
void CpuCalcCycles(const unsigned long nExecutedCycles) {
// Calc # of cycles executed since this func was last called
const long nCycles = nExecutedCycles - g_nCyclesExecuted;
assert(nCycles >= 0);
g_nCumulativeCycles += nCycles;
// HACK FIXME TODO
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
g_nCyclesExecuted = nExecutedCycles;
#pragma clang diagnostic pop
// Called when an IO-reg is accessed & accurate global cycle count info is needed
void timing_checkpoint_cycles(void) {
const int16_t d = cpu65_cycle_count - cycles_checkpoint_count;
assert(d >= 0);
cycles_count_total += d;
cycles_checkpoint_count = cpu65_cycle_count;
}

View File

@ -21,45 +21,71 @@
#include "common.h"
#define NANOSECONDS 1000000000
#if !defined(NANOSECONDS_PER_SECOND)
#define NANOSECONDS_PER_SECOND 1000000000
#endif
// timing values cribbed from AppleWin ... should double-check _Understanding the Apple IIe_
// timing values cribbed from AppleWin ... reference: Sather's _Understanding the Apple IIe_
// TODO: revisit this if/when attempting to actually sync up VBL/VSYNC to actual device vsync
// 14318181.81...
#define _M14 (157500000.0 / 11.0)
#define _M14 (157500000.0 / 11.0)
#define _M14_INT (157500000 / 11)
// 65 cycles per 912 14M clocks = 1020484.45...
#define CLK_6502 ((_M14 * 65.0) / 912.0)
#define CLK_6502 ((_M14 * 65.0) / 912.0)
#define CLK_6502_INT ((_M14_INT * 65) / 912)
#define CPU_SCALE_SLOWEST 0.25
#define CPU_SCALE_FASTEST 4.05
#define CPU_SCALE_STEP_DIV 0.01
#define CPU_SCALE_STEP 0.05
#ifdef INTERFACE_CLASSIC
# define CPU_SCALE_STEP_DIV 0.01
# define CPU_SCALE_STEP 0.05
#endif
#define SPKR_SAMPLE_RATE 22050
extern unsigned long long cycles_count_total;// cumulative cycles count from machine reset
extern double cycles_persec_target; // CLK_6502 * current CPU scale
extern int cycles_speaker_feedback; // current -/+ speaker requested feedback
extern double g_fCurrentCLK6502;
extern bool g_bFullSpeed;
extern uint64_t g_nCumulativeCycles;
extern int g_nCpuCyclesFeedback;
extern double cpu_scale_factor;
extern double cpu_altscale_factor;
extern bool auto_adjust_speed;
extern double cpu_scale_factor; // scale factor #1
extern double cpu_altscale_factor; // scale factor #2
extern bool is_fullspeed; // emulation in full native speed?
extern pthread_t cpu_thread_id;
extern int gc_cycles_timer_0;
extern int gc_cycles_timer_1;
/*
* calculate the difference between two timespec structures
*/
struct timespec timespec_diff(struct timespec start, struct timespec end, bool *negative);
/*
* toggles CPU speed between configured values
*/
void timing_toggle_cpu_speed(void);
/*
* (dis)allow automatic adjusting of CPU speed between presently configured and max (when loading disk images).
*/
void timing_set_auto_adjust_speed(bool auto_adjust);
/*
* check whether automatic adjusting of CPU speed is configured.
*/
bool timing_should_auto_adjust_speed(void);
/*
* initialize timing
*/
void timing_initialize(void);
/*
* timing/CPU thread entry point
*/
void *cpu_thread(void *ignored);
void CpuCalcCycles(const unsigned long nExecutedCycles);
/*
* checkpoints current cycle count and updates total (for timing-dependent I/O)
*/
void timing_checkpoint_cycles(void);
#endif // whole file

View File

@ -124,9 +124,9 @@ bool video_dirty(void);
/*
* VBL routines
*/
uint16_t video_scanner_get_address(bool *vblBarOut, const unsigned int executedCycles);
uint8_t floating_bus(const unsigned int executedCycles);
uint8_t floating_bus_hibit(const bool hibit, const unsigned int executedCycles);
uint16_t video_scanner_get_address(bool *vblBarOut);
uint8_t floating_bus(void);
uint8_t floating_bus_hibit(const bool hibit);
#endif /* !__ASSEMBLER__ */

112
src/vm.c
View File

@ -113,6 +113,10 @@
#include "common.h"
// joystick timer values
int gc_cycles_timer_0 = 0;
int gc_cycles_timer_1 = 0;
#if VM_TRACING
FILE *test_vm_fp = NULL;
@ -124,7 +128,7 @@ typedef struct vm_trace_range_t {
GLUE_C_READ(ram_nop)
{
return (cpu65_rw&MEM_WRITE_FLAG) ? 0x0 : floating_bus(cpu65_cycle_count);
return (cpu65_rw&MEM_WRITE_FLAG) ? 0x0 : floating_bus();
}
GLUE_C_READ(read_unmapped_softswitch)
@ -146,21 +150,13 @@ GLUE_C_READ(read_keyboard_strobe)
return apple_ii_64k[0][0xC000];
}
GLUE_C_READ(speaker_toggle)
{
#ifdef AUDIO_ENABLED
SpkrToggle();
#endif
return floating_bus(cpu65_cycle_count);
}
// ----------------------------------------------------------------------------
// graphics softswitches
GLUE_C_READ(iie_page2_off)
{
if (!(softswitches & SS_PAGE2)) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~(SS_PAGE2|SS_SCREEN);
@ -178,13 +174,13 @@ GLUE_C_READ(iie_page2_off)
video_setpage(0);
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_page2_on)
{
if (softswitches & SS_PAGE2) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_PAGE2;
@ -203,7 +199,7 @@ GLUE_C_READ(iie_page2_on)
video_setpage(1);
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_page2)
@ -217,7 +213,7 @@ GLUE_C_READ(read_switch_graphics)
softswitches &= ~SS_TEXT;
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(read_switch_text)
@ -226,7 +222,7 @@ GLUE_C_READ(read_switch_text)
softswitches |= SS_TEXT;
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_text)
@ -240,7 +236,7 @@ GLUE_C_READ(read_switch_no_mixed)
softswitches &= ~SS_MIXED;
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(read_switch_mixed)
@ -249,7 +245,7 @@ GLUE_C_READ(read_switch_mixed)
softswitches |= SS_MIXED;
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_mixed)
@ -262,13 +258,13 @@ GLUE_C_READ(iie_annunciator)
if ((ea >= 0xC058) && (ea <= 0xC05B)) {
// TODO: alternate joystick management?
}
return (cpu65_rw&MEM_WRITE_FLAG) ? 0x0 : floating_bus(cpu65_cycle_count);
return (cpu65_rw&MEM_WRITE_FLAG) ? 0x0 : floating_bus();
}
GLUE_C_READ(iie_hires_off)
{
if (!(softswitches & SS_HIRES)) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~(SS_HIRES|SS_HGRRD|SS_HGRWRT);
@ -286,13 +282,13 @@ GLUE_C_READ(iie_hires_off)
}
video_redraw();
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_hires_on)
{
if (softswitches & SS_HIRES) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_HIRES;
@ -310,7 +306,7 @@ GLUE_C_READ(iie_hires_on)
}
video_redraw();
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_hires)
@ -356,7 +352,7 @@ GLUE_C_READ(read_gc_strobe)
}
// NOTE (possible TODO FIXME): unimplemented GC2 and GC3 timers since they were not wired on the //e ...
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(read_gc0)
@ -381,12 +377,12 @@ GLUE_C_READ(read_gc1)
GLUE_C_READ(iie_read_gc2)
{
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_read_gc3)
{
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
// ----------------------------------------------------------------------------
@ -418,7 +414,7 @@ GLUE_C_READ(iie_c080)
if (softswitches & SS_ALTZP) {
_lc_to_auxmem();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_c081)
@ -438,7 +434,7 @@ GLUE_C_READ(iie_c081)
if (softswitches & SS_ALTZP) {
_lc_to_auxmem();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(lc_c082)
@ -452,7 +448,7 @@ GLUE_C_READ(lc_c082)
base_d000_wrt = 0;
base_e000_wrt = 0;
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_c083)
@ -470,7 +466,7 @@ GLUE_C_READ(iie_c083)
if (softswitches & SS_ALTZP) {
_lc_to_auxmem();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_c088)
@ -487,7 +483,7 @@ GLUE_C_READ(iie_c088)
if (softswitches & SS_ALTZP) {
_lc_to_auxmem();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_c089)
@ -507,7 +503,7 @@ GLUE_C_READ(iie_c089)
if (softswitches & SS_ALTZP) {
_lc_to_auxmem();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(lc_c08a)
@ -520,7 +516,7 @@ GLUE_C_READ(lc_c08a)
base_d000_wrt = 0;
base_e000_wrt = 0;
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_c08b)
@ -540,7 +536,7 @@ GLUE_C_READ(iie_c08b)
if (softswitches & SS_ALTZP) {
_lc_to_auxmem();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_bank)
@ -559,7 +555,7 @@ GLUE_C_READ(iie_check_lcram)
GLUE_C_READ(iie_80store_off)
{
if (!(softswitches & SS_80STORE)) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~(SS_80STORE|SS_TEXTRD|SS_TEXTWRT|SS_HGRRD|SS_HGRWRT);
@ -586,13 +582,13 @@ GLUE_C_READ(iie_80store_off)
video_setpage(1);
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_80store_on)
{
if (softswitches & SS_80STORE) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_80STORE;
@ -619,7 +615,7 @@ GLUE_C_READ(iie_80store_on)
softswitches &= ~SS_SCREEN;
video_setpage(0);
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_80store)
@ -630,7 +626,7 @@ GLUE_C_READ(iie_check_80store)
GLUE_C_READ(iie_ramrd_main)
{
if (!(softswitches & SS_RAMRD)) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~SS_RAMRD;
@ -647,13 +643,13 @@ GLUE_C_READ(iie_ramrd_main)
base_hgrrd = apple_ii_64k[0];
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_ramrd_aux)
{
if (softswitches & SS_RAMRD) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_RAMRD;
@ -670,7 +666,7 @@ GLUE_C_READ(iie_ramrd_aux)
base_hgrrd = apple_ii_64k[1];
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_ramrd)
@ -681,7 +677,7 @@ GLUE_C_READ(iie_check_ramrd)
GLUE_C_READ(iie_ramwrt_main)
{
if (!(softswitches & SS_RAMWRT)) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~SS_RAMWRT;
@ -698,13 +694,13 @@ GLUE_C_READ(iie_ramwrt_main)
base_hgrwrt = apple_ii_64k[0];
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_ramwrt_aux)
{
if (softswitches & SS_RAMWRT) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_RAMWRT;
@ -721,7 +717,7 @@ GLUE_C_READ(iie_ramwrt_aux)
base_hgrwrt = apple_ii_64k[1];
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_ramwrt)
@ -733,7 +729,7 @@ GLUE_C_READ_ALTZP(iie_altzp_main)
{
if (!(softswitches & SS_ALTZP)) {
/* NOTE : test if ALTZP already off - due to d000-bank issues it is *needed*, not just a shortcut */
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~SS_ALTZP;
@ -749,14 +745,14 @@ GLUE_C_READ_ALTZP(iie_altzp_main)
base_e000_wrt = language_card[0] - 0xE000;
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ_ALTZP(iie_altzp_aux)
{
if (softswitches & SS_ALTZP) {
/* NOTE : test if ALTZP already on - due to d000-bank issues it is *needed*, not just a shortcut */
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_ALTZP;
@ -764,7 +760,7 @@ GLUE_C_READ_ALTZP(iie_altzp_aux)
_lc_to_auxmem();
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_altzp)
@ -775,7 +771,7 @@ GLUE_C_READ(iie_check_altzp)
GLUE_C_READ(iie_80col_off)
{
if (!(softswitches & SS_80COL)) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches &= ~SS_80COL;
@ -784,13 +780,13 @@ GLUE_C_READ(iie_80col_off)
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_80col_on)
{
if (softswitches & SS_80COL) {
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
softswitches |= SS_80COL;
@ -799,7 +795,7 @@ GLUE_C_READ(iie_80col_on)
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_80col)
@ -813,7 +809,7 @@ GLUE_C_READ(iie_altchar_off)
softswitches &= ~SS_ALTCHAR;
c_set_primary_char();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_altchar_on)
@ -822,7 +818,7 @@ GLUE_C_READ(iie_altchar_on)
softswitches |= SS_ALTCHAR;
c_set_altchar();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_altchar)
@ -854,7 +850,7 @@ GLUE_C_READ(iie_dhires_on)
softswitches |= SS_DHIRES;
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_dhires_off)
@ -863,7 +859,7 @@ GLUE_C_READ(iie_dhires_off)
softswitches &= ~SS_DHIRES;
video_redraw();
}
return floating_bus(cpu65_cycle_count);
return floating_bus();
}
GLUE_C_READ(iie_check_dhires)
@ -875,7 +871,7 @@ GLUE_C_READ(iie_check_dhires)
GLUE_C_READ(iie_check_vbl)
{
bool vbl_bar = false;
video_scanner_get_address(&vbl_bar, cpu65_cycle_count);
video_scanner_get_address(&vbl_bar);
uint8_t key = apple_ii_64k[0][0xC000];
return (key & ~0x80) | (vbl_bar ? 0x80 : 0x00);
}