void S9xDoHBlankProcessing_HBLANK_START_EVENT () { //START_PROFILE_FUNC (S9xDoHBlankProcessing); #ifdef CPU_SHUTDOWN CPUPack.CPU.WaitCounter++; #endif if (IPPU.HDMA && CPUPack.CPU.V_Counter <= PPU.ScreenHeight) IPPU.HDMA = S9xDoHDMA (IPPU.HDMA); S9xReschedule (); //FINISH_PROFILE_FUNC (S9xDoHBlankProcessing); }
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 S9xDoHEventProcessing (void) { #ifdef DEBUGGER static char eventname[7][32] = { "", "HC_HBLANK_START_EVENT", "HC_HDMA_START_EVENT ", "HC_HCOUNTER_MAX_EVENT", "HC_HDMA_INIT_EVENT ", "HC_RENDER_EVENT ", "HC_WRAM_REFRESH_EVENT" }; #endif #ifdef DEBUGGER if (Settings.TraceHCEvent) S9xTraceFormattedMessage("--- HC event processing (%s) expected HC:%04d executed HC:%04d", eventname[CPU.WhichEvent], CPU.NextEvent, CPU.Cycles); #endif switch (CPU.WhichEvent) { case HC_HBLANK_START_EVENT: S9xReschedule(); break; case HC_HDMA_START_EVENT: S9xReschedule(); if (PPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) { #ifdef DEBUGGER S9xTraceFormattedMessage("*** HDMA Transfer HC:%04d, Channel:%02x", CPU.Cycles, PPU.HDMA); #endif PPU.HDMA = S9xDoHDMA(PPU.HDMA); } break; case HC_HCOUNTER_MAX_EVENT: if (Settings.SuperFX) { if (!SuperFX.oneLineDone) S9xSuperFXExec(); SuperFX.oneLineDone = FALSE; } S9xAPUEndScanline(); CPU.Cycles -= Timings.H_Max; CPU.PrevCycles -= Timings.H_Max; S9xAPUSetReferenceTime(CPU.Cycles); if ((Timings.NMITriggerPos != 0xffff) && (Timings.NMITriggerPos >= Timings.H_Max)) Timings.NMITriggerPos -= Timings.H_Max; 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.NMILine = FALSE; 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; if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) // VBlank starts from V=225(240). { S9xEndScreenRefresh(); PPU.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.NMILine = TRUE; 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; 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 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: asm_APU_EXECUTE(3); // notaz: run spc700 in sound 'speed hack' mode if (Settings.SuperFX) S9xSuperFXExec(); CPU.Cycles -= Settings.H_Max; if (/*IAPU.APUExecuting*/CPU.APU_APUExecuting) CPU.APU_Cycles -= Settings.H_Max; else CPU.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; 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. missing.dma_this_frame = 0; IPPU.MaxBrightness = PPU.Brightness; PPU.ForcedBlanking = (Memory.FillRAM [0x2100] >> 7) & 1; if (!PPU.ForcedBlanking) { PPU.OAMAddr = PPU.SavedOAMAddr; PPU.OAMFlip = 0; PPU.FirstSprite = 0; if (PPU.OAMPriorityRotation) PPU.FirstSprite = PPU.OAMAddr >> 1; } Memory.FillRAM[0x4210] = 0x80; if (Memory.FillRAM[0x4200] & 0x80) { CPU.NMIActive = TRUE; CPU.Flags |= NMI_FLAG; CPU.NMICycleCount = CPU.NMITriggerPoint; } }
void S9xDoHBlankProcessing (struct SCPUState *cpu, struct SAPU *apu, struct SIAPU *iapu) { struct SPPU *ppu = &PPU; struct InternalPPU *ippu = &IPPU; #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, ppu, cpu); break; case HBLANK_END_EVENT: #ifndef _ZAURUS S9xSuperFXExec (); #endif #ifndef STORM #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, cpu); } if (cpu->V_Counter == ppu->ScreenHeight + FIRST_VISIBLE_LINE) { // Start of V-blank S9xEndScreenRefresh (ppu); 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 (&IPPU); 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, ppu); } // 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, cpu); } break; }
void S9xDoHBlankProcessing () { START_PROFILE_FUNC (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 (); CPU.Cycles -= Settings.H_Max; (IAPUuncached->NextAPUTimerPos) -= (Settings.H_Max * 10000L); if ( (IAPUuncached->APUExecuting)) (APUuncached->Cycles) -= Settings.H_Max; else (APUuncached->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 = (FillRAM[0x2100] >> 7) & 1; FillRAM[0x4210] = 0x80; if (FillRAM[0x4200] & 0x80) { CPU.NMIActive = TRUE; CPU.Flags |= NMI_FLAG; CPU.NMICycleCount = CPU.NMITriggerPoint; } } if (CPU.V_Counter == PPU.ScreenHeight + 3) S9xUpdateJoypads (); if (CPU.V_Counter == FIRST_VISIBLE_LINE) { 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); } 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; }
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; }
void S9xDoHEventProcessing (void) { uint8 tmp; switch (CPU.WhichEvent) { case HC_HBLANK_START_EVENT: if (PPU.HTimerPosition == Timings.HBlankStart) S9xCheckMissingHTimerPosition(); S9xReschedule(); break; case HC_HDMA_START_EVENT: if (PPU.HTimerPosition == Timings.HDMAStart) S9xCheckMissingHTimerPosition(); S9xReschedule(); if (PPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) PPU.HDMA = S9xDoHDMA(PPU.HDMA); break; case HC_HCOUNTER_MAX_EVENT: if (Settings.SuperFX && !SuperFX.oneLineDone && CHECK_EXEC_SUPERFX()) S9xSuperFXExec(); SuperFX.oneLineDone = FALSE; // do this even without SFX S9xAPUExecute(); CPU.Cycles -= Timings.H_Max; S9xAPUSetReferenceTime(CPU.Cycles); if ((Timings.NMITriggerPos != 0xffff) && (Timings.NMITriggerPos >= Timings.H_Max)) Timings.NMITriggerPos -= Timings.H_Max; 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. */ Timings.V_Max = Timings.V_Max_Master; /* 262 (NTSC), 312?(PAL) */ if (IPPU.Interlace && !Timings.InterlaceField) Timings.V_Max += 1; /* 263 (NTSC), 313?(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] = MAX_5A22_VERSION; 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. */ Timings.H_Max = Timings.H_Max_Master; /* HC=1364 */ if (CPU.V_Counter == 240 && !IPPU.Interlace && Timings.InterlaceField) /* V=240 */ Timings.H_Max -= ONE_DOT_CYCLE; /* HC=1360 */ if (CPU.V_Counter != 240 || IPPU.Interlace || !Timings.InterlaceField) /* V=240 */ { if (Timings.WRAMRefreshPos == SNES_WRAM_REFRESH_HC_v2_MIN_ONE_DOT_CYCLE) /* HC=534 */ Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2; /* HC=538 */ else Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2_MIN_ONE_DOT_CYCLE; /* HC=534 */ } if (PPU.HTimerPosition == 0) S9xCheckMissingHTimerPosition(); if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) /* VBlank starts from V=225(240). */ { S9xEndScreenRefresh(); PPU.HDMA = 0; /* Bits 7 and 6 of $4212 are computed when read in S9xGetPPU. */ PPU.ForcedBlanking = (Memory.FillRAM[0x2100] >> 7) & 1; if (!PPU.ForcedBlanking) { PPU.OAMAddr = PPU.SavedOAMAddr; 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 | MAX_5A22_VERSION; 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; } }