From 2b9a9dc05b5c4abdf5dac5ceba9300734d4f1b65 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Sat, 26 Jan 2013 14:47:59 -0500 Subject: Decide whether SuperFX and SA-1 chips are enabled only once per frame. This saves a few million instructions per second. (Inspired by Snes9x-Euphoria) --- source/cpuexec.cpp | 447 ++++++++++++++++++++++++++++++++++++++++++++++++++++- source/cpuexec.h | 11 +- source/dma.cpp | 8 +- 3 files changed, 459 insertions(+), 7 deletions(-) (limited to 'source') diff --git a/source/cpuexec.cpp b/source/cpuexec.cpp index c4836ec..a0c7ebc 100644 --- a/source/cpuexec.cpp +++ b/source/cpuexec.cpp @@ -111,7 +111,115 @@ extern void S9xProcessSound (unsigned int); +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. + */ void S9xMainLoop (void) +{ + if (Settings.SA1) + { + if (Settings.SuperFX) S9xMainLoop_SA1_SFX (); + else /* if (!Settings.SuperFX) */ S9xMainLoop_SA1_NoSFX (); + } + else /* if (!Settings.SA1) */ + { + if (Settings.SuperFX) S9xMainLoop_NoSA1_SFX (); + else /* if (!Settings.SuperFX) */ S9xMainLoop_NoSA1_NoSFX (); + } +} + +void S9xMainLoop_SA1_SFX (void) +{ + for (;;) + { + APU_EXECUTE (); + + if (CPU.Flags) + { + if (CPU.Flags & NMI_FLAG) + { + if (--CPU.NMICycleCount == 0) { + CPU.Flags &= ~NMI_FLAG; + if (CPU.WaitingForInterrupt) { + CPU.WaitingForInterrupt = FALSE; + CPU.PC++; + } + S9xOpcode_NMI (); + } + } + + CHECK_SOUND (); + + if (CPU.Flags & IRQ_PENDING_FLAG) + { + if (CPU.IRQCycleCount == 0) + { + if (CPU.WaitingForInterrupt) { + CPU.WaitingForInterrupt = FALSE; + 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 + CPU.PCAtOpcodeStart = CPU.PC; +#endif + 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 (); + IAPU.Registers.PC = IAPU.PC - IAPU.RAM; + S9xAPUPackStatus (); + if (CPU.Flags & SCAN_KEYS_FLAG) + { + S9xSyncSpeed (); + CPU.Flags &= ~SCAN_KEYS_FLAG; + } + +#ifdef DETECT_NASTY_FX_INTERLEAVE + if (CPU.BRKTriggered && Settings.SuperFX && !CPU.TriedInterleavedMode2) + { + CPU.TriedInterleavedMode2 = TRUE; + CPU.BRKTriggered = FALSE; + S9xDeinterleaveMode2 (); + } +#endif +} + +void S9xMainLoop_SA1_NoSFX (void) { for (;;) { @@ -168,7 +276,76 @@ void S9xMainLoop (void) if (SA1.Executing) S9xSA1MainLoop (); - DO_HBLANK_CHECK(); + DO_HBLANK_CHECK_NoSFX(); + } + + ICPU.Registers.PC = CPU.PC - CPU.PCBase; + S9xPackStatus (); + IAPU.Registers.PC = IAPU.PC - IAPU.RAM; + S9xAPUPackStatus (); + if (CPU.Flags & SCAN_KEYS_FLAG) + { + S9xSyncSpeed (); + CPU.Flags &= ~SCAN_KEYS_FLAG; + } +} + +void S9xMainLoop_NoSA1_SFX (void) +{ + for (;;) + { + APU_EXECUTE (); + + if (CPU.Flags) + { + if (CPU.Flags & NMI_FLAG) + { + if (--CPU.NMICycleCount == 0) { + CPU.Flags &= ~NMI_FLAG; + if (CPU.WaitingForInterrupt) { + CPU.WaitingForInterrupt = FALSE; + CPU.PC++; + } + S9xOpcode_NMI (); + } + } + + CHECK_SOUND (); + + if (CPU.Flags & IRQ_PENDING_FLAG) + { + if (CPU.IRQCycleCount == 0) + { + if (CPU.WaitingForInterrupt) { + CPU.WaitingForInterrupt = FALSE; + 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 + CPU.PCAtOpcodeStart = CPU.PC; +#endif + CPU.Cycles += CPU.MemSpeed; + + (*ICPU.S9xOpcodes [*CPU.PC++].S9xOpcode) (); + + DO_HBLANK_CHECK_SFX(); } ICPU.Registers.PC = CPU.PC - CPU.PCBase; @@ -191,6 +368,75 @@ void S9xMainLoop (void) #endif } +void S9xMainLoop_NoSA1_NoSFX (void) +{ + for (;;) + { + APU_EXECUTE (); + + if (CPU.Flags) + { + if (CPU.Flags & NMI_FLAG) + { + if (--CPU.NMICycleCount == 0) { + CPU.Flags &= ~NMI_FLAG; + if (CPU.WaitingForInterrupt) { + CPU.WaitingForInterrupt = FALSE; + CPU.PC++; + } + S9xOpcode_NMI (); + } + } + + CHECK_SOUND (); + + if (CPU.Flags & IRQ_PENDING_FLAG) + { + if (CPU.IRQCycleCount == 0) + { + if (CPU.WaitingForInterrupt) { + CPU.WaitingForInterrupt = FALSE; + 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 + CPU.PCAtOpcodeStart = CPU.PC; +#endif + CPU.Cycles += CPU.MemSpeed; + + (*ICPU.S9xOpcodes [*CPU.PC++].S9xOpcode) (); + + DO_HBLANK_CHECK_NoSFX(); + } + + ICPU.Registers.PC = CPU.PC - CPU.PCBase; + S9xPackStatus (); + IAPU.Registers.PC = IAPU.PC - IAPU.RAM; + S9xAPUPackStatus (); + if (CPU.Flags & SCAN_KEYS_FLAG) + { + S9xSyncSpeed (); + CPU.Flags &= ~SCAN_KEYS_FLAG; + } +} + void S9xSetIRQ (uint32 source) { CPU.IRQActive |= source; @@ -211,7 +457,15 @@ void S9xClearIRQ (uint32 source) CLEAR_IRQ_SOURCE (source); } -void S9xDoHBlankProcessing () +/* + * 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. + */ +void S9xDoHBlankProcessing_SFX () { #ifdef CPU_SHUTDOWN CPU.WaitCounter++; @@ -401,4 +655,193 @@ void S9xDoHBlankProcessing () S9xReschedule (); } +void S9xDoHBlankProcessing_NoSFX () +{ +#ifdef CPU_SHUTDOWN + CPU.WaitCounter++; +#endif + switch (CPU.WhichEvent) + { + case HBLANK_START_EVENT: +#ifdef SYNC_JOYPAD_AT_HBLANK + // Re-get the controls every hblank. A resolution algorithm in + // ppu.cpp will determine with greater accuracy whether a key was + // pressed or released during the frame. + uint32 i; + for (i = 0; i < 5; i++) + { + IPPU.JoypadsAtHBlanks [i][CPU.V_Counter] = S9xReadJoypad (i); + } +#endif + if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) + IPPU.HDMA = S9xDoHDMA (IPPU.HDMA); + + break; + + case HBLANK_END_EVENT: + +#ifndef STORM + if (Settings.SoundSync) + S9xGenerateSound (); +#endif + + CPU.Cycles -= Settings.H_Max; + if (IAPU.APUExecuting) + { + APU.Cycles -= Settings.H_Max; +#ifdef MK_APU + S9xCatchupCount(); +#endif + } + else + APU.Cycles = 0; + + 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; + CPU.NMIActive = FALSE; + ICPU.Frame++; + PPU.HVBeamCounterLatched = 0; + CPU.Flags |= SCAN_KEYS_FLAG; + S9xStartHDMA (); + } + + S9xProcessSound (0); + + 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; + + uint8 tmp = 0; + if(PPU.OAMPriorityRotation) + tmp = (PPU.OAMAddr&0xFE)>>1; + if((PPU.OAMFlip&1) || PPU.FirstSprite!=tmp) + { + PPU.FirstSprite=tmp; + IPPU.OBJChanged=TRUE; + } + + PPU.OAMFlip = 0; + } + + Memory.FillRAM[0x4210] = 0x80 |Model->_5A22; + if (Memory.FillRAM[0x4200] & 0x80) + { + CPU.NMIActive = TRUE; + CPU.Flags |= NMI_FLAG; + CPU.NMICycleCount = CPU.NMITriggerPoint; + } + +#ifdef OLD_SNAPSHOT_CODE + 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. +// IAPU.TimerErrorCounter++; +// if (IAPU.TimerErrorCounter >= ) +// IAPU.TimerErrorCounter = 0; +// else + { + 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++; + IAPU.APUExecuting = TRUE; +#endif + } + } + 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++; + IAPU.APUExecuting = TRUE; +#endif + } + } + 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++; + IAPU.APUExecuting = TRUE; +#endif + } + } + } + } + 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 (); +} diff --git a/source/cpuexec.h b/source/cpuexec.h index a094220..dab9099 100644 --- a/source/cpuexec.h +++ b/source/cpuexec.h @@ -93,9 +93,13 @@ #include "memmap.h" #include "65c816.h" -#define DO_HBLANK_CHECK() \ +#define DO_HBLANK_CHECK_SFX() \ if (CPU.Cycles >= CPU.NextEvent) \ - S9xDoHBlankProcessing (); + S9xDoHBlankProcessing_SFX (); + +#define DO_HBLANK_CHECK_NoSFX() \ + if (CPU.Cycles >= CPU.NextEvent) \ + S9xDoHBlankProcessing_NoSFX (); struct SOpcodes { #ifdef __WIN32__ @@ -126,7 +130,8 @@ START_EXTERN_C void S9xMainLoop (void); void S9xReset (void); void S9xSoftReset (void); -void S9xDoHBlankProcessing (); +void S9xDoHBlankProcessing_SFX (); +void S9xDoHBlankProcessing_NoSFX (); void S9xClearIRQ (uint32); void S9xSetIRQ (uint32); diff --git a/source/dma.cpp b/source/dma.cpp index 2addceb..96f705f 100644 --- a/source/dma.cpp +++ b/source/dma.cpp @@ -855,8 +855,12 @@ void S9xDoDMA (uint8 Channel) IAPU.APUExecuting = Settings.APUEnabled; APU_EXECUTE (); #endif - while (CPU.Cycles > CPU.NextEvent) - S9xDoHBlankProcessing (); + if (Settings.SuperFX) + while (CPU.Cycles > CPU.NextEvent) + S9xDoHBlankProcessing_SFX (); + else /* if (!Settings.SuperFX) */ + while (CPU.Cycles > CPU.NextEvent) + S9xDoHBlankProcessing_NoSFX (); if(Settings.SPC7110&&spc7110_dma) { -- cgit v1.2.3