void S9xDoHBlankProcessing () { #ifdef CPU_SHUTDOWN CPU.WaitCounter++; #endif switch (CPU.WhichEvent) { case HBLANK_START_EVENT: if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) IPPU.HDMA = S9xDoHDMA (IPPU.HDMA); break; case HBLANK_END_EVENT: S9xSuperFXExec (); #ifndef STORM if (Settings.SoundSync) S9xGenerateSound (); #endif CPU.Cycles -= Settings.H_Max; IAPU.NextAPUTimerPos -= (Settings.H_Max * 10000L); 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 (); } 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. #ifndef OPTI missing.dma_this_frame = 0; #endif // OPTI 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 }
void S9xDoHEventProcessing (void) { #ifdef CPU_SHUTDOWN CPU.WaitCounter++; #endif switch (CPU.WhichEvent) { case HC_HBLANK_START_EVENT: S9xCheckMissingHTimerPosition(Timings.HBlankStart); break; case HC_HDMA_START_EVENT: if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) IPPU.HDMA = S9xDoHDMA(IPPU.HDMA); S9xCheckMissingHTimerPosition(Timings.HDMAStart); break; case HC_HCOUNTER_MAX_EVENT: #ifndef ZSNES_FX if (Settings.SuperFX) { if (!SuperFX.oneLineDone) S9xSuperFXExec(); SuperFX.oneLineDone = FALSE; } #else S9xSuperFXExec(); #endif #ifndef STORM if (Settings.SoundSync) S9xGenerateSound(); #endif CPU.TotalCycles += Timings.H_Max; CPU.Cycles -= Timings.H_Max; IAPU.NextAPUTimerPos -= (Timings.H_Max << SNES_APUTIMER_ACCURACY); if (IAPU.APUExecuting) APU.Cycles -= Timings.H_Max; else APU.Cycles = 0; if ((Timings.NMITriggerPos != 0xffff) && (Timings.NMITriggerPos >= Timings.H_Max)) Timings.NMITriggerPos -= Timings.H_Max; ICPU.Scanline++; CPU.V_Counter++; if (CPU.V_Counter >= Timings.V_Max) // V ranges from 0 to Timings.V_Max - 1 { CPU.V_Counter = 0; Timings.InterlaceField ^= 1; // From byuu: // [NTSC] // interlace mode has 525 scanlines: 263 on the even frame, and 262 on the odd. // non-interlace mode has 524 scanlines: 262 scanlines on both even and odd frames. // [PAL] <PAL info is unverified on hardware> // interlace mode has 625 scanlines: 313 on the even frame, and 312 on the odd. // non-interlace mode has 624 scanlines: 312 scanlines on both even and odd frames. if (IPPU.Interlace && !Timings.InterlaceField) Timings.V_Max = Timings.V_Max_Master + 1; // 263 (NTSC), 313?(PAL) else Timings.V_Max = Timings.V_Max_Master; // 262 (NTSC), 312?(PAL) Memory.FillRAM[0x213F] ^= 0x80; PPU.RangeTimeOver = 0; // FIXME: reading $4210 will wait 2 cycles, then perform reading, then wait 4 more cycles. Memory.FillRAM[0x4210] = Model->_5A22; CPU.Flags &= ~NMI_FLAG; Timings.NMITriggerPos = 0xffff; ICPU.Frame++; PPU.HVBeamCounterLatched = 0; CPU.Flags |= SCAN_KEYS_FLAG; } // From byuu: // In non-interlace mode, there are 341 dots per scanline, and 262 scanlines per frame. // On odd frames, scanline 240 is one dot short. // In interlace mode, there are always 341 dots per scanline. Even frames have 263 scanlines, // and odd frames have 262 scanlines. // Interlace mode scanline 240 on odd frames is not missing a dot. if (CPU.V_Counter == 240 && !IPPU.Interlace && Timings.InterlaceField) // V=240 Timings.H_Max = Timings.H_Max_Master - ONE_DOT_CYCLE; // HC=1360 else Timings.H_Max = Timings.H_Max_Master; // HC=1364 if (Model->_5A22 == 2) { if (CPU.V_Counter != 240 || IPPU.Interlace || !Timings.InterlaceField) // V=240 { if (Timings.WRAMRefreshPos == SNES_WRAM_REFRESH_HC_v2 - ONE_DOT_CYCLE) // HC=534 Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2; // HC=538 else Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2 - ONE_DOT_CYCLE; // HC=534 } } else Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v1; S9xCheckMissingHTimerPosition(0); if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) // VBlank starts from V=225(240). { S9xEndScreenRefresh(); IPPU.HDMA = 0; // Bits 7 and 6 of $4212 are computed when read in S9xGetPPU. #ifdef DEBUGGER missing.dma_this_frame = 0; #endif 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; } // FIXME: writing to $4210 will wait 6 cycles. Memory.FillRAM[0x4210] = 0x80 | Model->_5A22; if (Memory.FillRAM[0x4200] & 0x80) { // FIXME: triggered at HC=6, checked just before the final CPU cycle, // then, when to call S9xOpcode_NMI()? CPU.Flags |= NMI_FLAG; Timings.NMITriggerPos = 6 + 6; } }
void S9xDoHBlankProcessing () { #ifdef CPU_SHUTDOWN CPU.WaitCounter++; #endif switch (CPU.WhichEvent) { case HBLANK_START_EVENT: if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) IPPU.HDMA = S9xDoHDMA (IPPU.HDMA); break; case HBLANK_END_EVENT: S9xSuperFXExec (); #ifndef STORM if (Settings.SoundSync) S9xGenerateSound (); #endif CPU.Cycles -= Settings.H_Max; if (IAPU.APUExecuting) APU.Cycles -= Settings.H_Max; else APU.Cycles = 0; CPU.NextEvent = -1; ICPU.Scanline++; if (++CPU.V_Counter > (Settings.PAL ? SNES_MAX_PAL_VCOUNTER : SNES_MAX_NTSC_VCOUNTER)) { PPU.OAMAddr = PPU.SavedOAMAddr; PPU.OAMFlip = 0; CPU.V_Counter = 0; CPU.NMIActive = FALSE; 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 (); PPU.FirstSprite = 0; 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; Memory.FillRAM[0x4210] = 0x80; 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] = 0; 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; //HBLANK_END_EVENT 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; }