Refactor Mockingboard into a class (#1171)

Mockingboard or Phasor cards can be inserted into any slot.
Change Config->Sound to that slots 4+5 to be individually selected for the 3 soundcard types.
Add MockingboardCardManager class to manage multiple cards and mix the sound buffers.
This commit is contained in:
TomCh 2023-01-28 18:15:28 +00:00 committed by GitHub
parent 4668718fb3
commit 71c67cf132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1439 additions and 1251 deletions

View File

@ -91,6 +91,8 @@
<ClInclude Include="source\Log.h" />
<ClInclude Include="source\Memory.h" />
<ClInclude Include="source\Mockingboard.h" />
<ClInclude Include="source\MockingboardCardManager.h" />
<ClInclude Include="source\MockingboardDefs.h" />
<ClInclude Include="source\MouseInterface.h" />
<ClInclude Include="source\NoSlotClock.h" />
<ClInclude Include="source\NTSC.h" />
@ -176,6 +178,7 @@
<ClCompile Include="source\Disk2CardManager.cpp" />
<ClCompile Include="source\FourPlay.cpp" />
<ClCompile Include="source\FrameBase.cpp" />
<ClCompile Include="source\MockingboardCardManager.cpp" />
<ClCompile Include="source\RGBMonitor.cpp" />
<ClCompile Include="source\SAM.cpp" />
<ClCompile Include="source\Debugger\Debug.cpp" />

View File

@ -271,6 +271,9 @@
<ClCompile Include="source\CopyProtectionDongles.cpp">
<Filter>Source Files\Emulator</Filter>
</ClCompile>
<ClCompile Include="source\MockingboardCardManager.cpp">
<Filter>Source Files\Emulator</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\CommonVICE\6510core.h">
@ -615,6 +618,12 @@
<ClInclude Include="source\CopyProtectionDongles.h">
<Filter>Source Files\Emulator</Filter>
</ClInclude>
<ClInclude Include="source\MockingboardCardManager.h">
<Filter>Source Files\Emulator</Filter>
</ClInclude>
<ClInclude Include="source\MockingboardDefs.h">
<Filter>Source Files\Emulator</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="resource\Applewin.bmp">

View File

@ -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

View File

@ -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

View File

@ -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<MockingboardCard&>(GetCardMgr().GetRef(m_slot)).UpdateIRQ();
}
//-----------------------------------------------------------------------------

View File

@ -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;
};

View File

@ -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; i++)
{
g_AY8910[i].sound_init(NULL); // Inits mainly static members (except ay_tick_incr)
g_AY8910[i].sound_ay_init();
}
}
void AY8910_InitClock(int nClock)
{
CAY8910::SetCLK( (double)nClock );
for (UINT i=0; i<MAX_8910; i++)
{
g_AY8910[i].sound_init(NULL); // ay_tick_incr is dependent on AY_CLK
}
}
BYTE* AY8910_GetRegsPtr(UINT uChip)
{
if (uChip >= 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;
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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[];
};

View File

@ -227,7 +227,7 @@ void SetCurrentCLK6502(void)
//
SpkrReinitialize();
MB_Reinitialize();
GetCardMgr().GetMockingboardCardMgr().ReinitializeClock();
}
void UseClockMultiplier(double clockMultiplier)

View File

@ -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<MockingboardCard&>(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
{

View File

@ -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<Disk2InterfaceCard&>(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)

File diff suppressed because it is too large Load Diff

View File

@ -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;
};

View File

@ -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<MockingboardCard&>(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<MockingboardCard&>(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<MockingboardCard&>(GetCardMgr().GetRef(i)).SetCumulativeCycles();
}
}
void MockingboardCardManager::UpdateCycles(ULONG executedCycles)
{
for (UINT i = SLOT0; i < NUM_SLOTS; i++)
{
if (IsMockingboard(i))
dynamic_cast<MockingboardCard&>(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<MockingboardCard&>(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<MockingboardCard&>(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<MockingboardCard&>(GetCardMgr().GetRef(i)).CheckCumulativeCycles();
}
}
void MockingboardCardManager::Get6522IrqDescription(std::string& desc)
{
for (UINT i = SLOT0; i < NUM_SLOTS; i++)
{
if (IsMockingboard(i))
dynamic_cast<MockingboardCard&>(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<MockingboardCard&>(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<MockingboardCard&>(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<MockingboardCard&>(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);
}

View File

@ -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;
};

13
source/MockingboardDefs.h Normal file
View File

@ -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;

View File

@ -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<MockingboardCard&>(GetCardMgr().GetRef(m_slot)).GetLastCumulativeCycles();
}
void SSI263::UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask)
{
dynamic_cast<MockingboardCard&>(GetCardMgr().GetRef(m_slot)).UpdateIFR(nDevice, clr_mask, set_mask);
}
BYTE SSI263::GetPCR(BYTE nDevice)
{
return dynamic_cast<MockingboardCard&>(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();
}

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -30,6 +30,8 @@ struct VOICE
bRecentlyActive = false;
name = "";
}
~VOICE(void);
};
typedef VOICE* PVOICE;

View File

@ -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

View File

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

View File

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