From 3fbe41642482ef8b3d1ef1325af8f34640c6f9c0 Mon Sep 17 00:00:00 2001 From: tomcw Date: Sat, 15 Jun 2019 17:41:53 +0100 Subject: [PATCH] 6522: Underflow on 0x0001 -> 0x0000; and FRT's period is N+2 cycles (#652) --- source/Mockingboard.cpp | 79 ++++++++++++++++++++++++------- source/SaveState_Structs_common.h | 2 + 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/source/Mockingboard.cpp b/source/Mockingboard.cpp index 4d713904..8e373727 100644 --- a/source/Mockingboard.cpp +++ b/source/Mockingboard.cpp @@ -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; isy6522.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); diff --git a/source/SaveState_Structs_common.h b/source/SaveState_Structs_common.h index 8a44ce8f..e920753f 100644 --- a/source/SaveState_Structs_common.h +++ b/source/SaveState_Structs_common.h @@ -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