diff --git a/AppleWinExpress2019.vcxproj b/AppleWinExpress2019.vcxproj
index caabd4ce..657c1223 100644
--- a/AppleWinExpress2019.vcxproj
+++ b/AppleWinExpress2019.vcxproj
@@ -91,6 +91,8 @@
+
+
@@ -176,6 +178,7 @@
+
diff --git a/AppleWinExpress2019.vcxproj.filters b/AppleWinExpress2019.vcxproj.filters
index 36ee3cf2..8fcd1399 100644
--- a/AppleWinExpress2019.vcxproj.filters
+++ b/AppleWinExpress2019.vcxproj.filters
@@ -271,6 +271,9 @@
Source Files\Emulator
+
+ Source Files\Emulator
+
@@ -615,6 +618,12 @@
Source Files\Emulator
+
+ Source Files\Emulator
+
+
+ Source Files\Emulator
+
diff --git a/resource/Applewin.rc b/resource/Applewin.rc
index 54e1cdcb..59201b7a 100644
--- a/resource/Applewin.rc
+++ b/resource/Applewin.rc
@@ -168,10 +168,10 @@ BEGIN
LTEXT "&Mockingboard:",IDC_STATIC,49,39,51,8
CONTROL "Slider1",IDC_MB_VOLUME,"msctls_trackbar32",TBS_AUTOTICKS | TBS_VERT | TBS_BOTH | WS_TABSTOP,59,47,25,60
GROUPBOX "Sound Cards",IDC_STATIC,6,122,197,64
- CONTROL "Mockingboards (in slots 4 && 5)",IDC_MB_ENABLE,"Button",BS_AUTORADIOBUTTON,10,136,142,8
- CONTROL "Phasor (in slot 4)",IDC_PHASOR_ENABLE,"Button",BS_AUTORADIOBUTTON,10,149,92,10
- CONTROL "SAM/DAC (in slot 5)",IDC_SAM_ENABLE,"Button",BS_AUTORADIOBUTTON,10,162,95,10
- CONTROL "No sound cards",IDC_SOUNDCARD_DISABLE,"Button",BS_AUTORADIOBUTTON,10,175,78,10
+ LTEXT "Slot &4:",IDC_STATIC,16,136,84,10
+ COMBOBOX IDC_SOUNDCARD_SLOT4,45,134,100,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Slot &5:",IDC_STATIC,16,152,82,10
+ COMBOBOX IDC_SOUNDCARD_SLOT5,45,150,100,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
END
IDD_PROPPAGE_DISK DIALOGEX 0, 0, 210, 240
diff --git a/resource/resource.h b/resource/resource.h
index b41b5a9f..cbe032fc 100644
--- a/resource/resource.h
+++ b/resource/resource.h
@@ -30,7 +30,6 @@
#define IDR_APPLE2_PLUS_ROM 127
#define IDR_APPLE2E_ROM 128
#define IDR_APPLE2E_ENHANCED_ROM 129
-#define IDC_MB_ENABLE 130
#define IDD_TFE_SETTINGS_DIALOG 131
#define IDR_PRINTDRVR_FW 132
#define IDD_PROPPAGE_ADVANCED 132
@@ -68,9 +67,8 @@
#define IDC_PASTE_FROM_CLIPBOARD 1023
#define IDC_SPIN_XTRIM 1026
#define IDC_SPIN_YTRIM 1027
-#define IDC_PHASOR_ENABLE 1029
-#define IDC_SAM_ENABLE 1030
-#define IDC_SOUNDCARD_DISABLE 1031
+#define IDC_SOUNDCARD_SLOT4 1028
+#define IDC_SOUNDCARD_SLOT5 1029
#define IDC_TFE_SETTINGS_ENABLE_T 1032
#define IDC_TFE_SETTINGS_ENABLE 1033
#define IDC_TFE_SETTINGS_INTERFACE_T 1034
diff --git a/source/6522.cpp b/source/6522.cpp
index 90be685e..5c43fc21 100644
--- a/source/6522.cpp
+++ b/source/6522.cpp
@@ -29,6 +29,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "StdAfx.h"
#include "6522.h"
+#include "CardManager.h"
+#include "Mockingboard.h"
#include "Core.h"
#include "CPU.h"
#include "Memory.h"
@@ -115,8 +117,6 @@ USHORT SY6522::SetTimerSyncEvent(BYTE reg, USHORT timerLatch)
//-----------------------------------------------------------------------------
-extern void MB_UpdateIRQ(void);
-
void SY6522::UpdateIFR(BYTE clr_ifr, BYTE set_ifr /*= 0*/)
{
m_regs.IFR &= ~clr_ifr;
@@ -127,7 +127,8 @@ void SY6522::UpdateIFR(BYTE clr_ifr, BYTE set_ifr /*= 0*/)
else
m_regs.IFR &= ~IFR_IRQ;
- MB_UpdateIRQ();
+ if (GetCardMgr().GetObj(m_slot)) // If called from MockingboardCard ctor, then CardManager::m_slot[slot] == NULL
+ dynamic_cast(GetCardMgr().GetRef(m_slot)).UpdateIRQ();
}
//-----------------------------------------------------------------------------
diff --git a/source/6522.h b/source/6522.h
index 07877b43..aa25cb23 100644
--- a/source/6522.h
+++ b/source/6522.h
@@ -3,7 +3,7 @@
class SY6522
{
public:
- SY6522(void)
+ SY6522(UINT slot) : m_slot(slot)
{
for (UINT i = 0; i < kNumTimersPer6522; i++)
m_syncEvent[i] = NULL;
@@ -140,4 +140,5 @@ private:
bool m_timer2Active;
class SyncEvent* m_syncEvent[kNumTimersPer6522];
+ UINT m_slot;
};
diff --git a/source/AY8910.cpp b/source/AY8910.cpp
index afbcb23f..602f165c 100644
--- a/source/AY8910.cpp
+++ b/source/AY8910.cpp
@@ -42,7 +42,7 @@
* very high at all. And I speak as a Cyrix owner. :-)
*/
-libspectrum_signed_word** g_ppSoundBuffers; // Used to pass param to sound_ay_overlay()
+//libspectrum_signed_word** g_ppSoundBuffers; // Used to pass param to sound_ay_overlay() // Moved to class AY8913
/* configuration */
//int sound_enabled = 0; /* Are we currently using the sound card */
@@ -69,7 +69,7 @@ libspectrum_signed_word** g_ppSoundBuffers; // Used to pass param to sound_ay_ov
* given the number of port writes theoretically possible in a
* 50th I think this should be plenty.
*/
-//#define AY_CHANGE_MAX 8000 // [TC] Moved into AY8910.h
+//#define AY_CHANGE_MAX 8000 // [TC] Moved into class AY8910.h
///* frequency to generate sound at for hifi sound */
//#define HIFI_FREQ 88200
@@ -78,14 +78,14 @@ libspectrum_signed_word** g_ppSoundBuffers; // Used to pass param to sound_ay_ov
static SRC_STATE *src_state;
#endif /* #ifdef HAVE_SAMPLERATE */
-int sound_generator_framesiz;
-int sound_framesiz;
+//static int sound_generator_framesiz; // Moved to class AY8913
+//int sound_framesiz; // unused
-static int sound_generator_freq;
+//static int sound_generator_freq; // Moved to class AY8913
-static int sound_channels;
+//static int sound_channels; // unused
-static unsigned int ay_tone_levels[16];
+//static unsigned int ay_tone_levels[16]; // Moved to class AY8913
//static libspectrum_signed_word *sound_buf, *tape_buf;
//static float *convert_input_buffer, *convert_output_buffer;
@@ -108,10 +108,10 @@ static int rstereopos, rchan1pos, rchan2pos, rchan3pos;
// Statics:
-double CAY8910::m_fCurrentCLK_AY8910 = 0.0;
+double AY8913::m_fCurrentCLK_AY8910 = 0.0;
-void CAY8910::init(void)
+void AY8913::init(void)
{
// Init the statics that were in sound_ay_overlay()
rng = 1;
@@ -119,14 +119,14 @@ void CAY8910::init(void)
env_first = 1; env_rev = 0; env_counter = 15;
}
-CAY8910::CAY8910(void)
+AY8913::AY8913(void)
{
init();
m_fCurrentCLK_AY8910 = g_fCurrentCLK6502;
};
-void CAY8910::sound_ay_init( void )
+void AY8913::sound_ay_init( void )
{
/* AY output doesn't match the claimed levels; these levels are based
* on the measurements posted to comp.sys.sinclair in Dec 2001 by
@@ -155,7 +155,7 @@ void CAY8910::sound_ay_init( void )
}
-void CAY8910::sound_init( const char *device )
+void AY8913::sound_init( const char *device )
{
// static int first_init = 1;
// int f, ret;
@@ -202,7 +202,7 @@ void CAY8910::sound_init( const char *device )
sound_channels = ( sound_stereo ? 2 : 1 );
#endif
- sound_channels = 3; // 3 mono channels: ABC
+// sound_channels = 3; // 3 mono channels: ABC
// hz = ( float ) machine_current->timings.processor_speed /
// machine_current->timings.tstates_per_frame;
@@ -230,7 +230,7 @@ void CAY8910::sound_init( const char *device )
#endif
// sound_framesiz = ( float ) settings_current.sound_freq / hz;
- sound_framesiz = sound_generator_freq / (int)hz;
+// sound_framesiz = sound_generator_freq / (int)hz;
#ifdef HAVE_SAMPLERATE
if( settings_current.sound_hifi ) {
@@ -338,7 +338,7 @@ sound_unpause( void )
#endif
-void CAY8910::sound_end( void )
+void AY8913::sound_end( void )
{
#if 0
if( sound_enabled ) {
@@ -470,7 +470,7 @@ sound_write_buf_pstereo( libspectrum_signed_word * out, int c )
#define HZ_COMMON_DENOMINATOR 50
#include "Log.h"
-void CAY8910::sound_ay_overlay( void )
+void AY8913::sound_ay_overlay( void )
{
int tone_level[3];
int mixer, envshape;
@@ -511,9 +511,9 @@ void CAY8910::sound_ay_overlay( void )
}
#endif
- libspectrum_signed_word* pBuf1 = g_ppSoundBuffers[0];
- libspectrum_signed_word* pBuf2 = g_ppSoundBuffers[1];
- libspectrum_signed_word* pBuf3 = g_ppSoundBuffers[2];
+ libspectrum_signed_word* pBuf1 = ppSoundBuffers[0];
+ libspectrum_signed_word* pBuf2 = ppSoundBuffers[1];
+ libspectrum_signed_word* pBuf3 = ppSoundBuffers[2];
// for( f = 0, ptr = sound_buf; f < sound_generator_framesiz; f++ ) {
for( f = 0; f < sound_generator_framesiz; f++ ) {
@@ -725,7 +725,7 @@ void CAY8910::sound_ay_overlay( void )
}
}
-BYTE CAY8910::sound_ay_read( int reg )
+BYTE AY8913::sound_ay_read( int reg )
{
reg &= 15;
@@ -773,7 +773,7 @@ BYTE CAY8910::sound_ay_read( int reg )
/* don't make the change immediately; record it for later,
* to be made by sound_frame() (via sound_ay_overlay()).
*/
-void CAY8910::sound_ay_write( int reg, int val, libspectrum_dword now )
+void AY8913::sound_ay_write( int reg, int val, libspectrum_dword now )
{
if( ay_change_count < AY_CHANGE_MAX ) {
ay_change[ ay_change_count ].tstates = now;
@@ -787,7 +787,7 @@ void CAY8910::sound_ay_write( int reg, int val, libspectrum_dword now )
/* no need to call this initially, but should be called
* on reset otherwise.
*/
-void CAY8910::sound_ay_reset( void )
+void AY8913::sound_ay_reset( void )
{
int f;
@@ -868,7 +868,7 @@ sound_resample( void )
}
#endif /* #ifdef HAVE_SAMPLERATE */
-void CAY8910::sound_frame( void )
+void AY8913::sound_frame( void )
{
#if 0
libspectrum_signed_word *ptr, *tptr;
@@ -1046,7 +1046,7 @@ sound_beeper( int is_tape, int on )
#define SS_YAML_KEY_CHANGE "Change"
#define SS_YAML_VALUE_CHANGE_FORMAT "%d, %d, 0x%1X, 0x%02X"
-void CAY8910::SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const std::string& suffix)
+void AY8913::SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const std::string& suffix)
{
std::string unit = std::string(SS_YAML_KEY_AY8910) + suffix;
YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", unit.c_str());
@@ -1101,7 +1101,7 @@ void CAY8910::SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const std::string& su
}
}
-bool CAY8910::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, const std::string& suffix)
+bool AY8913::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, const std::string& suffix)
{
std::string unit = std::string(SS_YAML_KEY_AY8910) + suffix;
if (!yamlLoadHelper.GetSubMap(unit))
@@ -1192,86 +1192,3 @@ bool CAY8910::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, const std::string& su
return true;
}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// AY8910 interface
-
-#include "CPU.h" // For g_nCumulativeCycles
-
-static CAY8910 g_AY8910[MAX_8910];
-static unsigned __int64 g_uLastCumulativeCycles = 0;
-
-BYTE AYReadReg(int chip, int r)
-{
- return g_AY8910[chip].sound_ay_read(r);
-}
-
-void _AYWriteReg(int chip, int r, int v)
-{
- libspectrum_dword uOffset = (libspectrum_dword) (g_nCumulativeCycles - g_uLastCumulativeCycles);
- g_AY8910[chip].sound_ay_write(r, v, uOffset);
-}
-
-void AY8910_reset(int chip)
-{
- // Don't reset the AY CLK, as this is a property of the card (MB/Phasor), not the AY chip
- g_AY8910[chip].sound_ay_reset(); // Calls: sound_ay_init();
-}
-
-void AY8910UpdateSetCycles()
-{
- g_uLastCumulativeCycles = g_nCumulativeCycles;
-}
-
-void AY8910Update(int chip, INT16** buffer, int nNumSamples)
-{
- AY8910UpdateSetCycles();
-
- sound_generator_framesiz = nNumSamples;
- g_ppSoundBuffers = buffer;
- g_AY8910[chip].sound_frame();
-}
-
-void AY8910_InitAll(int nClock, int nSampleRate)
-{
- for (UINT i=0; i= MAX_8910)
- return NULL;
-
- return g_AY8910[uChip].GetAYRegsPtr();
-}
-
-UINT AY8910_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, UINT uChip, const std::string& suffix)
-{
- if (uChip >= MAX_8910)
- return 0;
-
- g_AY8910[uChip].SaveSnapshot(yamlSaveHelper, suffix);
- return 1;
-}
-
-UINT AY8910_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT uChip, const std::string& suffix)
-{
- if (uChip >= MAX_8910)
- return 0;
-
- return g_AY8910[uChip].LoadSnapshot(yamlLoadHelper, suffix) ? 1 : 0;
-}
diff --git a/source/AY8910.h b/source/AY8910.h
index 0dc272b1..4c613be3 100644
--- a/source/AY8910.h
+++ b/source/AY8910.h
@@ -1,26 +1,5 @@
#pragma once
-#define MAX_8910 4
-
-BYTE AYReadReg(int chip, int r); // TC
-
-//-------------------------------------
-// MAME interface
-
-void _AYWriteReg(int chip, int r, int v);
-//void AY8910_write_ym(int chip, int addr, int data);
-void AY8910_reset(int chip);
-void AY8910Update(int chip, INT16** buffer, int nNumSamples);
-
-void AY8910_InitAll(int nClock, int nSampleRate);
-void AY8910_InitClock(int nClock);
-BYTE* AY8910_GetRegsPtr(UINT uChip);
-
-void AY8910UpdateSetCycles();
-
-UINT AY8910_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, UINT uChip, const std::string& suffix);
-UINT AY8910_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT uChip, const std::string& suffix);
-
//-------------------------------------
// FUSE stuff
@@ -34,11 +13,11 @@ typedef SHORT libspectrum_signed_word;
*/
#define AY_CHANGE_MAX 8000
-class CAY8910
+class AY8913
{
public:
- CAY8910();
- virtual ~CAY8910() {};
+ AY8913(void);
+ ~AY8913(void) {};
void sound_ay_init( void );
void sound_init( const char *device );
@@ -47,6 +26,8 @@ public:
void sound_ay_reset( void );
void sound_frame( void );
BYTE* GetAYRegsPtr( void ) { return &sound_ay_registers[0]; }
+ void SetFramesize(int frameSize) { sound_generator_framesiz = frameSize; }
+ void SetSoundBuffers(INT16** buffers) { ppSoundBuffers = buffers; }
static void SetCLK( double CLK ) { m_fCurrentCLK_AY8910 = CLK; }
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const std::string& suffix);
bool LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, const std::string& suffix);
@@ -86,6 +67,12 @@ private:
int noise_toggle;
int env_first, env_rev, env_counter;
+ // Vars
+ libspectrum_signed_word** ppSoundBuffers; // Used to pass param to sound_ay_overlay()
+ int sound_generator_framesiz;
+ int sound_generator_freq;
+ unsigned int ay_tone_levels[16];
+
// Vars shared between all AY's
static double m_fCurrentCLK_AY8910;
};
diff --git a/source/CPU.cpp b/source/CPU.cpp
index 14c54152..9b04deca 100644
--- a/source/CPU.cpp
+++ b/source/CPU.cpp
@@ -441,7 +441,7 @@ static __forceinline bool IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
CYC(7);
#if defined(_DEBUG) && LOG_IRQ_TAKEN_AND_RTI
std::string irq6522;
- MB_Get6522IrqDescription(irq6522);
+ GetCardMgr().GetMockingboardCardMgr().Get6522IrqDescription(irq6522);
const char* pSrc = (g_bmIRQ & 1) ? irq6522.c_str() :
(g_bmIRQ & 2) ? "SPEECH" :
(g_bmIRQ & 4) ? "SSC" :
@@ -614,7 +614,7 @@ DWORD CpuExecute(const DWORD uCycles, const bool bVideoUpdate)
g_interruptInLastExecutionBatch = false;
#ifdef _DEBUG
- MB_CheckCumulativeCycles();
+ GetCardMgr().GetMockingboardCardMgr().CheckCumulativeCycles();
#endif
// uCycles:
@@ -625,7 +625,7 @@ DWORD CpuExecute(const DWORD uCycles, const bool bVideoUpdate)
// Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
// . Ensures that 6522 regs are up-to-date for any potential save-state
// . SyncEvent will trigger the 6522 TIMER1/2 underflow on the correct cycle
- MB_UpdateCycles(uExecutedCycles);
+ GetCardMgr().GetMockingboardCardMgr().UpdateCycles(uExecutedCycles);
const UINT nRemainingCycles = uExecutedCycles - g_nCyclesExecuted;
g_nCumulativeCycles += nRemainingCycles;
diff --git a/source/Card.cpp b/source/Card.cpp
index 7f45d5dd..a66f5725 100644
--- a/source/Card.cpp
+++ b/source/Card.cpp
@@ -71,17 +71,11 @@ void DummyCard::InitializeIO(LPBYTE pCxRomPeripheral)
{
case CT_GenericClock:
break; // nothing to do
- case CT_MockingboardC:
- case CT_Phasor:
- // only in slot 4
- if (m_slot == SLOT4)
- {
- MB_InitializeIO(pCxRomPeripheral, SLOT4, SLOT5);
- }
- break;
case CT_Z80:
Z80_InitializeIO(pCxRomPeripheral, m_slot);
break;
+ default:
+ _ASSERT(0);
}
}
@@ -89,13 +83,10 @@ void DummyCard::Update(const ULONG nExecutedCycles)
{
switch (QueryType())
{
- case CT_MockingboardC:
- case CT_Phasor:
- // only in slot 4
- if (m_slot == SLOT4)
- {
- MB_PeriodicUpdate(nExecutedCycles);
- }
+ case CT_Z80:
+ break; // nothing to do
+ default:
+ _ASSERT(0);
break;
}
}
@@ -104,14 +95,10 @@ void DummyCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
switch (QueryType())
{
- case CT_MockingboardC:
- MB_SaveSnapshot(yamlSaveHelper, m_slot);
- break;
- case CT_Phasor:
- Phasor_SaveSnapshot(yamlSaveHelper, m_slot);
- break;
case CT_Z80:
Z80_SaveSnapshot(yamlSaveHelper, m_slot);
+ default:
+ _ASSERT(0);
break;
}
}
@@ -120,12 +107,10 @@ bool DummyCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
switch (QueryType())
{
- case CT_MockingboardC:
- return MB_LoadSnapshot(yamlLoadHelper, m_slot, version);
- case CT_Phasor:
- return Phasor_LoadSnapshot(yamlLoadHelper, m_slot, version);
case CT_Z80:
return Z80_LoadSnapshot(yamlLoadHelper, m_slot, version);
+ default:
+ _ASSERT(0);
}
return false;
}
@@ -150,7 +135,7 @@ std::string Card::GetCardName(const SS_CARDTYPE cardType)
case CT_SSC:
return CSuperSerialCard::GetSnapshotCardName();
case CT_MockingboardC:
- return MB_GetSnapshotCardName();
+ return MockingboardCard::GetSnapshotCardName();
case CT_GenericPrinter:
return ParallelPrinterCard::GetSnapshotCardName();
case CT_GenericHDD:
@@ -162,7 +147,7 @@ std::string Card::GetCardName(const SS_CARDTYPE cardType)
case CT_Z80:
return Z80_GetSnapshotCardName();
case CT_Phasor:
- return Phasor_GetSnapshotCardName();
+ return MockingboardCard::GetSnapshotCardNamePhasor();
case CT_Echo:
return "Echo";
case CT_SAM:
@@ -200,11 +185,11 @@ SS_CARDTYPE Card::GetCardType(const std::string & card)
{
return CT_Z80;
}
- else if (card == MB_GetSnapshotCardName())
+ else if (card == MockingboardCard::GetSnapshotCardName())
{
return CT_MockingboardC;
}
- else if (card == Phasor_GetSnapshotCardName())
+ else if (card == MockingboardCard::GetSnapshotCardNamePhasor())
{
return CT_Phasor;
}
diff --git a/source/CardManager.cpp b/source/CardManager.cpp
index 92a55195..cbb036c2 100644
--- a/source/CardManager.cpp
+++ b/source/CardManager.cpp
@@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "Disk.h"
#include "FourPlay.h"
#include "Harddisk.h"
+#include "Mockingboard.h"
#include "MouseInterface.h"
#include "ParallelPrinter.h"
#include "SAM.h"
@@ -65,7 +66,7 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
m_slot[slot] = m_pSSC = new CSuperSerialCard(slot);
break;
case CT_MockingboardC:
- m_slot[slot] = new DummyCard(type, slot);
+ m_slot[slot] = new MockingboardCard(slot, type);
break;
case CT_GenericPrinter:
_ASSERT(m_pParallelPrinterCard == NULL);
@@ -87,7 +88,7 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
m_slot[slot] = new DummyCard(type, slot);
break;
case CT_Phasor:
- m_slot[slot] = new DummyCard(type, slot);
+ m_slot[slot] = new MockingboardCard(slot, type);
break;
case CT_Echo:
m_slot[slot] = new DummyCard(type, slot);
@@ -237,6 +238,33 @@ void CardManager::InitializeIO(LPBYTE pCxRomPeripheral)
}
}
+void CardManager::Destroy()
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; ++i)
+ {
+ if (m_slot[i])
+ {
+ m_slot[i]->Destroy();
+ }
+ }
+
+ GetCardMgr().GetDisk2CardMgr().Destroy();
+ GetCardMgr().GetMockingboardCardMgr().Destroy();
+}
+
+void CardManager::Reset(const bool powerCycle)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; ++i)
+ {
+ if (m_slot[i])
+ {
+ m_slot[i]->Reset(powerCycle);
+ }
+ }
+
+ GetCardMgr().GetMockingboardCardMgr().Reset(powerCycle);
+}
+
void CardManager::Update(const ULONG nExecutedCycles)
{
for (UINT i = SLOT0; i < NUM_SLOTS; ++i)
@@ -246,6 +274,8 @@ void CardManager::Update(const ULONG nExecutedCycles)
m_slot[i]->Update(nExecutedCycles);
}
}
+
+ GetCardMgr().GetMockingboardCardMgr().Update(nExecutedCycles);
}
void CardManager::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
@@ -258,25 +288,3 @@ void CardManager::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
}
}
}
-
-void CardManager::Reset(const bool powerCycle)
-{
- for (UINT i = SLOT0; i < NUM_SLOTS; ++i)
- {
- if (m_slot[i])
- {
- m_slot[i]->Reset(powerCycle);
- }
- }
-}
-
-void CardManager::Destroy()
-{
- for (UINT i = SLOT0; i < NUM_SLOTS; ++i)
- {
- if (m_slot[i])
- {
- m_slot[i]->Destroy();
- }
- }
-}
diff --git a/source/CardManager.h b/source/CardManager.h
index b1edd992..42a52db5 100644
--- a/source/CardManager.h
+++ b/source/CardManager.h
@@ -2,6 +2,7 @@
#include "Card.h"
#include "Disk2CardManager.h"
+#include "MockingboardCardManager.h"
#include "Common.h"
class CardManager
@@ -51,6 +52,7 @@ public:
//
Disk2CardManager& GetDisk2CardMgr(void) { return m_disk2CardMgr; }
+ MockingboardCardManager& GetMockingboardCardMgr(void) { return m_mockingboardCardMgr; }
class CMouseInterface* GetMouseCard(void) { return m_pMouseCard; }
bool IsMouseCardInstalled(void) { return m_pMouseCard != NULL; }
class CSuperSerialCard* GetSSC(void) { return m_pSSC; }
@@ -61,8 +63,8 @@ public:
class LanguageCardUnit* GetLanguageCard(void) { return m_pLanguageCard; }
void InitializeIO(LPBYTE pCxRomPeripheral);
+ void Destroy(void);
void Reset(const bool powerCycle);
- void Destroy();
void Update(const ULONG nExecutedCycles);
void SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
@@ -75,6 +77,7 @@ private:
Card* m_slot[NUM_SLOTS];
Card* m_aux;
Disk2CardManager m_disk2CardMgr;
+ MockingboardCardManager m_mockingboardCardMgr;
class CMouseInterface* m_pMouseCard;
class CSuperSerialCard* m_pSSC;
class LanguageCardUnit* m_pLanguageCard;
diff --git a/source/Configuration/PageSound.cpp b/source/Configuration/PageSound.cpp
index 1681de02..38c52f32 100644
--- a/source/Configuration/PageSound.cpp
+++ b/source/Configuration/PageSound.cpp
@@ -40,6 +40,13 @@ const TCHAR CPageSound::m_soundchoices[] = TEXT("Disabled\0")
TEXT("Sound Card\0");
+const char CPageSound::m_soundCardChoices[] = "Mockingboard\0"
+ "Phasor\0"
+ "SAM\0"
+ "Empty\0";
+
+const char CPageSound::m_soundCardChoice_Unavailable[] = "Unavailable\0\0"; // doubly-null terminate
+
INT_PTR CALLBACK CPageSound::DlgProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam)
{
// Switch from static func to our instance
@@ -85,21 +92,25 @@ INT_PTR CPageSound::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPAR
break;
case IDC_MB_VOLUME:
break;
- case IDC_MB_ENABLE:
- if (NewSoundcardConfigured(hWnd, wparam, CT_MockingboardC))
- InitOptions(hWnd); // re-init
- break;
- case IDC_PHASOR_ENABLE:
- if (NewSoundcardConfigured(hWnd, wparam, CT_Phasor))
- InitOptions(hWnd); // re-init
- break;
- case IDC_SAM_ENABLE:
- if (NewSoundcardConfigured(hWnd, wparam, CT_SAM))
- InitOptions(hWnd); // re-init
- break;
- case IDC_SOUNDCARD_DISABLE:
- if (NewSoundcardConfigured(hWnd, wparam, CT_Empty))
- InitOptions(hWnd); // re-init
+ case IDC_SOUNDCARD_SLOT4:
+ case IDC_SOUNDCARD_SLOT5:
+ if (HIWORD(wparam) == CBN_SELCHANGE)
+ {
+ UINT slot = (LOWORD(wparam) == IDC_SOUNDCARD_SLOT4) ? SLOT4 : SLOT5;
+ DWORD newChoiceItem = (DWORD)SendDlgItemMessage(hWnd, LOWORD(wparam), CB_GETCURSEL, 0, 0);
+
+ SS_CARDTYPE newCard = CT_Empty;
+ switch (newChoiceItem)
+ {
+ case SC_MOCKINGBOARD: newCard = CT_MockingboardC; break;
+ case SC_PHASOR: newCard = CT_Phasor; break;
+ case SC_SAM: newCard = CT_SAM; break;
+ case SC_EMPTY: newCard = CT_Empty; break;
+ default: _ASSERT(0); break;
+ }
+
+ m_PropertySheetHelper.GetConfigNew().m_Slot[slot] = newCard;
+ }
break;
}
break;
@@ -116,12 +127,7 @@ INT_PTR CPageSound::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPAR
SendDlgItemMessage(hWnd,IDC_MB_VOLUME,TBM_SETRANGE,1,MAKELONG(VOLUME_MIN,VOLUME_MAX));
SendDlgItemMessage(hWnd,IDC_MB_VOLUME,TBM_SETPAGESIZE,0,10);
SendDlgItemMessage(hWnd,IDC_MB_VOLUME,TBM_SETTICFREQ,10,0);
- SendDlgItemMessage(hWnd,IDC_MB_VOLUME,TBM_SETPOS,1,MB_GetVolume());
-
- if (GetCardMgr().QuerySlot(SLOT5) == CT_SAM)
- m_NewCardType = CT_SAM;
- else
- m_NewCardType = MB_GetSoundcardType(); // Reinit 1st time page is activated (fires before PSN_SETACTIVE)
+ SendDlgItemMessage(hWnd,IDC_MB_VOLUME,TBM_SETPOS,1, GetCardMgr().GetMockingboardCardMgr().GetVolume());
InitOptions(hWnd);
@@ -145,95 +151,44 @@ void CPageSound::DlgOK(HWND hWnd)
// NB. Volume: 0=Loudest, VOLUME_MAX=Silence
SpkrSetVolume(dwSpkrVolume, VOLUME_MAX);
- MB_SetVolume(dwMBVolume, VOLUME_MAX);
+ GetCardMgr().GetMockingboardCardMgr().SetVolume(dwMBVolume, VOLUME_MAX);
REGSAVE(TEXT(REGVALUE_SPKR_VOLUME), SpkrGetVolume());
- REGSAVE(TEXT(REGVALUE_MB_VOLUME), MB_GetVolume());
+ REGSAVE(TEXT(REGVALUE_MB_VOLUME), GetCardMgr().GetMockingboardCardMgr().GetVolume());
m_PropertySheetHelper.PostMsgAfterClose(hWnd, m_Page);
}
+CPageSound::SOUNDCARDCHOICE CPageSound::CardTypeToComboItem(SS_CARDTYPE card)
+{
+ switch (card)
+ {
+ case CT_MockingboardC: return SC_MOCKINGBOARD;
+ case CT_Phasor: return SC_PHASOR;
+ case CT_SAM: return SC_SAM;
+ case CT_Empty: return SC_EMPTY;
+ default: _ASSERT(0); return SC_EMPTY;
+ }
+}
+
void CPageSound::InitOptions(HWND hWnd)
{
- // CheckRadioButton
- if(m_NewCardType == CT_MockingboardC)
- m_nCurrentIDCheckButton = IDC_MB_ENABLE;
- else if(m_NewCardType == CT_Phasor)
- m_nCurrentIDCheckButton = IDC_PHASOR_ENABLE;
- else if(m_NewCardType == CT_SAM)
- m_nCurrentIDCheckButton = IDC_SAM_ENABLE;
+ const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[4];
+ const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[5];
+
+ bool isSlot4SoundCard = slot4 == CT_MockingboardC || slot4 == CT_Phasor || slot4 == CT_SAM || slot4 == CT_Empty;
+ bool isSlot5SoundCard = slot5 == CT_MockingboardC || slot5 == CT_Phasor || slot5 == CT_SAM || slot5 == CT_Empty;
+
+ if (isSlot4SoundCard)
+ m_PropertySheetHelper.FillComboBox(hWnd, IDC_SOUNDCARD_SLOT4, m_soundCardChoices, (int)CardTypeToComboItem(slot4));
else
- m_nCurrentIDCheckButton = IDC_SOUNDCARD_DISABLE;
+ m_PropertySheetHelper.FillComboBox(hWnd, IDC_SOUNDCARD_SLOT4, m_soundCardChoice_Unavailable, 0);
- CheckRadioButton(hWnd, IDC_MB_ENABLE, IDC_SOUNDCARD_DISABLE, m_nCurrentIDCheckButton);
-
- //
-
- const SS_CARDTYPE Slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[4];
- const SS_CARDTYPE Slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[5];
-
- const bool bIsSlot4Empty = Slot4 == CT_Empty;
- const bool bIsSlot5Empty = Slot5 == CT_Empty;
-
- // Phasor button
- {
- const BOOL bEnable = bIsSlot4Empty || Slot4 == CT_MockingboardC || Slot4 == CT_Phasor;
- EnableWindow(GetDlgItem(hWnd, IDC_PHASOR_ENABLE), bEnable); // Disable Phasor (slot 4)
- }
-
- // Mockingboard button
- {
- const BOOL bEnable = (bIsSlot4Empty || Slot4 == CT_Phasor || Slot4 == CT_MockingboardC) &&
- (bIsSlot5Empty || Slot5 == CT_SAM || Slot5 == CT_MockingboardC);
- EnableWindow(GetDlgItem(hWnd, IDC_MB_ENABLE), bEnable); // Disable Mockingboard (slot 4 & 5)
- }
-
- // SAM button
- {
- const BOOL bEnable = bIsSlot5Empty || Slot5 == CT_MockingboardC || Slot5 == CT_SAM;
- EnableWindow(GetDlgItem(hWnd, IDC_SAM_ENABLE), bEnable); // Disable SAM (slot 5)
- }
-
- EnableWindow(GetDlgItem(hWnd, IDC_MB_VOLUME), (m_nCurrentIDCheckButton != IDC_SOUNDCARD_DISABLE) ? TRUE : FALSE);
-}
-
-bool CPageSound::NewSoundcardConfigured(HWND hWnd, WPARAM wparam, SS_CARDTYPE NewCardType)
-{
- if (HIWORD(wparam) != BN_CLICKED)
- return false;
-
- if (LOWORD(wparam) == m_nCurrentIDCheckButton)
- return false;
-
- m_NewCardType = NewCardType;
-
- const SS_CARDTYPE Slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[4];
- const SS_CARDTYPE Slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[5];
-
- if (NewCardType == CT_MockingboardC)
- {
- m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_MockingboardC;
- m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_MockingboardC;
- }
- else if (NewCardType == CT_Phasor)
- {
- m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Phasor;
- if ((Slot5 == CT_MockingboardC) || (Slot5 == CT_SAM))
- m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_Empty;
- }
- else if (NewCardType == CT_SAM)
- {
- if ((Slot4 == CT_MockingboardC) || (Slot4 == CT_Phasor))
- m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Empty;
- m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_SAM;
- }
+ if (isSlot5SoundCard)
+ m_PropertySheetHelper.FillComboBox(hWnd, IDC_SOUNDCARD_SLOT5, m_soundCardChoices, (int)CardTypeToComboItem(slot5));
else
- {
- if ((Slot4 == CT_MockingboardC) || (Slot4 == CT_Phasor))
- m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Empty;
- if ((Slot5 == CT_MockingboardC) || (Slot5 == CT_SAM))
- m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_Empty;
- }
+ m_PropertySheetHelper.FillComboBox(hWnd, IDC_SOUNDCARD_SLOT5, m_soundCardChoice_Unavailable, 0);
- return true;
+ bool enableMBVolume = slot4 == CT_MockingboardC || slot5 == CT_MockingboardC || slot4 == CT_Phasor || slot5 == CT_Phasor;
+ EnableWindow(GetDlgItem(hWnd, IDC_MB_VOLUME), enableMBVolume ? TRUE : FALSE);
}
diff --git a/source/Configuration/PageSound.h b/source/Configuration/PageSound.h
index a1e9c6f7..80d6fb82 100644
--- a/source/Configuration/PageSound.h
+++ b/source/Configuration/PageSound.h
@@ -11,9 +11,7 @@ class CPageSound : private IPropertySheetPage
public:
CPageSound(CPropertySheetHelper& PropertySheetHelper) :
m_Page(PG_SOUND),
- m_PropertySheetHelper(PropertySheetHelper),
- m_NewCardType(CT_Empty),
- m_nCurrentIDCheckButton(0)
+ m_PropertySheetHelper(PropertySheetHelper)
{
CPageSound::ms_this = this;
}
@@ -30,8 +28,10 @@ protected:
virtual void DlgCANCEL(HWND hWnd){}
private:
+ enum SOUNDCARDCHOICE { SC_MOCKINGBOARD = 0, SC_PHASOR, SC_SAM, SC_EMPTY, _SOUNDCARD_MAX_CHOICES, SC_UNAVAILABLE };
+
void InitOptions(HWND hWnd);
- bool NewSoundcardConfigured(HWND hWnd, WPARAM wparam, SS_CARDTYPE NewCardType);
+ SOUNDCARDCHOICE CardTypeToComboItem(SS_CARDTYPE card);
static CPageSound* ms_this;
@@ -41,7 +41,6 @@ private:
static const UINT VOLUME_MIN = 0;
static const UINT VOLUME_MAX = 59;
static const TCHAR m_soundchoices[];
-
- SS_CARDTYPE m_NewCardType;
- int m_nCurrentIDCheckButton;
+ static const char m_soundCardChoices[];
+ static const char m_soundCardChoice_Unavailable[];
};
diff --git a/source/Core.cpp b/source/Core.cpp
index d13a778d..83344839 100644
--- a/source/Core.cpp
+++ b/source/Core.cpp
@@ -227,7 +227,7 @@ void SetCurrentCLK6502(void)
//
SpkrReinitialize();
- MB_Reinitialize();
+ GetCardMgr().GetMockingboardCardMgr().ReinitializeClock();
}
void UseClockMultiplier(double clockMultiplier)
diff --git a/source/Debugger/Debugger_Display.cpp b/source/Debugger/Debugger_Display.cpp
index 2b7412e0..c7391001 100644
--- a/source/Debugger/Debugger_Display.cpp
+++ b/source/Debugger/Debugger_Display.cpp
@@ -1954,7 +1954,13 @@ void DrawMemory ( int line, int iMemDump )
SS_CARD_MOCKINGBOARD_v1 SS_MB;
if ((eDevice == DEV_SY6522) || (eDevice == DEV_AY8910))
- MB_GetSnapshot_v1(&SS_MB, 4+(nAddr>>1)); // Slot4 or Slot5
+ {
+ UINT slot = 4 + (nAddr >> 1); // Slot4 or Slot5
+ if (GetCardMgr().GetMockingboardCardMgr().IsMockingboard(slot))
+ dynamic_cast(GetCardMgr().GetRef(slot)).GetSnapshot_v1(&SS_MB);
+ else // No MB in this slot
+ SS_MB.Hdr.UnitHdr.hdr.v2.Type = UT_Reserved;
+ }
RECT rect = { 0 };
rect.left = DISPLAY_MINIMEM_COLUMN;
@@ -2059,31 +2065,45 @@ void DrawMemory ( int line, int iMemDump )
// else
if (eDevice == DEV_SY6522)
{
- sText = StrFormat( "%02X ", (unsigned)((BYTE*)&SS_MB.Unit[nAddr & 1].RegsSY6522)[iAddress] );
- if (SS_MB.Unit[nAddr & 1].bTimer1Active && (iAddress == 4 || iAddress == 5)) // T1C
+ if (SS_MB.Hdr.UnitHdr.hdr.v2.Type == UT_Card)
{
- DebuggerSetColorFG(DebuggerGetColor(FG_INFO_TITLE)); // if timer1 active then draw in white
- }
- else if (SS_MB.Unit[nAddr & 1].bTimer2Active && (iAddress == 8 || iAddress == 9)) // T2C
- {
- DebuggerSetColorFG(DebuggerGetColor(FG_INFO_TITLE)); // if timer2 active then draw in white
+ sText = StrFormat("%02X ", (unsigned)((BYTE*)&SS_MB.Unit[nAddr & 1].RegsSY6522)[iAddress]);
+ if (SS_MB.Unit[nAddr & 1].bTimer1Active && (iAddress == 4 || iAddress == 5)) // T1C
+ {
+ DebuggerSetColorFG(DebuggerGetColor(FG_INFO_TITLE)); // if timer1 active then draw in white
+ }
+ else if (SS_MB.Unit[nAddr & 1].bTimer2Active && (iAddress == 8 || iAddress == 9)) // T2C
+ {
+ DebuggerSetColorFG(DebuggerGetColor(FG_INFO_TITLE)); // if timer2 active then draw in white
+ }
+ else
+ {
+ if (iCol & 1)
+ DebuggerSetColorFG(DebuggerGetColor(iForeground));
+ else
+ DebuggerSetColorFG(DebuggerGetColor(FG_INFO_ADDRESS));
+ }
}
else
{
- if (iCol & 1)
- DebuggerSetColorFG( DebuggerGetColor( iForeground ));
- else
- DebuggerSetColorFG( DebuggerGetColor( FG_INFO_ADDRESS ));
+ sText = "-- "; // No MB card in this slot
}
}
else
if (eDevice == DEV_AY8910)
{
- sText = StrFormat( "%02X ", (unsigned)SS_MB.Unit[nAddr & 1].RegsAY8910[iAddress] );
- if (iCol & 1)
- DebuggerSetColorFG( DebuggerGetColor( iForeground ));
+ if (SS_MB.Hdr.UnitHdr.hdr.v2.Type == UT_Card)
+ {
+ sText = StrFormat("%02X ", (unsigned)SS_MB.Unit[nAddr & 1].RegsAY8910[iAddress]);
+ if (iCol & 1)
+ DebuggerSetColorFG(DebuggerGetColor(iForeground));
+ else
+ DebuggerSetColorFG(DebuggerGetColor(FG_INFO_ADDRESS));
+ }
else
- DebuggerSetColorFG( DebuggerGetColor( FG_INFO_ADDRESS ));
+ {
+ sText = "-- "; // No MB card in this slot
+ }
}
else
{
diff --git a/source/Disk2CardManager.cpp b/source/Disk2CardManager.cpp
index 28d645fc..b0dd10ad 100644
--- a/source/Disk2CardManager.cpp
+++ b/source/Disk2CardManager.cpp
@@ -106,15 +106,11 @@ void Disk2CardManager::LoadLastDiskImage(void)
}
}
+// Called by CardManager::Destroy()
void Disk2CardManager::Destroy(void)
{
- for (UINT i = 0; i < NUM_SLOTS; i++)
- {
- if (GetCardMgr().QuerySlot(i) == CT_Disk2)
- {
- dynamic_cast(GetCardMgr().GetRef(i)).Destroy();
- }
- }
+ // NB. All cards (including any Disk2 cards) have just been destroyed by CardManager
+ // - so nothing to do
}
bool Disk2CardManager::IsAnyFirmware13Sector(void)
diff --git a/source/Mockingboard.cpp b/source/Mockingboard.cpp
index 6474770d..58a7f06b 100644
--- a/source/Mockingboard.cpp
+++ b/source/Mockingboard.cpp
@@ -23,44 +23,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/* Description: Mockingboard/Phasor emulation
*
- * Author: Copyright (c) 2002-2006, Tom Charlesworth
*/
-// History:
-//
-// v1.12.07.1 (30 Dec 2005)
-// - Update 6522 TIMERs after every 6502 opcode, giving more precise IRQs
-// - Minimum TIMER freq is now 0x100 cycles
-// - Added Phasor support
-//
-// v1.12.06.1 (16 July 2005)
-// - Reworked 6522's ORB -> AY8910 decoder
-// - Changed MB output so L=All voices from AY0 & AY2 & R=All voices from AY1 & AY3
-// - Added crude support for Votrax speech chip (by using SSI263 phonemes)
-//
-// v1.12.04.1 (14 Sep 2004)
-// - Switch MB output from dual-mono to stereo.
-// - Relaxed TIMER1 freq from ~62Hz (period=0x4000) to ~83Hz (period=0x3000).
-//
-// 25 Apr 2004:
-// - Added basic support for the SSI263 speech chip
-//
-// 15 Mar 2004:
-// - Switched to MAME's AY8910 emulation (includes envelope support)
-//
-// v1.12.03 (11 Jan 2004)
-// - For free-running 6522 timer1 IRQ, reload with current ACCESS_TIMER1 value.
-// (Fixes Ultima 4/5 playback speed problem.)
-//
-// v1.12.01 (24 Nov 2002)
-// - Shaped the tone waveform more logarithmically
-// - Added support for MB ena/dis switch on Config dialog
-// - Added log file support
-//
-// v1.12.00 (17 Nov 2002)
-// - Initial version (no AY8910 envelope support)
-//
-
// Notes on Votrax chip (on original Mockingboards):
// From Crimewave (Penguin Software):
// . Init:
@@ -78,8 +42,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "StdAfx.h"
#include "Mockingboard.h"
+#include "MockingboardDefs.h"
#include "6522.h"
-//#include "SaveState_Structs_v1.h"
#include "Core.h"
#include "CardManager.h"
@@ -89,179 +53,193 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SoundCore.h"
#include "SynchronousEventManager.h"
#include "YamlHelper.h"
-#include "Riff.h"
#include "AY8910.h"
#include "SSI263.h"
-#define DBG_MB_SS_CARD 0 // From UI, select Mockingboard (not Phasor)
+#define DBG_MB_SS_CARD 0 // NB. From UI, select Mockingboard (not Phasor)
-#define SY6522_DEVICE_A 0
-#define SY6522_DEVICE_B 1
+//---------------------------------------------------------------------------
-#define NUM_MB 2
-#define NUM_DEVS_PER_MB 2
-#define NUM_AY8910 (NUM_MB*NUM_DEVS_PER_MB)
-#define NUM_SY6522 NUM_AY8910
-#define NUM_VOICES_PER_AY8910 3
-#define NUM_VOICES (NUM_AY8910*NUM_VOICES_PER_AY8910)
-
-
-// Chip offsets from card base.
-#define SY6522A_Offset 0x00
-#define SY6522B_Offset 0x80
-#define SSI263B_Offset 0x20
-#define SSI263A_Offset 0x40
-
-//#define Phasor_SY6522A_CS 4
-//#define Phasor_SY6522B_CS 7
-//#define Phasor_SY6522A_Offset (1<> 3) & 3) : kAY0;
+
+ if (nAY_CS & kAY0)
+ AY8910_Write(subunit, AY8913_DEVICE_A, value);
+
+ if (nAY_CS & kAY1)
+ AY8910_Write(subunit, AY8913_DEVICE_B, value);
+ }
+ else
+ {
+ AY8910_Write(subunit, AY8913_DEVICE_A, value);
+ }
+#endif
}
//-----------------------------------------------------------------------------
-static void AY8910_Write(BYTE nDevice, BYTE nValue, BYTE nAYDevice)
+void MockingboardCard::AY8910_Write(BYTE subunit, BYTE ay, BYTE value)
{
- g_bMB_RegAccessedFlag = true;
- SY6522_AY8910* pMB = &g_MB[nDevice];
+ m_regAccessedFlag = true;
+ MB_SUBUNIT* pMB = &m_MBSubUnit[subunit];
- if ((nValue & 4) == 0)
+ if ((value & 4) == 0)
{
// RESET: Reset AY8910 only
- AY8910_reset(nDevice+2*nAYDevice);
+ AY8910_reset(subunit, ay);
}
else
{
// Determine the AY8910 inputs
- int nBDIR = (nValue & 2) ? 1 : 0;
+ int nBDIR = (value & 2) ? 1 : 0;
const int nBC2 = 1; // Hardwired to +5V
- int nBC1 = nValue & 1;
+ int nBC1 = value & 1;
MockingboardUnitState_e nAYFunc = (MockingboardUnitState_e) ((nBDIR<<2) | (nBC2<<1) | nBC1);
- MockingboardUnitState_e& state = (nAYDevice == 0) ? pMB->state : pMB->stateB; // GH#659
+ MockingboardUnitState_e& state = (ay == AY8913_DEVICE_A) ? pMB->state : pMB->stateB; // GH#659
#if _DEBUG
- if (!g_bPhasorEnable)
- _ASSERT(nAYDevice == 0);
+ if (!m_phasorEnable)
+ _ASSERT(ay == AY8913_DEVICE_A);
if (nAYFunc == AY_WRITE || nAYFunc == AY_LATCH)
_ASSERT(state == AY_INACTIVE);
#endif
@@ -274,14 +252,14 @@ static void AY8910_Write(BYTE nDevice, BYTE nValue, BYTE nAYDevice)
break;
case AY_READ: // 5: READ FROM PSG (need to set DDRA to input)
- if (g_bPhasorEnable && g_phasorMode == PH_EchoPlus)
+ if (m_phasorEnable && m_phasorMode == PH_EchoPlus)
pMB->sy6522.SetRegORA( 0xff & (pMB->sy6522.GetReg(SY6522::rDDRA) ^ 0xff) ); // Phasor (Echo+ mode) doesn't support reading AY8913s - it just reads 1's for the input bits
else
- pMB->sy6522.SetRegORA( AYReadReg(nDevice+2*nAYDevice, pMB->nAYCurrentRegister) & (pMB->sy6522.GetReg(SY6522::rDDRA) ^ 0xff) );
+ pMB->sy6522.SetRegORA( AYReadReg(subunit, ay, pMB->nAYCurrentRegister) & (pMB->sy6522.GetReg(SY6522::rDDRA) ^ 0xff) );
break;
case AY_WRITE: // 6: WRITE TO PSG
- _AYWriteReg(nDevice+2*nAYDevice, pMB->nAYCurrentRegister, pMB->sy6522.GetReg(SY6522::rORA));
+ _AYWriteReg(subunit, ay, pMB->nAYCurrentRegister, pMB->sy6522.GetReg(SY6522::rORA));
break;
case AY_LATCH: // 7: LATCH ADDRESS
@@ -289,7 +267,7 @@ static void AY8910_Write(BYTE nDevice, BYTE nValue, BYTE nAYDevice)
// Selecting an unused register number above 0x0f puts the AY into a state where
// any values written to the data/address bus are ignored, but can be read back
// within a few tens of thousands of cycles before they decay to zero.
- if(pMB->sy6522.GetReg(SY6522::rORA) <= 0x0F)
+ if (pMB->sy6522.GetReg(SY6522::rORA) <= 0x0F)
pMB->nAYCurrentRegister = pMB->sy6522.GetReg(SY6522::rORA) & 0x0F;
// else Pro-Mockingboard (clone from HK)
break;
@@ -302,57 +280,21 @@ static void AY8910_Write(BYTE nDevice, BYTE nValue, BYTE nAYDevice)
//-----------------------------------------------------------------------------
-static void WriteToORB(BYTE device)
+void MockingboardCard::UpdateIFRandIRQ(MB_SUBUNIT* pMB, BYTE clr_mask, BYTE set_mask)
{
- BYTE value = g_MB[device].sy6522.Read(SY6522::rORB);
-
- if ((device & 1) == 0 && // SC01 only at $Cn00 (not $Cn80)
- g_MB[device].sy6522.Read(SY6522::rPCR) == 0xB0)
- {
- // Votrax speech data
- const BYTE DDRB = g_MB[device].sy6522.Read(SY6522::rDDRB);
- g_MB[device].ssi263.Votrax_Write((value & DDRB) | (DDRB ^ 0xff)); // DDRB's zero bits (inputs) are high impedence, so output as 1 (GH#952)
- return;
- }
-
-#if DBG_MB_SS_CARD
- if ((nDevice & 1) == 1)
- AY8910_Write(nDevice, nValue, 0);
-#else
- if (g_bPhasorEnable)
- {
- const int kAY0 = 2; // bit4=0 (active low) selects the 1st AY8913, ie. the only AY8913 in Mockingboard mode (confirmed on real Phasor h/w)
- const int kAY1 = 1; // bit3=0 (active low) selects the 2nd AY8913 attached to this 6522 (unavailable in Mockingboard mode)
- int nAY_CS = (g_phasorMode == PH_Phasor) ? (~(value >> 3) & 3) : kAY0;
-
- if (nAY_CS & kAY0)
- AY8910_Write(device, value, 0);
-
- if (nAY_CS & kAY1)
- AY8910_Write(device, value, 1);
- }
- else
- {
- AY8910_Write(device, value, 0);
- }
-#endif
+ pMB->sy6522.UpdateIFR(clr_mask, set_mask); // which calls UpdateIRQ()
}
-//-----------------------------------------------------------------------------
-
-static void UpdateIFRandIRQ(SY6522_AY8910* pMB, BYTE clr_mask, BYTE set_mask)
-{
- pMB->sy6522.UpdateIFR(clr_mask, set_mask); // which calls MB_UpdateIRQ()
-}
+//---------------------------------------------------------------------------
// Called from class SY6522
-void MB_UpdateIRQ(void)
+void MockingboardCard::UpdateIRQ(void)
{
// Now update the IRQ signal from all 6522s
// . OR-sum of all active TIMER1, TIMER2 & SPEECH sources (from all 6522s)
UINT bIRQ = 0;
- for (UINT i=0; i MockingboardCardManager::UpdateSoundBuffer() on a TIMER1 (not TIMER2) underflow - when IsAnyTimer1Active() == true (for any MB)
+// . MockingboardCardManager::Update() - when IsAnyTimer1Active() == false (for all MB's)
+UINT MockingboardCard::MB_Update(void)
{
- if (!MockingboardVoice.bActive)
- return;
-
if (g_bFullSpeed)
{
// Keep AY reg writes relative to the current 'frame'
@@ -386,34 +340,34 @@ static void MB_UpdateInt(void)
// . Tune ends
// . g_bFullSpeed:=true (disk-spinning) for ~50 frames
// . U3 sets AY_ENABLE:=0xFF (as a side-effect, this sets g_bFullSpeed:=false)
- // o Without this, the write to AY_ENABLE gets ignored (since AY8910's /g_uLastCumulativeCycles/ was last set 50 frame ago)
+ // o Without this, the write to AY_ENABLE gets ignored (since AY8910's /m_lastCumulativeCycle/ was last set 50 frame ago)
AY8910UpdateSetCycles();
// TODO:
// If any AY regs have changed then push them out to the AY chip
- return;
+ return 0;
}
//
- if (!g_bMB_RegAccessedFlag)
+ if (!m_regAccessedFlag)
{
- if(!g_nMB_InActiveCycleCount)
+ if (!m_inActiveCycleCount)
{
- g_nMB_InActiveCycleCount = g_nCumulativeCycles;
+ m_inActiveCycleCount = g_nCumulativeCycles;
}
- else if(g_nCumulativeCycles - g_nMB_InActiveCycleCount > (unsigned __int64)g_fCurrentCLK6502/10)
+ else if (g_nCumulativeCycles - m_inActiveCycleCount > (unsigned __int64)g_fCurrentCLK6502 / 10)
{
// After 0.1 sec of Apple time, assume MB is not active
- g_bMB_Active = false;
+ m_isActive = false;
}
}
else
{
- g_nMB_InActiveCycleCount = 0;
- g_bMB_RegAccessedFlag = false;
- g_bMB_Active = true;
+ m_inActiveCycleCount = 0;
+ m_regAccessedFlag = false;
+ m_isActive = true;
}
//
@@ -421,321 +375,51 @@ static void MB_UpdateInt(void)
// For small timer periods, wait for a period of 500cy before updating DirectSound ring-buffer.
// NB. A timer period of less than 24cy will yield nNumSamplesPerPeriod=0.
const double kMinimumUpdateInterval = 500.0; // Arbitary (500 cycles = 21 samples)
- const double kMaximumUpdateInterval = (double)(0xFFFF+2); // Max 6522 timer interval (2756 samples)
+ const double kMaximumUpdateInterval = (double)(0xFFFF + 2); // Max 6522 timer interval (2756 samples)
- if (g_uLastMBUpdateCycle == 0)
- g_uLastMBUpdateCycle = g_uLastCumulativeCycles; // Initial call to MB_Update() after reset/power-cycle
+ if (m_lastMBUpdateCycle == 0)
+ m_lastMBUpdateCycle = m_lastCumulativeCycle; // Initial call to MB_Update() after reset/power-cycle
- _ASSERT(g_uLastCumulativeCycles >= g_uLastMBUpdateCycle);
- double updateInterval = (double)(g_uLastCumulativeCycles - g_uLastMBUpdateCycle);
+ _ASSERT(m_lastCumulativeCycle >= m_lastMBUpdateCycle);
+ double updateInterval = (double)(m_lastCumulativeCycle - m_lastMBUpdateCycle);
if (updateInterval < kMinimumUpdateInterval)
- return;
+ return 0;
if (updateInterval > kMaximumUpdateInterval)
updateInterval = kMaximumUpdateInterval;
- g_uLastMBUpdateCycle = g_uLastCumulativeCycles;
+ m_lastMBUpdateCycle = m_lastCumulativeCycle;
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up
- const int nNumSamplesPerPeriod = (int) ((double)SAMPLE_RATE / nIrqFreq); // Eg. For 60Hz this is 735
+ const int nNumSamplesPerPeriod = (int)((double)SAMPLE_RATE / nIrqFreq); // Eg. For 60Hz this is 735
- static int nNumSamplesError = 0;
- int nNumSamples = nNumSamplesPerPeriod + nNumSamplesError; // Apply correction
- if(nNumSamples <= 0)
+ int nNumSamples = nNumSamplesPerPeriod + m_numSamplesError; // Apply correction
+ if (nNumSamples <= 0)
nNumSamples = 0;
- if(nNumSamples > 2*nNumSamplesPerPeriod)
- nNumSamples = 2*nNumSamplesPerPeriod;
+ if (nNumSamples > 2 * nNumSamplesPerPeriod)
+ nNumSamples = 2 * nNumSamplesPerPeriod;
if (nNumSamples > MAX_SAMPLES)
nNumSamples = MAX_SAMPLES; // Clamp to prevent buffer overflow
- if(nNumSamples)
- for(int nChip=0; nChipGetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor);
- if(FAILED(hr))
- return;
-
- static DWORD dwByteOffset = (DWORD)-1;
- if(dwByteOffset == (DWORD)-1)
+ if (nNumSamples)
{
- // First time in this func
-
- dwByteOffset = dwCurrentWriteCursor;
- }
- else
- {
- // Check that our offset isn't between Play & Write positions
-
- if(dwCurrentWriteCursor > dwCurrentPlayCursor)
+ for (BYTE subunit = 0; subunit < NUM_SUBUNITS_PER_MB; subunit++)
{
- // |-----PxxxxxW-----|
- if((dwByteOffset > dwCurrentPlayCursor) && (dwByteOffset < dwCurrentWriteCursor))
+ for (BYTE ay = 0; ay < NUM_AY8913_PER_SUBUNIT; ay++)
{
-#ifdef DBG_MB_UPDATE
- double fTicksSecs = (double)GetTickCount() / 1000.0;
- LogOutput("%010.3f: [MBUpdt] PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X xxx\n", fTicksSecs, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor, dwByteOffset, nNumSamples);
-#endif
- dwByteOffset = dwCurrentWriteCursor;
- nNumSamplesError = 0;
- }
- }
- else
- {
- // |xxW----------Pxxx|
- if((dwByteOffset > dwCurrentPlayCursor) || (dwByteOffset < dwCurrentWriteCursor))
- {
-#ifdef DBG_MB_UPDATE
- double fTicksSecs = (double)GetTickCount() / 1000.0;
- LogOutput("%010.3f: [MBUpdt] PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X XXX\n", fTicksSecs, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor, dwByteOffset, nNumSamples);
-#endif
- dwByteOffset = dwCurrentWriteCursor;
- nNumSamplesError = 0;
+ const UINT chip = subunit * NUM_AY8913_PER_SUBUNIT + ay;
+ AY8910Update(subunit, ay, &m_ppAYVoiceBuffer[chip * NUM_VOICES_PER_AY8913], nNumSamples);
}
}
}
- int nBytesRemaining = dwByteOffset - dwCurrentPlayCursor;
- if(nBytesRemaining < 0)
- nBytesRemaining += g_dwDSBufferSize;
-
- // Calc correction factor so that play-buffer doesn't under/overflow
- const int nErrorInc = SoundCore_GetErrorInc();
- if(nBytesRemaining < g_dwDSBufferSize / 4)
- nNumSamplesError += nErrorInc; // < 0.25 of buffer remaining
- else if(nBytesRemaining > g_dwDSBufferSize / 2)
- nNumSamplesError -= nErrorInc; // > 0.50 of buffer remaining
- else
- nNumSamplesError = 0; // Acceptable amount of data in buffer
-
-#ifdef DBG_MB_UPDATE
- double fTicksSecs = (double)GetTickCount() / 1000.0;
- LogOutput("%010.3f: [MBUpdt] PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X, NSE=%08X, Interval=%f\n", fTicksSecs, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor - dwCurrentPlayCursor, dwByteOffset, nNumSamples, nNumSamplesError, updateInterval);
-#endif
-
- if(nNumSamples == 0)
- return;
-
- //
-
- const double fAttenuation = g_bPhasorEnable ? 2.0/3.0 : 1.0;
-
- for(int i=0; i nWaveDataMax)
- nDataL = nWaveDataMax;
-
- if(nDataR < nWaveDataMin)
- nDataR = nWaveDataMin;
- else if(nDataR > nWaveDataMax)
- nDataR = nWaveDataMax;
-
- g_nMixBuffer[i*g_nMB_NumChannels+0] = (short)nDataL; // L
- g_nMixBuffer[i*g_nMB_NumChannels+1] = (short)nDataR; // R
- }
-
- //
-
- DWORD dwDSLockedBufferSize0, dwDSLockedBufferSize1;
- SHORT *pDSLockedBuffer0, *pDSLockedBuffer1;
-
- hr = DSGetLock(MockingboardVoice.lpDSBvoice,
- dwByteOffset, (DWORD)nNumSamples * sizeof(short) * g_nMB_NumChannels,
- &pDSLockedBuffer0, &dwDSLockedBufferSize0,
- &pDSLockedBuffer1, &dwDSLockedBufferSize1);
- if (FAILED(hr))
- return;
-
- memcpy(pDSLockedBuffer0, &g_nMixBuffer[0], dwDSLockedBufferSize0);
- if(pDSLockedBuffer1)
- memcpy(pDSLockedBuffer1, &g_nMixBuffer[dwDSLockedBufferSize0/sizeof(short)], dwDSLockedBufferSize1);
-
- // Commit sound buffer
- hr = MockingboardVoice.lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
- (void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
-
- dwByteOffset = (dwByteOffset + (DWORD)nNumSamples*sizeof(short)*g_nMB_NumChannels) % g_dwDSBufferSize;
-
- if (g_bMBOutputToRiff)
- RiffPutSamples(&g_nMixBuffer[0], nNumSamples);
-}
-
-static void MB_Update(void)
-{
-#ifdef LOG_PERF_TIMINGS
- extern UINT64 g_timeMB_NoTimer;
- extern UINT64 g_timeMB_Timer;
- PerfMarker perfMarker(!IsAnyTimer1Active() ? g_timeMB_NoTimer : g_timeMB_Timer);
-#endif
-
- MB_UpdateInt();
-}
-
-//-----------------------------------------------------------------------------
-
-static bool MB_DSInit()
-{
- LogFileOutput("MB_DSInit\n");
-#ifdef NO_DIRECT_X
-
- return false;
-
-#else // NO_DIRECT_X
-
- //
- // Create single Mockingboard voice
- //
-
- if(!g_bDSAvailable)
- return false;
-
- HRESULT hr = DSGetSoundBuffer(&MockingboardVoice, DSBCAPS_CTRLVOLUME, g_dwDSBufferSize, SAMPLE_RATE, g_nMB_NumChannels, "MB");
- LogFileOutput("MB_DSInit: DSGetSoundBuffer(), hr=0x%08X\n", hr);
- if(FAILED(hr))
- {
- LogFileOutput("MB_DSInit: DSGetSoundBuffer failed (%08X)\n", hr);
- return false;
- }
-
- bool bRes = DSZeroVoiceBuffer(&MockingboardVoice, g_dwDSBufferSize);
- LogFileOutput("MB_DSInit: DSZeroVoiceBuffer(), res=%d\n", bRes ? 1 : 0);
- if (!bRes)
- return false;
-
- MockingboardVoice.bActive = true;
-
- // Volume might've been setup from value in Registry
- if(!MockingboardVoice.nVolume)
- MockingboardVoice.nVolume = DSBVOLUME_MAX;
-
- hr = MockingboardVoice.lpDSBvoice->SetVolume(MockingboardVoice.nVolume);
- LogFileOutput("MB_DSInit: SetVolume(), hr=0x%08X\n", hr);
-
- //---------------------------------
-
- for (UINT i=0; im_active)
- g_SynchronousEventMgr.Remove(id);
+ delete[] m_ppAYVoiceBuffer[i];
+ m_ppAYVoiceBuffer[i] = NULL;
+ }
- delete g_syncEvent[id];
- g_syncEvent[id] = NULL;
+ for (UINT id = 0; id < kNumSyncEvents; id++)
+ {
+ if (m_syncEvent[id] && m_syncEvent[id]->m_active)
+ g_SynchronousEventMgr.Remove(m_syncEvent[id]->m_id);
+
+ delete m_syncEvent[id];
+ m_syncEvent[id] = NULL;
}
}
//-----------------------------------------------------------------------------
-void MB_Reset(const bool powerCycle) // CTRL+RESET or power-cycle
+void MockingboardCard::Reset(const bool powerCycle) // CTRL+RESET or power-cycle
{
- if (!g_bDSAvailable)
- return;
-
- for (int i=0; im_active)
- g_SynchronousEventMgr.Remove(id);
+ if (m_syncEvent[id] && m_syncEvent[id]->m_active)
+ g_SynchronousEventMgr.Remove(m_syncEvent[id]->m_id);
}
- // Not these, as they don't change on a CTRL+RESET or power-cycle:
-// g_bMBAvailable = false;
-// g_SoundcardType = CT_Empty; // Don't uncomment, else _ASSERT will fire in MB_Read() after an F2->MB_Reset()
+ // Not this, since no change on a CTRL+RESET or power-cycle:
// g_bPhasorEnable = false;
}
- MB_Reinitialize(); // Reset CLK for AY8910s
+ ReinitializeClock(); // Reset CLK for AY8910s
}
//-----------------------------------------------------------------------------
// Echo+ mode - Phasor's 2nd 6522 is mapped to every 16-byte offset in $Cnxx (Echo+ has a single 6522 controlling two AY-3-8913's)
-static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
+BYTE __stdcall MockingboardCard::IORead(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{
- MB_UpdateCycles(nExecutedCycles);
+ UINT slot = (nAddr >> 8) & 0xf;
+ MockingboardCard* pCard = (MockingboardCard*)MemGetSlotParameters(slot);
+ return pCard->IOReadInternal(PC, nAddr, bWrite, nValue, nExecutedCycles);
+}
+
+BYTE MockingboardCard::IOReadInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
+{
+ GetCardMgr().GetMockingboardCardMgr().UpdateCycles(nExecutedCycles);
#ifdef _DEBUG
if (!IS_APPLE2 && MemCheckINTCXROM())
@@ -823,49 +514,37 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
_ASSERT(0); // Card ROM disabled, so IO_Cxxx() returns the internal ROM
return mem[nAddr];
}
-
- if (g_SoundcardType == CT_Empty)
- {
- _ASSERT(0); // Card unplugged, so IO_Cxxx() returns the floating bus
- return MemReadFloatingBus(nExecutedCycles);
- }
#endif
- BYTE nMB = (nAddr>>8)&0xf - SLOT4;
- BYTE nOffset = nAddr&0xff;
-
- if (g_bPhasorEnable)
+ if (m_phasorEnable)
{
- if(nMB != 0) // Slot4 only
- return MemReadFloatingBus(nExecutedCycles);
-
int CS = 0;
- if (g_phasorMode == PH_Mockingboard)
+ if (m_phasorMode == PH_Mockingboard)
CS = ( ( nAddr & 0x80 ) >> 7 ) + 1; // 1 or 2
- else if (g_phasorMode == PH_Phasor)
+ else if (m_phasorMode == PH_Phasor)
CS = ( ( nAddr & 0x80 ) >> 6 ) | ( ( nAddr & 0x10 ) >> 4 ); // 0, 1, 2 or 3
- else if (g_phasorMode == PH_EchoPlus)
+ else if (m_phasorMode == PH_EchoPlus)
CS = 2;
BYTE nRes = 0;
if (CS & 1)
- nRes |= g_MB[nMB * NUM_DEVS_PER_MB + SY6522_DEVICE_A].sy6522.Read(nAddr & 0xf);
+ nRes |= m_MBSubUnit[SY6522_DEVICE_A].sy6522.Read(nAddr & 0xf);
if (CS & 2)
- nRes |= g_MB[nMB * NUM_DEVS_PER_MB + SY6522_DEVICE_B].sy6522.Read(nAddr & 0xf);
+ nRes |= m_MBSubUnit[SY6522_DEVICE_B].sy6522.Read(nAddr & 0xf);
bool bAccessedDevice = (CS & 3) ? true : false;
bool CS_SSI263 = !(nAddr & 0x10) && (nAddr & 0x60) && !(nAddr & 0x80); // SSI263 at $Cn2x and/or $Cn4x
- if (g_phasorMode == PH_Phasor && CS_SSI263) // NB. Mockingboard mode: SSI263.bit7 not readable
+ if (m_phasorMode == PH_Phasor && CS_SSI263) // NB. Mockingboard mode: SSI263.bit7 not readable
{
_ASSERT(!bAccessedDevice); // In Phasor native mode, 6522 & SSI263 are interleaved in $Cn10-$Cn7F card I/O memory
if (nAddr & 0x40) // Primary SSI263
- nRes = g_MB[nMB * NUM_DEVS_PER_MB + 1].ssi263.Read(nExecutedCycles); // SSI263 only drives bit7
+ nRes = m_MBSubUnit[1].ssi263.Read(nExecutedCycles); // SSI263 only drives bit7
if (nAddr & 0x20) // Secondary SSI263
- nRes = g_MB[nMB * NUM_DEVS_PER_MB + 0].ssi263.Read(nExecutedCycles); // SSI263 only drives bit7
+ nRes = m_MBSubUnit[0].ssi263.Read(nExecutedCycles); // SSI263 only drives bit7
bAccessedDevice = true;
}
@@ -878,16 +557,24 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
#endif
// NB. Mockingboard: SSI263.bit7 not readable (TODO: check this with real h/w)
- const BYTE device = nMB * NUM_DEVS_PER_MB + ((nOffset < SY6522B_Offset) ? SY6522_DEVICE_A : SY6522_DEVICE_B);
+ const BYTE offset = nAddr & 0xff;
+ const BYTE subunit = (offset < SY6522B_Offset) ? SY6522_DEVICE_A : SY6522_DEVICE_B;
const BYTE reg = nAddr & 0xf;
- return g_MB[device].sy6522.Read(reg);
+ return m_MBSubUnit[subunit].sy6522.Read(reg);
}
//-----------------------------------------------------------------------------
-static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
+BYTE __stdcall MockingboardCard::IOWrite(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{
- MB_UpdateCycles(nExecutedCycles);
+ UINT slot = (nAddr >> 8) & 0xf;
+ MockingboardCard* pCard = (MockingboardCard*)MemGetSlotParameters(slot);
+ return pCard->IOWriteInternal(PC, nAddr, bWrite, nValue, nExecutedCycles);
+}
+
+BYTE MockingboardCard::IOWriteInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
+{
+ GetCardMgr().GetMockingboardCardMgr().UpdateCycles(nExecutedCycles);
#ifdef _DEBUG
if (!IS_APPLE2 && MemCheckINTCXROM())
@@ -895,12 +582,6 @@ static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
_ASSERT(0); // Card ROM disabled, so IO_Cxxx() returns the internal ROM
return 0;
}
-
- if (g_SoundcardType == CT_Empty)
- {
- _ASSERT(0); // Card unplugged, so IO_Cxxx() returns the floating bus
- return 0;
- }
#endif
// Support 6502/65C02 false-reads of 6522 (GH#52)
@@ -928,74 +609,67 @@ static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
if (addr16 == nAddr) // Check we've reverse looked-up the 6502 opcode correctly
{
if ( ((nAddr&0xf) == 4) || ((nAddr&0xf) == 8) ) // Only reading 6522 reg-4 or reg-8 actually has an effect
- MB_Read(PC, nAddr, 0, 0, nExecutedCycles);
+ IOReadInternal(PC, nAddr, 0, 0, nExecutedCycles);
}
}
}
- BYTE nMB = ((nAddr>>8)&0xf) - SLOT4;
- BYTE nOffset = nAddr&0xff;
-
- if (g_bPhasorEnable)
+ if (m_phasorEnable)
{
- if(nMB != 0) // Slot4 only
- return 0;
-
int CS = 0;
- if (g_phasorMode == PH_Mockingboard)
+ if (m_phasorMode == PH_Mockingboard)
CS = ( ( nAddr & 0x80 ) >> 7 ) + 1; // 1 or 2
- else if (g_phasorMode == PH_Phasor)
+ else if (m_phasorMode == PH_Phasor)
CS = ( ( nAddr & 0x80 ) >> 6 ) | ( ( nAddr & 0x10 ) >> 4 ); // 0, 1, 2 or 3
- else if (g_phasorMode == PH_EchoPlus)
+ else if (m_phasorMode == PH_EchoPlus)
CS = 2;
if (CS & 1)
{
- const BYTE device = nMB * NUM_DEVS_PER_MB + SY6522_DEVICE_A;
const BYTE reg = nAddr & 0xf;
- g_MB[device].sy6522.Write(reg, nValue);
+ m_MBSubUnit[SY6522_DEVICE_A].sy6522.Write(reg, nValue);
if (reg == SY6522::rORB)
- WriteToORB(device);
+ WriteToORB(SY6522_DEVICE_A);
}
if (CS & 2)
{
- const BYTE device = nMB * NUM_DEVS_PER_MB + SY6522_DEVICE_B;
const BYTE reg = nAddr & 0xf;
- g_MB[device].sy6522.Write(reg, nValue);
+ m_MBSubUnit[SY6522_DEVICE_B].sy6522.Write(reg, nValue);
if (reg == SY6522::rORB)
- WriteToORB(device);
+ WriteToORB(SY6522_DEVICE_B);
}
- bool CS_SSI263_A = (g_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x40) // SSI263 at $Cn4x, $Cn6x
+ bool CS_SSI263_A = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x40) // SSI263 at $Cn4x, $Cn6x
: nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
- bool CS_SSI263_B = (g_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x20) // SSI263 at $Cn2x, $Cn6x
+ bool CS_SSI263_B = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x20) // SSI263 at $Cn2x, $Cn6x
: nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
- if (g_phasorMode == PH_Mockingboard || g_phasorMode == PH_Phasor) // No SSI263 for Echo+
+ if (m_phasorMode == PH_Mockingboard || m_phasorMode == PH_Phasor) // No SSI263 for Echo+
{
// NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w)
if (CS_SSI263_A) // Primary SSI263
- g_MB[nMB * NUM_DEVS_PER_MB + 1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
+ m_MBSubUnit[1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
if (CS_SSI263_B) // Secondary SSI263
- g_MB[nMB * NUM_DEVS_PER_MB + 0].ssi263.Write(nAddr&0x7, nValue); // 1st 6522 is used for 2nd speech chip
+ m_MBSubUnit[0].ssi263.Write(nAddr&0x7, nValue); // 1st 6522 is used for 2nd speech chip
}
return 0;
}
- const BYTE device = nMB * NUM_DEVS_PER_MB + ((nOffset < SY6522B_Offset) ? SY6522_DEVICE_A : SY6522_DEVICE_B);
+ const BYTE offset = nAddr & 0xff;
+ const BYTE subunit = (offset < SY6522B_Offset) ? SY6522_DEVICE_A : SY6522_DEVICE_B;
const BYTE reg = nAddr & 0xf;
- g_MB[device].sy6522.Write(reg, nValue);
+ m_MBSubUnit[subunit].sy6522.Write(reg, nValue);
if (reg == SY6522::rORB)
- WriteToORB(device);
+ WriteToORB(subunit);
#if !DBG_MB_SS_CARD
if (nAddr & 0x40)
- g_MB[nMB * NUM_DEVS_PER_MB + 1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
+ m_MBSubUnit[1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
if (nAddr & 0x20)
- g_MB[nMB * NUM_DEVS_PER_MB + 0].ssi263.Write(nAddr&0x7, nValue); // 1st 6522 is used for 2nd speech chip
+ m_MBSubUnit[0].ssi263.Write(nAddr&0x7, nValue); // 1st 6522 is used for 2nd speech chip
#endif
return 0;
@@ -1019,160 +693,96 @@ static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
// So $C0C5 seemingly results in 2 different modes.
//
-static BYTE __stdcall PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
+BYTE __stdcall MockingboardCard::PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{
- if (!g_bPhasorEnable)
+ UINT slot = ((nAddr & 0xff) >> 4) - 8;
+ MockingboardCard* pCard = (MockingboardCard*)MemGetSlotParameters(slot);
+ return pCard->PhasorIOInternal(PC, nAddr, bWrite, nValue, nExecutedCycles);
+}
+
+BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
+{
+ if (!m_phasorEnable)
return MemReadFloatingBus(nExecutedCycles);
- UINT bits = (UINT) g_phasorMode;
+ UINT bits = (UINT) m_phasorMode;
if (nAddr & 8)
bits = 0;
bits |= (nAddr & 7);
- g_phasorMode = (PHASOR_MODE) bits;
+ m_phasorMode = (PHASOR_MODE) bits;
- if (g_phasorMode == PH_Mockingboard || g_phasorMode == PH_EchoPlus)
- g_PhasorClockScaleFactor = 1;
- else if (g_phasorMode == PH_Phasor)
- g_PhasorClockScaleFactor = 2;
+ if (m_phasorMode == PH_Mockingboard || m_phasorMode == PH_EchoPlus)
+ m_phasorClockScaleFactor = 1;
+ else if (m_phasorMode == PH_Phasor)
+ m_phasorClockScaleFactor = 2;
- AY8910_InitClock((int)(Get6502BaseClock() * g_PhasorClockScaleFactor));
+ AY8910_InitClock((int)(Get6502BaseClock() * m_phasorClockScaleFactor));
- for (UINT i=0; i : Slot 4 & 5
-
- if (GetCardMgr().QuerySlot(SLOT4) != CT_MockingboardC && GetCardMgr().QuerySlot(SLOT4) != CT_Phasor)
- {
- MB_SetSoundcardType(CT_Empty);
- return;
- }
-
- if (GetCardMgr().QuerySlot(SLOT4) == CT_MockingboardC)
- RegisterIoHandler(uSlot4, IO_Null, IO_Null, MB_Read, MB_Write, NULL, NULL);
+ if (QueryType() == CT_MockingboardC)
+ RegisterIoHandler(m_slot, IO_Null, IO_Null, IORead, IOWrite, this, NULL);
else // Phasor
- RegisterIoHandler(uSlot4, PhasorIO, PhasorIO, MB_Read, MB_Write, NULL, NULL);
-
- if (GetCardMgr().QuerySlot(SLOT5) == CT_MockingboardC)
- RegisterIoHandler(uSlot5, IO_Null, IO_Null, MB_Read, MB_Write, NULL, NULL);
-
- MB_SetSoundcardType(GetCardMgr().QuerySlot(SLOT4));
+ RegisterIoHandler(m_slot, PhasorIO, PhasorIO, IORead, IOWrite, this, NULL);
if (g_bDisableDirectSound || g_bDisableDirectSoundMockingboard)
return;
- // Sound buffer may have been stopped by MB_InitializeForLoadingSnapshot().
- // NB. DSZeroVoiceBuffer() also zeros the sound buffer, so it's better than directly calling IDirectSoundBuffer::Play():
- // - without zeroing, then the previous sound buffer can be heard for a fraction of a second
- // - eg. when doing Mockingboard playback, then loading a save-state which is also doing Mockingboard playback
- DSZeroVoiceBuffer(&MockingboardVoice, g_dwDSBufferSize);
+#ifdef NO_DIRECT_X
+#else // NO_DIRECT_X
+ for (UINT i = 0; i < NUM_SSI263; i++)
+ {
+ if (!m_MBSubUnit[i].ssi263.DSInit())
+ break;
+ }
+#endif // NO_DIRECT_X
}
//-----------------------------------------------------------------------------
-void MB_Mute(void)
+void MockingboardCard::MuteControl(bool mute)
{
- if(g_SoundcardType == CT_Empty)
- return;
-
- if(MockingboardVoice.bActive && !MockingboardVoice.bMute)
+ if (mute)
{
- MockingboardVoice.lpDSBvoice->SetVolume(DSBVOLUME_MIN);
- MockingboardVoice.bMute = true;
+ for (UINT i = 0; i < NUM_SSI263; i++)
+ m_MBSubUnit[i].ssi263.Mute();
}
-
- for (UINT i=0; iSetVolume(MockingboardVoice.nVolume);
- MockingboardVoice.bMute = false;
+ for (UINT i = 0; i < NUM_SSI263; i++)
+ m_MBSubUnit[i].ssi263.Unmute();
}
-
- for (UINT i=0; i= 0);
if (uCycles == 0)
return;
- g_uLastCumulativeCycles = g_nCumulativeCycles;
+ m_lastCumulativeCycle = g_nCumulativeCycles;
_ASSERT(uCycles < 0x10000 || g_nAppMode == MODE_BENCHMARK);
USHORT nClocks = (USHORT)uCycles;
- for (int i = 0; i < NUM_SY6522; i++)
+ for (UINT i = 0; i < NUM_SUBUNITS_PER_MB; i++)
{
- g_MB[i].sy6522.UpdateTimer1(nClocks);
- g_MB[i].sy6522.UpdateTimer2(nClocks);
+ m_MBSubUnit[i].sy6522.UpdateTimer1(nClocks);
+ m_MBSubUnit[i].sy6522.UpdateTimer2(nClocks);
}
}
//-----------------------------------------------------------------------------
// Called on a 6522 TIMER1/2 underflow
-static int MB_SyncEventCallback(int id, int /*cycles*/, ULONG uExecutedCycles)
+int MockingboardCard::MB_SyncEventCallback(int id, int /*cycles*/, ULONG uExecutedCycles)
{
- MB_UpdateCycles(uExecutedCycles); // Underflow: so keep TIMER1/2 counters in sync
+ UINT slot = (id >> 4);
+ MockingboardCard* pCard = (MockingboardCard*)MemGetSlotParameters(slot);
+ return pCard->MB_SyncEventCallbackInternal(id, 0, uExecutedCycles);
+}
- SY6522_AY8910* pMB = &g_MB[id / SY6522::kNumTimersPer6522];
+int MockingboardCard::MB_SyncEventCallbackInternal(int id, int /*cycles*/, ULONG uExecutedCycles)
+{
+ //UpdateCycles(uExecutedCycles); // Underflow: so keep TIMER1/2 counters in sync
+ // Update all MBs, so that m_lastCumulativeCycle remains in sync for all
+ GetCardMgr().GetMockingboardCardMgr().UpdateCycles(uExecutedCycles); // Underflow: so keep TIMER1/2 counters in sync
+
+ MB_SUBUNIT* pMB = &m_MBSubUnit[(id & 0xf) / SY6522::kNumTimersPer6522];
if ((id & 1) == 0)
{
_ASSERT(pMB->sy6522.IsTimer1Active());
UpdateIFRandIRQ(pMB, 0, SY6522::IxR_TIMER1);
- MB_Update();
+ GetCardMgr().GetMockingboardCardMgr().UpdateSoundBuffer();
if ((pMB->sy6522.GetReg(SY6522::rACR) & SY6522::ACR_RUNMODE) == SY6522::ACR_RM_FREERUNNING)
{
@@ -1232,7 +848,7 @@ static int MB_SyncEventCallback(int id, int /*cycles*/, ULONG uExecutedCycles)
}
else
{
- // NB. Since not calling MB_Update(), then AppleWin doesn't (accurately?) support AY-playback using T2 (which is one-shot only)
+ // NB. Since not calling UpdateSoundBuffer(), then AppleWin doesn't (accurately?) support AY-playback using T2 (which is one-shot only)
_ASSERT(pMB->sy6522.IsTimer2Active());
UpdateIFRandIRQ(pMB, 0, SY6522::IxR_TIMER2);
@@ -1243,56 +859,21 @@ static int MB_SyncEventCallback(int id, int /*cycles*/, ULONG uExecutedCycles)
//-----------------------------------------------------------------------------
-bool MB_IsActive()
+bool MockingboardCard::IsActive(void)
{
- if (!MockingboardVoice.bActive)
- return false;
-
bool isSSI263Active = false;
- for (UINT i=0; iSetVolume(MockingboardVoice.nVolume);
-
- //
-
- for (UINT i=0; iHdr.UnitHdr.hdr.v2.Length = sizeof(SS_CARD_MOCKINGBOARD_v1);
pSS->Hdr.UnitHdr.hdr.v2.Type = UT_Card;
pSS->Hdr.UnitHdr.hdr.v2.Version = 1;
- pSS->Hdr.Slot = dwSlot;
+ pSS->Hdr.Slot = m_slot;
pSS->Hdr.Type = CT_MockingboardC;
- UINT nMbCardNum = dwSlot - SLOT4;
- UINT nDeviceNum = nMbCardNum*2;
- SY6522_AY8910* pMB = &g_MB[nDeviceNum];
+ MB_SUBUNIT* pMB = &m_MBSubUnit[0];
for (UINT i=0; iUnit[i].RegsAY8910[j] = AYReadReg(nDeviceNum, j);
+ pSS->Unit[i].RegsAY8910[j] = AYReadReg(i, 0, j); // FIXME: also support Phasor's 2nd AY8913
}
memset(&pSS->Unit[i].RegsSSI263, 0, sizeof(SSI263A)); // Not used by debugger
@@ -1330,11 +909,92 @@ void MB_GetSnapshot_v1(SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot)
pSS->Unit[i].bTimer2Active = pMB->sy6522.IsTimer2Active();
pSS->Unit[i].bSpeechIrqPending = false;
- nDeviceNum++;
pMB++;
}
}
+//=============================================================================
+// AY8913 interface
+
+BYTE MockingboardCard::AYReadReg(BYTE subunit, BYTE ay, int r)
+{
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ return m_MBSubUnit[subunit].ay8913[ay].sound_ay_read(r);
+}
+
+void MockingboardCard::_AYWriteReg(BYTE subunit, BYTE ay, int r, int v)
+{
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ libspectrum_dword uOffset = (libspectrum_dword)(g_nCumulativeCycles - m_lastAYUpdateCycle);
+ m_MBSubUnit[subunit].ay8913[ay].sound_ay_write(r, v, uOffset);
+}
+
+void MockingboardCard::AY8910_reset(BYTE subunit, BYTE ay)
+{
+ // Don't reset the AY CLK, as this is a property of the card (MB/Phasor), not the AY chip
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ m_MBSubUnit[subunit].ay8913[ay].sound_ay_reset(); // Calls: sound_ay_init();
+}
+
+void MockingboardCard::AY8910UpdateSetCycles()
+{
+ m_lastAYUpdateCycle = g_nCumulativeCycles;
+}
+
+void MockingboardCard::AY8910Update(BYTE subunit, BYTE ay, INT16** buffer, int nNumSamples)
+{
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ AY8910UpdateSetCycles();
+
+ m_MBSubUnit[subunit].ay8913[ay].SetFramesize(nNumSamples);
+ m_MBSubUnit[subunit].ay8913[ay].SetSoundBuffers(buffer);
+ m_MBSubUnit[subunit].ay8913[ay].sound_frame();
+}
+
+void MockingboardCard::AY8910_InitAll(int nClock, int nSampleRate)
+{
+ for (UINT subunit = 0; subunit < NUM_SUBUNITS_PER_MB; subunit++)
+ {
+ for (UINT ay = 0; ay < 2; ay++)
+ {
+ m_MBSubUnit[subunit].ay8913[ay].sound_init(NULL); // Inits mainly static members (except ay_tick_incr)
+ m_MBSubUnit[subunit].ay8913[ay].sound_ay_init();
+ }
+ }
+}
+
+void MockingboardCard::AY8910_InitClock(int nClock)
+{
+ AY8913::SetCLK((double)nClock);
+
+ for (UINT subunit = 0; subunit < NUM_SUBUNITS_PER_MB; subunit++)
+ {
+ for (UINT ay = 0; ay < NUM_AY8913_PER_SUBUNIT; ay++)
+ {
+ m_MBSubUnit[subunit].ay8913[ay].sound_init(NULL); // Inits mainly static members (except ay_tick_incr)
+ }
+ }
+}
+
+BYTE* MockingboardCard::AY8910_GetRegsPtr(BYTE subunit, BYTE ay)
+{
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ return m_MBSubUnit[subunit].ay8913[ay].GetAYRegsPtr();
+}
+
+UINT MockingboardCard::AY8910_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, BYTE subunit, BYTE ay, const std::string& suffix)
+{
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ m_MBSubUnit[subunit].ay8913[ay].SaveSnapshot(yamlSaveHelper, suffix);
+ return 1;
+}
+
+UINT MockingboardCard::AY8910_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, BYTE subunit, BYTE ay, const std::string& suffix)
+{
+ _ASSERT(subunit < NUM_SUBUNITS_PER_MB && ay < NUM_AY8913_PER_SUBUNIT);
+ return m_MBSubUnit[subunit].ay8913[ay].LoadSnapshot(yamlLoadHelper, suffix) ? 1 : 0;
+}
+
//=============================================================================
// Unit version history:
@@ -1353,9 +1013,6 @@ void MB_GetSnapshot_v1(SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot)
// Changed at AppleWin 1.30.12
const UINT kUNIT_VERSION = 9;
-const UINT NUM_MB_UNITS = 2;
-const UINT NUM_PHASOR_UNITS = 2;
-
#define SS_YAML_KEY_MB_UNIT "Unit"
#define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
#define SS_YAML_KEY_MB_UNIT_STATE "Unit State"
@@ -1372,74 +1029,77 @@ const UINT NUM_PHASOR_UNITS = 2;
#define SS_YAML_KEY_VOTRAX_PHONEME "Votrax Phoneme"
-const std::string& MB_GetSnapshotCardName(void)
+std::string MockingboardCard::GetSnapshotCardName(void)
{
static const std::string name("Mockingboard C");
return name;
}
-const std::string& Phasor_GetSnapshotCardName(void)
+std::string MockingboardCard::GetSnapshotCardNamePhasor(void)
{
static const std::string name("Phasor");
return name;
}
-void MB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
+void MockingboardCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
- const UINT nMbCardNum = uSlot - SLOT4;
- UINT nDeviceNum = nMbCardNum*2;
- SY6522_AY8910* pMB = &g_MB[nDeviceNum];
+ if (QueryType() == CT_Phasor)
+ return Phasor_SaveSnapshot(yamlSaveHelper);
- YamlSaveHelper::Slot slot(yamlSaveHelper, MB_GetSnapshotCardName(), uSlot, kUNIT_VERSION); // fixme: object should be just 1 Mockingboard card & it will know its slot
+ //
+
+ YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
- yamlSaveHelper.SaveBool(SS_YAML_KEY_VOTRAX_PHONEME, pMB->ssi263.GetVotraxPhoneme());
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_VOTRAX_PHONEME, m_MBSubUnit[0].ssi263.GetVotraxPhoneme()); // SC01 only in subunit 0
- for(UINT i=0; isy6522.SaveSnapshot(yamlSaveHelper);
- AY8910_SaveSnapshot(yamlSaveHelper, nDeviceNum, std::string(""));
+ AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string(""));
pMB->ssi263.SaveSnapshot(yamlSaveHelper);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister);
-
- nDeviceNum++;
- pMB++;
}
}
-bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
+bool MockingboardCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
- if (slot != 4 && slot != 5) // fixme
- Card::ThrowErrorInvalidSlot(CT_MockingboardC, slot);
+ if (m_slot == 0 || m_slot == 3)
+ throw std::runtime_error("Card: wrong slot");
if (version < 1 || version > kUNIT_VERSION)
- Card::ThrowErrorInvalidVersion(CT_MockingboardC, version);
+ throw std::runtime_error("Card: wrong version");
+
+ if (QueryType() == CT_Phasor)
+ return Phasor_LoadSnapshot(yamlLoadHelper, version);
+
+ //
AY8910UpdateSetCycles();
- const UINT nMbCardNum = slot - SLOT4;
- UINT nDeviceNum = nMbCardNum*2;
- SY6522_AY8910* pMB = &g_MB[nDeviceNum];
-
bool isVotrax = (version >= 6) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_VOTRAX_PHONEME) : false;
- pMB->ssi263.SetVotraxPhoneme(isVotrax);
+ m_MBSubUnit[0].ssi263.SetVotraxPhoneme(isVotrax); // SC01 only in subunit 0
- for(UINT i=0; isy6522.LoadSnapshot(yamlLoadHelper, version);
UpdateIFRandIRQ(pMB, 0, pMB->sy6522.GetReg(SY6522::rIFR)); // Assert any pending IRQs (GH#677)
- AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum, std::string(""));
- pMB->ssi263.LoadSnapshot(yamlLoadHelper, nDeviceNum, PH_Mockingboard, version); // Pre: SetVotraxPhoneme()
+ AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_A, std::string(""));
+ pMB->ssi263.LoadSnapshot(yamlLoadHelper, PH_Mockingboard, version); // Pre: SetVotraxPhoneme()
pMB->nAYCurrentRegister = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
@@ -1467,61 +1127,43 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
pMB->state = (MockingboardUnitState_e) (yamlLoadHelper.LoadUint(SS_YAML_KEY_MB_UNIT_STATE) & 7);
yamlLoadHelper.PopMap();
-
- //
-
- nDeviceNum++;
- pMB++;
}
AY8910_InitClock((int)Get6502BaseClock());
- // NB. g_SoundcardType & g_bPhasorEnable setup in MB_InitializeIO() -> MB_SetSoundcardType()
+ // NB. g_bPhasorEnable setup in ctor
return true;
}
-void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
+void MockingboardCard::Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
- if (uSlot != 4)
- throw std::runtime_error("Card: Phasor only supported in slot-4");
-
- UINT nDeviceNum = 0;
- SY6522_AY8910* pMB = &g_MB[0]; // fixme: Phasor uses MB's slot4(2x6522), slot4(2xSSI263), but slot4+5(4xAY8910)
-
- YamlSaveHelper::Slot slot(yamlSaveHelper, Phasor_GetSnapshotCardName(), uSlot, kUNIT_VERSION); // fixme: object should be just 1 Mockingboard card & it will know its slot
+ YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardNamePhasor(), m_slot, kUNIT_VERSION);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
- yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASOR_MODE, g_phasorMode);
- yamlSaveHelper.SaveBool(SS_YAML_KEY_VOTRAX_PHONEME, pMB->ssi263.GetVotraxPhoneme());
+ yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASOR_MODE, m_phasorMode);
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_VOTRAX_PHONEME, m_MBSubUnit[0].ssi263.GetVotraxPhoneme()); // SC01 only in subunit 0
- for(UINT i=0; isy6522.SaveSnapshot(yamlSaveHelper);
- AY8910_SaveSnapshot(yamlSaveHelper, nDeviceNum+0, std::string("-A"));
- AY8910_SaveSnapshot(yamlSaveHelper, nDeviceNum+1, std::string("-B"));
+ AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string("-A"));
+ AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
pMB->ssi263.SaveSnapshot(yamlSaveHelper);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE_B, pMB->stateB);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister);
-
- nDeviceNum += 2;
- pMB++;
}
}
-bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
+bool MockingboardCard::Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
- if (slot != 4) // fixme
- Card::ThrowErrorInvalidSlot(CT_Phasor, slot);
-
- if (version < 1 || version > kUNIT_VERSION)
- Card::ThrowErrorInvalidVersion(CT_Phasor, version);
-
if (version < 6)
yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR); // Consume redundant data
@@ -1533,20 +1175,22 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
else
phasorMode = PH_Phasor;
}
- g_phasorMode = (PHASOR_MODE) phasorMode;
- g_PhasorClockScaleFactor = (g_phasorMode == PH_Phasor) ? 2 : 1;
+ m_phasorMode = (PHASOR_MODE) phasorMode;
+ m_phasorClockScaleFactor = (m_phasorMode == PH_Phasor) ? 2 : 1;
AY8910UpdateSetCycles();
UINT nDeviceNum = 0;
- SY6522_AY8910* pMB = &g_MB[0];
+ MB_SUBUNIT* pMB = &m_MBSubUnit[0];
bool isVotrax = (version >= 6) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_VOTRAX_PHONEME) : false;
- pMB->ssi263.SetVotraxPhoneme(isVotrax);
+ m_MBSubUnit[0].ssi263.SetVotraxPhoneme(isVotrax); // SC01 only in subunit 0
- for(UINT i=0; isy6522.GetReg(SY6522::rIFR)); // Assert any pending IRQs (GH#677)
if (version >= 5 && version <= 8)
{
- const UINT phaseDeviceNum = nDeviceNum == 0 ? 2 : 0;
- AY8910_LoadSnapshot(yamlLoadHelper, phaseDeviceNum + 0, std::string("-A"));
- AY8910_LoadSnapshot(yamlLoadHelper, phaseDeviceNum + 1, std::string("-B"));
+ const UINT phasorSubunit = subunit == 0 ? 1 : 0;
+ AY8910_LoadSnapshot(yamlLoadHelper, phasorSubunit, AY8913_DEVICE_A, std::string("-A"));
+ AY8910_LoadSnapshot(yamlLoadHelper, phasorSubunit, AY8913_DEVICE_B, std::string("-B"));
}
else
{
- AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum + 0, std::string("-A"));
- AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum + 1, std::string("-B"));
+ AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_A, std::string("-A"));
+ AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
}
- pMB->ssi263.LoadSnapshot(yamlLoadHelper, nDeviceNum, PH_Phasor, version); // Pre: SetVotraxPhoneme()
+ pMB->ssi263.LoadSnapshot(yamlLoadHelper, PH_Phasor, version); // Pre: SetVotraxPhoneme()
pMB->nAYCurrentRegister = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
@@ -1594,16 +1238,11 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
pMB->stateB = (MockingboardUnitState_e) (yamlLoadHelper.LoadUint(SS_YAML_KEY_MB_UNIT_STATE_B) & 7);
yamlLoadHelper.PopMap();
-
- //
-
- nDeviceNum += 2;
- pMB++;
}
- AY8910_InitClock((int)(Get6502BaseClock() * g_PhasorClockScaleFactor));
+ AY8910_InitClock((int)(Get6502BaseClock() * m_phasorClockScaleFactor));
- // NB. g_SoundcardType & g_bPhasorEnable setup in MB_InitializeIO() -> MB_SetSoundcardType()
+ // NB. g_bPhasorEnable setup in ctor
return true;
}
diff --git a/source/Mockingboard.h b/source/Mockingboard.h
index edd67d55..081b5281 100644
--- a/source/Mockingboard.h
+++ b/source/Mockingboard.h
@@ -1,41 +1,147 @@
#pragma once
+#include "6522.h"
+#include "AY8910.h"
#include "Card.h"
+#include "SoundCore.h"
+#include "SSI263.h"
+#include "SynchronousEventManager.h"
-enum PHASOR_MODE {PH_Mockingboard=0, PH_UNDEF1, PH_UNDEF2, PH_UNDEF3, PH_UNDEF4, PH_Phasor/*=5*/, PH_UNDEF6, PH_EchoPlus/*=7*/};
+class MockingboardCard : public Card
+{
+public:
+ MockingboardCard(UINT slot, SS_CARDTYPE type);
+ virtual ~MockingboardCard(void);
-void MB_Initialize();
-void MB_Reinitialize();
-void MB_Destroy();
-void MB_Reset(const bool powerCycle);
-void MB_InitializeForLoadingSnapshot(void);
-void MB_InitializeIO(LPBYTE pCxRomPeripheral, UINT uSlot4, UINT uSlot5);
-void MB_Mute();
-void MB_Unmute();
+ virtual void InitializeIO(LPBYTE pCxRomPeripheral);
+ virtual void Destroy();
+ virtual void Reset(const bool powerCycle);
+ virtual void Update(const ULONG executedCycles);
+ virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
+ virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
+
+ static BYTE __stdcall IORead(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
+ static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
+ static BYTE __stdcall PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles);
+
+ BYTE IOReadInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles);
+ BYTE IOWriteInternal(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
+ BYTE PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles);
+
+ void ReinitializeClock(void);
+ void MuteControl(bool mute);
+ void UpdateCycles(ULONG executedCycles);
+ bool IsActive(void);
+ void SetVolume(DWORD dwVolume, DWORD dwVolumeMax);
+ void SetCumulativeCycles(void);
+ UINT MB_Update(void);
+ short** GetVoiceBuffers(void) { return m_ppAYVoiceBuffer; }
+ int GetNumSamplesError(void) { return m_numSamplesError; }
+ void SetNumSamplesError(int numSamplesError) { m_numSamplesError = numSamplesError; }
#ifdef _DEBUG
-void MB_CheckCumulativeCycles(); // DEBUG
+ void CheckCumulativeCycles(void);
+ void Get6522IrqDescription(std::string& desc);
#endif
-void MB_SetCumulativeCycles();
-void MB_PeriodicUpdate(UINT executedCycles);
-void MB_CheckIRQ();
-void MB_UpdateCycles(ULONG uExecutedCycles);
-SS_CARDTYPE MB_GetSoundcardType();
-bool MB_IsActive();
-DWORD MB_GetVolume();
-void MB_SetVolume(DWORD dwVolume, DWORD dwVolumeMax);
-void MB_Get6522IrqDescription(std::string& desc);
-void MB_OutputToRiff(void);
-void MB_UpdateIRQ(void);
-UINT64 MB_GetLastCumulativeCycles(void);
-void MB_UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask);
-BYTE MB_GetPCR(BYTE nDevice);
+ void UpdateIRQ(void);
+ UINT64 GetLastCumulativeCycles(void);
+ void UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask);
+ BYTE GetPCR(BYTE nDevice);
+ bool IsAnyTimer1Active(void);
-void MB_GetSnapshot_v1(struct SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot); // For debugger
-const std::string& MB_GetSnapshotCardName(void);
-void MB_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot);
-bool MB_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);
+ void GetSnapshot_v1(struct SS_CARD_MOCKINGBOARD_v1* const pSS);
-const std::string& Phasor_GetSnapshotCardName(void);
-void Phasor_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot);
-bool Phasor_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);
+ static std::string GetSnapshotCardName(void);
+ static std::string GetSnapshotCardNamePhasor(void);
+
+private:
+ enum MockingboardUnitState_e { AY_NOP0, AY_NOP1, AY_INACTIVE, AY_READ, AY_NOP4, AY_NOP5, AY_WRITE, AY_LATCH };
+
+ struct MB_SUBUNIT
+ {
+ SY6522 sy6522;
+ AY8913 ay8913[2]; // Phasor has 2x AY per 6522
+ SSI263 ssi263;
+ BYTE nAY8910Number;
+ BYTE nAYCurrentRegister;
+ MockingboardUnitState_e state; // Where a unit is a 6522+AY8910 pair
+ MockingboardUnitState_e stateB; // Phasor: 6522 & 2nd AY8910
+
+ MB_SUBUNIT(UINT slot) : sy6522(slot), ssi263(slot)
+ {
+ nAY8910Number = 0;
+ nAYCurrentRegister = 0;
+ state = AY_NOP0;
+ stateB = AY_NOP0;
+ // sy6522 & ssi263 have already been default constructed
+ }
+ };
+
+ void WriteToORB(BYTE subunit);
+ void AY8910_Write(BYTE subunit, BYTE ay, BYTE value);
+ void UpdateIFRandIRQ(MB_SUBUNIT* pMB, BYTE clr_mask, BYTE set_mask);
+
+ void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
+ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
+
+ static int MB_SyncEventCallback(int id, int /*cycles*/, ULONG uExecutedCycles);
+ int MB_SyncEventCallbackInternal(int id, int /*cycles*/, ULONG uExecutedCycles);
+
+ //-------------------------------------
+ // MAME interface
+ BYTE AYReadReg(BYTE subunit, BYTE ay, int r);
+ void _AYWriteReg(BYTE subunit, BYTE ay, int r, int v);
+ void AY8910_reset(BYTE subunit, BYTE ay);
+ void AY8910Update(BYTE subunit, BYTE ay, INT16** buffer, int nNumSamples);
+
+ void AY8910_InitAll(int nClock, int nSampleRate);
+ void AY8910_InitClock(int nClock);
+ BYTE* AY8910_GetRegsPtr(BYTE subunit, BYTE ay);
+
+ void AY8910UpdateSetCycles();
+
+ UINT AY8910_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, BYTE subunit, BYTE ay, const std::string& suffix);
+ UINT AY8910_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, BYTE subunit, BYTE ay, const std::string& suffix);
+
+ UINT64 m_lastAYUpdateCycle;
+ //-------------------------------------
+
+ static const UINT SY6522_DEVICE_A = 0;
+ static const UINT SY6522_DEVICE_B = 1;
+
+ static const UINT AY8913_DEVICE_A = 0;
+ static const UINT AY8913_DEVICE_B = 1; // Phasor only
+
+ // Chip offsets from card base:
+ static const UINT SY6522A_Offset = 0x00;
+ static const UINT SY6522B_Offset = 0x80;
+ static const UINT SSI263B_Offset = 0x20;
+ static const UINT SSI263A_Offset = 0x40;
+
+ // MB has 2x (1x SY6522 + 1x AY8913), Phasor has 2x (1x SY6522 + 2x AY8913)
+ MB_SUBUNIT m_MBSubUnit[NUM_SUBUNITS_PER_MB];
+
+ static const UINT kNumSyncEvents = NUM_SY6522 * SY6522::kNumTimersPer6522;
+ SyncEvent* m_syncEvent[kNumSyncEvents];
+
+ UINT64 m_lastCumulativeCycle;
+
+ static const DWORD SAMPLE_RATE = 44100; // Use a base freq so that DirectX (or sound h/w) doesn't have to up/down-sample
+
+ short* m_ppAYVoiceBuffer[NUM_VOICES];
+
+ UINT64 m_inActiveCycleCount;
+ bool m_regAccessedFlag;
+ bool m_isActive;
+
+ //
+
+ bool m_phasorEnable;
+ PHASOR_MODE m_phasorMode;
+ UINT m_phasorClockScaleFactor;
+
+ //
+
+ UINT64 m_lastMBUpdateCycle;
+ int m_numSamplesError;
+};
diff --git a/source/MockingboardCardManager.cpp b/source/MockingboardCardManager.cpp
new file mode 100644
index 00000000..9be7d0cc
--- /dev/null
+++ b/source/MockingboardCardManager.cpp
@@ -0,0 +1,445 @@
+/*
+AppleWin : An Apple //e emulator for Windows
+
+Copyright (C) 1994-1996, Michael O'Brien
+Copyright (C) 1999-2001, Oliver Schmidt
+Copyright (C) 2002-2005, Tom Charlesworth
+Copyright (C) 2006-2022, Tom Charlesworth, Michael Pohoreski, Nick Westgate
+
+AppleWin is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+AppleWin is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AppleWin; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Description: Mockingboard/Phasor Card Manager
+ *
+ * Author: Various
+ *
+ */
+
+#include "StdAfx.h"
+
+#include "MockingboardCardManager.h"
+#include "Core.h"
+#include "CardManager.h"
+#include "Mockingboard.h"
+#include "MockingboardDefs.h"
+#include "Riff.h"
+
+//#define DBG_MB_UPDATE
+
+bool MockingboardCardManager::IsMockingboard(UINT slot)
+{
+ SS_CARDTYPE type = GetCardMgr().QuerySlot(slot);
+ return type == CT_MockingboardC || type == CT_Phasor;
+}
+
+void MockingboardCardManager::ReinitializeClock(void)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).ReinitializeClock();
+ }
+}
+
+void MockingboardCardManager::InitializeForLoadingSnapshot(void)
+{
+ if (g_bDisableDirectSound || g_bDisableDirectSoundMockingboard)
+ return;
+
+ if (!m_mockingboardVoice.lpDSBvoice)
+ return;
+
+ DSVoiceStop(&m_mockingboardVoice); // Reason: 'MB voice is playing' then loading a save-state where 'no MB present' (GH#609)
+}
+
+void MockingboardCardManager::MuteControl(bool mute)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).MuteControl(mute);
+ }
+
+ if (mute)
+ {
+ if (m_mockingboardVoice.bActive && !m_mockingboardVoice.bMute)
+ {
+ m_mockingboardVoice.lpDSBvoice->SetVolume(DSBVOLUME_MIN);
+ m_mockingboardVoice.bMute = true;
+ }
+ }
+ else
+ {
+ if (m_mockingboardVoice.bActive && m_mockingboardVoice.bMute)
+ {
+ m_mockingboardVoice.lpDSBvoice->SetVolume(m_mockingboardVoice.nVolume);
+ m_mockingboardVoice.bMute = false;
+ }
+ }
+}
+
+void MockingboardCardManager::SetCumulativeCycles(void)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).SetCumulativeCycles();
+ }
+}
+
+void MockingboardCardManager::UpdateCycles(ULONG executedCycles)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).UpdateCycles(executedCycles);
+ }
+}
+
+bool MockingboardCardManager::IsActive(void)
+{
+ if (!m_mockingboardVoice.bActive)
+ return false;
+
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ if (dynamic_cast(GetCardMgr().GetRef(i)).IsActive())
+ return true; // if any card is true then the condition for active is true
+ }
+
+ return false;
+}
+
+DWORD MockingboardCardManager::GetVolume(void)
+{
+ return m_userVolume;
+}
+
+void MockingboardCardManager::SetVolume(DWORD volume, DWORD volumeMax)
+{
+ m_userVolume = volume;
+
+ m_mockingboardVoice.dwUserVolume = volume;
+ m_mockingboardVoice.nVolume = NewVolume(volume, volumeMax);
+
+ if (m_mockingboardVoice.bActive && !m_mockingboardVoice.bMute)
+ m_mockingboardVoice.lpDSBvoice->SetVolume(m_mockingboardVoice.nVolume);
+
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).SetVolume(volume, volumeMax);
+ }
+}
+
+#ifdef _DEBUG
+void MockingboardCardManager::CheckCumulativeCycles(void)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).CheckCumulativeCycles();
+ }
+}
+
+void MockingboardCardManager::Get6522IrqDescription(std::string& desc)
+{
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ dynamic_cast(GetCardMgr().GetRef(i)).Get6522IrqDescription(desc);
+ }
+}
+#endif
+
+// Called by CardManager::Destroy()
+void MockingboardCardManager::Destroy(void)
+{
+ // NB. All cards (including any Mockingboard cards) have just been destroyed by CardManager
+
+ if (m_mockingboardVoice.lpDSBvoice && m_mockingboardVoice.bActive)
+ DSVoiceStop(&m_mockingboardVoice);
+
+ DSReleaseSoundBuffer(&m_mockingboardVoice);
+}
+
+// Called by ContinueExecution() at the end of every execution period (~1000 cycles or ~3 cycles when MODE_STEPPING)
+// NB. Required for FT's TEST LAB #1 player
+void MockingboardCardManager::Update(const ULONG executedCycles)
+{
+ // NB. CardManager has just called each card's Update()
+
+ bool active = false;
+ for (UINT i = SLOT0; i < NUM_SLOTS; i++)
+ {
+ if (IsMockingboard(i))
+ active |= dynamic_cast(GetCardMgr().GetRef(i)).IsAnyTimer1Active();
+ }
+
+ if (active)
+ return;
+
+ // No 6522 TIMER1's are active, so periodically update AY8913's here...
+
+ const UINT kCyclesPerAudioFrame = 1000;
+ m_cyclesThisAudioFrame += executedCycles;
+ if (m_cyclesThisAudioFrame < kCyclesPerAudioFrame)
+ return;
+
+ m_cyclesThisAudioFrame %= kCyclesPerAudioFrame;
+
+ UpdateSoundBuffer();
+}
+
+// Called by:
+// . MB_SyncEventCallback() on a TIMER1 (not TIMER2) underflow - when IsAnyTimer1Active() == true
+// . Update() - when IsAnyTimer1Active() == false
+void MockingboardCardManager::UpdateSoundBuffer(void)
+{
+#ifdef LOG_PERF_TIMINGS
+ extern UINT64 g_timeMB_NoTimer;
+ extern UINT64 g_timeMB_Timer;
+ PerfMarker perfMarker(!IsAnyTimer1Active() ? g_timeMB_NoTimer : g_timeMB_Timer);
+#endif
+
+ if (!m_mockingboardVoice.lpDSBvoice)
+ {
+ if (g_bDisableDirectSound || g_bDisableDirectSoundMockingboard)
+ return;
+
+ if (!Init())
+ return;
+ }
+
+ if (!m_mockingboardVoice.bActive)
+ {
+ // Sound buffer may have been stopped by MB_InitializeForLoadingSnapshot().
+ // NB. DSZeroVoiceBuffer() also zeros the sound buffer, so it's better than directly calling IDirectSoundBuffer::Play():
+ // - without zeroing, then the previous sound buffer can be heard for a fraction of a second
+ // - eg. when doing Mockingboard playback, then loading a save-state which is also doing Mockingboard playback
+ DSZeroVoiceBuffer(&m_mockingboardVoice, SOUNDBUFFER_SIZE); // ... and Play()
+ }
+
+ UINT numSamples = GenerateAllSoundData();
+ if (numSamples)
+ MixAllAndCopyToRingBuffer(numSamples);
+}
+
+bool MockingboardCardManager::Init(void)
+{
+ if (!g_bDSAvailable)
+ return false;
+
+ const DWORD SAMPLE_RATE = 44100; // Use a base freq so that DirectX (or sound h/w) doesn't have to up/down-sample
+
+ HRESULT hr = DSGetSoundBuffer(&m_mockingboardVoice, DSBCAPS_CTRLVOLUME, SOUNDBUFFER_SIZE, SAMPLE_RATE, NUM_MB_CHANNELS, "MB");
+ LogFileOutput("MBCardMgr: DSGetSoundBuffer(), hr=0x%08X\n", hr);
+ if (FAILED(hr))
+ {
+ LogFileOutput("MBCardMgr: DSGetSoundBuffer failed (%08X)\n", hr);
+ return false;
+ }
+
+ bool bRes = DSZeroVoiceBuffer(&m_mockingboardVoice, SOUNDBUFFER_SIZE); // ... and Play()
+ LogFileOutput("MBCardMgr: DSZeroVoiceBuffer(), res=%d\n", bRes ? 1 : 0);
+ if (!bRes)
+ return false;
+
+ m_mockingboardVoice.bActive = true;
+
+ // Volume might've been setup from value in Registry
+ if (!m_mockingboardVoice.nVolume)
+ m_mockingboardVoice.nVolume = DSBVOLUME_MAX;
+
+ hr = m_mockingboardVoice.lpDSBvoice->SetVolume(m_mockingboardVoice.nVolume);
+ LogFileOutput("MBCardMgr: SetVolume(), hr=0x%08X\n", hr);
+ return true;
+}
+
+UINT MockingboardCardManager::GenerateAllSoundData(void)
+{
+ UINT nNumSamples = 0;
+
+ UINT numSamples0 = (UINT)-1;
+ int numSamplesError0 = -1;
+
+ for (UINT slot = SLOT0; slot < NUM_SLOTS; slot++)
+ {
+ if (!IsMockingboard(slot))
+ continue;
+
+ MockingboardCard& MB = dynamic_cast(GetCardMgr().GetRef(slot));
+
+ MB.SetNumSamplesError(m_numSamplesError);
+ nNumSamples = MB.MB_Update();
+ m_numSamplesError = MB.GetNumSamplesError();
+
+#if 1 // debug
+ if (numSamples0 == (UINT)-1)
+ {
+ numSamples0 = nNumSamples;
+ numSamplesError0 = m_numSamplesError;
+ }
+
+ _ASSERT(numSamples0 == nNumSamples);
+ _ASSERT(numSamplesError0 == m_numSamplesError);
+#endif
+ }
+
+ //
+
+ DWORD dwCurrentPlayCursor, dwCurrentWriteCursor;
+ HRESULT hr = m_mockingboardVoice.lpDSBvoice->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor);
+ if (FAILED(hr))
+ return 0;
+
+ if (m_byteOffset == (DWORD)-1)
+ {
+ // First time in this func
+
+ m_byteOffset = dwCurrentWriteCursor;
+ }
+ else
+ {
+ // Check that our offset isn't between Play & Write positions
+
+ if (dwCurrentWriteCursor > dwCurrentPlayCursor)
+ {
+ // |-----PxxxxxW-----|
+ if ((m_byteOffset > dwCurrentPlayCursor) && (m_byteOffset < dwCurrentWriteCursor))
+ {
+#ifdef DBG_MB_UPDATE
+ double fTicksSecs = (double)GetTickCount() / 1000.0;
+ LogOutput("%010.3f: [MBUpdt] PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X xxx\n", fTicksSecs, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor - dwCurrentPlayCursor, dwByteOffset, nNumSamples);
+#endif
+ m_byteOffset = dwCurrentWriteCursor;
+ m_numSamplesError = 0;
+ }
+ }
+ else
+ {
+ // |xxW----------Pxxx|
+ if ((m_byteOffset > dwCurrentPlayCursor) || (m_byteOffset < dwCurrentWriteCursor))
+ {
+#ifdef DBG_MB_UPDATE
+ double fTicksSecs = (double)GetTickCount() / 1000.0;
+ LogOutput("%010.3f: [MBUpdt] PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X XXX\n", fTicksSecs, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor - dwCurrentPlayCursor, dwByteOffset, nNumSamples);
+#endif
+ m_byteOffset = dwCurrentWriteCursor;
+ m_numSamplesError = 0;
+ }
+ }
+ }
+
+ int nBytesRemaining = m_byteOffset - dwCurrentPlayCursor;
+ if (nBytesRemaining < 0)
+ nBytesRemaining += SOUNDBUFFER_SIZE;
+
+ // Calc correction factor so that play-buffer doesn't under/overflow
+ const int nErrorInc = SoundCore_GetErrorInc();
+ if (nBytesRemaining < SOUNDBUFFER_SIZE / 4)
+ m_numSamplesError += nErrorInc; // < 0.25 of buffer remaining
+ else if (nBytesRemaining > SOUNDBUFFER_SIZE / 2)
+ m_numSamplesError -= nErrorInc; // > 0.50 of buffer remaining
+ else
+ m_numSamplesError = 0; // Acceptable amount of data in buffer
+
+#ifdef DBG_MB_UPDATE
+ double fTicksSecs = (double)GetTickCount() / 1000.0;
+ LogOutput("%010.3f: [MBUpdt] PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X, NSE=%08X, Interval=%f\n", fTicksSecs, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor - dwCurrentPlayCursor, dwByteOffset, nNumSamples, nNumSamplesError, updateInterval);
+#endif
+
+ return nNumSamples;
+}
+
+void MockingboardCardManager::MixAllAndCopyToRingBuffer(UINT nNumSamples)
+{
+// const double fAttenuation = g_bPhasorEnable ? 2.0 / 3.0 : 1.0;
+ const double fAttenuation = true ? 2.0 / 3.0 : 1.0;
+
+ short** slotAYVoiceBuffers[NUM_SLOTS] = {0};
+
+ for (UINT slot = SLOT0; slot < NUM_SLOTS; slot++)
+ {
+ if (IsMockingboard(slot))
+ slotAYVoiceBuffers[slot] = dynamic_cast(GetCardMgr().GetRef(slot)).GetVoiceBuffers();
+ }
+
+ for (UINT i = 0; i < nNumSamples; i++)
+ {
+ // Mockingboard stereo (all voices on an AY8910 wire-or'ed together)
+ // L = Address.b7=0, R = Address.b7=1
+ int nDataL = 0, nDataR = 0;
+
+ for (UINT slot = SLOT0; slot < NUM_SLOTS; slot++)
+ {
+ if (!slotAYVoiceBuffers[slot])
+ continue;
+
+ for (UINT j = 0; j < NUM_VOICES_PER_AY8913; j++)
+ {
+ short** ppAYVoiceBuffer = slotAYVoiceBuffers[slot];
+
+ // Regular MB-C AY's
+ nDataL += (int)((double)ppAYVoiceBuffer[0 * NUM_VOICES_PER_AY8913 + j][i] * fAttenuation);
+ nDataR += (int)((double)ppAYVoiceBuffer[2 * NUM_VOICES_PER_AY8913 + j][i] * fAttenuation);
+
+ // Extra Phasor AY's
+ nDataL += (int)((double)ppAYVoiceBuffer[1 * NUM_VOICES_PER_AY8913 + j][i] * fAttenuation);
+ nDataR += (int)((double)ppAYVoiceBuffer[3 * NUM_VOICES_PER_AY8913 + j][i] * fAttenuation);
+ }
+ }
+
+ // Cap the superpositioned output
+ if (nDataL < WAVE_DATA_MIN)
+ nDataL = WAVE_DATA_MIN;
+ else if (nDataL > WAVE_DATA_MAX)
+ nDataL = WAVE_DATA_MAX;
+
+ if (nDataR < WAVE_DATA_MIN)
+ nDataR = WAVE_DATA_MIN;
+ else if (nDataR > WAVE_DATA_MAX)
+ nDataR = WAVE_DATA_MAX;
+
+ m_mixBuffer[i * NUM_MB_CHANNELS + 0] = (short)nDataL; // L
+ m_mixBuffer[i * NUM_MB_CHANNELS + 1] = (short)nDataR; // R
+ }
+
+ //
+
+ DWORD dwDSLockedBufferSize0, dwDSLockedBufferSize1;
+ SHORT* pDSLockedBuffer0, * pDSLockedBuffer1;
+
+ HRESULT hr = DSGetLock(m_mockingboardVoice.lpDSBvoice,
+ m_byteOffset, (DWORD)nNumSamples * sizeof(short) * NUM_MB_CHANNELS,
+ &pDSLockedBuffer0, &dwDSLockedBufferSize0,
+ &pDSLockedBuffer1, &dwDSLockedBufferSize1);
+ if (FAILED(hr))
+ return;
+
+ memcpy(pDSLockedBuffer0, &m_mixBuffer[0], dwDSLockedBufferSize0);
+ if (pDSLockedBuffer1)
+ memcpy(pDSLockedBuffer1, &m_mixBuffer[dwDSLockedBufferSize0 / sizeof(short)], dwDSLockedBufferSize1);
+
+ // Commit sound buffer
+ hr = m_mockingboardVoice.lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
+ (void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
+
+ m_byteOffset = (m_byteOffset + (DWORD)nNumSamples * sizeof(short) * NUM_MB_CHANNELS) % SOUNDBUFFER_SIZE;
+
+ if (m_outputToRiff)
+ RiffPutSamples(&m_mixBuffer[0], nNumSamples);
+}
diff --git a/source/MockingboardCardManager.h b/source/MockingboardCardManager.h
new file mode 100644
index 00000000..10172d4e
--- /dev/null
+++ b/source/MockingboardCardManager.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "Core.h"
+#include "SoundCore.h"
+
+class MockingboardCardManager
+{
+public:
+ MockingboardCardManager(void)
+ {
+ m_numSamplesError = 0;
+ m_byteOffset = (DWORD)-1;
+ m_cyclesThisAudioFrame = 0;
+ m_userVolume = 0;
+ m_outputToRiff = false;
+
+ // NB. Cmd line has already been processed
+ LogFileOutput("MBCardMgr::ctor() g_bDisableDirectSound=%d, g_bDisableDirectSoundMockingboard=%d\n", g_bDisableDirectSound, g_bDisableDirectSoundMockingboard);
+ }
+ ~MockingboardCardManager(void)
+ {}
+
+ bool IsMockingboard(UINT slot);
+ void ReinitializeClock(void);
+ void InitializeForLoadingSnapshot(void);
+ void MuteControl(bool mute);
+ void SetCumulativeCycles(void);
+ void UpdateCycles(ULONG executedCycles);
+ bool IsActive(void);
+ DWORD GetVolume(void);
+ void SetVolume(DWORD volume, DWORD volumeMax);
+ void OutputToRiff(void) { m_outputToRiff = true; }
+
+ void Destroy(void);
+ void Reset(const bool powerCycle)
+ {
+ m_cyclesThisAudioFrame = 0;
+ }
+ void Update(const ULONG executedCycles);
+ void UpdateSoundBuffer(void);
+
+#ifdef _DEBUG
+ void CheckCumulativeCycles(void);
+ void Get6522IrqDescription(std::string& desc);
+#endif
+
+private:
+ bool Init(void);
+ UINT GenerateAllSoundData(void);
+ void MixAllAndCopyToRingBuffer(UINT nNumSamples);
+
+ static const unsigned short NUM_MB_CHANNELS = 2;
+ static const DWORD SOUNDBUFFER_SIZE = MAX_SAMPLES * sizeof(short) * NUM_MB_CHANNELS;
+
+ static const SHORT WAVE_DATA_MIN = (SHORT)0x8000;
+ static const SHORT WAVE_DATA_MAX = (SHORT)0x7FFF;
+
+ short m_mixBuffer[SOUNDBUFFER_SIZE / sizeof(short)];
+ VOICE m_mockingboardVoice;
+
+ //
+
+ int m_numSamplesError;
+ DWORD m_byteOffset;
+ UINT m_cyclesThisAudioFrame;
+ DWORD m_userVolume; // GUI's slide volume
+ bool m_outputToRiff;
+};
diff --git a/source/MockingboardDefs.h b/source/MockingboardDefs.h
new file mode 100644
index 00000000..3e60e21a
--- /dev/null
+++ b/source/MockingboardDefs.h
@@ -0,0 +1,13 @@
+#pragma once
+
+// PHASOR_MODE: Circular dependency for Mockingboard.h & SSI263.h - so put it here for now
+enum PHASOR_MODE { PH_Mockingboard = 0, PH_UNDEF1, PH_UNDEF2, PH_UNDEF3, PH_UNDEF4, PH_Phasor/*=5*/, PH_UNDEF6, PH_EchoPlus/*=7*/ };
+
+const UINT NUM_SY6522 = 2;
+const UINT NUM_AY8913 = 4; // Phasor has 4, MB has 2
+const UINT NUM_SSI263 = 2;
+
+const UINT NUM_SUBUNITS_PER_MB = NUM_SY6522;
+const UINT NUM_AY8913_PER_SUBUNIT = NUM_AY8913 / NUM_SUBUNITS_PER_MB;
+const UINT NUM_VOICES_PER_AY8913 = 3;
+const UINT NUM_VOICES = NUM_AY8913 * NUM_VOICES_PER_AY8913;
diff --git a/source/SSI263.cpp b/source/SSI263.cpp
index 7b7abd2a..67f3728d 100644
--- a/source/SSI263.cpp
+++ b/source/SSI263.cpp
@@ -29,6 +29,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "StdAfx.h"
#include "6522.h"
+#include "CardManager.h"
+#include "Mockingboard.h"
#include "Core.h"
#include "CPU.h"
#include "Log.h"
@@ -104,6 +106,23 @@ void SSI_Output(void)
//-----------------------------------------------------------------------------
+UINT64 SSI263::GetLastCumulativeCycles(void)
+{
+ return dynamic_cast(GetCardMgr().GetRef(m_slot)).GetLastCumulativeCycles();
+}
+
+void SSI263::UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask)
+{
+ dynamic_cast(GetCardMgr().GetRef(m_slot)).UpdateIFR(nDevice, clr_mask, set_mask);
+}
+
+BYTE SSI263::GetPCR(BYTE nDevice)
+{
+ return dynamic_cast(GetCardMgr().GetRef(m_slot)).GetPCR(nDevice);
+}
+
+//-----------------------------------------------------------------------------
+
BYTE SSI263::Read(ULONG nExecutedCycles)
{
// Regardless of register, just return inverted A/!R in bit7
@@ -292,7 +311,7 @@ void SSI263::Votrax_Write(BYTE value)
m_isVotraxPhoneme = true;
// !A/R: Acknowledge receipt of phoneme data (signal goes from high to low)
- MB_UpdateIFR(m_device, SY6522::IxR_VOTRAX, 0);
+ UpdateIFR(m_device, SY6522::IxR_VOTRAX, 0);
// NB. Don't set reg0.DUR, as SC01's phoneme duration doesn't change with pitch (empirically determined from MAME's SC01 emulation)
//m_durationPhoneme = value; // Set reg0.DUR = I1:0 (inflection or pitch)
@@ -363,7 +382,7 @@ void SSI263::Play(unsigned int nPhoneme)
m_currSampleMod4 = 0;
// Set m_lastUpdateCycle, otherwise UpdateAccurateLength() can immediately complete phoneme! (GH#1104)
- m_lastUpdateCycle = MB_GetLastCumulativeCycles();
+ m_lastUpdateCycle = GetLastCumulativeCycles();
}
void SSI263::Stop(void)
@@ -479,7 +498,7 @@ void SSI263::Update(void)
// . NB. this is fine, since it's the steady state; and it's likely that no actual data will ever occur during this initial time.
// This means that the '1st phoneme playback time' (in cycles) will be a bit longer for subsequent times.
- m_lastUpdateCycle = MB_GetLastCumulativeCycles();
+ m_lastUpdateCycle = GetLastCumulativeCycles();
nNumSamples = kMinBytesInBuffer / sizeof(short);
memset(&m_mixBufferSSI263[0], 0, nNumSamples);
@@ -491,14 +510,14 @@ void SSI263::Update(void)
const double kMinimumUpdateInterval = 500.0; // Arbitary (500 cycles = 21 samples)
const double kMaximumUpdateInterval = (double)(0xFFFF + 2); // Max 6522 timer interval (1372 samples)
- _ASSERT(MB_GetLastCumulativeCycles() >= m_lastUpdateCycle);
- updateInterval = (double)(MB_GetLastCumulativeCycles() - m_lastUpdateCycle);
+ _ASSERT(GetLastCumulativeCycles() >= m_lastUpdateCycle);
+ updateInterval = (double)(GetLastCumulativeCycles() - m_lastUpdateCycle);
if (updateInterval < kMinimumUpdateInterval)
return;
if (updateInterval > kMaximumUpdateInterval)
updateInterval = kMaximumUpdateInterval;
- m_lastUpdateCycle = MB_GetLastCumulativeCycles();
+ m_lastUpdateCycle = GetLastCumulativeCycles();
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up
const int nNumSamplesPerPeriod = (int)((double)(SAMPLE_RATE_SSI263) / nIrqFreq); // Eg. For 60Hz this is 367
@@ -659,7 +678,7 @@ void SSI263::UpdateAccurateLength(void)
if (m_lastUpdateCycle == 0)
return;
- double updateInterval = (double)(MB_GetLastCumulativeCycles() - m_lastUpdateCycle);
+ double updateInterval = (double)(GetLastCumulativeCycles() - m_lastUpdateCycle);
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up
const int nNumSamplesPerPeriod = (int)((double)(SAMPLE_RATE_SSI263) / nIrqFreq); // Eg. For 60Hz this is 367
@@ -717,9 +736,9 @@ void SSI263::SetSpeechIRQ(void)
{
if (m_cardMode == PH_Mockingboard)
{
- if ((MB_GetPCR(m_device) & 1) == 0) // CA1 Latch/Input = 0 (Negative active edge)
- MB_UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
- if (MB_GetPCR(m_device) == 0x0C) // CA2 Control = b#110 (Low output)
+ if ((GetPCR(m_device) & 1) == 0) // CA1 Latch/Input = 0 (Negative active edge)
+ UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
+ if (GetPCR(m_device) == 0x0C) // CA2 Control = b#110 (Low output)
m_currentMode &= ~1; // Clear SSI263's D7 pin (cleared by 6522's PCR CA1/CA2 handshake)
// NB. Don't set CTL=1, as Mockingboard(SMS) speech doesn't work (it sets MODE_IRQ_DISABLED mode during ISR)
@@ -738,11 +757,11 @@ void SSI263::SetSpeechIRQ(void)
//
- if (m_isVotraxPhoneme && MB_GetPCR(m_device) == 0xB0)
+ if (m_isVotraxPhoneme && GetPCR(m_device) == 0xB0)
{
// !A/R: Time-out of old phoneme (signal goes from low to high)
- MB_UpdateIFR(m_device, 0, SY6522::IxR_VOTRAX);
+ UpdateIFR(m_device, 0, SY6522::IxR_VOTRAX);
m_isVotraxPhoneme = false;
}
@@ -863,7 +882,7 @@ void SSI263::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME, IsPhonemeActive());
}
-void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT device, PHASOR_MODE mode, UINT version)
+void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT version)
{
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_SSI263))
throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_SSI263);
@@ -891,5 +910,5 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT device, PHASOR_MO
if (IsPhonemeActive())
UpdateIRQ(); // Pre: m_device, m_cardMode
- m_lastUpdateCycle = MB_GetLastCumulativeCycles();
+ m_lastUpdateCycle = GetLastCumulativeCycles();
}
diff --git a/source/SSI263.h b/source/SSI263.h
index f4ee40cd..3096bed4 100644
--- a/source/SSI263.h
+++ b/source/SSI263.h
@@ -1,11 +1,11 @@
#pragma once
-#include "Mockingboard.h" // enum PHASOR_MODE
+#include "MockingboardDefs.h"
class SSI263
{
public:
- SSI263(void)
+ SSI263(UINT slot) : m_slot(slot)
{
m_device = -1; // undefined
m_cardMode = PH_Mockingboard;
@@ -59,6 +59,9 @@ public:
m_dbgStartTime = 0;
}
+ void SetDevice(UINT device) { m_device = device; }
+ void SetCardMode(PHASOR_MODE mode) { m_cardMode = mode; }
+
bool DSInit(void);
void DSUninit(void);
@@ -80,11 +83,8 @@ public:
bool GetVotraxPhoneme(void) { return m_isVotraxPhoneme; }
void SetVotraxPhoneme(bool value) { m_isVotraxPhoneme = value; }
- void SetCardMode(PHASOR_MODE mode) { m_cardMode = mode; }
- void SetDevice(UINT device) { m_device = device; }
-
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
- void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT device, PHASOR_MODE mode, UINT version);
+ void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT version);
private:
void Play(unsigned int nPhoneme);
@@ -92,6 +92,10 @@ private:
void UpdateIRQ(void);
void UpdateAccurateLength(void);
+ UINT64 GetLastCumulativeCycles(void);
+ void UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask);
+ BYTE GetPCR(BYTE nDevice);
+
static const BYTE m_Votrax2SSI263[/*64*/];
static const unsigned short m_kNumChannels = 1;
@@ -101,6 +105,7 @@ private:
//
+ UINT m_slot;
BYTE m_device; // SSI263 device# which is generating phoneme-complete IRQ (and only required whilst Mockingboard isn't a class)
PHASOR_MODE m_cardMode;
short* m_pPhonemeData00;
diff --git a/source/SaveState.cpp b/source/SaveState.cpp
index 641c2152..cfc6e170 100644
--- a/source/SaveState.cpp
+++ b/source/SaveState.cpp
@@ -388,7 +388,7 @@ static void Snapshot_LoadState_v2(void)
GetVideo().SetVidHD(false); // Set true later only if VidHDCard is instantiated
GetVideo().VideoResetState();
GetVideo().SetVideoRefreshRate(VR_60HZ); // Default to 60Hz as older save-states won't contain refresh rate
- MB_InitializeForLoadingSnapshot(); // GH#609
+ GetCardMgr().GetMockingboardCardMgr().InitializeForLoadingSnapshot(); // GH#609
#ifdef USE_SPEECH_API
g_Speech.Reset();
#endif
@@ -402,7 +402,7 @@ static void Snapshot_LoadState_v2(void)
throw std::runtime_error("Unknown top-level scalar: " + scalar);
}
- MB_SetCumulativeCycles();
+ GetCardMgr().GetMockingboardCardMgr().SetCumulativeCycles();
frame.SetLoadedSaveStateFlag(true);
// NB. The following disparity should be resolved:
diff --git a/source/SoundCore.cpp b/source/SoundCore.cpp
index 5f1723a3..4edbb414 100644
--- a/source/SoundCore.cpp
+++ b/source/SoundCore.cpp
@@ -48,7 +48,7 @@ static LPDIRECTSOUND g_lpDS = NULL;
// Used for muting & fading:
-static const UINT uMAX_VOICES = 6; // 4x SSI263 + spkr + mockingboard
+static const UINT uMAX_VOICES = NUM_SLOTS * 2 + 1 + 1; // 8x (2x SSI263) + spkr + MockingboardCardManager
static UINT g_uNumVoices = 0;
static VOICE* g_pVoices[uMAX_VOICES] = {NULL};
@@ -58,6 +58,21 @@ static VOICE* g_pSpeakerVoice = NULL;
bool g_bDSAvailable = false;
+//-----------------------------------------------------------------------------
+
+// NB. Also similar is done by: MockingboardCardManager::Destroy()
+// - which is called from WM_DESTROY (when both restarting VM & exiting the app)
+
+VOICE::~VOICE(void)
+{
+ if (lpDSBvoice)
+ {
+ DSVoiceStop(this);
+ DSReleaseSoundBuffer(this);
+ }
+}
+
+
//-----------------------------------------------------------------------------
static BOOL CALLBACK DSEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext)
@@ -540,8 +555,10 @@ bool DSInit()
return false;
}
- hr = g_lpDS->SetCooperativeLevel(GetFrame().g_hFrameWindow, DSSCL_NORMAL);
- if(FAILED(hr))
+ HWND hwnd = GetFrame().g_hFrameWindow;
+ _ASSERT(hwnd);
+ hr = g_lpDS->SetCooperativeLevel(hwnd, DSSCL_NORMAL);
+ if (FAILED(hr))
{
if(g_fh) fprintf(g_fh, "SetCooperativeLevel failed (%08X)\n",hr);
return false;
diff --git a/source/SoundCore.h b/source/SoundCore.h
index 93f409e8..cdd2f46f 100644
--- a/source/SoundCore.h
+++ b/source/SoundCore.h
@@ -30,6 +30,8 @@ struct VOICE
bRecentlyActive = false;
name = "";
}
+
+ ~VOICE(void);
};
typedef VOICE* PVOICE;
diff --git a/source/Utilities.cpp b/source/Utilities.cpp
index f636180c..c38b587d 100644
--- a/source/Utilities.cpp
+++ b/source/Utilities.cpp
@@ -211,7 +211,7 @@ void LoadConfiguration(bool loadImages)
SpkrSetVolume(dwTmp, GetPropertySheet().GetVolumeMax());
if(REGLOAD(TEXT(REGVALUE_MB_VOLUME), &dwTmp))
- MB_SetVolume(dwTmp, GetPropertySheet().GetVolumeMax());
+ GetCardMgr().GetMockingboardCardMgr().SetVolume(dwTmp, GetPropertySheet().GetVolumeMax());
if(REGLOAD(TEXT(REGVALUE_SAVE_STATE_ON_EXIT), &dwTmp))
g_bSaveStateOnExit = dwTmp ? true : false;
@@ -528,7 +528,6 @@ void ResetMachineState()
GetVideo().VideoResetState();
KeybReset();
JoyReset();
- MB_Reset(true);
SpkrReset();
SetActiveCpu(GetMainCpu());
#ifdef USE_SPEECH_API
@@ -568,7 +567,6 @@ void CtrlReset()
GetPravets().Reset();
GetCardMgr().Reset(false);
KeybReset();
- MB_Reset(false);
#ifdef USE_SPEECH_API
g_Speech.Reset();
#endif
diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp
index eacbff03..afa5b035 100644
--- a/source/Windows/AppleWin.cpp
+++ b/source/Windows/AppleWin.cpp
@@ -179,7 +179,7 @@ static void ContinueExecution(void)
const bool bWasFullSpeed = g_bFullSpeed;
g_bFullSpeed = (g_dwSpeed == SPEED_MAX) ||
bScrollLock_FullSpeed ||
- (GetCardMgr().GetDisk2CardMgr().IsConditionForFullSpeed() && !Spkr_IsActive() && !MB_IsActive()) ||
+ (GetCardMgr().GetDisk2CardMgr().IsConditionForFullSpeed() && !Spkr_IsActive() && !GetCardMgr().GetMockingboardCardMgr().IsActive()) ||
IsDebugSteppingAtFullSpeed();
if (g_bFullSpeed)
@@ -188,7 +188,7 @@ static void ContinueExecution(void)
GetFrame().VideoRedrawScreenDuringFullSpeed(0, true); // Init for full-speed mode
// Don't call Spkr_Mute() - will get speaker clicks
- MB_Mute();
+ GetCardMgr().GetMockingboardCardMgr().MuteControl(true);
SysClk_StopTimer();
#ifdef USE_SPEECH_API
g_Speech.Reset(); // TODO: Put this on a timer (in emulated cycles)... otherwise CATALOG cuts out
@@ -206,7 +206,7 @@ static void ContinueExecution(void)
GetFrame().VideoRedrawScreenAfterFullSpeed(g_dwCyclesThisFrame);
// Don't call Spkr_Unmute()
- MB_Unmute();
+ GetCardMgr().GetMockingboardCardMgr().MuteControl(false);
SysClk_StartTimerUsec(nExecutionPeriodUsec);
// Switch to higher priority, eg. for audio (BUG #015394)
@@ -633,7 +633,7 @@ static void OneTimeInitialization(HINSTANCE passinstance)
else if (!g_cmdLine.wavFileMockingboard.empty())
{
if (RiffInitWriteFile(g_cmdLine.wavFileMockingboard.c_str(), 44100, 2))
- MB_OutputToRiff();
+ GetCardMgr().GetMockingboardCardMgr().OutputToRiff();
}
// Initialize COM - so we can use CoCreateInstance
@@ -678,7 +678,7 @@ static void RepeatInitialization(void)
// NB. g_OldAppleWinVersion needed by LoadConfiguration() -> Config_Load_Video()
const bool bShowAboutDlg = CheckOldAppleWinVersion(); // Post: g_OldAppleWinVersion
- // Load configuration from Registry
+ // Load configuration from Registry (+ will insert cards)
{
bool loadImages = g_cmdLine.szSnapshotName == NULL; // don't load floppy/harddisk images if a snapshot is to be loaded later on
LoadConfiguration(loadImages);
diff --git a/source/Windows/WinFrame.cpp b/source/Windows/WinFrame.cpp
index d7f0f300..a379087b 100644
--- a/source/Windows/WinFrame.cpp
+++ b/source/Windows/WinFrame.cpp
@@ -1054,14 +1054,11 @@ LRESULT Win32Frame::WndProc(
if (!g_bRestart) // GH#564: Only save-state on shutdown (not on a restart)
Snapshot_Shutdown();
DebugDestroy();
- if (!g_bRestart) {
- GetCardMgr().Destroy();
- }
+ GetCardMgr().Destroy();
CpuDestroy();
MemDestroy();
SpkrDestroy();
Destroy();
- MB_Destroy();
DeleteGdiObjects();
DIMouse::DirectInputUninit(window); // NB. do before window is destroyed
PostQuitMessage(0); // Post WM_QUIT message to the thread's message queue
@@ -1076,15 +1073,12 @@ LRESULT Win32Frame::WndProc(
CreateGdiObjects();
LogFileOutput("WM_CREATE: CreateGdiObjects()\n");
- DSInit();
+ DSInit(); // NB. Need g_hFrameWindow for IDirectSound::SetCooperativeLevel()
LogFileOutput("WM_CREATE: DSInit()\n");
DIMouse::DirectInputInit(window);
LogFileOutput("WM_CREATE: DIMouse::DirectInputInit()\n");
- MB_Initialize();
- LogFileOutput("WM_CREATE: MB_Initialize()\n");
-
SpkrInitialize();
LogFileOutput("WM_CREATE: SpkrInitialize()\n");