mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-04-02 09:29:31 +00:00
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:
parent
780905155a
commit
f234de116f
@ -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])
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
1664
src/audio/speaker.c
1664
src/audio/speaker.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
14
src/disk.c
14
src/disk.c
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1280,7 +1280,7 @@ void c_interface_parameters()
|
||||
#ifdef __linux__
|
||||
LOG("Back to Linux, w00t!\n");
|
||||
#endif
|
||||
SpkrDestroy();
|
||||
speaker_destroy();
|
||||
MB_Destroy();
|
||||
|
||||
video_shutdown();
|
||||
|
@ -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;
|
||||
}
|
||||
|
21
src/misc.c
21
src/misc.c
@ -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__)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
228
src/timing.c
228
src/timing.c
@ -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;
|
||||
}
|
||||
|
||||
|
64
src/timing.h
64
src/timing.h
@ -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
|
||||
|
@ -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
112
src/vm.c
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user