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");