2006-02-25 20:50:29 +00:00
/*
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
2007-04-01 15:24:52 +00:00
Copyright ( C ) 2006 - 2007 , Tom Charlesworth , Michael Pohoreski
2006-02-25 20:50:29 +00:00
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 emulation
*
*/
// Notes on Votrax chip (on original Mockingboards):
// From Crimewave (Penguin Software):
// . Init:
// . DDRB = 0xFF
// . PCR = 0xB0
// . IER = 0x90
// . ORB = 0x03 (PAUSE0) or 0x3F (STOP)
// . IRQ:
// . ORB = Phoneme value
// . IRQ last phoneme complete:
// . IER = 0x10
// . ORB = 0x3F (STOP)
//
# include "StdAfx.h"
2020-11-11 21:15:27 +00:00
# include "Mockingboard.h"
2023-01-28 18:15:28 +00:00
# include "MockingboardDefs.h"
2022-02-05 18:48:36 +00:00
# include "6522.h"
2015-02-13 22:40:53 +00:00
2020-11-26 21:50:06 +00:00
# include "Core.h"
2019-12-19 19:42:30 +00:00
# include "CardManager.h"
2014-08-13 20:30:35 +00:00
# include "CPU.h"
# include "Log.h"
# include "Memory.h"
# include "SoundCore.h"
2020-10-11 15:08:05 +00:00
# include "SynchronousEventManager.h"
2015-12-05 16:50:27 +00:00
# include "YamlHelper.h"
2014-08-13 20:30:35 +00:00
2009-01-09 23:27:29 +00:00
# include "AY8910.h"
2021-03-23 22:01:41 +00:00
# include "SSI263.h"
2014-08-13 20:30:35 +00:00
2023-01-28 18:15:28 +00:00
# define DBG_MB_SS_CARD 0 // NB. From UI, select Mockingboard (not Phasor)
2023-02-19 16:38:06 +00:00
# define DBG_SUPPORT_ECHOPLUS 0 // Allow Phasor (in Echo+ mode) to pass the TMS5220 detection used by Echo+ disk
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
//---------------------------------------------------------------------------
2017-10-27 10:10:15 +00:00
2023-01-28 18:15:28 +00:00
MockingboardCard : : MockingboardCard ( UINT slot , SS_CARDTYPE type ) : Card ( type , slot ) , m_MBSubUnit { slot , slot }
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
m_lastCumulativeCycle = 0 ;
m_lastAYUpdateCycle = 0 ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_VOICES ; i + + )
2023-02-05 21:03:37 +00:00
m_ppAYVoiceBuffer [ i ] = new short [ MAX_SAMPLES ] ; // Buffer can hold a max of 0.37 seconds worth of samples (16384/44100)
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
m_inActiveCycleCount = 0 ;
m_regAccessedFlag = false ;
m_isActive = false ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
m_phasorEnable = ( QueryType ( ) = = CT_Phasor ) ;
m_phasorMode = PH_Mockingboard ;
2023-01-28 20:56:54 +00:00
m_phasorClockScaleFactor = 1 ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
m_lastMBUpdateCycle = 0 ;
m_numSamplesError = 0 ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
//
2019-09-05 19:42:34 +00:00
2023-01-28 18:15:28 +00:00
for ( int id = 0 ; id < kNumSyncEvents ; id + + )
{
int syncId = ( m_slot < < 4 ) + id ; // NB. Encode the slot# into the id - used by MB_SyncEventCallback()
m_syncEvent [ id ] = new SyncEvent ( syncId , 0 , MB_SyncEventCallback ) ;
}
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
{
m_MBSubUnit [ i ] . nAY8910Number = i ;
2023-03-05 15:37:52 +00:00
m_MBSubUnit [ i ] . Reset ( QueryType ( ) ) ;
2023-01-28 18:15:28 +00:00
const UINT id0 = i * SY6522 : : kNumTimersPer6522 + 0 ; // TIMER1
const UINT id1 = i * SY6522 : : kNumTimersPer6522 + 1 ; // TIMER2
m_MBSubUnit [ i ] . sy6522 . InitSyncEvents ( m_syncEvent [ id0 ] , m_syncEvent [ id1 ] ) ;
m_MBSubUnit [ i ] . ssi263 . SetDevice ( i ) ;
}
2022-04-17 15:23:46 +00:00
2023-01-28 18:15:28 +00:00
AY8910_InitAll ( ( int ) g_fCurrentCLK6502 , SAMPLE_RATE ) ;
LogFileOutput ( " MockingboardCard::ctor: AY8910_InitAll() \n " ) ;
2022-04-17 15:23:46 +00:00
2023-01-28 18:15:28 +00:00
Reset ( true ) ;
LogFileOutput ( " MockingboardCard::ctor: Reset() \n " ) ;
}
2022-04-17 15:23:46 +00:00
2023-01-28 18:15:28 +00:00
MockingboardCard : : ~ MockingboardCard ( void )
2022-04-17 15:23:46 +00:00
{
2023-01-28 18:15:28 +00:00
Destroy ( ) ;
2022-04-17 15:23:46 +00:00
}
2006-02-25 20:50:29 +00:00
//---------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
bool MockingboardCard : : IsAnyTimer1Active ( void )
2022-02-12 18:42:58 +00:00
{
bool active = false ;
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
active | = m_MBSubUnit [ i ] . sy6522 . IsTimer1Active ( ) ;
2022-02-12 18:42:58 +00:00
return active ;
}
//---------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
# ifdef _DEBUG
void MockingboardCard : : Get6522IrqDescription ( std : : string & desc )
2021-03-23 22:01:41 +00:00
{
2023-01-28 18:15:28 +00:00
bool isIRQ = false ;
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
2021-03-23 22:01:41 +00:00
{
2023-01-28 18:15:28 +00:00
if ( m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & SY6522 : : IFR_IRQ )
2021-03-23 22:01:41 +00:00
{
2023-01-28 18:15:28 +00:00
isIRQ = true ;
break ;
}
}
if ( ! isIRQ )
return ;
//
desc + = " Slot- " ;
desc + = m_slot ;
desc + = " : " ;
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
{
if ( m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & SY6522 : : IFR_IRQ )
{
if ( m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & SY6522 : : IxR_TIMER1 )
2021-03-23 22:01:41 +00:00
{
desc + = ( ( i & 1 ) = = 0 ) ? " A: " : " B: " ;
desc + = " TIMER1 " ;
}
2023-01-28 18:15:28 +00:00
if ( m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & SY6522 : : IxR_TIMER2 )
2021-03-23 22:01:41 +00:00
{
desc + = ( ( i & 1 ) = = 0 ) ? " A: " : " B: " ;
desc + = " TIMER2 " ;
}
2023-01-28 18:15:28 +00:00
if ( m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & SY6522 : : IxR_VOTRAX )
2021-03-23 22:01:41 +00:00
{
desc + = ( ( i & 1 ) = = 0 ) ? " A: " : " B: " ;
desc + = " VOTRAX " ;
}
2023-01-28 18:15:28 +00:00
if ( m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & SY6522 : : IxR_SSI263 )
2021-03-23 22:01:41 +00:00
{
desc + = ( ( i & 1 ) = = 0 ) ? " A: " : " B: " ;
desc + = " SSI263 " ;
}
}
}
2023-01-28 18:15:28 +00:00
desc + = " \n " ;
}
# endif
//-----------------------------------------------------------------------------
2023-03-05 15:37:52 +00:00
// Notes on Phasor's AY-3-8913 chip-select & r/w: (GH#1192)
// ----------------------------------------------
//
// Where: AY1 is the primary AY-3-8913 connected to 6522, and AY2 is the 2nd-ary.
//
// AFAICT, inputs to the Phasor GAL are:
// . ORB.b4:3 = Chip Select (CS) for AY1 & AY2 (active low)
2023-03-07 18:06:06 +00:00
// . ORB.b2:0 = PSG Function (RESET, INACTIVE, READ, WRITE, LATCH) [Or since LATCH=%111, then maybe a 3-input AND: b2.b1.b0 -> GAL?]
2023-03-05 15:37:52 +00:00
// . Phasor mode (Mockingboard, Echo+, Phasor-native)
// . Slot inputs (address, reset, etc)
// And outputs from the GAL are:
// . GAL CS' for AY1 & AY2 (not just passed-through, but dependent on PSG Function)
// (Not PSG Function - probably just passed-through from 6522 to the chip-selected AY-3-8913's)
//
// In Phasor-native mode, GAL logic:
// . AY2 LATCH func selects AY2 and AY1; sets latch addr for AY2 and AY1
// . AY1 LATCH func selects AY1; deselects AY2; sets latch addr for AY1
2023-03-07 18:06:06 +00:00
// . AY2 & AY1 LATCH func selects AY2 and AY1; sets latch addr for AY2 and AY1
// . AY2 WRITE(READ) func writes(reads) AY2 if it's selected
// . AY1 WRITE(READ) func writes(reads) AY1; writes(reads) AY2 if it's selected. NB. If both chips, then the READ is the OR-sum.
2023-03-05 15:37:52 +00:00
//
// EG, to do a "AY1 LATCH", then write 6522 ORB with b4:3=%01, b2:0=%111
//
2023-01-28 18:15:28 +00:00
void MockingboardCard : : WriteToORB ( BYTE subunit )
{
BYTE value = m_MBSubUnit [ subunit ] . sy6522 . Read ( SY6522 : : rORB ) ;
if ( subunit = = 0 & & // SC01 only at $Cn00 (not $Cn80)
m_MBSubUnit [ subunit ] . sy6522 . Read ( SY6522 : : rPCR ) = = 0xB0 )
{
// Votrax speech data
const BYTE DDRB = m_MBSubUnit [ subunit ] . sy6522 . Read ( SY6522 : : rDDRB ) ;
m_MBSubUnit [ subunit ] . 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 ( ( subunit & 1 ) = = 1 )
AY8910_Write ( subunit , 0 , nValue ) ;
# else
if ( m_phasorEnable )
{
2023-03-07 21:51:24 +00:00
const int kAY1 = 2 ; // Phasor mode: bit4=0 (active low) selects the 1st AY8913, ie. the only AY8913 in Mockingboard mode (confirmed on real Phasor h/w)
2023-02-21 21:08:20 +00:00
// Echo+ mode: bit3=1 (active high) selects the 1st AY8913
2023-03-07 21:51:24 +00:00
const int kAY2 = 1 ; // Phasor mode: bit3=0 (active low) selects the 2nd AY8913 attached to this 6522 (unavailable in Mockingboard mode)
2023-02-21 21:32:11 +00:00
// Echo+ mode: bit4=1 (active high) selects the 2nd AY8913
const int nAY_CS = ( m_phasorMode = = PH_EchoPlus ) ? ( ( value > > 4 ) & 1 ) | ( ( value > > 2 ) & 2 ) // swap bits 4 & 3
2023-02-21 21:08:20 +00:00
: ( m_phasorMode = = PH_Phasor ) ? ( ~ ( value > > 3 ) & 3 )
2023-03-07 21:51:24 +00:00
: kAY1 ; // Anything else is Mockingboard
2023-01-28 18:15:28 +00:00
2023-02-19 16:38:06 +00:00
if ( m_phasorMode = = PH_EchoPlus )
2023-02-19 21:22:44 +00:00
subunit = SY6522_DEVICE_B ;
2023-02-19 16:38:06 +00:00
2023-03-07 18:06:06 +00:00
// NB. For PH_Phasor, when selecting *both* AYs, then order matters: first do AY8913_DEVICE_A then AY8913_DEVICE_B
// Reason: from GAL logic: 'AY1 LATCH func' deselects AY2, then 'AY2 LATCH func' selects AY2 and AY1. (And we want both selected)
2023-03-07 21:51:24 +00:00
if ( nAY_CS & kAY1 )
2023-02-21 21:32:11 +00:00
AY8910_Write ( subunit , AY8913_DEVICE_A , value ) ;
2023-01-28 18:15:28 +00:00
2023-03-07 21:51:24 +00:00
if ( nAY_CS & kAY2 )
2023-02-21 21:32:11 +00:00
AY8910_Write ( subunit , AY8913_DEVICE_B , value ) ;
2023-02-26 21:52:46 +00:00
if ( nAY_CS = = 0 )
2023-03-05 15:37:52 +00:00
m_MBSubUnit [ subunit ] . sy6522 . UpdatePortAForHiZ ( ) ;
2023-01-28 18:15:28 +00:00
}
else
{
AY8910_Write ( subunit , AY8913_DEVICE_A , value ) ;
}
# endif
2021-03-23 22:01:41 +00:00
}
2006-02-25 20:50:29 +00:00
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
void MockingboardCard : : AY8910_Write ( BYTE subunit , BYTE ay , BYTE value )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
m_regAccessedFlag = true ;
MB_SUBUNIT * pMB = & m_MBSubUnit [ subunit ] ;
2023-02-26 21:52:46 +00:00
SY6522 & r6522 = pMB - > sy6522 ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
if ( ( value & 4 ) = = 0 )
2006-02-25 20:50:29 +00:00
{
// RESET: Reset AY8910 only
2023-01-28 18:15:28 +00:00
AY8910_reset ( subunit , ay ) ;
2023-03-05 15:37:52 +00:00
pMB - > Reset ( QueryType ( ) ) ;
2006-02-25 20:50:29 +00:00
}
else
{
// Determine the AY8910 inputs
2023-01-28 18:15:28 +00:00
int nBDIR = ( value & 2 ) ? 1 : 0 ;
2006-02-25 20:50:29 +00:00
const int nBC2 = 1 ; // Hardwired to +5V
2023-01-28 18:15:28 +00:00
int nBC1 = value & 1 ;
2006-02-25 20:50:29 +00:00
2017-10-27 10:10:15 +00:00
MockingboardUnitState_e nAYFunc = ( MockingboardUnitState_e ) ( ( nBDIR < < 2 ) | ( nBC2 < < 1 ) | nBC1 ) ;
2023-03-04 22:41:07 +00:00
MockingboardUnitState_e & state = pMB - > state [ ay ] ; // GH#659
2006-02-25 20:50:29 +00:00
2019-11-11 17:35:10 +00:00
# if _DEBUG
2023-02-19 21:22:44 +00:00
if ( ! m_phasorEnable | | m_phasorMode = = PH_Mockingboard )
2023-01-28 18:15:28 +00:00
_ASSERT ( ay = = AY8913_DEVICE_A ) ;
2023-02-19 16:38:06 +00:00
if ( nAYFunc = = AY_READ | | nAYFunc = = AY_WRITE | | nAYFunc = = AY_LATCH )
2019-11-11 17:35:10 +00:00
_ASSERT ( state = = AY_INACTIVE ) ;
# endif
if ( state = = AY_INACTIVE ) // GH#320: functions only work from inactive state
2006-02-25 20:50:29 +00:00
{
2017-10-27 10:10:15 +00:00
switch ( nAYFunc )
{
case AY_INACTIVE : // 4: INACTIVE
break ;
2006-02-25 20:50:29 +00:00
2017-10-27 10:10:15 +00:00
case AY_READ : // 5: READ FROM PSG (need to set DDRA to input)
2023-03-04 22:41:07 +00:00
if ( pMB - > isChipSelected [ ay ] & & pMB - > isAYLatchedAddressValid [ ay ] )
r6522 . SetRegORA ( AYReadReg ( subunit , ay , pMB - > nAYCurrentRegister [ ay ] ) & ( r6522 . GetReg ( SY6522 : : rDDRA ) ^ 0xff ) ) ;
else
2023-03-05 15:37:52 +00:00
r6522 . UpdatePortAForHiZ ( ) ;
2023-03-06 22:25:23 +00:00
if ( m_phasorEnable & & m_phasorMode = = PH_Phasor ) // GH#1192
{
if ( ay = = AY8913_DEVICE_A )
{
if ( pMB - > isChipSelected [ AY8913_DEVICE_B ] & & pMB - > isAYLatchedAddressValid [ AY8913_DEVICE_B ] )
r6522 . SetRegORA ( r6522 . GetReg ( SY6522 : : rORA ) | ( AYReadReg ( subunit , AY8913_DEVICE_B , pMB - > nAYCurrentRegister [ AY8913_DEVICE_B ] ) & ( r6522 . GetReg ( SY6522 : : rDDRA ) ^ 0xff ) ) ) ;
}
}
2017-10-27 10:10:15 +00:00
break ;
2006-02-25 20:50:29 +00:00
2017-10-27 10:10:15 +00:00
case AY_WRITE : // 6: WRITE TO PSG
2023-03-04 19:38:11 +00:00
if ( pMB - > isChipSelected [ ay ] & & pMB - > isAYLatchedAddressValid [ ay ] )
2023-03-04 18:14:00 +00:00
_AYWriteReg ( subunit , ay , pMB - > nAYCurrentRegister [ ay ] , r6522 . GetReg ( SY6522 : : rORA ) ) ;
2023-03-01 21:01:59 +00:00
// else if invalid then just ignore
2023-03-04 18:14:00 +00:00
if ( m_phasorEnable & & m_phasorMode = = PH_Phasor ) // GH#1192
{
2023-03-04 19:38:11 +00:00
if ( ay = = AY8913_DEVICE_A )
{
if ( pMB - > isChipSelected [ AY8913_DEVICE_B ] & & pMB - > isAYLatchedAddressValid [ AY8913_DEVICE_B ] )
_AYWriteReg ( subunit , AY8913_DEVICE_B , pMB - > nAYCurrentRegister [ AY8913_DEVICE_B ] , r6522 . GetReg ( SY6522 : : rORA ) ) ;
}
2023-03-04 18:14:00 +00:00
}
2017-10-27 10:10:15 +00:00
break ;
2006-02-25 20:50:29 +00:00
2017-10-27 10:10:15 +00:00
case AY_LATCH : // 7: LATCH ADDRESS
// http://www.worldofspectrum.org/forums/showthread.php?t=23327
// 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.
2023-02-19 16:38:06 +00:00
if ( r6522 . GetReg ( SY6522 : : rORA ) < = 0x0F )
2023-03-01 21:01:59 +00:00
{
2023-03-04 18:14:00 +00:00
pMB - > nAYCurrentRegister [ ay ] = r6522 . GetReg ( SY6522 : : rORA ) & 0x0F ;
2023-03-04 19:38:11 +00:00
pMB - > isChipSelected [ ay ] = true ;
2023-03-01 21:01:59 +00:00
pMB - > isAYLatchedAddressValid [ ay ] = true ;
2023-03-04 18:14:00 +00:00
if ( m_phasorEnable & & m_phasorMode = = PH_Phasor ) // GH#1192
{
if ( ay = = AY8913_DEVICE_A )
{
pMB - > isChipSelected [ AY8913_DEVICE_B ] = false ;
}
else // AY8913_DEVICE_B
{
2023-03-04 19:38:11 +00:00
pMB - > isChipSelected [ AY8913_DEVICE_A ] = true ;
2023-03-04 18:14:00 +00:00
pMB - > nAYCurrentRegister [ AY8913_DEVICE_A ] = pMB - > nAYCurrentRegister [ AY8913_DEVICE_B ] ;
pMB - > isAYLatchedAddressValid [ AY8913_DEVICE_A ] = true ;
}
}
2023-03-01 21:01:59 +00:00
}
2017-10-27 10:10:15 +00:00
// else Pro-Mockingboard (clone from HK)
break ;
}
2006-02-25 20:50:29 +00:00
}
2017-10-27 10:10:15 +00:00
2019-11-11 17:35:10 +00:00
state = nAYFunc ;
2023-03-05 15:43:13 +00:00
2023-03-07 21:51:24 +00:00
if ( state = = AY_INACTIVE & & m_phasorEnable ) // Phasor(even in MB mode) will read PortA inputs as high.
2023-03-05 15:43:13 +00:00
r6522 . UpdatePortAForHiZ ( ) ; // Float high any PortA input bits (GH#1193)
2006-02-25 20:50:29 +00:00
}
}
2022-02-05 18:48:36 +00:00
//-----------------------------------------------------------------------------
2021-02-06 18:02:35 +00:00
2023-01-28 18:15:28 +00:00
void MockingboardCard : : UpdateIFRandIRQ ( MB_SUBUNIT * pMB , BYTE clr_mask , BYTE set_mask )
2022-02-05 18:48:36 +00:00
{
2023-01-28 18:15:28 +00:00
pMB - > sy6522 . UpdateIFR ( clr_mask , set_mask ) ; // which calls UpdateIRQ()
2020-10-11 15:08:05 +00:00
}
2023-01-28 18:15:28 +00:00
//---------------------------------------------------------------------------
2020-10-11 15:08:05 +00:00
2023-01-29 13:37:07 +00:00
// Called from MockingboardCardMgr
2023-01-29 13:32:55 +00:00
bool MockingboardCard : : Is6522IRQ ( void )
2006-02-25 20:50:29 +00:00
{
2006-05-02 21:56:28 +00:00
// Now update the IRQ signal from all 6522s
// . OR-sum of all active TIMER1, TIMER2 & SPEECH sources (from all 6522s)
2023-01-29 13:32:55 +00:00
bool irq = false ;
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
2023-01-29 13:32:55 +00:00
irq | = m_MBSubUnit [ i ] . sy6522 . GetReg ( SY6522 : : rIFR ) & 0x80 ? true : false ;
2006-05-02 21:56:28 +00:00
2006-12-27 19:11:19 +00:00
// NB. Mockingboard generates IRQ on both 6522s:
2021-02-21 19:12:36 +00:00
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522 (at $Cn80) and must generate a 6502 IRQ (not NMI)
// - NB. 2nd SSI263's IRQ is routed via the 1st 6522 (at $Cn00) and again generates a 6502 IRQ
// . SC-01's IRQ (A/!R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01)
2020-04-19 20:00:37 +00:00
// Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
2006-12-27 19:11:19 +00:00
2023-01-29 13:32:55 +00:00
return irq ;
2006-02-25 20:50:29 +00:00
}
2023-01-28 18:15:28 +00:00
//---------------------------------------------------------------------------
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
// Called from class SSI263
UINT64 MockingboardCard : : GetLastCumulativeCycles ( void )
{
return m_lastCumulativeCycle ;
}
2019-11-10 15:52:07 +00:00
2023-01-28 18:15:28 +00:00
void MockingboardCard : : UpdateIFR ( BYTE nDevice , BYTE clr_mask , BYTE set_mask )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
UpdateIFRandIRQ ( & m_MBSubUnit [ nDevice ] , clr_mask , set_mask ) ;
}
2009-01-09 23:27:29 +00:00
2023-01-28 18:15:28 +00:00
BYTE MockingboardCard : : GetPCR ( BYTE nDevice )
{
return m_MBSubUnit [ nDevice ] . sy6522 . GetReg ( SY6522 : : rPCR ) ;
}
//===========================================================================
// Called by:
// . MB_SyncEventCallback() -> 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 )
{
2009-01-09 23:27:29 +00:00
if ( g_bFullSpeed )
{
// Keep AY reg writes relative to the current 'frame'
// - Required for Ultima3:
// . Tune ends
// . g_bFullSpeed:=true (disk-spinning) for ~50 frames
// . U3 sets AY_ENABLE:=0xFF (as a side-effect, this sets g_bFullSpeed:=false)
2023-01-28 18:15:28 +00:00
// o Without this, the write to AY_ENABLE gets ignored (since AY8910's /m_lastCumulativeCycle/ was last set 50 frame ago)
2009-01-09 23:27:29 +00:00
AY8910UpdateSetCycles ( ) ;
// TODO:
// If any AY regs have changed then push them out to the AY chip
2023-01-28 18:15:28 +00:00
return 0 ;
2009-01-09 23:27:29 +00:00
}
2006-02-25 20:50:29 +00:00
//
2023-01-28 18:15:28 +00:00
if ( ! m_regAccessedFlag )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
if ( ! m_inActiveCycleCount )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
m_inActiveCycleCount = g_nCumulativeCycles ;
2006-02-25 20:50:29 +00:00
}
2023-01-28 18:15:28 +00:00
else if ( g_nCumulativeCycles - m_inActiveCycleCount > ( unsigned __int64 ) g_fCurrentCLK6502 / 10 )
2006-02-25 20:50:29 +00:00
{
// After 0.1 sec of Apple time, assume MB is not active
2023-01-28 18:15:28 +00:00
m_isActive = false ;
2006-02-25 20:50:29 +00:00
}
}
else
{
2023-01-28 18:15:28 +00:00
m_inActiveCycleCount = 0 ;
m_regAccessedFlag = false ;
m_isActive = true ;
2006-02-25 20:50:29 +00:00
}
//
2019-11-10 15:52:07 +00:00
// 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)
2023-01-28 18:15:28 +00:00
const double kMaximumUpdateInterval = ( double ) ( 0xFFFF + 2 ) ; // Max 6522 timer interval (2756 samples)
2019-11-10 15:52:07 +00:00
2023-01-28 18:15:28 +00:00
if ( m_lastMBUpdateCycle = = 0 )
m_lastMBUpdateCycle = m_lastCumulativeCycle ; // Initial call to MB_Update() after reset/power-cycle
2019-11-10 15:52:07 +00:00
2023-01-28 18:15:28 +00:00
_ASSERT ( m_lastCumulativeCycle > = m_lastMBUpdateCycle ) ;
double updateInterval = ( double ) ( m_lastCumulativeCycle - m_lastMBUpdateCycle ) ;
2019-11-10 15:52:07 +00:00
if ( updateInterval < kMinimumUpdateInterval )
2023-01-28 18:15:28 +00:00
return 0 ;
2019-11-10 15:52:07 +00:00
if ( updateInterval > kMaximumUpdateInterval )
updateInterval = kMaximumUpdateInterval ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
m_lastMBUpdateCycle = m_lastCumulativeCycle ;
2006-02-25 20:50:29 +00:00
2019-11-10 15:52:07 +00:00
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5 ; // Round-up
2023-01-28 18:15:28 +00:00
const int nNumSamplesPerPeriod = ( int ) ( ( double ) SAMPLE_RATE / nIrqFreq ) ; // Eg. For 60Hz this is 735
2019-11-10 15:52:07 +00:00
2023-01-28 18:15:28 +00:00
int nNumSamples = nNumSamplesPerPeriod + m_numSamplesError ; // Apply correction
if ( nNumSamples < = 0 )
2006-02-25 20:50:29 +00:00
nNumSamples = 0 ;
2023-01-28 18:15:28 +00:00
if ( nNumSamples > 2 * nNumSamplesPerPeriod )
nNumSamples = 2 * nNumSamplesPerPeriod ;
2006-02-25 20:50:29 +00:00
2021-07-28 11:22:52 +00:00
if ( nNumSamples > MAX_SAMPLES )
nNumSamples = MAX_SAMPLES ; // Clamp to prevent buffer overflow
2019-11-10 15:52:07 +00:00
2023-01-28 18:15:28 +00:00
if ( nNumSamples )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
for ( BYTE subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
for ( BYTE ay = 0 ; ay < NUM_AY8913_PER_SUBUNIT ; ay + + )
2009-10-07 21:38:42 +00:00
{
2023-01-28 18:15:28 +00:00
const UINT chip = subunit * NUM_AY8913_PER_SUBUNIT + ay ;
AY8910Update ( subunit , ay , & m_ppAYVoiceBuffer [ chip * NUM_VOICES_PER_AY8913 ] , nNumSamples ) ;
2009-10-07 21:38:42 +00:00
}
2006-02-25 20:50:29 +00:00
}
2023-02-25 21:25:46 +00:00
// Echo+ right speaker is also output to left speaker
if ( m_phasorEnable & & m_phasorMode = = PH_EchoPlus )
{
for ( UINT j = 0 ; j < NUM_VOICES_PER_AY8913 ; j + + )
{
memcpy ( m_ppAYVoiceBuffer [ 0 * NUM_VOICES_PER_AY8913 + j ] , m_ppAYVoiceBuffer [ 2 * NUM_VOICES_PER_AY8913 + j ] , nNumSamples * sizeof ( short ) ) ;
memcpy ( m_ppAYVoiceBuffer [ 1 * NUM_VOICES_PER_AY8913 + j ] , m_ppAYVoiceBuffer [ 3 * NUM_VOICES_PER_AY8913 + j ] , nNumSamples * sizeof ( short ) ) ;
}
}
2006-02-25 20:50:29 +00:00
}
2023-01-28 18:15:28 +00:00
return ( UINT ) nNumSamples ;
2019-02-24 10:49:09 +00:00
}
2006-02-25 20:50:29 +00:00
//-----------------------------------------------------------------------------
// NB. Called when /g_fCurrentCLK6502/ changes
2023-01-28 18:15:28 +00:00
void MockingboardCard : : ReinitializeClock ( void )
2006-02-25 20:50:29 +00:00
{
2015-04-11 21:24:54 +00:00
AY8910_InitClock ( ( int ) g_fCurrentCLK6502 ) ; // todo: account for g_PhasorClockScaleFactor?
// NB. Other calls to AY8910_InitClock() use the constant CLK_6502
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
void MockingboardCard : : Destroy ( void )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
m_MBSubUnit [ i ] . ssi263 . DSUninit ( ) ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_VOICES ; i + + )
{
delete [ ] m_ppAYVoiceBuffer [ i ] ;
m_ppAYVoiceBuffer [ i ] = NULL ;
}
2017-10-28 17:39:45 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT id = 0 ; id < kNumSyncEvents ; id + + )
2020-10-11 15:08:05 +00:00
{
2023-01-28 18:15:28 +00:00
if ( m_syncEvent [ id ] & & m_syncEvent [ id ] - > m_active )
g_SynchronousEventMgr . Remove ( m_syncEvent [ id ] - > m_id ) ;
2020-12-12 17:46:36 +00:00
2023-01-28 18:15:28 +00:00
delete m_syncEvent [ id ] ;
m_syncEvent [ id ] = NULL ;
2020-10-11 15:08:05 +00:00
}
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
void MockingboardCard : : Reset ( const bool powerCycle ) // CTRL+RESET or power-cycle
2012-08-12 11:03:38 +00:00
{
2023-01-28 18:15:28 +00:00
for ( BYTE subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
2020-10-11 15:08:05 +00:00
{
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ subunit ] . sy6522 . Reset ( powerCycle ) ;
for ( BYTE ay = 0 ; ay < NUM_AY8913_PER_SUBUNIT ; ay + + )
AY8910_reset ( subunit , ay ) ;
2022-02-05 18:48:36 +00:00
2023-03-05 15:37:52 +00:00
m_MBSubUnit [ subunit ] . Reset ( QueryType ( ) ) ;
2023-01-29 19:20:07 +00:00
m_MBSubUnit [ subunit ] . ssi263 . SetCardMode ( PH_Mockingboard ) ; // Revert to PH_Mockingboard mode
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ subunit ] . ssi263 . Reset ( ) ;
2021-03-23 22:01:41 +00:00
}
2022-02-05 18:48:36 +00:00
// Reset state
{
2023-01-28 18:15:28 +00:00
SetCumulativeCycles ( ) ;
2012-08-12 11:03:38 +00:00
2023-01-28 18:15:28 +00:00
m_inActiveCycleCount = 0 ;
m_regAccessedFlag = false ;
m_isActive = false ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
m_phasorMode = PH_Mockingboard ;
m_phasorClockScaleFactor = 1 ;
2022-02-05 18:48:36 +00:00
2023-01-28 18:15:28 +00:00
m_lastMBUpdateCycle = 0 ;
2022-02-05 18:48:36 +00:00
for ( int id = 0 ; id < kNumSyncEvents ; id + + )
{
2023-01-28 18:15:28 +00:00
if ( m_syncEvent [ id ] & & m_syncEvent [ id ] - > m_active )
g_SynchronousEventMgr . Remove ( m_syncEvent [ id ] - > m_id ) ;
2022-02-05 18:48:36 +00:00
}
2023-01-28 18:15:28 +00:00
// Not this, since no change on a CTRL+RESET or power-cycle:
2023-02-25 21:25:46 +00:00
// m_phasorEnable = false;
2006-02-25 20:50:29 +00:00
}
2023-01-28 18:15:28 +00:00
ReinitializeClock ( ) ; // Reset CLK for AY8910s
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2020-04-19 20:00:37 +00:00
// 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)
2023-01-28 18:15:28 +00:00
BYTE __stdcall MockingboardCard : : IORead ( WORD PC , WORD nAddr , BYTE bWrite , BYTE nValue , ULONG 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 )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
GetCardMgr ( ) . GetMockingboardCardMgr ( ) . UpdateCycles ( nExecutedCycles ) ;
2007-05-28 11:16:42 +00:00
2013-03-22 20:54:14 +00:00
# ifdef _DEBUG
2022-02-05 18:48:36 +00:00
if ( ! IS_APPLE2 & & MemCheckINTCXROM ( ) )
2013-03-22 20:54:14 +00:00
{
2017-05-21 21:06:37 +00:00
_ASSERT ( 0 ) ; // Card ROM disabled, so IO_Cxxx() returns the internal ROM
2007-05-28 11:16:42 +00:00
return mem [ nAddr ] ;
2013-03-22 20:54:14 +00:00
}
# endif
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
if ( m_phasorEnable )
2006-02-25 20:50:29 +00:00
{
2020-04-19 20:00:37 +00:00
int CS = 0 ;
2023-01-28 18:15:28 +00:00
if ( m_phasorMode = = PH_Mockingboard )
2006-02-25 20:50:29 +00:00
CS = ( ( nAddr & 0x80 ) > > 7 ) + 1 ; // 1 or 2
2023-01-28 18:15:28 +00:00
else if ( m_phasorMode = = PH_Phasor )
2020-04-19 20:00:37 +00:00
CS = ( ( nAddr & 0x80 ) > > 6 ) | ( ( nAddr & 0x10 ) > > 4 ) ; // 0, 1, 2 or 3
2023-01-28 18:15:28 +00:00
else if ( m_phasorMode = = PH_EchoPlus )
2020-04-19 20:00:37 +00:00
CS = 2 ;
2006-02-25 20:50:29 +00:00
2012-01-21 14:36:35 +00:00
BYTE nRes = 0 ;
2022-02-05 18:48:36 +00:00
if ( CS & 1 )
2023-01-28 18:15:28 +00:00
nRes | = m_MBSubUnit [ SY6522_DEVICE_A ] . sy6522 . Read ( nAddr & 0xf ) ;
2006-02-25 20:50:29 +00:00
2022-02-05 18:48:36 +00:00
if ( CS & 2 )
2023-01-28 18:15:28 +00:00
nRes | = m_MBSubUnit [ SY6522_DEVICE_B ] . sy6522 . Read ( nAddr & 0xf ) ;
2006-02-25 20:50:29 +00:00
2012-01-21 14:36:35 +00:00
bool bAccessedDevice = ( CS & 3 ) ? true : false ;
2022-06-03 21:09:32 +00:00
bool CS_SSI263 = ! ( nAddr & 0x10 ) & & ( nAddr & 0x60 ) & & ! ( nAddr & 0x80 ) ; // SSI263 at $Cn2x and/or $Cn4x
2021-02-21 19:12:36 +00:00
2023-01-28 18:15:28 +00:00
if ( m_phasorMode = = PH_Phasor & & CS_SSI263 ) // NB. Mockingboard mode: SSI263.bit7 not readable
2012-01-21 14:36:35 +00:00
{
2022-06-02 19:32:16 +00:00
_ASSERT ( ! bAccessedDevice ) ; // In Phasor native mode, 6522 & SSI263 are interleaved in $Cn10-$Cn7F card I/O memory
2021-02-21 19:12:36 +00:00
if ( nAddr & 0x40 ) // Primary SSI263
2023-01-28 18:15:28 +00:00
nRes = m_MBSubUnit [ 1 ] . ssi263 . Read ( nExecutedCycles ) ; // SSI263 only drives bit7
2021-02-21 19:12:36 +00:00
if ( nAddr & 0x20 ) // Secondary SSI263
2023-01-28 18:15:28 +00:00
nRes = m_MBSubUnit [ 0 ] . ssi263 . Read ( nExecutedCycles ) ; // SSI263 only drives bit7
2012-01-21 14:36:35 +00:00
bAccessedDevice = true ;
}
2006-02-25 20:50:29 +00:00
2018-03-03 21:27:50 +00:00
return bAccessedDevice ? nRes : MemReadFloatingBus ( nExecutedCycles ) ;
2006-02-25 20:50:29 +00:00
}
2021-05-03 18:58:13 +00:00
# if DBG_MB_SS_CARD
if ( nMB = = 1 )
return MemReadFloatingBus ( nExecutedCycles ) ;
# endif
2020-04-25 15:42:50 +00:00
// NB. Mockingboard: SSI263.bit7 not readable (TODO: check this with real h/w)
2023-01-28 18:15:28 +00:00
const BYTE offset = nAddr & 0xff ;
const BYTE subunit = ( offset < SY6522B_Offset ) ? SY6522_DEVICE_A : SY6522_DEVICE_B ;
2022-02-05 18:48:36 +00:00
const BYTE reg = nAddr & 0xf ;
2023-01-28 18:15:28 +00:00
return m_MBSubUnit [ subunit ] . sy6522 . Read ( reg ) ;
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
BYTE __stdcall MockingboardCard : : IOWrite ( WORD PC , WORD nAddr , BYTE bWrite , BYTE nValue , ULONG nExecutedCycles )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
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 ) ;
2007-05-28 11:16:42 +00:00
2013-03-22 20:54:14 +00:00
# ifdef _DEBUG
2022-02-05 18:48:36 +00:00
if ( ! IS_APPLE2 & & MemCheckINTCXROM ( ) )
2013-03-22 20:54:14 +00:00
{
2017-05-21 21:06:37 +00:00
_ASSERT ( 0 ) ; // Card ROM disabled, so IO_Cxxx() returns the internal ROM
2007-05-28 11:16:42 +00:00
return 0 ;
2013-03-22 20:54:14 +00:00
}
# endif
2006-02-25 20:50:29 +00:00
2020-07-24 18:39:31 +00:00
// Support 6502/65C02 false-reads of 6522 (GH#52)
if ( ( ( mem [ ( PC - 2 ) & 0xffff ] = = 0x91 ) & & GetMainCpu ( ) = = CPU_6502 ) | | // sta (zp),y - 6502 only (no-PX variant only) (UTAIIe:4-23)
2020-08-22 15:06:26 +00:00
( mem [ ( PC - 3 ) & 0xffff ] = = 0x99 ) | | // sta abs16,y - 6502/65C02, but for 65C02 only the no-PX variant that does the false-read (UTAIIe:4-27)
( mem [ ( PC - 3 ) & 0xffff ] = = 0x9D ) ) // sta abs16,x - 6502/65C02, but for 65C02 only the no-PX variant that does the false-read (UTAIIe:4-27)
2020-07-24 18:39:31 +00:00
{
2020-08-22 15:06:26 +00:00
WORD base ;
2020-07-24 18:39:31 +00:00
WORD addr16 ;
if ( mem [ ( PC - 2 ) & 0xffff ] = = 0x91 )
{
BYTE zp = mem [ ( PC - 1 ) & 0xffff ] ;
2020-08-22 15:06:26 +00:00
base = ( mem [ zp ] | ( mem [ ( zp + 1 ) & 0xff ] < < 8 ) ) ;
addr16 = base + regs . y ;
2020-07-24 18:39:31 +00:00
}
else
{
2020-08-22 15:06:26 +00:00
base = mem [ ( PC - 2 ) & 0xffff ] | ( mem [ ( PC - 1 ) & 0xffff ] < < 8 ) ;
addr16 = base + ( ( mem [ ( PC - 3 ) & 0xffff ] = = 0x99 ) ? regs . y : regs . x ) ;
2020-07-24 18:39:31 +00:00
}
2020-08-22 15:06:26 +00:00
if ( ( ( base ^ addr16 ) > > 8 ) = = 0 ) // Only the no-PX variant does the false read (to the same I/O SELECT page)
2020-07-24 18:39:31 +00:00
{
2020-08-22 15:06:26 +00:00
_ASSERT ( addr16 = = nAddr ) ;
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
2023-01-28 18:15:28 +00:00
IOReadInternal ( PC , nAddr , 0 , 0 , nExecutedCycles ) ;
2020-08-22 15:06:26 +00:00
}
2020-07-24 18:39:31 +00:00
}
}
2023-01-28 18:15:28 +00:00
if ( m_phasorEnable )
2006-02-25 20:50:29 +00:00
{
2020-11-17 21:31:57 +00:00
int CS = 0 ;
2023-01-28 18:15:28 +00:00
if ( m_phasorMode = = PH_Mockingboard )
2006-02-25 20:50:29 +00:00
CS = ( ( nAddr & 0x80 ) > > 7 ) + 1 ; // 1 or 2
2023-01-28 18:15:28 +00:00
else if ( m_phasorMode = = PH_Phasor )
2020-04-19 20:00:37 +00:00
CS = ( ( nAddr & 0x80 ) > > 6 ) | ( ( nAddr & 0x10 ) > > 4 ) ; // 0, 1, 2 or 3
2023-01-28 18:15:28 +00:00
else if ( m_phasorMode = = PH_EchoPlus )
2020-04-19 20:00:37 +00:00
CS = 2 ;
2006-02-25 20:50:29 +00:00
2022-02-05 18:48:36 +00:00
if ( CS & 1 )
{
const BYTE reg = nAddr & 0xf ;
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ SY6522_DEVICE_A ] . sy6522 . Write ( reg , nValue ) ;
2022-02-05 18:48:36 +00:00
if ( reg = = SY6522 : : rORB )
2023-01-28 18:15:28 +00:00
WriteToORB ( SY6522_DEVICE_A ) ;
2022-02-05 18:48:36 +00:00
}
2006-02-25 20:50:29 +00:00
2022-02-05 18:48:36 +00:00
if ( CS & 2 )
{
const BYTE reg = nAddr & 0xf ;
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ SY6522_DEVICE_B ] . sy6522 . Write ( reg , nValue ) ;
2022-02-05 18:48:36 +00:00
if ( reg = = SY6522 : : rORB )
2023-01-28 18:15:28 +00:00
WriteToORB ( SY6522_DEVICE_B ) ;
2022-02-05 18:48:36 +00:00
}
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
bool CS_SSI263_A = ( m_phasorMode = = PH_Phasor ) ? ! ( nAddr & 0x80 ) & & ( nAddr & 0x40 ) // SSI263 at $Cn4x, $Cn6x
2022-06-02 19:32:16 +00:00
: nAddr & 0x40 ; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
2020-04-19 20:00:37 +00:00
2023-01-28 18:15:28 +00:00
bool CS_SSI263_B = ( m_phasorMode = = PH_Phasor ) ? ! ( nAddr & 0x80 ) & & ( nAddr & 0x20 ) // SSI263 at $Cn2x, $Cn6x
2022-06-02 19:32:16 +00:00
: nAddr & 0x20 ; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
2023-01-28 18:15:28 +00:00
if ( m_phasorMode = = PH_Mockingboard | | m_phasorMode = = PH_Phasor ) // No SSI263 for Echo+
2020-04-19 20:00:37 +00:00
{
// NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w)
2022-06-02 19:32:16 +00:00
if ( CS_SSI263_A ) // Primary SSI263
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ 1 ] . ssi263 . Write ( nAddr & 0x7 , nValue ) ; // 2nd 6522 is used for 1st speech chip
2022-06-02 19:32:16 +00:00
if ( CS_SSI263_B ) // Secondary SSI263
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ 0 ] . ssi263 . Write ( nAddr & 0x7 , nValue ) ; // 1st 6522 is used for 2nd speech chip
2020-04-19 20:00:37 +00:00
}
2006-02-25 20:50:29 +00:00
2007-05-28 11:16:42 +00:00
return 0 ;
2006-02-25 20:50:29 +00:00
}
2023-01-28 18:15:28 +00:00
const BYTE offset = nAddr & 0xff ;
const BYTE subunit = ( offset < SY6522B_Offset ) ? SY6522_DEVICE_A : SY6522_DEVICE_B ;
2022-02-05 18:48:36 +00:00
const BYTE reg = nAddr & 0xf ;
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ subunit ] . sy6522 . Write ( reg , nValue ) ;
2022-02-05 18:48:36 +00:00
if ( reg = = SY6522 : : rORB )
2023-01-28 18:15:28 +00:00
WriteToORB ( subunit ) ;
2020-04-25 15:42:50 +00:00
2021-05-03 18:58:13 +00:00
# if !DBG_MB_SS_CARD
2021-02-21 19:12:36 +00:00
if ( nAddr & 0x40 )
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ 1 ] . ssi263 . Write ( nAddr & 0x7 , nValue ) ; // 2nd 6522 is used for 1st speech chip
2021-02-21 19:12:36 +00:00
if ( nAddr & 0x20 )
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ 0 ] . ssi263 . Write ( nAddr & 0x7 , nValue ) ; // 1st 6522 is used for 2nd speech chip
2021-05-03 18:58:13 +00:00
# endif
2007-05-28 11:16:42 +00:00
return 0 ;
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2020-04-19 20:00:37 +00:00
// Phasor's DEVICE SELECT' logic:
// . if addr.[b3]==1, then clear the card's mode bits b2:b0
// . if any of addr.[b2:b0] are a logic 1, then set these bits in the card's mode
//
// Example DEVICE SELECT' accesses for Phasor in slot-4: (from empirical observations on real Phasor h/w)
// 1)
// . RESET -> Mockingboard mode (b#000)
// . $C0C5 -> Phasor mode (b#101)
// 2)
// . RESET -> Mockingboard mode (b#000)
// . $C0C1, then $C0C4 (or $C0C4, then $C0C1) -> Phasor mode (b#101)
// . $C0C2 -> Echo+ mode (b#111)
2021-02-10 20:51:11 +00:00
// . $C0C5 -> remaining in Echo+ mode (b#111)
2020-04-19 20:00:37 +00:00
// So $C0C5 seemingly results in 2 different modes.
//
2023-01-28 18:15:28 +00:00
BYTE __stdcall MockingboardCard : : PhasorIO ( WORD PC , WORD nAddr , BYTE bWrite , BYTE nValue , ULONG nExecutedCycles )
{
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 )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
if ( ! m_phasorEnable )
2018-03-03 21:27:50 +00:00
return MemReadFloatingBus ( nExecutedCycles ) ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
UINT bits = ( UINT ) m_phasorMode ;
2020-04-19 20:00:37 +00:00
if ( nAddr & 8 )
bits = 0 ;
bits | = ( nAddr & 7 ) ;
2023-01-28 18:15:28 +00:00
m_phasorMode = ( PHASOR_MODE ) bits ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
if ( m_phasorMode = = PH_Mockingboard | | m_phasorMode = = PH_EchoPlus )
m_phasorClockScaleFactor = 1 ;
else if ( m_phasorMode = = PH_Phasor )
m_phasorClockScaleFactor = 2 ;
2006-02-25 20:50:29 +00:00
2023-03-05 15:37:52 +00:00
if ( m_phasorMode = = PH_Mockingboard )
{
for ( BYTE subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
m_MBSubUnit [ subunit ] . isChipSelected [ 0 ] = true ;
}
2023-01-28 18:15:28 +00:00
AY8910_InitClock ( ( int ) ( Get6502BaseClock ( ) * m_phasorClockScaleFactor ) ) ;
2006-02-25 20:50:29 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
m_MBSubUnit [ i ] . ssi263 . SetCardMode ( m_phasorMode ) ;
2021-03-23 22:01:41 +00:00
2023-02-19 16:38:06 +00:00
# if DBG_SUPPORT_ECHOPLUS
if ( m_phasorMode = = PH_EchoPlus & & ( nAddr & 0xf ) = = 0 )
return 0x1f ; // for TMS5220 detection
# endif
2018-03-03 21:27:50 +00:00
return MemReadFloatingBus ( nExecutedCycles ) ;
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
void MockingboardCard : : InitializeIO ( LPBYTE pCxRomPeripheral )
2009-04-16 21:18:13 +00:00
{
2023-01-28 18:15:28 +00:00
if ( QueryType ( ) = = CT_MockingboardC )
RegisterIoHandler ( m_slot , IO_Null , IO_Null , IORead , IOWrite , this , NULL ) ;
2012-03-20 23:17:06 +00:00
else // Phasor
2023-01-28 18:15:28 +00:00
RegisterIoHandler ( m_slot , PhasorIO , PhasorIO , IORead , IOWrite , this , NULL ) ;
2019-02-24 15:37:15 +00:00
2019-11-03 14:23:47 +00:00
if ( g_bDisableDirectSound | | g_bDisableDirectSoundMockingboard )
return ;
2023-01-28 18:15:28 +00:00
# ifdef NO_DIRECT_X
# else // NO_DIRECT_X
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
if ( ! m_MBSubUnit [ i ] . ssi263 . DSInit ( ) )
break ;
2006-02-25 20:50:29 +00:00
}
2023-01-28 18:15:28 +00:00
# endif // NO_DIRECT_X
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
void MockingboardCard : : MuteControl ( bool mute )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
if ( mute )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
m_MBSubUnit [ i ] . ssi263 . Mute ( ) ;
}
else
{
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
m_MBSubUnit [ i ] . ssi263 . Unmute ( ) ;
2006-02-25 20:50:29 +00:00
}
}
//-----------------------------------------------------------------------------
2019-12-24 13:58:20 +00:00
# ifdef _DEBUG
2023-01-28 18:15:28 +00:00
void MockingboardCard : : CheckCumulativeCycles ( void )
2019-12-24 13:58:20 +00:00
{
2023-01-28 18:15:28 +00:00
_ASSERT ( m_lastCumulativeCycle = = g_nCumulativeCycles ) ;
m_lastCumulativeCycle = g_nCumulativeCycles ;
2019-12-24 13:58:20 +00:00
}
# endif
// Called by: ResetState() and Snapshot_LoadState_v2()
2023-01-28 18:15:28 +00:00
void MockingboardCard : : SetCumulativeCycles ( void )
2007-08-06 21:38:35 +00:00
{
2023-01-28 18:15:28 +00:00
m_lastCumulativeCycle = g_nCumulativeCycles ;
2007-08-06 21:38:35 +00:00
}
2022-02-05 18:48:36 +00:00
// Called by ContinueExecution() at the end of every execution period (~1000 cycles or ~3 cycles when MODE_STEPPING)
2023-01-28 18:15:28 +00:00
void MockingboardCard : : Update ( const ULONG executedCycles )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
m_MBSubUnit [ i ] . ssi263 . PeriodicUpdate ( executedCycles ) ;
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2017-10-21 17:47:22 +00:00
// Called by:
2022-02-05 18:48:36 +00:00
// . CpuExecute() every ~1000 cycles @ 1MHz (or ~3 cycles when MODE_STEPPING)
2020-10-11 15:08:05 +00:00
// . MB_SyncEventCallback() on a TIMER1/2 underflow
// . MB_Read() / MB_Write() (for both normal & full-speed)
2023-01-28 18:15:28 +00:00
void MockingboardCard : : UpdateCycles ( ULONG executedCycles )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
CpuCalcCycles ( executedCycles ) ;
UINT64 uCycles = g_nCumulativeCycles - m_lastCumulativeCycle ;
2020-10-11 15:08:05 +00:00
_ASSERT ( uCycles > = 0 ) ;
2019-11-16 23:49:21 +00:00
if ( uCycles = = 0 )
2020-10-11 15:08:05 +00:00
return ;
2019-11-16 23:49:21 +00:00
2023-01-28 18:15:28 +00:00
m_lastCumulativeCycle = g_nCumulativeCycles ;
2020-10-11 15:08:05 +00:00
_ASSERT ( uCycles < 0x10000 | | g_nAppMode = = MODE_BENCHMARK ) ;
USHORT nClocks = ( USHORT ) uCycles ;
2019-11-16 23:49:21 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
2006-02-25 20:50:29 +00:00
{
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ i ] . sy6522 . UpdateTimer1 ( nClocks ) ;
m_MBSubUnit [ i ] . sy6522 . UpdateTimer2 ( nClocks ) ;
2020-10-11 15:08:05 +00:00
}
}
2019-11-16 23:49:21 +00:00
2020-10-11 15:08:05 +00:00
//-----------------------------------------------------------------------------
2006-02-25 20:50:29 +00:00
2022-02-05 18:48:36 +00:00
// Called on a 6522 TIMER1/2 underflow
2023-01-28 18:15:28 +00:00
int MockingboardCard : : MB_SyncEventCallback ( int id , int /*cycles*/ , ULONG uExecutedCycles )
{
UINT slot = ( id > > 4 ) ;
MockingboardCard * pCard = ( MockingboardCard * ) MemGetSlotParameters ( slot ) ;
return pCard - > MB_SyncEventCallbackInternal ( id , 0 , uExecutedCycles ) ;
}
int MockingboardCard : : MB_SyncEventCallbackInternal ( int id , int /*cycles*/ , ULONG uExecutedCycles )
2020-10-11 15:08:05 +00:00
{
2023-01-28 18:15:28 +00:00
//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
2022-02-05 18:48:36 +00:00
2023-01-28 18:15:28 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ ( id & 0xf ) / SY6522 : : kNumTimersPer6522 ] ;
2006-05-02 21:56:28 +00:00
2020-10-11 15:08:05 +00:00
if ( ( id & 1 ) = = 0 )
{
2022-02-05 18:48:36 +00:00
_ASSERT ( pMB - > sy6522 . IsTimer1Active ( ) ) ;
UpdateIFRandIRQ ( pMB , 0 , SY6522 : : IxR_TIMER1 ) ;
2023-01-28 18:15:28 +00:00
GetCardMgr ( ) . GetMockingboardCardMgr ( ) . UpdateSoundBuffer ( ) ;
2017-10-24 21:28:22 +00:00
2022-02-05 18:48:36 +00:00
if ( ( pMB - > sy6522 . GetReg ( SY6522 : : rACR ) & SY6522 : : ACR_RUNMODE ) = = SY6522 : : ACR_RM_FREERUNNING )
2019-11-16 23:49:21 +00:00
{
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . StartTimer1 ( ) ;
2023-02-04 12:09:19 +00:00
if ( pMB - > sy6522 . IsTimer1IrqDelay ( ) )
return 0x0001 ; // T1C=0xFFFF, which is really -1, as there's 1 cycle until underflow occurs
2022-02-05 18:48:36 +00:00
return pMB - > sy6522 . GetRegT1C ( ) + SY6522 : : kExtraTimerCycles ;
2019-11-16 23:49:21 +00:00
}
2022-02-05 18:48:36 +00:00
// One-shot mode
// - Phasor's playback code uses one-shot mode
pMB - > sy6522 . StopTimer1 ( ) ;
return 0 ; // Don't repeat event
2006-02-25 20:50:29 +00:00
}
2020-10-11 15:08:05 +00:00
else
{
2023-01-28 18:15:28 +00:00
// NB. Since not calling UpdateSoundBuffer(), then AppleWin doesn't (accurately?) support AY-playback using T2 (which is one-shot only)
2022-02-05 18:48:36 +00:00
_ASSERT ( pMB - > sy6522 . IsTimer2Active ( ) ) ;
UpdateIFRandIRQ ( pMB , 0 , SY6522 : : IxR_TIMER2 ) ;
2019-11-16 23:49:21 +00:00
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . StopTimer2 ( ) ; // TIMER2 only runs in one-shot mode
2020-10-11 15:08:05 +00:00
return 0 ; // Don't repeat event
}
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
bool MockingboardCard : : IsActive ( void )
2006-02-25 20:50:29 +00:00
{
2021-03-23 22:01:41 +00:00
bool isSSI263Active = false ;
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
isSSI263Active | = m_MBSubUnit [ i ] . ssi263 . IsPhonemeActive ( ) ;
2021-03-23 22:01:41 +00:00
2023-01-28 18:15:28 +00:00
return m_isActive | | isSSI263Active ;
2006-02-25 20:50:29 +00:00
}
//-----------------------------------------------------------------------------
2023-01-28 18:15:28 +00:00
void MockingboardCard : : SetVolume ( DWORD volume , DWORD volumeMax )
2021-03-23 22:01:41 +00:00
{
2023-01-28 18:15:28 +00:00
for ( UINT i = 0 ; i < NUM_SSI263 ; i + + )
m_MBSubUnit [ i ] . ssi263 . SetVolume ( volume , volumeMax ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2023-03-09 20:16:21 +00:00
void MockingboardCard : : GetSnapshotForDebugger ( DEBUGGER_MB_CARD * const pMBForDebugger )
2006-02-25 20:50:29 +00:00
{
2023-03-09 22:53:09 +00:00
pMBForDebugger - > type = QueryType ( ) ;
2023-03-09 20:16:21 +00:00
for ( UINT i = 0 ; i < NUM_SUBUNITS_PER_MB ; i + + )
2006-02-25 20:50:29 +00:00
{
2023-03-09 20:16:21 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ i ] ;
2021-03-07 13:11:22 +00:00
2023-03-09 20:16:21 +00:00
pMB - > sy6522 . GetRegs ( pMBForDebugger - > subUnit [ i ] . regsSY6522 ) ; // continuous 16-byte array
pMBForDebugger - > subUnit [ i ] . timer1Active = pMB - > sy6522 . IsTimer1Active ( ) ;
pMBForDebugger - > subUnit [ i ] . timer2Active = pMB - > sy6522 . IsTimer2Active ( ) ;
2021-03-07 13:11:22 +00:00
2023-03-09 20:16:21 +00:00
for ( UINT j = 0 ; j < NUM_AY8913_PER_SUBUNIT ; j + + )
{
for ( UINT k = 0 ; k < 16 ; k + + )
pMBForDebugger - > subUnit [ i ] . regsAY8913 [ j ] [ k ] = AYReadReg ( i , j , k ) ;
2006-02-25 20:50:29 +00:00
2023-03-09 20:16:21 +00:00
pMBForDebugger - > subUnit [ i ] . nAYCurrentRegister [ j ] = pMB - > nAYCurrentRegister [ j ] ;
pMBForDebugger - > subUnit [ i ] . isAYLatchedAddressValid [ j ] = pMB - > isAYLatchedAddressValid [ j ] ;
2023-03-11 13:56:44 +00:00
switch ( pMB - > state [ j ] )
{
case AY_READ :
strcpy ( ( char * ) & pMBForDebugger - > subUnit [ i ] . szState [ j ] , " RD " ) ; break ;
case AY_WRITE :
strcpy ( ( char * ) & pMBForDebugger - > subUnit [ i ] . szState [ j ] , " WR " ) ; break ;
case AY_LATCH :
strcpy ( ( char * ) & pMBForDebugger - > subUnit [ i ] . szState [ j ] , " LA " ) ; break ;
default : //AY_INACTIVE
strcpy ( ( char * ) & pMBForDebugger - > subUnit [ i ] . szState [ j ] , " -- " ) ; break ;
}
2023-03-09 20:16:21 +00:00
}
2006-02-25 20:50:29 +00:00
}
}
2023-01-28 18:15:28 +00:00
//=============================================================================
// 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 ;
}
2021-03-23 22:01:41 +00:00
//=============================================================================
2015-02-13 22:40:53 +00:00
2017-10-27 10:10:15 +00:00
// Unit version history:
// 2: Added: Timer1 & Timer2 active
2019-11-11 17:35:10 +00:00
// 3: Added: Unit state - GH#320
// 4: Added: 6522 timerIrqDelay - GH#652
// 5: Added: Unit state-B (Phasor only) - GH#659
2020-04-19 20:00:37 +00:00
// 6: Changed SS_YAML_KEY_PHASOR_MODE from (0,1) to (0,5,7)
2020-04-26 17:07:38 +00:00
// Added SS_YAML_KEY_VOTRAX_PHONEME
// Removed: redundant SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR
2021-03-23 22:01:41 +00:00
// 7: Added SS_YAML_KEY_SSI263_REG_ACTIVE_PHONEME to SSI263 sub-unit
2022-02-05 18:48:36 +00:00
// 8: Moved Timer1 & Timer2 active to 6522 sub-unit
// Removed Timer1/Timer2/Speech IRQ Pending
2022-09-24 19:12:25 +00:00
// Changed at AppleWin 1.30.8
// 9: Phasor AY's are swapped (means that AppleWin 1.30.10 and 1.30.11 are wrong)
// Changed at AppleWin 1.30.12
2023-01-30 21:27:49 +00:00
//10: Phasor AY's are ordered correctly
2023-03-04 18:14:00 +00:00
// "AY Current Register B"
// "Chip Select A" + "Chip Select B"
// "Reg Address Latch Valid A" + "Reg Address Latch Valid B"
2023-01-30 21:27:49 +00:00
// Changed at AppleWin 1.30.14
const UINT kUNIT_VERSION = 10 ;
2017-10-27 10:10:15 +00:00
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_MB_UNIT "Unit"
# define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
2023-03-04 18:14:00 +00:00
# define SS_YAML_KEY_AY_CURR_REG_B "AY Current Register B" // Phasor only
2023-03-04 23:25:53 +00:00
# define SS_YAML_KEY_CS_A "Chip Select A"
# define SS_YAML_KEY_CS_B "Chip Select B" // Phasor only
# define SS_YAML_KEY_LATCH_ADDR_VALID_A "Reg Address Latch Valid A"
# define SS_YAML_KEY_LATCH_ADDR_VALID_B "Reg Address Latch Valid B" // Phasor only
2017-10-27 10:10:15 +00:00
# define SS_YAML_KEY_MB_UNIT_STATE "Unit State"
2019-11-11 17:35:10 +00:00
# define SS_YAML_KEY_MB_UNIT_STATE_B "Unit State-B" // Phasor only
2022-02-05 18:48:36 +00:00
# define SS_YAML_KEY_TIMER1_IRQ "Timer1 IRQ Pending" // v8: deprecated
# define SS_YAML_KEY_TIMER2_IRQ "Timer2 IRQ Pending" // v8: deprecated
# define SS_YAML_KEY_SPEECH_IRQ "Speech IRQ Pending" // v8: deprecated
# define SS_YAML_KEY_TIMER1_ACTIVE "Timer1 Active" // v8: move to 6522 sub-unit
# define SS_YAML_KEY_TIMER2_ACTIVE "Timer2 Active" // v8: move to 6522 sub-unit
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_PHASOR_UNIT "Unit"
2022-02-05 18:48:36 +00:00
# define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor" // v6: deprecated
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_PHASOR_MODE "Mode"
2020-04-26 17:07:38 +00:00
# define SS_YAML_KEY_VOTRAX_PHONEME "Votrax Phoneme"
2023-01-28 18:15:28 +00:00
std : : string MockingboardCard : : GetSnapshotCardName ( void )
2015-12-05 16:50:27 +00:00
{
static const std : : string name ( " Mockingboard C " ) ;
return name ;
}
2023-01-28 18:15:28 +00:00
std : : string MockingboardCard : : GetSnapshotCardNamePhasor ( void )
2015-12-05 16:50:27 +00:00
{
static const std : : string name ( " Phasor " ) ;
return name ;
}
2023-01-28 18:15:28 +00:00
void MockingboardCard : : SaveSnapshot ( YamlSaveHelper & yamlSaveHelper )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
if ( QueryType ( ) = = CT_Phasor )
return Phasor_SaveSnapshot ( yamlSaveHelper ) ;
2015-12-05 16:50:27 +00:00
2023-01-28 18:15:28 +00:00
//
YamlSaveHelper : : Slot slot ( yamlSaveHelper , GetSnapshotCardName ( ) , m_slot , kUNIT_VERSION ) ;
2015-12-05 16:50:27 +00:00
YamlSaveHelper : : Label state ( yamlSaveHelper , " %s: \n " , SS_YAML_KEY_STATE ) ;
2023-01-28 18:15:28 +00:00
yamlSaveHelper . SaveBool ( SS_YAML_KEY_VOTRAX_PHONEME , m_MBSubUnit [ 0 ] . ssi263 . GetVotraxPhoneme ( ) ) ; // SC01 only in subunit 0
2020-04-26 17:07:38 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ subunit ] ;
YamlSaveHelper : : Label unit ( yamlSaveHelper , " %s%d: \n " , SS_YAML_KEY_MB_UNIT , subunit ) ;
2015-12-05 16:50:27 +00:00
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . SaveSnapshot ( yamlSaveHelper ) ;
2023-01-28 18:15:28 +00:00
AY8910_SaveSnapshot ( yamlSaveHelper , subunit , AY8913_DEVICE_A , std : : string ( " " ) ) ;
2021-03-23 22:01:41 +00:00
pMB - > ssi263 . SaveSnapshot ( yamlSaveHelper ) ;
2015-12-05 16:50:27 +00:00
2023-03-04 22:41:07 +00:00
yamlSaveHelper . SaveHexUint4 ( SS_YAML_KEY_MB_UNIT_STATE , pMB - > state [ 0 ] ) ;
2023-03-04 18:14:00 +00:00
yamlSaveHelper . SaveHexUint8 ( SS_YAML_KEY_AY_CURR_REG , pMB - > nAYCurrentRegister [ 0 ] ) ; // save all 8 bits (even though top 4 bits should be 0)
2023-03-04 23:25:53 +00:00
yamlSaveHelper . SaveBool ( SS_YAML_KEY_CS_A , pMB - > isChipSelected [ 0 ] ) ;
yamlSaveHelper . SaveBool ( SS_YAML_KEY_LATCH_ADDR_VALID_A , pMB - > isAYLatchedAddressValid [ 0 ] ) ;
2015-12-05 16:50:27 +00:00
}
}
2023-01-28 18:15:28 +00:00
bool MockingboardCard : : LoadSnapshot ( YamlLoadHelper & yamlLoadHelper , UINT version )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
if ( m_slot = = 0 | | m_slot = = 3 )
throw std : : runtime_error ( " Card: wrong slot " ) ;
2015-12-05 16:50:27 +00:00
2017-10-27 10:10:15 +00:00
if ( version < 1 | | version > kUNIT_VERSION )
2023-01-28 18:15:28 +00:00
throw std : : runtime_error ( " Card: wrong version " ) ;
2015-12-05 16:50:27 +00:00
2023-01-28 18:15:28 +00:00
if ( QueryType ( ) = = CT_Phasor )
return Phasor_LoadSnapshot ( yamlLoadHelper , version ) ;
2015-12-05 16:50:27 +00:00
2023-01-28 18:15:28 +00:00
//
AY8910UpdateSetCycles ( ) ;
2015-12-05 16:50:27 +00:00
2021-03-23 22:01:41 +00:00
bool isVotrax = ( version > = 6 ) ? yamlLoadHelper . LoadBool ( SS_YAML_KEY_VOTRAX_PHONEME ) : false ;
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ 0 ] . ssi263 . SetVotraxPhoneme ( isVotrax ) ; // SC01 only in subunit 0
2015-12-05 16:50:27 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ subunit ] ;
char szNum [ 2 ] = { char ( ' 0 ' + subunit ) , 0 } ;
2015-12-05 16:50:27 +00:00
std : : string unit = std : : string ( SS_YAML_KEY_MB_UNIT ) + std : : string ( szNum ) ;
if ( ! yamlLoadHelper . GetSubMap ( unit ) )
2021-12-18 16:37:28 +00:00
throw std : : runtime_error ( " Card: Expected key: " + unit ) ;
2015-12-05 16:50:27 +00:00
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . LoadSnapshot ( yamlLoadHelper , version ) ;
UpdateIFRandIRQ ( pMB , 0 , pMB - > sy6522 . GetReg ( SY6522 : : rIFR ) ) ; // Assert any pending IRQs (GH#677)
2023-01-28 18:15:28 +00:00
AY8910_LoadSnapshot ( yamlLoadHelper , subunit , AY8913_DEVICE_A , std : : string ( " " ) ) ;
pMB - > ssi263 . LoadSnapshot ( yamlLoadHelper , PH_Mockingboard , version ) ; // Pre: SetVotraxPhoneme()
2015-12-05 16:50:27 +00:00
2023-03-04 18:14:00 +00:00
pMB - > nAYCurrentRegister [ 0 ] = yamlLoadHelper . LoadUint ( SS_YAML_KEY_AY_CURR_REG ) ;
2015-12-05 16:50:27 +00:00
2022-02-05 18:48:36 +00:00
if ( version = = 1 )
2017-10-21 17:47:22 +00:00
{
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . SetTimersActiveFromSnapshot ( false , false , version ) ;
}
else if ( version > = 2 & & version < = 7 )
{
bool timer1Active = yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER1_ACTIVE ) ;
bool timer2Active = yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER2_ACTIVE ) ;
pMB - > sy6522 . SetTimersActiveFromSnapshot ( timer1Active , timer2Active , version ) ;
}
if ( version < = 7 )
{
yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER1_IRQ ) ; // Consume redundant data
yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER2_IRQ ) ; // Consume redundant data
yamlLoadHelper . LoadBool ( SS_YAML_KEY_SPEECH_IRQ ) ; // Consume redundant data
2017-10-21 17:47:22 +00:00
}
2023-03-04 22:41:07 +00:00
pMB - > state [ 0 ] = AY_INACTIVE ;
pMB - > state [ 1 ] = AY_INACTIVE ;
2017-10-27 10:10:15 +00:00
if ( version > = 3 )
2023-03-04 22:41:07 +00:00
pMB - > state [ 0 ] = ( MockingboardUnitState_e ) ( yamlLoadHelper . LoadUint ( SS_YAML_KEY_MB_UNIT_STATE ) & 7 ) ;
2017-10-27 10:10:15 +00:00
2023-03-04 23:25:53 +00:00
if ( version > = 10 )
{
pMB - > isChipSelected [ 0 ] = yamlLoadHelper . LoadBool ( SS_YAML_KEY_CS_A ) ;
pMB - > isAYLatchedAddressValid [ 0 ] = yamlLoadHelper . LoadBool ( SS_YAML_KEY_LATCH_ADDR_VALID_A ) ;
}
2015-12-05 16:50:27 +00:00
yamlLoadHelper . PopMap ( ) ;
}
2019-06-28 20:34:34 +00:00
AY8910_InitClock ( ( int ) Get6502BaseClock ( ) ) ;
2015-12-05 16:50:27 +00:00
2023-02-25 21:25:46 +00:00
// NB. m_phasorEnable setup in ctor
2015-12-05 16:50:27 +00:00
return true ;
}
2023-01-28 18:15:28 +00:00
void MockingboardCard : : Phasor_SaveSnapshot ( YamlSaveHelper & yamlSaveHelper )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
YamlSaveHelper : : Slot slot ( yamlSaveHelper , GetSnapshotCardNamePhasor ( ) , m_slot , kUNIT_VERSION ) ;
2015-12-05 16:50:27 +00:00
YamlSaveHelper : : Label state ( yamlSaveHelper , " %s: \n " , SS_YAML_KEY_STATE ) ;
2023-01-28 18:15:28 +00:00
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
2015-12-05 16:50:27 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ subunit ] ;
YamlSaveHelper : : Label unit ( yamlSaveHelper , " %s%d: \n " , SS_YAML_KEY_PHASOR_UNIT , subunit ) ;
2015-12-05 16:50:27 +00:00
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . SaveSnapshot ( yamlSaveHelper ) ;
2023-01-28 18:15:28 +00:00
AY8910_SaveSnapshot ( yamlSaveHelper , subunit , AY8913_DEVICE_A , std : : string ( " -A " ) ) ;
AY8910_SaveSnapshot ( yamlSaveHelper , subunit , AY8913_DEVICE_B , std : : string ( " -B " ) ) ;
2021-03-23 22:01:41 +00:00
pMB - > ssi263 . SaveSnapshot ( yamlSaveHelper ) ;
2015-12-05 16:50:27 +00:00
2023-03-04 22:41:07 +00:00
yamlSaveHelper . SaveHexUint4 ( SS_YAML_KEY_MB_UNIT_STATE , pMB - > state [ 0 ] ) ;
yamlSaveHelper . SaveHexUint4 ( SS_YAML_KEY_MB_UNIT_STATE_B , pMB - > state [ 1 ] ) ;
2023-03-04 18:14:00 +00:00
yamlSaveHelper . SaveHexUint8 ( SS_YAML_KEY_AY_CURR_REG , pMB - > nAYCurrentRegister [ 0 ] ) ; // save all 8 bits (even though top 4 bits should be 0)
yamlSaveHelper . SaveHexUint8 ( SS_YAML_KEY_AY_CURR_REG_B , pMB - > nAYCurrentRegister [ 1 ] ) ; // save all 8 bits (even though top 4 bits should be 0)
2023-03-04 23:25:53 +00:00
yamlSaveHelper . SaveBool ( SS_YAML_KEY_CS_A , pMB - > isChipSelected [ 0 ] ) ;
yamlSaveHelper . SaveBool ( SS_YAML_KEY_CS_B , pMB - > isChipSelected [ 1 ] ) ;
yamlSaveHelper . SaveBool ( SS_YAML_KEY_LATCH_ADDR_VALID_A , pMB - > isAYLatchedAddressValid [ 0 ] ) ;
yamlSaveHelper . SaveBool ( SS_YAML_KEY_LATCH_ADDR_VALID_B , pMB - > isAYLatchedAddressValid [ 1 ] ) ;
2015-12-05 16:50:27 +00:00
}
}
2023-01-28 18:15:28 +00:00
bool MockingboardCard : : Phasor_LoadSnapshot ( YamlLoadHelper & yamlLoadHelper , UINT version )
2015-12-05 16:50:27 +00:00
{
2020-04-26 17:07:38 +00:00
if ( version < 6 )
yamlLoadHelper . LoadUint ( SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR ) ; // Consume redundant data
2020-04-19 20:00:37 +00:00
UINT phasorMode = yamlLoadHelper . LoadUint ( SS_YAML_KEY_PHASOR_MODE ) ;
if ( version < 6 )
{
if ( phasorMode = = 0 )
phasorMode = PH_Mockingboard ;
else
phasorMode = PH_Phasor ;
}
2023-01-28 18:15:28 +00:00
m_phasorMode = ( PHASOR_MODE ) phasorMode ;
m_phasorClockScaleFactor = ( m_phasorMode = = PH_Phasor ) ? 2 : 1 ;
2020-04-26 17:07:38 +00:00
2015-12-05 16:50:27 +00:00
AY8910UpdateSetCycles ( ) ;
UINT nDeviceNum = 0 ;
2023-01-28 18:15:28 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ 0 ] ;
2015-12-05 16:50:27 +00:00
2021-03-23 22:01:41 +00:00
bool isVotrax = ( version > = 6 ) ? yamlLoadHelper . LoadBool ( SS_YAML_KEY_VOTRAX_PHONEME ) : false ;
2023-01-28 18:15:28 +00:00
m_MBSubUnit [ 0 ] . ssi263 . SetVotraxPhoneme ( isVotrax ) ; // SC01 only in subunit 0
2015-12-05 16:50:27 +00:00
2023-01-28 18:15:28 +00:00
for ( UINT subunit = 0 ; subunit < NUM_SUBUNITS_PER_MB ; subunit + + )
2015-12-05 16:50:27 +00:00
{
2023-01-28 18:15:28 +00:00
MB_SUBUNIT * pMB = & m_MBSubUnit [ subunit ] ;
char szNum [ 2 ] = { char ( ' 0 ' + subunit ) , 0 } ;
2015-12-05 16:50:27 +00:00
std : : string unit = std : : string ( SS_YAML_KEY_MB_UNIT ) + std : : string ( szNum ) ;
if ( ! yamlLoadHelper . GetSubMap ( unit ) )
2021-12-18 16:37:28 +00:00
throw std : : runtime_error ( " Card: Expected key: " + unit ) ;
2015-12-05 16:50:27 +00:00
2022-02-05 18:48:36 +00:00
pMB - > sy6522 . LoadSnapshot ( yamlLoadHelper , version ) ;
UpdateIFRandIRQ ( pMB , 0 , pMB - > sy6522 . GetReg ( SY6522 : : rIFR ) ) ; // Assert any pending IRQs (GH#677)
2022-09-24 19:12:25 +00:00
if ( version > = 5 & & version < = 8 )
{
2023-01-30 21:27:49 +00:00
const BYTE phasorDevice = subunit = = 0 ? AY8913_DEVICE_B : AY8913_DEVICE_A ;
AY8910_LoadSnapshot ( yamlLoadHelper , 0 , phasorDevice , std : : string ( " -A " ) ) ;
AY8910_LoadSnapshot ( yamlLoadHelper , 1 , phasorDevice , std : : string ( " -B " ) ) ;
}
else if ( version < = 4 | | version = = 9 )
{
const BYTE phasorDevice = subunit = = 0 ? AY8913_DEVICE_A : AY8913_DEVICE_B ;
AY8910_LoadSnapshot ( yamlLoadHelper , 0 , phasorDevice , std : : string ( " -A " ) ) ;
AY8910_LoadSnapshot ( yamlLoadHelper , 1 , phasorDevice , std : : string ( " -B " ) ) ;
2022-09-24 19:12:25 +00:00
}
else
{
2023-01-28 18:15:28 +00:00
AY8910_LoadSnapshot ( yamlLoadHelper , subunit , AY8913_DEVICE_A , std : : string ( " -A " ) ) ;
AY8910_LoadSnapshot ( yamlLoadHelper , subunit , AY8913_DEVICE_B , std : : string ( " -B " ) ) ;
2022-09-24 19:12:25 +00:00
}
2023-01-29 19:20:07 +00:00
pMB - > ssi263 . LoadSnapshot ( yamlLoadHelper , m_phasorMode , version ) ; // Pre: SetVotraxPhoneme()
2015-12-05 16:50:27 +00:00
2023-03-04 18:14:00 +00:00
pMB - > nAYCurrentRegister [ 0 ] = yamlLoadHelper . LoadUint ( SS_YAML_KEY_AY_CURR_REG ) ;
if ( version > = 10 )
pMB - > nAYCurrentRegister [ 1 ] = yamlLoadHelper . LoadUint ( SS_YAML_KEY_AY_CURR_REG_B ) ;
2015-12-05 16:50:27 +00:00
2022-02-05 18:48:36 +00:00
if ( version = = 1 )
{
pMB - > sy6522 . SetTimersActiveFromSnapshot ( false , false , version ) ;
}
else if ( version > = 2 & & version < = 7 )
2017-10-21 17:47:22 +00:00
{
2022-02-05 18:48:36 +00:00
bool timer1Active = yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER1_ACTIVE ) ;
bool timer2Active = yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER2_ACTIVE ) ;
pMB - > sy6522 . SetTimersActiveFromSnapshot ( timer1Active , timer2Active , version ) ;
}
if ( version < = 7 )
{
yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER1_IRQ ) ; // Consume redundant data
yamlLoadHelper . LoadBool ( SS_YAML_KEY_TIMER2_IRQ ) ; // Consume redundant data
yamlLoadHelper . LoadBool ( SS_YAML_KEY_SPEECH_IRQ ) ; // Consume redundant data
2017-10-21 17:47:22 +00:00
}
2023-03-04 22:41:07 +00:00
pMB - > state [ 0 ] = AY_INACTIVE ;
pMB - > state [ 1 ] = AY_INACTIVE ;
2017-10-27 10:10:15 +00:00
if ( version > = 3 )
2023-03-04 22:41:07 +00:00
pMB - > state [ 0 ] = ( MockingboardUnitState_e ) ( yamlLoadHelper . LoadUint ( SS_YAML_KEY_MB_UNIT_STATE ) & 7 ) ;
2019-11-11 17:35:10 +00:00
if ( version > = 5 )
2023-03-04 22:41:07 +00:00
pMB - > state [ 1 ] = ( MockingboardUnitState_e ) ( yamlLoadHelper . LoadUint ( SS_YAML_KEY_MB_UNIT_STATE_B ) & 7 ) ;
2017-10-27 10:10:15 +00:00
2023-03-04 18:14:00 +00:00
if ( version > = 10 )
{
2023-03-04 23:25:53 +00:00
pMB - > isChipSelected [ 0 ] = yamlLoadHelper . LoadBool ( SS_YAML_KEY_CS_A ) ;
pMB - > isChipSelected [ 1 ] = yamlLoadHelper . LoadBool ( SS_YAML_KEY_CS_B ) ;
pMB - > isAYLatchedAddressValid [ 0 ] = yamlLoadHelper . LoadBool ( SS_YAML_KEY_LATCH_ADDR_VALID_A ) ;
pMB - > isAYLatchedAddressValid [ 1 ] = yamlLoadHelper . LoadBool ( SS_YAML_KEY_LATCH_ADDR_VALID_B ) ;
2023-03-04 18:14:00 +00:00
}
2015-12-05 16:50:27 +00:00
yamlLoadHelper . PopMap ( ) ;
}
2023-01-28 18:15:28 +00:00
AY8910_InitClock ( ( int ) ( Get6502BaseClock ( ) * m_phasorClockScaleFactor ) ) ;
2015-12-05 16:50:27 +00:00
2023-02-25 21:25:46 +00:00
// NB. m_phasorEnable setup in ctor
2015-12-05 16:50:27 +00:00
return true ;
}