snes9x2005/source/cpuexec.c

719 lines
18 KiB
C
Raw Normal View History

#include "../copyright"
2011-03-06 02:39:25 +00:00
#include "snes9x.h"
#include "memmap.h"
#include "cpuops.h"
#include "ppu.h"
#include "cpuexec.h"
#include "gfx.h"
#include "missing.h"
#include "apu.h"
#include "dma.h"
#include "fxemu.h"
#include "sa1.h"
#include "spc7110.h"
2014-10-29 23:23:30 +00:00
void S9xMainLoop_SA1_SFX(void);
void S9xMainLoop_SA1_NoSFX(void);
void S9xMainLoop_NoSA1_SFX(void);
void S9xMainLoop_NoSA1_NoSFX(void);
/*
* This is a CATSFC modification inspired by a Snes9x-Euphoria modification.
* The emulator selects a main loop based on the chips used in an entire
* frame. This avoids the constant SA1.Executing and Settings.SuperFX checks.
*
* The original version of S9xMainLoop is S9xMainLoop_SA1_SFX below. Remember
* to propagate modifications to the SA1_NoSFX, NoSA1_SFX and NoSA1_NoSFX
* versions.
*/
2014-10-29 23:23:30 +00:00
void S9xMainLoop(void)
{
2014-10-29 23:23:30 +00:00
if (Settings.SA1)
{
2017-01-14 23:08:50 +00:00
if (Settings.SuperFX) S9xMainLoop_SA1_SFX();
else S9xMainLoop_SA1_NoSFX();
2014-10-29 23:23:30 +00:00
}
2017-01-14 23:08:50 +00:00
else
2014-10-29 23:23:30 +00:00
{
2017-01-14 23:08:50 +00:00
if (Settings.SuperFX) S9xMainLoop_NoSA1_SFX();
else S9xMainLoop_NoSA1_NoSFX();
2014-10-29 23:23:30 +00:00
}
}
2014-10-29 23:23:30 +00:00
void S9xMainLoop_SA1_SFX(void)
{
2014-10-29 23:23:30 +00:00
for (;;)
{
APU_EXECUTE();
if (CPU.Flags)
{
if (CPU.Flags & NMI_FLAG)
{
if (--CPU.NMICycleCount == 0)
{
CPU.Flags &= ~NMI_FLAG;
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
S9xOpcode_NMI();
}
}
if (CPU.Flags & IRQ_PENDING_FLAG)
{
if (CPU.IRQCycleCount == 0)
{
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
if (CPU.IRQActive && !Settings.DisableIRQ)
{
if (!CheckFlag(IRQ))
S9xOpcode_IRQ();
}
else
CPU.Flags &= ~IRQ_PENDING_FLAG;
}
else
{
if (--CPU.IRQCycleCount == 0 && CheckFlag(IRQ))
CPU.IRQCycleCount = 1;
}
}
if (CPU.Flags & SCAN_KEYS_FLAG)
break;
}
#ifdef CPU_SHUTDOWN
2014-10-29 23:23:30 +00:00
CPU.PCAtOpcodeStart = CPU.PC;
#endif
2014-10-29 23:23:30 +00:00
CPU.Cycles += CPU.MemSpeed;
(*ICPU.S9xOpcodes [*CPU.PC++].S9xOpcode)();
if (SA1.Executing)
S9xSA1MainLoop();
DO_HBLANK_CHECK_SFX();
}
ICPU.Registers.PC = CPU.PC - CPU.PCBase;
S9xPackStatus();
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
IAPU.Registers.PC = IAPU.PC - IAPU.RAM;
S9xAPUPackStatus();
#endif
2014-10-29 23:23:30 +00:00
if (CPU.Flags & SCAN_KEYS_FLAG)
CPU.Flags &= ~SCAN_KEYS_FLAG;
#ifdef DETECT_NASTY_FX_INTERLEAVE
2014-10-29 23:23:30 +00:00
if (CPU.BRKTriggered && Settings.SuperFX && !CPU.TriedInterleavedMode2)
{
2014-11-03 14:26:54 +00:00
CPU.TriedInterleavedMode2 = true;
CPU.BRKTriggered = false;
2014-10-29 23:23:30 +00:00
S9xDeinterleaveMode2();
}
#endif
}
2014-10-29 23:23:30 +00:00
void S9xMainLoop_SA1_NoSFX(void)
2011-03-06 02:39:25 +00:00
{
2014-10-29 23:23:30 +00:00
for (;;)
{
APU_EXECUTE();
if (CPU.Flags)
{
if (CPU.Flags & NMI_FLAG)
{
if (--CPU.NMICycleCount == 0)
{
CPU.Flags &= ~NMI_FLAG;
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
S9xOpcode_NMI();
}
}
if (CPU.Flags & IRQ_PENDING_FLAG)
{
if (CPU.IRQCycleCount == 0)
{
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
if (CPU.IRQActive && !Settings.DisableIRQ)
{
if (!CheckFlag(IRQ))
S9xOpcode_IRQ();
}
else
CPU.Flags &= ~IRQ_PENDING_FLAG;
}
else
{
if (--CPU.IRQCycleCount == 0 && CheckFlag(IRQ))
CPU.IRQCycleCount = 1;
}
}
if (CPU.Flags & SCAN_KEYS_FLAG)
break;
}
2011-03-06 02:39:25 +00:00
#ifdef CPU_SHUTDOWN
2014-10-29 23:23:30 +00:00
CPU.PCAtOpcodeStart = CPU.PC;
2011-03-06 02:39:25 +00:00
#endif
2014-10-29 23:23:30 +00:00
CPU.Cycles += CPU.MemSpeed;
(*ICPU.S9xOpcodes [*CPU.PC++].S9xOpcode)();
if (SA1.Executing)
S9xSA1MainLoop();
DO_HBLANK_CHECK_NoSFX();
}
ICPU.Registers.PC = CPU.PC - CPU.PCBase;
S9xPackStatus();
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
IAPU.Registers.PC = IAPU.PC - IAPU.RAM;
S9xAPUPackStatus();
#endif
2014-10-29 23:23:30 +00:00
if (CPU.Flags & SCAN_KEYS_FLAG)
CPU.Flags &= ~SCAN_KEYS_FLAG;
}
2014-10-29 23:23:30 +00:00
void S9xMainLoop_NoSA1_SFX(void)
{
2014-10-29 23:23:30 +00:00
for (;;)
{
APU_EXECUTE();
if (CPU.Flags)
{
if (CPU.Flags & NMI_FLAG)
{
if (--CPU.NMICycleCount == 0)
{
CPU.Flags &= ~NMI_FLAG;
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
S9xOpcode_NMI();
}
}
if (CPU.Flags & IRQ_PENDING_FLAG)
{
if (CPU.IRQCycleCount == 0)
{
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
if (CPU.IRQActive && !Settings.DisableIRQ)
{
if (!CheckFlag(IRQ))
S9xOpcode_IRQ();
}
else
CPU.Flags &= ~IRQ_PENDING_FLAG;
}
else
{
if (--CPU.IRQCycleCount == 0 && CheckFlag(IRQ))
CPU.IRQCycleCount = 1;
}
}
if (CPU.Flags & SCAN_KEYS_FLAG)
break;
}
#ifdef CPU_SHUTDOWN
2014-10-29 23:23:30 +00:00
CPU.PCAtOpcodeStart = CPU.PC;
#endif
2014-10-29 23:23:30 +00:00
CPU.Cycles += CPU.MemSpeed;
(*ICPU.S9xOpcodes [*CPU.PC++].S9xOpcode)();
DO_HBLANK_CHECK_SFX();
}
ICPU.Registers.PC = CPU.PC - CPU.PCBase;
S9xPackStatus();
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
IAPU.Registers.PC = IAPU.PC - IAPU.RAM;
S9xAPUPackStatus();
#endif
2014-10-29 23:23:30 +00:00
if (CPU.Flags & SCAN_KEYS_FLAG)
CPU.Flags &= ~SCAN_KEYS_FLAG;
2011-03-06 02:39:25 +00:00
#ifdef DETECT_NASTY_FX_INTERLEAVE
2014-10-29 23:23:30 +00:00
if (CPU.BRKTriggered && Settings.SuperFX && !CPU.TriedInterleavedMode2)
{
2014-11-03 14:26:54 +00:00
CPU.TriedInterleavedMode2 = true;
CPU.BRKTriggered = false;
2014-10-29 23:23:30 +00:00
S9xDeinterleaveMode2();
}
2011-03-06 02:39:25 +00:00
#endif
}
2014-10-29 23:23:30 +00:00
void S9xMainLoop_NoSA1_NoSFX(void)
{
2014-10-29 23:23:30 +00:00
for (;;)
{
APU_EXECUTE();
if (CPU.Flags)
{
if (CPU.Flags & NMI_FLAG)
{
if (--CPU.NMICycleCount == 0)
{
CPU.Flags &= ~NMI_FLAG;
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
S9xOpcode_NMI();
}
}
if (CPU.Flags & IRQ_PENDING_FLAG)
{
if (CPU.IRQCycleCount == 0)
{
if (CPU.WaitingForInterrupt)
{
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
if (CPU.IRQActive && !Settings.DisableIRQ)
{
if (!CheckFlag(IRQ))
S9xOpcode_IRQ();
}
else
CPU.Flags &= ~IRQ_PENDING_FLAG;
}
else
{
if (--CPU.IRQCycleCount == 0 && CheckFlag(IRQ))
CPU.IRQCycleCount = 1;
}
}
if (CPU.Flags & SCAN_KEYS_FLAG)
break;
}
#ifdef CPU_SHUTDOWN
2014-10-29 23:23:30 +00:00
CPU.PCAtOpcodeStart = CPU.PC;
#endif
2014-10-29 23:23:30 +00:00
CPU.Cycles += CPU.MemSpeed;
(*ICPU.S9xOpcodes [*CPU.PC++].S9xOpcode)();
DO_HBLANK_CHECK_NoSFX();
}
ICPU.Registers.PC = CPU.PC - CPU.PCBase;
S9xPackStatus();
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
IAPU.Registers.PC = IAPU.PC - IAPU.RAM;
S9xAPUPackStatus();
#endif
2014-10-29 23:23:30 +00:00
if (CPU.Flags & SCAN_KEYS_FLAG)
CPU.Flags &= ~SCAN_KEYS_FLAG;
}
2014-11-03 14:26:54 +00:00
void S9xSetIRQ(uint32_t source)
2011-03-06 02:39:25 +00:00
{
2014-10-29 23:23:30 +00:00
CPU.IRQActive |= source;
CPU.Flags |= IRQ_PENDING_FLAG;
CPU.IRQCycleCount = 3;
if (CPU.WaitingForInterrupt)
{
// Force IRQ to trigger immediately after WAI -
// Final Fantasy Mystic Quest crashes without this.
CPU.IRQCycleCount = 0;
2014-11-03 14:26:54 +00:00
CPU.WaitingForInterrupt = false;
2014-10-29 23:23:30 +00:00
CPU.PC++;
}
2011-03-06 02:39:25 +00:00
}
2014-11-03 14:26:54 +00:00
void S9xClearIRQ(uint32_t source)
2011-03-06 02:39:25 +00:00
{
2014-10-29 23:23:30 +00:00
CLEAR_IRQ_SOURCE(source);
2011-03-06 02:39:25 +00:00
}
/*
* This is a CATSFC modification inspired by a Snes9x-Euphoria modification.
* The emulator selects an HBlank processor based on the chips used in an
* entire frame. This avoids the constant Settings.SuperFX checks.
*
* The original version of S9xDoHBlankProcessing is S9xDoHBlankProcessing_SFX
* below. Remember to propagate modifications to the NoSFX version.
*/
2014-10-29 23:23:30 +00:00
void S9xDoHBlankProcessing_SFX()
2011-03-06 02:39:25 +00:00
{
#ifdef CPU_SHUTDOWN
2014-10-29 23:23:30 +00:00
CPU.WaitCounter++;
2011-03-06 02:39:25 +00:00
#endif
2014-10-29 23:23:30 +00:00
switch (CPU.WhichEvent)
{
case HBLANK_START_EVENT:
if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
IPPU.HDMA = S9xDoHDMA(IPPU.HDMA);
2011-03-06 02:39:25 +00:00
2014-10-29 23:23:30 +00:00
break;
2011-03-06 02:39:25 +00:00
2014-10-29 23:23:30 +00:00
case HBLANK_END_EVENT:
S9xSuperFXExec();
2011-03-06 02:39:25 +00:00
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
CPU.Cycles -= Settings.H_Max;
if (IAPU.APUExecuting)
{
APU.Cycles -= Settings.H_Max;
#ifdef MK_APU
2014-10-29 23:23:30 +00:00
S9xCatchupCount();
#endif
2014-10-29 23:23:30 +00:00
}
else
APU.Cycles = 0;
#else
S9xAPUExecute();
CPU.Cycles -= Settings.H_Max;
S9xAPUSetReferenceTime(CPU.Cycles);
#endif
2014-10-29 23:23:30 +00:00
CPU.NextEvent = -1;
ICPU.Scanline++;
if (++CPU.V_Counter >= (Settings.PAL ? SNES_MAX_PAL_VCOUNTER :
SNES_MAX_NTSC_VCOUNTER))
{
CPU.V_Counter = 0;
Memory.FillRAM[0x213F] ^= 0x80;
PPU.RangeTimeOver = 0;
2014-11-03 14:26:54 +00:00
CPU.NMIActive = false;
2014-10-29 23:23:30 +00:00
ICPU.Frame++;
PPU.HVBeamCounterLatched = 0;
CPU.Flags |= SCAN_KEYS_FLAG;
S9xStartHDMA();
}
if (PPU.VTimerEnabled && !PPU.HTimerEnabled && CPU.V_Counter == PPU.IRQVBeamPos)
S9xSetIRQ(PPU_V_BEAM_IRQ_SOURCE);
if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE)
{
// Start of V-blank
S9xEndScreenRefresh();
IPPU.HDMA = 0;
// Bits 7 and 6 of $4212 are computed when read in S9xGetPPU.
missing.dma_this_frame = 0;
IPPU.MaxBrightness = PPU.Brightness;
PPU.ForcedBlanking = (Memory.FillRAM [0x2100] >> 7) & 1;
if (!PPU.ForcedBlanking)
{
PPU.OAMAddr = PPU.SavedOAMAddr;
2014-11-03 14:26:54 +00:00
uint8_t tmp = 0;
2014-10-29 23:23:30 +00:00
if (PPU.OAMPriorityRotation)
tmp = (PPU.OAMAddr & 0xFE) >> 1;
if ((PPU.OAMFlip & 1) || PPU.FirstSprite != tmp)
{
PPU.FirstSprite = tmp;
2014-11-03 14:26:54 +00:00
IPPU.OBJChanged = true;
2014-10-29 23:23:30 +00:00
}
PPU.OAMFlip = 0;
}
Memory.FillRAM[0x4210] = 0x80 | Model->_5A22;
if (Memory.FillRAM[0x4200] & 0x80)
{
2014-11-03 14:26:54 +00:00
CPU.NMIActive = true;
2014-10-29 23:23:30 +00:00
CPU.Flags |= NMI_FLAG;
CPU.NMICycleCount = CPU.NMITriggerPoint;
}
#ifdef OLD_SNAPSHOT_CODE
2014-10-29 23:23:30 +00:00
if (CPU.Flags & SAVE_SNAPSHOT_FLAG)
{
CPU.Flags &= ~SAVE_SNAPSHOT_FLAG;
Registers.PC = CPU.PC - CPU.PCBase;
S9xPackStatus();
S9xAPUPackStatus();
Snapshot(NULL);
}
#endif
}
if (CPU.V_Counter == PPU.ScreenHeight + 3)
S9xUpdateJoypads();
if (CPU.V_Counter == FIRST_VISIBLE_LINE)
{
Memory.FillRAM[0x4210] = Model->_5A22;
CPU.Flags &= ~NMI_FLAG;
S9xStartScreenRefresh();
}
if (CPU.V_Counter >= FIRST_VISIBLE_LINE &&
CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE)
RenderLine(CPU.V_Counter - FIRST_VISIBLE_LINE);
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
// Use TimerErrorCounter to skip update of SPC700 timers once
// every 128 updates. Needed because this section of code is called
// once every emulated 63.5 microseconds, which coresponds to
// 15.750KHz, but the SPC700 timers need to be updated at multiples
// of 8KHz, hence the error correction.
{
if (APU.TimerEnabled [2])
{
APU.Timer [2] += 4;
while (APU.Timer [2] >= APU.TimerTarget [2])
{
IAPU.RAM [0xff] = (IAPU.RAM [0xff] + 1) & 0xf;
APU.Timer [2] -= APU.TimerTarget [2];
#ifdef SPC700_SHUTDOWN
IAPU.WaitCounter++;
2014-11-03 14:26:54 +00:00
IAPU.APUExecuting = true;
#endif
2014-10-29 23:23:30 +00:00
}
}
if (CPU.V_Counter & 1)
{
if (APU.TimerEnabled [0])
{
APU.Timer [0]++;
if (APU.Timer [0] >= APU.TimerTarget [0])
{
IAPU.RAM [0xfd] = (IAPU.RAM [0xfd] + 1) & 0xf;
APU.Timer [0] = 0;
#ifdef SPC700_SHUTDOWN
IAPU.WaitCounter++;
2014-11-03 14:26:54 +00:00
IAPU.APUExecuting = true;
#endif
2014-10-29 23:23:30 +00:00
}
}
if (APU.TimerEnabled [1])
{
APU.Timer [1]++;
if (APU.Timer [1] >= APU.TimerTarget [1])
{
IAPU.RAM [0xfe] = (IAPU.RAM [0xfe] + 1) & 0xf;
APU.Timer [1] = 0;
#ifdef SPC700_SHUTDOWN
IAPU.WaitCounter++;
2014-11-03 14:26:54 +00:00
IAPU.APUExecuting = true;
#endif
2014-10-29 23:23:30 +00:00
}
}
}
}
#endif // #ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
break;
case HTIMER_BEFORE_EVENT:
case HTIMER_AFTER_EVENT:
if (PPU.HTimerEnabled && (!PPU.VTimerEnabled
|| CPU.V_Counter == PPU.IRQVBeamPos))
S9xSetIRQ(PPU_H_BEAM_IRQ_SOURCE);
break;
}
S9xReschedule();
}
2014-10-29 23:23:30 +00:00
void S9xDoHBlankProcessing_NoSFX()
{
#ifdef CPU_SHUTDOWN
2014-10-29 23:23:30 +00:00
CPU.WaitCounter++;
#endif
2014-10-29 23:23:30 +00:00
switch (CPU.WhichEvent)
{
case HBLANK_START_EVENT:
if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
IPPU.HDMA = S9xDoHDMA(IPPU.HDMA);
2014-10-29 23:23:30 +00:00
break;
2014-10-29 23:23:30 +00:00
case HBLANK_END_EVENT:
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
CPU.Cycles -= Settings.H_Max;
if (IAPU.APUExecuting)
{
APU.Cycles -= Settings.H_Max;
2011-03-06 02:39:25 +00:00
#ifdef MK_APU
2014-10-29 23:23:30 +00:00
S9xCatchupCount();
2011-03-06 02:39:25 +00:00
#endif
2014-10-29 23:23:30 +00:00
}
else
APU.Cycles = 0;
#else
S9xAPUExecute();
CPU.Cycles -= Settings.H_Max;
S9xAPUSetReferenceTime(CPU.Cycles);
#endif
2014-10-29 23:23:30 +00:00
CPU.NextEvent = -1;
ICPU.Scanline++;
if (++CPU.V_Counter >= (Settings.PAL ? SNES_MAX_PAL_VCOUNTER :
SNES_MAX_NTSC_VCOUNTER))
{
CPU.V_Counter = 0;
Memory.FillRAM[0x213F] ^= 0x80;
PPU.RangeTimeOver = 0;
2014-11-03 14:26:54 +00:00
CPU.NMIActive = false;
2014-10-29 23:23:30 +00:00
ICPU.Frame++;
PPU.HVBeamCounterLatched = 0;
CPU.Flags |= SCAN_KEYS_FLAG;
S9xStartHDMA();
}
if (PPU.VTimerEnabled && !PPU.HTimerEnabled && CPU.V_Counter == PPU.IRQVBeamPos)
S9xSetIRQ(PPU_V_BEAM_IRQ_SOURCE);
if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE)
{
// Start of V-blank
S9xEndScreenRefresh();
IPPU.HDMA = 0;
// Bits 7 and 6 of $4212 are computed when read in S9xGetPPU.
missing.dma_this_frame = 0;
IPPU.MaxBrightness = PPU.Brightness;
PPU.ForcedBlanking = (Memory.FillRAM [0x2100] >> 7) & 1;
if (!PPU.ForcedBlanking)
{
PPU.OAMAddr = PPU.SavedOAMAddr;
2014-11-03 14:26:54 +00:00
uint8_t tmp = 0;
2014-10-29 23:23:30 +00:00
if (PPU.OAMPriorityRotation)
tmp = (PPU.OAMAddr & 0xFE) >> 1;
if ((PPU.OAMFlip & 1) || PPU.FirstSprite != tmp)
{
PPU.FirstSprite = tmp;
2014-11-03 14:26:54 +00:00
IPPU.OBJChanged = true;
2014-10-29 23:23:30 +00:00
}
PPU.OAMFlip = 0;
}
Memory.FillRAM[0x4210] = 0x80 | Model->_5A22;
if (Memory.FillRAM[0x4200] & 0x80)
{
2014-11-03 14:26:54 +00:00
CPU.NMIActive = true;
2014-10-29 23:23:30 +00:00
CPU.Flags |= NMI_FLAG;
CPU.NMICycleCount = CPU.NMITriggerPoint;
}
2011-03-06 02:39:25 +00:00
#ifdef OLD_SNAPSHOT_CODE
2014-10-29 23:23:30 +00:00
if (CPU.Flags & SAVE_SNAPSHOT_FLAG)
{
CPU.Flags &= ~SAVE_SNAPSHOT_FLAG;
Registers.PC = CPU.PC - CPU.PCBase;
S9xPackStatus();
S9xAPUPackStatus();
Snapshot(NULL);
}
#endif
}
if (CPU.V_Counter == PPU.ScreenHeight + 3)
S9xUpdateJoypads();
if (CPU.V_Counter == FIRST_VISIBLE_LINE)
{
Memory.FillRAM[0x4210] = Model->_5A22;
CPU.Flags &= ~NMI_FLAG;
S9xStartScreenRefresh();
}
if (CPU.V_Counter >= FIRST_VISIBLE_LINE &&
CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE)
RenderLine(CPU.V_Counter - FIRST_VISIBLE_LINE);
// Use TimerErrorCounter to skip update of SPC700 timers once
// every 128 updates. Needed because this section of code is called
// once every emulated 63.5 microseconds, which coresponds to
// 15.750KHz, but the SPC700 timers need to be updated at multiples
// of 8KHz, hence the error correction.
#ifndef USE_BLARGG_APU
2014-10-29 23:23:30 +00:00
{
if (APU.TimerEnabled [2])
{
APU.Timer [2] += 4;
while (APU.Timer [2] >= APU.TimerTarget [2])
{
IAPU.RAM [0xff] = (IAPU.RAM [0xff] + 1) & 0xf;
APU.Timer [2] -= APU.TimerTarget [2];
#ifdef SPC700_SHUTDOWN
IAPU.WaitCounter++;
2014-11-03 14:26:54 +00:00
IAPU.APUExecuting = true;
2011-03-06 02:39:25 +00:00
#endif
2014-10-29 23:23:30 +00:00
}
}
if (CPU.V_Counter & 1)
{
if (APU.TimerEnabled [0])
{
APU.Timer [0]++;
if (APU.Timer [0] >= APU.TimerTarget [0])
{
IAPU.RAM [0xfd] = (IAPU.RAM [0xfd] + 1) & 0xf;
APU.Timer [0] = 0;
#ifdef SPC700_SHUTDOWN
IAPU.WaitCounter++;
2014-11-03 14:26:54 +00:00
IAPU.APUExecuting = true;
2011-03-06 02:39:25 +00:00
#endif
2014-10-29 23:23:30 +00:00
}
}
if (APU.TimerEnabled [1])
{
APU.Timer [1]++;
if (APU.Timer [1] >= APU.TimerTarget [1])
{
IAPU.RAM [0xfe] = (IAPU.RAM [0xfe] + 1) & 0xf;
APU.Timer [1] = 0;
#ifdef SPC700_SHUTDOWN
IAPU.WaitCounter++;
2014-11-03 14:26:54 +00:00
IAPU.APUExecuting = true;
2011-03-06 02:39:25 +00:00
#endif
2014-10-29 23:23:30 +00:00
}
}
}
}
#endif
2014-10-29 23:23:30 +00:00
break;
case HTIMER_BEFORE_EVENT:
case HTIMER_AFTER_EVENT:
if (PPU.HTimerEnabled && (!PPU.VTimerEnabled
|| CPU.V_Counter == PPU.IRQVBeamPos))
S9xSetIRQ(PPU_H_BEAM_IRQ_SOURCE);
break;
}
S9xReschedule();
2011-03-06 02:39:25 +00:00
}