6522: Underflow on 0x0001 -> 0x0000; and FRT's period is N+2 cycles (#652)

This commit is contained in:
tomcw 2019-06-15 17:41:53 +01:00
parent 9a7424e704
commit 3fbe416424
2 changed files with 63 additions and 18 deletions

View File

@ -1737,13 +1737,46 @@ void MB_EndOfVideoFrame()
//-----------------------------------------------------------------------------
static bool CheckTimerUnderflowAndIrq(USHORT& timerCounter, int& timerIrqDelay, const USHORT nClocks, bool* pTimerUnderflow=NULL)
{
int oldTimer = timerCounter; // Catch the case for 0x0000 -> -ve, as this isn't an underflow
int timer = timerCounter;
timer -= nClocks;
timerCounter = (USHORT)timer;
bool timerIrq = false;
if (timerIrqDelay) // Deal with any previous counter underflow which didn't yet result in an IRQ
{
timerIrqDelay -= nClocks;
if (timerIrqDelay <= 0)
{
timerIrqDelay = 0;
timerIrq = true;
}
// don't re-underflow if TIMER = 0x0000 or 0xFFFF (so just return)
}
else if (oldTimer > 0 && timer <= 0) // Underflow occurs for 0x0001 -> 0x0000
{
if (pTimerUnderflow)
*pTimerUnderflow = true; // Just for Willy Byte!
if (timer <= -2)
timerIrq = true;
else // TIMER = 0x0000 or 0xFFFF
timerIrqDelay = 2 + timer; // ...so 2 or 1 cycles until IRQ
}
return timerIrq;
}
// Called by:
// . CpuExecute() every ~1000 @ 1MHz
// . CheckInterruptSources() every 128 cycles
// . MB_Read() / MB_Write()
void MB_UpdateCycles(ULONG uExecutedCycles)
{
if(g_SoundcardType == CT_Empty)
if (g_SoundcardType == CT_Empty)
return;
CpuCalcCycles(uExecutedCycles);
@ -1752,19 +1785,13 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
_ASSERT(uCycles < 0x10000);
USHORT nClocks = (USHORT) uCycles;
for(int i=0; i<NUM_SY6522; i++)
for (int i=0; i<NUM_SY6522; i++)
{
SY6522_AY8910* pMB = &g_MB[i];
USHORT OldTimer1 = pMB->sy6522.TIMER1_COUNTER.w;
USHORT OldTimer2 = pMB->sy6522.TIMER2_COUNTER.w;
pMB->sy6522.TIMER1_COUNTER.w -= nClocks;
pMB->sy6522.TIMER2_COUNTER.w -= nClocks;
// Check for counter underflow
bool bTimer1Underflow = (!(OldTimer1 & 0x8000) && (pMB->sy6522.TIMER1_COUNTER.w & 0x8000));
bool bTimer2Underflow = (!(OldTimer2 & 0x8000) && (pMB->sy6522.TIMER2_COUNTER.w & 0x8000));
bool bTimer1Underflow = false; // Just for Willy Byte!
const bool bTimer1Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER1_COUNTER.w, pMB->sy6522.timer1IrqDelay, nClocks, &bTimer1Underflow);
const bool bTimer2Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER2_COUNTER.w, pMB->sy6522.timer2IrqDelay, nClocks);
if (!pMB->bTimer1Active && bTimer1Underflow)
{
@ -1774,11 +1801,12 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
{
// Fix for Willy Byte - need to confirm that 6522 really does this!
// . It never accesses IER/IFR/TIMER1 regs to clear IRQ
// . NB. Willy Byte doesn't work with Phasor.
UpdateIFR(pMB, IxR_TIMER1); // Deassert the TIMER IRQ
}
}
if (pMB->bTimer1Active && bTimer1Underflow)
if (pMB->bTimer1Active && bTimer1Irq)
{
UpdateIFR(pMB, 0, IxR_TIMER1);
@ -1786,7 +1814,7 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
if (g_nMBTimerDevice == i)
MB_Update();
if((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT)
if ((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT)
{
// One-shot mode
// - Phasor's playback code uses one-shot mode
@ -1798,6 +1826,8 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
// Free-running mode
// - Ultima4/5 change ACCESS_TIMER1 after a couple of IRQs into tune
pMB->sy6522.TIMER1_COUNTER.w += pMB->sy6522.TIMER1_LATCH.w; // GH#651: account for underflowed cycles too
pMB->sy6522.TIMER1_COUNTER.w += 2; // GH#652: account for extra 2 cycles (Rockwell, Fig.16: period=N+2cycles)
// - or maybe the counter doesn't count down during these 2 cycles?
if (pMB->sy6522.TIMER1_COUNTER.w > pMB->sy6522.TIMER1_LATCH.w)
{
if (pMB->sy6522.TIMER1_LATCH.w)
@ -1809,7 +1839,7 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
}
}
if (pMB->bTimer2Active && bTimer2Underflow)
if (pMB->bTimer2Active && bTimer2Irq)
{
UpdateIFR(pMB, 0, IxR_TIMER2);
@ -1918,7 +1948,8 @@ void MB_GetSnapshot_v1(SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot)
// Unit version history:
// 2: Added: Timer1 & Timer2 active
// 3: Added: Unit state
const UINT kUNIT_VERSION = 3;
// 4: Added: 6522 timerIrqDelay
const UINT kUNIT_VERSION = 4;
const UINT NUM_MB_UNITS = 2;
const UINT NUM_PHASOR_UNITS = 2;
@ -1952,6 +1983,8 @@ const UINT NUM_PHASOR_UNITS = 2;
#define SS_YAML_KEY_SPEECH_IRQ "Speech IRQ Pending"
#define SS_YAML_KEY_TIMER1_ACTIVE "Timer1 Active"
#define SS_YAML_KEY_TIMER2_ACTIVE "Timer2 Active"
#define SS_YAML_KEY_SY6522_TIMER1_IRQ_DELAY "Timer1 IRQ Delay"
#define SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY "Timer2 IRQ Delay"
#define SS_YAML_KEY_PHASOR_UNIT "Unit"
#define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor"
@ -1979,8 +2012,10 @@ static void SaveSnapshotSY6522(YamlSaveHelper& yamlSaveHelper, SY6522& sy6522)
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_DDRA, sy6522.DDRA);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T1_COUNTER, sy6522.TIMER1_COUNTER.w);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T1_LATCH, sy6522.TIMER1_LATCH.w);
yamlSaveHelper.SaveUint(SS_YAML_KEY_SY6522_TIMER1_IRQ_DELAY, sy6522.timer1IrqDelay); // v4
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T2_COUNTER, sy6522.TIMER2_COUNTER.w);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T2_LATCH, sy6522.TIMER2_LATCH.w);
yamlSaveHelper.SaveUint(SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY, sy6522.timer2IrqDelay); // v4
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_SERIAL_SHIFT, sy6522.SERIAL_SHIFT);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_ACR, sy6522.ACR);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_PCR, sy6522.PCR);
@ -2032,7 +2067,7 @@ void MB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
}
}
static void LoadSnapshotSY6522(YamlLoadHelper& yamlLoadHelper, SY6522& sy6522)
static void LoadSnapshotSY6522(YamlLoadHelper& yamlLoadHelper, SY6522& sy6522, UINT version)
{
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_SY6522))
throw std::string("Card: Expected key: ") + std::string(SS_YAML_KEY_SY6522);
@ -2052,6 +2087,14 @@ static void LoadSnapshotSY6522(YamlLoadHelper& yamlLoadHelper, SY6522& sy6522)
sy6522.IER = yamlLoadHelper.LoadUint(SS_YAML_KEY_SY6522_REG_IER);
sy6522.ORA_NO_HS = 0; // Not saved
sy6522.timer1IrqDelay = sy6522.timer2IrqDelay = 0;
if (version >= 4)
{
sy6522.timer1IrqDelay = yamlLoadHelper.LoadUint(SS_YAML_KEY_SY6522_TIMER1_IRQ_DELAY);
sy6522.timer2IrqDelay = yamlLoadHelper.LoadUint(SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY);
}
yamlLoadHelper.PopMap();
}
@ -2094,7 +2137,7 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
if (!yamlLoadHelper.GetSubMap(unit))
throw std::string("Card: Expected key: ") + std::string(unit);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522, version);
AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum, std::string(""));
LoadSnapshotSSI263(yamlLoadHelper, pMB->SpeechChip);
@ -2216,7 +2259,7 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
if (!yamlLoadHelper.GetSubMap(unit))
throw std::string("Card: Expected key: ") + std::string(unit);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522, version);
AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum+0, std::string("-A"));
AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum+1, std::string("-B"));
LoadSnapshotSSI263(yamlLoadHelper, pMB->SpeechChip);

View File

@ -115,6 +115,8 @@ struct SY6522
IWORD TIMER1_LATCH;
IWORD TIMER2_COUNTER;
IWORD TIMER2_LATCH;
int timer1IrqDelay;
int timer2IrqDelay;
//
BYTE SERIAL_SHIFT; // $0A
BYTE ACR; // $0B - Auxiliary Control Register