Phasor: AY RESET resets both AYs attached to 6522 regardless of Phasor mode & CS bits. (#1197)

This commit is contained in:
tomcw 2023-09-07 21:40:13 +01:00
parent cd2f29b723
commit be427a6a30
2 changed files with 108 additions and 91 deletions

View File

@ -185,13 +185,17 @@ void MockingboardCard::Get6522IrqDescription(std::string& desc)
// //
// AFAICT, inputs to the Phasor GAL are: // AFAICT, inputs to the Phasor GAL are:
// . ORB.b4:3 = Chip Select (CS) for AY1 & AY2 (active low) // . ORB.b4:3 = Chip Select (CS) for AY1 & AY2 (active low)
// . ORB.b2:0 = PSG Function (RESET, INACTIVE, READ, WRITE, LATCH) [Or since LATCH=%111, then maybe a 3-input AND: b2.b1.b0 -> GAL?] // .(ORB.b2 : AY /RESET is not an input - see below)
// . ORB.b1:0 = PSG Function (INACTIVE, READ, WRITE, LATCH) [Or since LATCH=%11, then maybe a 2-input AND: b1.b0 -> GAL?]
// . Phasor mode (Mockingboard, Echo+, Phasor-native) // . Phasor mode (Mockingboard, Echo+, Phasor-native)
// . Slot inputs (address, reset, etc) // . Slot inputs (address, reset, etc)
// And outputs from the GAL are: // And outputs from the GAL are:
// . GAL CS' for AY1 & AY2 (not just passed-through, but dependent on PSG Function) // . 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) // (Not PSG Function - probably just passed-through from 6522 to the chip-selected AY-3-8913's)
// //
// Not an input to Phasor GAL:
// . ORB.b2 = AY /RESET (NB. not a PSG Function). Directly connected to AY's /RESET pin (or in Phasor's case: both AYs' /RESET pins).
//
// In Phasor-native mode, GAL logic: // In Phasor-native mode, GAL logic:
// . AY2 LATCH func selects AY2 and AY1; sets latch addr for AY2 and AY1 // . 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 // . AY1 LATCH func selects AY1; deselects AY2; sets latch addr for AY1
@ -233,138 +237,150 @@ void MockingboardCard::WriteToORB(BYTE subunit, BYTE subunitForAY/*=0*/)
if (m_phasorMode == PH_EchoPlus) if (m_phasorMode == PH_EchoPlus)
subunit = SY6522_DEVICE_B; subunit = SY6522_DEVICE_B;
if ((value & 4) == 0)
{
AY8913_Reset(subunit);
return;
}
// NB. For PH_Phasor, when selecting *both* AYs, then order matters: first do AY8913_DEVICE_A then AY8913_DEVICE_B // 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) // Reason: from GAL logic: 'AY1 LATCH func' deselects AY2, then 'AY2 LATCH func' selects AY2 and AY1. (And we want both selected)
if (nAY_CS & kAY1) if (nAY_CS & kAY1)
AY8910_Write(subunit, AY8913_DEVICE_A, value); AY8913_Write(subunit, AY8913_DEVICE_A, value);
if (nAY_CS & kAY2) if (nAY_CS & kAY2)
AY8910_Write(subunit, AY8913_DEVICE_B, value); AY8913_Write(subunit, AY8913_DEVICE_B, value);
if (nAY_CS == 0) if (nAY_CS == 0)
m_MBSubUnit[subunit].sy6522.UpdatePortAForHiZ(); m_MBSubUnit[subunit].sy6522.UpdatePortAForHiZ();
} }
else else
{ {
if ((value & 4) == 0)
{
AY8913_Reset(subunit);
return;
}
if (QueryType() == CT_SDMusic) if (QueryType() == CT_SDMusic)
AY8910_Write(subunitForAY, AY8913_DEVICE_A, value); AY8913_Write(subunitForAY, AY8913_DEVICE_A, value);
else else
AY8910_Write(subunit, AY8913_DEVICE_A, value); AY8913_Write(subunit, AY8913_DEVICE_A, value);
} }
#endif #endif
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void MockingboardCard::AY8910_Write(BYTE subunit, BYTE ay, BYTE value) void MockingboardCard::AY8913_Reset(BYTE subunit)
{
AY8910_reset(subunit, AY8913_DEVICE_A);
if (QueryType() == CT_Phasor)
AY8910_reset(subunit, AY8913_DEVICE_B); // GH#1197: Reset both AYs regardless of Phasor mode & chip-select bits
m_MBSubUnit[subunit].Reset(QueryType());
}
void MockingboardCard::AY8913_Write(BYTE subunit, BYTE ay, BYTE value)
{ {
m_regAccessedFlag = true; m_regAccessedFlag = true;
MB_SUBUNIT* pMB = &m_MBSubUnit[subunit]; MB_SUBUNIT* pMB = &m_MBSubUnit[subunit];
SY6522& r6522 = (QueryType() != CT_SDMusic) ? pMB->sy6522 : m_MBSubUnit[0].sy6522; SY6522& r6522 = (QueryType() != CT_SDMusic) ? pMB->sy6522 : m_MBSubUnit[0].sy6522;
if ((value & 4) == 0) // Determine the AY8913 inputs
{ int nBDIR = (value & 2) ? 1 : 0;
// RESET: Reset AY8910 only const int nBC2 = 1; // Hardwired to +5V
AY8910_reset(subunit, ay); int nBC1 = value & 1;
pMB->Reset(QueryType());
}
else
{
// Determine the AY8910 inputs
int nBDIR = (value & 2) ? 1 : 0;
const int nBC2 = 1; // Hardwired to +5V
int nBC1 = value & 1;
MockingboardUnitState_e nAYFunc = (MockingboardUnitState_e) ((nBDIR<<2) | (nBC2<<1) | nBC1); MockingboardUnitState_e nAYFunc = (MockingboardUnitState_e) ((nBDIR<<2) | (nBC2<<1) | nBC1);
MockingboardUnitState_e& state = pMB->state[ay]; // GH#659 MockingboardUnitState_e& state = pMB->state[ay]; // GH#659
#if _DEBUG #if _DEBUG
if (!m_phasorEnable || m_phasorMode == PH_Mockingboard) if (!m_phasorEnable || m_phasorMode == PH_Mockingboard)
_ASSERT(ay == AY8913_DEVICE_A); _ASSERT(ay == AY8913_DEVICE_A);
if (nAYFunc == AY_READ || nAYFunc == AY_WRITE || nAYFunc == AY_LATCH) if (nAYFunc == AY_READ || nAYFunc == AY_WRITE || nAYFunc == AY_LATCH)
_ASSERT(state == AY_INACTIVE); _ASSERT(state == AY_INACTIVE);
#endif #endif
if (state == AY_INACTIVE) // GH#320: functions only work from inactive state if (state == AY_INACTIVE) // GH#320: functions only work from inactive state
{
switch (nAYFunc)
{ {
switch (nAYFunc) case AY_INACTIVE: // 4: INACTIVE
{ break;
case AY_INACTIVE: // 4: INACTIVE
break;
case AY_READ: // 5: READ FROM PSG (need to set DDRA to input) case AY_READ: // 5: READ FROM PSG (need to set DDRA to input)
if (QueryType() != CT_MegaAudio) if (QueryType() != CT_MegaAudio)
{ {
if (pMB->isChipSelected[ay] && pMB->isAYLatchedAddressValid[ay])
r6522.SetRegORA(AYReadReg(subunit, ay, pMB->nAYCurrentRegister[ay]) & (r6522.GetReg(SY6522::rDDRA) ^ 0xff));
else
r6522.UpdatePortAForHiZ();
}
else
{
r6522.SetRegORA(0x00); // Reads not supported.
}
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)));
}
}
break;
case AY_WRITE: // 6: WRITE TO PSG
if (pMB->isChipSelected[ay] && pMB->isAYLatchedAddressValid[ay]) if (pMB->isChipSelected[ay] && pMB->isAYLatchedAddressValid[ay])
_AYWriteReg(subunit, ay, pMB->nAYCurrentRegister[ay], r6522.GetReg(SY6522::rORA)); r6522.SetRegORA(AYReadReg(subunit, ay, pMB->nAYCurrentRegister[ay]) & (r6522.GetReg(SY6522::rDDRA) ^ 0xff));
// else if invalid then just ignore else
r6522.UpdatePortAForHiZ();
}
else
{
r6522.SetRegORA(0x00); // Reads not supported.
}
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)));
}
}
break;
case AY_WRITE: // 6: WRITE TO PSG
if (pMB->isChipSelected[ay] && pMB->isAYLatchedAddressValid[ay])
_AYWriteReg(subunit, ay, pMB->nAYCurrentRegister[ay], r6522.GetReg(SY6522::rORA));
// else if invalid then just ignore
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])
_AYWriteReg(subunit, AY8913_DEVICE_B, pMB->nAYCurrentRegister[AY8913_DEVICE_B], r6522.GetReg(SY6522::rORA));
}
}
break;
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.
if (r6522.GetReg(SY6522::rORA) <= 0x0F)
{
pMB->nAYCurrentRegister[ay] = r6522.GetReg(SY6522::rORA) & 0x0F;
pMB->isChipSelected[ay] = true;
pMB->isAYLatchedAddressValid[ay] = true;
if (m_phasorEnable && m_phasorMode == PH_Phasor) // GH#1192 if (m_phasorEnable && m_phasorMode == PH_Phasor) // GH#1192
{ {
if (ay == AY8913_DEVICE_A) if (ay == AY8913_DEVICE_A)
{ {
if (pMB->isChipSelected[AY8913_DEVICE_B] && pMB->isAYLatchedAddressValid[AY8913_DEVICE_B]) pMB->isChipSelected[AY8913_DEVICE_B] = false;
_AYWriteReg(subunit, AY8913_DEVICE_B, pMB->nAYCurrentRegister[AY8913_DEVICE_B], r6522.GetReg(SY6522::rORA));
} }
} else // AY8913_DEVICE_B
break;
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.
if (r6522.GetReg(SY6522::rORA) <= 0x0F)
{
pMB->nAYCurrentRegister[ay] = r6522.GetReg(SY6522::rORA) & 0x0F;
pMB->isChipSelected[ay] = true;
pMB->isAYLatchedAddressValid[ay] = true;
if (m_phasorEnable && m_phasorMode == PH_Phasor) // GH#1192
{ {
if (ay == AY8913_DEVICE_A) pMB->isChipSelected[AY8913_DEVICE_A] = true;
{ pMB->nAYCurrentRegister[AY8913_DEVICE_A] = pMB->nAYCurrentRegister[AY8913_DEVICE_B];
pMB->isChipSelected[AY8913_DEVICE_B] = false; pMB->isAYLatchedAddressValid[AY8913_DEVICE_A] = true;
}
else // AY8913_DEVICE_B
{
pMB->isChipSelected[AY8913_DEVICE_A] = true;
pMB->nAYCurrentRegister[AY8913_DEVICE_A] = pMB->nAYCurrentRegister[AY8913_DEVICE_B];
pMB->isAYLatchedAddressValid[AY8913_DEVICE_A] = true;
}
} }
} }
// else Pro-Mockingboard (clone from HK) }
break; // else Pro-Mockingboard (clone from HK)
} break;
} }
state = nAYFunc;
if (state == AY_INACTIVE && m_phasorEnable) // Phasor(even in MB mode) will read PortA inputs as high.
r6522.UpdatePortAForHiZ(); // Float high any PortA input bits (GH#1193)
} }
state = nAYFunc;
if (state == AY_INACTIVE && m_phasorEnable) // Phasor(even in MB mode) will read PortA inputs as high.
r6522.UpdatePortAForHiZ(); // Float high any PortA input bits (GH#1193)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -106,7 +106,8 @@ private:
}; };
void WriteToORB(BYTE subunit, BYTE subunitForAY=0); void WriteToORB(BYTE subunit, BYTE subunitForAY=0);
void AY8910_Write(BYTE subunit, BYTE ay, BYTE value); void AY8913_Reset(BYTE subunit);
void AY8913_Write(BYTE subunit, BYTE ay, BYTE value);
void UpdateIFRandIRQ(MB_SUBUNIT* pMB, BYTE clr_mask, BYTE set_mask); void UpdateIFRandIRQ(MB_SUBUNIT* pMB, BYTE clr_mask, BYTE set_mask);
void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper); void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper);