Fix for "Mad Effect 1&2" demos (PR #725)

. 6502 interrupt delays 1 opcode when interrupt occurs on last cycle of opcode (#724).
. Only 1-cycle delay for VF_TEXT & VF_MIXED mode changes (#656).
NB. Mad Effect 1 still has a bit of flicker on Space Invader (left edge)
This commit is contained in:
TomCh 2019-11-16 23:49:21 +00:00 committed by GitHub
parent 97e73c632d
commit d1b595f7bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 15 deletions

View File

@ -403,10 +403,23 @@ static __forceinline void NMI(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
#endif #endif
} }
static bool g_irqOnLastOpcodeCycle = false;
static bool g_irqDefer1Opcode = false;
static __forceinline void IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn, BOOL& flagv, BOOL& flagz) static __forceinline void IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn, BOOL& flagv, BOOL& flagz)
{ {
if(g_bmIRQ && !(regs.ps & AF_INTERRUPT)) if(g_bmIRQ && !(regs.ps & AF_INTERRUPT))
{ {
// if 6522 interrupt occurs on opcode's last cycle, then defer IRQ by 1 opcode
if (g_irqOnLastOpcodeCycle && !g_irqDefer1Opcode)
{
g_irqOnLastOpcodeCycle = false;
g_irqDefer1Opcode = true; // if INT occurs again on next opcode, then do NOT defer
return;
}
g_irqDefer1Opcode = false;
// IRQ signals are deasserted when a specific r/w operation is done on device // IRQ signals are deasserted when a specific r/w operation is done on device
#ifdef _DEBUG #ifdef _DEBUG
g_nCycleIrqStart = g_nCumulativeCycles + uExecutedCycles; g_nCycleIrqStart = g_nCumulativeCycles + uExecutedCycles;
@ -420,6 +433,8 @@ static __forceinline void IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
UINT uExtraCycles = 0; // Needed for CYC(a) macro UINT uExtraCycles = 0; // Needed for CYC(a) macro
CYC(7) CYC(7)
} }
g_irqOnLastOpcodeCycle = false;
} }
const int IRQ_CHECK_OPCODE_FULL_SPEED = 40; // ~128 cycles (assume 3 cycles per opcode) const int IRQ_CHECK_OPCODE_FULL_SPEED = 40; // ~128 cycles (assume 3 cycles per opcode)
@ -435,7 +450,9 @@ static __forceinline void CheckInterruptSources(ULONG uExecutedCycles, const boo
g_fullSpeedOpcodeCount = IRQ_CHECK_OPCODE_FULL_SPEED; g_fullSpeedOpcodeCount = IRQ_CHECK_OPCODE_FULL_SPEED;
} }
MB_UpdateCycles(uExecutedCycles); if (MB_UpdateCycles(uExecutedCycles))
g_irqOnLastOpcodeCycle = true;
if (sg_Mouse.IsActive()) if (sg_Mouse.IsActive())
sg_Mouse.SetVBlank( !VideoGetVblBar(uExecutedCycles) ); sg_Mouse.SetVBlank( !VideoGetVblBar(uExecutedCycles) );
} }
@ -536,11 +553,10 @@ DWORD CpuExecute(const DWORD uCycles, const bool bVideoUpdate)
// >0 : Do multi-opcode emulation // >0 : Do multi-opcode emulation
const DWORD uExecutedCycles = InternalCpuExecute(uCycles, bVideoUpdate); const DWORD uExecutedCycles = InternalCpuExecute(uCycles, bVideoUpdate);
// NB. Required for normal-speed (even though 6522 is updated after every opcode), as may've finished on IRQ()
MB_UpdateCycles(uExecutedCycles); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below) MB_UpdateCycles(uExecutedCycles); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
// NB. Ensures that 6522 regs are up-to-date for any potential save-state // NB. Ensures that 6522 regs are up-to-date for any potential save-state
//
const UINT nRemainingCycles = uExecutedCycles - g_nCyclesExecuted; const UINT nRemainingCycles = uExecutedCycles - g_nCyclesExecuted;
g_nCumulativeCycles += nRemainingCycles; g_nCumulativeCycles += nRemainingCycles;

View File

@ -129,6 +129,8 @@ struct SY6522_AY8910
SSI263A SpeechChip; SSI263A SpeechChip;
MockingboardUnitState_e state; // Where a unit is a 6522+AY8910 pair MockingboardUnitState_e state; // Where a unit is a 6522+AY8910 pair
MockingboardUnitState_e stateB; // Phasor: 6522 & 2nd AY8910 MockingboardUnitState_e stateB; // Phasor: 6522 & 2nd AY8910
bool bLoadT1C;
bool bLoadT2C;
}; };
@ -376,6 +378,8 @@ static void UpdateIFR(SY6522_AY8910* pMB, BYTE clr_ifr, BYTE set_ifr=0)
CpuIrqDeassert(IS_6522); CpuIrqDeassert(IS_6522);
} }
#define DEFER_T1C_LOAD
static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue) static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
{ {
g_bMB_Active = true; g_bMB_Active = true;
@ -433,7 +437,7 @@ static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
UpdateIFR(pMB, IxR_TIMER1); UpdateIFR(pMB, IxR_TIMER1);
pMB->sy6522.TIMER1_LATCH.h = nValue; pMB->sy6522.TIMER1_LATCH.h = nValue;
pMB->sy6522.TIMER1_COUNTER.w = pMB->sy6522.TIMER1_LATCH.w; pMB->bLoadT1C = true;
StartTimer1(pMB); StartTimer1(pMB);
CpuAdjustIrqCheck(pMB->sy6522.TIMER1_LATCH.w); // Sync IRQ check timeout with 6522 counter underflow - GH#608 CpuAdjustIrqCheck(pMB->sy6522.TIMER1_LATCH.w); // Sync IRQ check timeout with 6522 counter underflow - GH#608
@ -529,7 +533,8 @@ static BYTE SY6522_Read(BYTE nDevice, BYTE nReg)
nValue = pMB->sy6522.DDRA; nValue = pMB->sy6522.DDRA;
break; break;
case 0x04: // TIMER1L_COUNTER case 0x04: // TIMER1L_COUNTER
nValue = pMB->sy6522.TIMER1_COUNTER.l; // NB. GH#701 (T1C:=0xFFFF, LDA T1C_L, A==0xFC)
nValue = (pMB->sy6522.TIMER1_COUNTER.w - 3) & 0xff; // -3 to compensate for the (assumed) 4-cycle STA 6522.T1C_H
UpdateIFR(pMB, IxR_TIMER1); UpdateIFR(pMB, IxR_TIMER1);
break; break;
case 0x05: // TIMER1H_COUNTER case 0x05: // TIMER1H_COUNTER
@ -1541,7 +1546,8 @@ void MB_Reset() // CTRL+RESET or power-cycle
static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles) static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{ {
MB_UpdateCycles(nExecutedCycles); if (g_bFullSpeed)
MB_UpdateCycles(nExecutedCycles);
#ifdef _DEBUG #ifdef _DEBUG
if(!IS_APPLE2 && MemCheckINTCXROM()) if(!IS_APPLE2 && MemCheckINTCXROM())
@ -1604,7 +1610,8 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles) static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{ {
MB_UpdateCycles(nExecutedCycles); if (g_bFullSpeed)
MB_UpdateCycles(nExecutedCycles);
#ifdef _DEBUG #ifdef _DEBUG
if(!IS_APPLE2 && MemCheckINTCXROM()) if(!IS_APPLE2 && MemCheckINTCXROM())
@ -1828,24 +1835,45 @@ static bool CheckTimerUnderflowAndIrq(USHORT& timerCounter, int& timerIrqDelay,
// Called by: // Called by:
// . CpuExecute() every ~1000 @ 1MHz // . CpuExecute() every ~1000 @ 1MHz
// . CheckInterruptSources() every opcode (or every 40 opcodes at full-speed) // . CheckInterruptSources() every opcode (or every 40 opcodes at full-speed)
// . MB_Read() / MB_Write() // . MB_Read() / MB_Write() (only for full-speed)
void MB_UpdateCycles(ULONG uExecutedCycles) bool MB_UpdateCycles(ULONG uExecutedCycles)
{ {
if (g_SoundcardType == CT_Empty) if (g_SoundcardType == CT_Empty)
return; return false;
CpuCalcCycles(uExecutedCycles); CpuCalcCycles(uExecutedCycles);
UINT64 uCycles = g_nCumulativeCycles - g_uLastCumulativeCycles; UINT64 uCycles = g_nCumulativeCycles - g_uLastCumulativeCycles;
if (uCycles == 0)
return false; // Likely when called from CpuExecute()
_ASSERT(uCycles > 1);
const bool isOpcode = (uCycles > 1 && uCycles <= 7); // todo: better to pass in a flag?
g_uLastCumulativeCycles = g_nCumulativeCycles; g_uLastCumulativeCycles = g_nCumulativeCycles;
_ASSERT(uCycles < 0x10000); _ASSERT(uCycles < 0x10000);
USHORT nClocks = (USHORT) uCycles; USHORT nClocks = (USHORT) uCycles;
bool bIrqOnLastOpcodeCycle = false;
for (int i=0; i<NUM_SY6522; i++) for (int i=0; i<NUM_SY6522; i++)
{ {
SY6522_AY8910* pMB = &g_MB[i]; SY6522_AY8910* pMB = &g_MB[i];
bool bTimer1Underflow = false; // Just for Willy Byte! bool bTimer1Underflow = false; // Just for Willy Byte!
const bool bTimer1Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER1_COUNTER.w, pMB->sy6522.timer1IrqDelay, nClocks, &bTimer1Underflow); bool bTimer1Irq = false;
bool bTimer1IrqOnLastCycle = false;
if (isOpcode)
{
bTimer1Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER1_COUNTER.w, pMB->sy6522.timer1IrqDelay, nClocks-1, &bTimer1Underflow);
bTimer1IrqOnLastCycle = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER1_COUNTER.w, pMB->sy6522.timer1IrqDelay, 1, &bTimer1Underflow);
bTimer1Irq = bTimer1Irq || bTimer1IrqOnLastCycle;
}
else
{
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); const bool bTimer2Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER2_COUNTER.w, pMB->sy6522.timer2IrqDelay, nClocks);
if (!pMB->bTimer1Active && bTimer1Underflow) if (!pMB->bTimer1Active && bTimer1Underflow)
@ -1864,6 +1892,7 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
if (pMB->bTimer1Active && bTimer1Irq) if (pMB->bTimer1Active && bTimer1Irq)
{ {
UpdateIFR(pMB, 0, IxR_TIMER1); UpdateIFR(pMB, 0, IxR_TIMER1);
bIrqOnLastOpcodeCycle = true;
MB_Update(); MB_Update();
@ -1880,6 +1909,9 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
// - Ultima4/5 change ACCESS_TIMER1 after a couple of IRQs into tune // - 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 += 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) pMB->sy6522.TIMER1_COUNTER.w += 2; // GH#652: account for extra 2 cycles (Rockwell, Fig.16: period=N+2cycles)
// EG. T1C=0xFFFE, T1L=0x0001
// . T1C += T1L = 0xFFFF
// . T1C += 2 = 0x0001
if (pMB->sy6522.TIMER1_COUNTER.w > pMB->sy6522.TIMER1_LATCH.w) if (pMB->sy6522.TIMER1_COUNTER.w > pMB->sy6522.TIMER1_LATCH.w)
{ {
if (pMB->sy6522.TIMER1_LATCH.w) if (pMB->sy6522.TIMER1_LATCH.w)
@ -1891,6 +1923,12 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
} }
} }
if (pMB->bLoadT1C)
{
pMB->bLoadT1C = false;
pMB->sy6522.TIMER1_COUNTER.w = pMB->sy6522.TIMER1_LATCH.w;
}
if (pMB->bTimer2Active && bTimer2Irq) if (pMB->bTimer2Active && bTimer2Irq)
{ {
UpdateIFR(pMB, 0, IxR_TIMER2); UpdateIFR(pMB, 0, IxR_TIMER2);
@ -1913,6 +1951,8 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
} }
} }
} }
return bIrqOnLastOpcodeCycle;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -11,7 +11,7 @@ void MB_Demute();
void MB_StartOfCpuExecute(); void MB_StartOfCpuExecute();
void MB_PeriodicUpdate(UINT executedCycles); void MB_PeriodicUpdate(UINT executedCycles);
void MB_CheckIRQ(); void MB_CheckIRQ();
void MB_UpdateCycles(ULONG uExecutedCycles); bool MB_UpdateCycles(ULONG uExecutedCycles);
SS_CARDTYPE MB_GetSoundcardType(); SS_CARDTYPE MB_GetSoundcardType();
bool MB_IsActive(); bool MB_IsActive();
DWORD MB_GetVolume(); DWORD MB_GetVolume();

View File

@ -660,9 +660,10 @@ BYTE VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles)
if (!IS_APPLE2) if (!IS_APPLE2)
RGB_SetVideoMode(address); RGB_SetVideoMode(address);
bool delay = true; // Only 1-cycle delay for VF_TEXT & VF_MIXED mode changes (GH#656)
if ((oldVideoMode ^ g_uVideoMode) & VF_PAGE2) bool delay = false;
delay = false; // PAGE2 flag changed state, so no 1 cycle delay (GH#656) if ((oldVideoMode ^ g_uVideoMode) & (VF_TEXT|VF_MIXED))
delay = true;
NTSC_SetVideoMode( g_uVideoMode, delay ); NTSC_SetVideoMode( g_uVideoMode, delay );