void S9xUpdateHVTimerPosition (void) { PPU.HTimerPosition = PPU.IRQHBeamPos * ONE_DOT_CYCLE + Timings.IRQTriggerCycles; if (Timings.H_Max == Timings.H_Max_Master) // 1364 { if (PPU.IRQHBeamPos > 322) PPU.HTimerPosition += (ONE_DOT_CYCLE / 2); if (PPU.IRQHBeamPos > 326) PPU.HTimerPosition += (ONE_DOT_CYCLE / 2); } PPU.VTimerPosition = PPU.IRQVBeamPos; if ((PPU.HTimerPosition >= Timings.H_Max) && (PPU.IRQHBeamPos < 340)) { PPU.HTimerPosition -= Timings.H_Max; PPU.VTimerPosition++; // FIXME if (PPU.VTimerPosition >= Timings.V_Max) PPU.VTimerPosition = 0; } #ifdef DEBUGGER S9xTraceFormattedMessage("--- IRQ Timer set HTimer:%d Pos:%04d VTimer:%d Pos:%03d", PPU.HTimerEnabled, PPU.HTimerPosition, PPU.VTimerEnabled, PPU.VTimerPosition); #endif }
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; } }
static void S9xSA1UpdateTimer (void) // FIXME { SA1.PrevHCounter = SA1.HCounter; if (Memory.FillRAM[0x2210] & 0x80) { SA1.HCounter += (SA1.Cycles - SA1.PrevCycles); if (SA1.HCounter >= 0x800) { SA1.HCounter -= 0x800; SA1.PrevHCounter -= 0x800; if (++SA1.VCounter >= 0x200) SA1.VCounter = 0; } } else { SA1.HCounter += (SA1.Cycles - SA1.PrevCycles); if (SA1.HCounter >= Timings.H_Max_Master) { SA1.HCounter -= Timings.H_Max_Master; SA1.PrevHCounter -= Timings.H_Max_Master; if (++SA1.VCounter >= Timings.V_Max_Master) SA1.VCounter = 0; } } if (SA1.Cycles >= Timings.H_Max_Master) SA1.Cycles -= Timings.H_Max_Master; SA1.PrevCycles = SA1.Cycles; bool8 thisIRQ = Memory.FillRAM[0x2210] & 0x03; if (Memory.FillRAM[0x2210] & 0x01) { if (SA1.PrevHCounter >= SA1.HTimerIRQPos * ONE_DOT_CYCLE || SA1.HCounter < SA1.HTimerIRQPos * ONE_DOT_CYCLE) thisIRQ = FALSE; } if (Memory.FillRAM[0x2210] & 0x02) { if (SA1.VCounter != SA1.VTimerIRQPos * ONE_DOT_CYCLE) thisIRQ = FALSE; } // SA-1 Timer IRQ control if (!SA1.TimerIRQLastState && thisIRQ) { Memory.FillRAM[0x2301] |= 0x40; if (Memory.FillRAM[0x220a] & 0x40) { Memory.FillRAM[0x220b] &= ~0x40; #ifdef DEBUGGER S9xTraceFormattedMessage("--- SA-1 Timer IRQ triggered prev HC:%04d curr HC:%04d HTimer:%d Pos:%04d VTimer:%d Pos:%03d", SA1.PrevHCounter, SA1.HCounter, (Memory.FillRAM[0x2210] & 0x01) ? 1 : 0, SA1.HTimerIRQPos * ONE_DOT_CYCLE, (Memory.FillRAM[0x2210] & 0x02) ? 1 : 0, SA1.VTimerIRQPos); #endif } } SA1.TimerIRQLastState = thisIRQ; }
void S9xSetPPU (uint8 Byte, uint16 Address) { // MAP_PPU: $2000-$3FFF if (CPU.InDMAorHDMA) { if (CPU.CurrentDMAorHDMAChannel >= 0 && DMA[CPU.CurrentDMAorHDMAChannel].ReverseTransfer) { // S9xSetPPU() is called to write to DMA[].AAddress if ((Address & 0xff00) == 0x2100) { // Cannot access to Address Bus B ($2100-$21ff) via (H)DMA return; } else { // 0x2000-0x3FFF is connected to Address Bus A // SA1, SuperFX and SRTC are mapped here // I don't bother for now... return; } } else { // S9xSetPPU() is called to read from $21xx // Take care of DMA wrapping if (Address > 0x21ff) Address = 0x2100 + (Address & 0xff); } } #ifdef DEBUGGER if (CPU.InHDMA) S9xTraceFormattedMessage("--- HDMA PPU %04X -> %02X", Address, Byte); #endif if (Settings.MSU1 && (Address & 0xfff8) == 0x2000) // MSU-1 S9xMSU1WritePort(Address & 7, Byte); else if ((Address & 0xffc0) == 0x2140) // APUIO0, APUIO1, APUIO2, APUIO3 // write_port will run the APU until given clock before writing value S9xAPUWritePort(Address & 3, Byte); else if (Address <= 0x2183) { switch (Address) { case 0x2100: // INIDISP if (Byte != Memory.FillRAM[0x2100]) { FLUSH_REDRAW(); if (PPU.Brightness != (Byte & 0xf)) { IPPU.ColorsChanged = TRUE; IPPU.DirectColourMapsNeedRebuild = TRUE; PPU.Brightness = Byte & 0xf; S9xFixColourBrightness(); if (PPU.Brightness > IPPU.MaxBrightness) IPPU.MaxBrightness = PPU.Brightness; } if ((Memory.FillRAM[0x2100] & 0x80) != (Byte & 0x80)) { IPPU.ColorsChanged = TRUE; PPU.ForcedBlanking = (Byte >> 7) & 1; } } if ((Memory.FillRAM[0x2100] & 0x80) && CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) { 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; }
void S9xMainLoop (void) { #define CHECK_FOR_IRQ_CHANGE() \ if (Timings.IRQFlagChanging) \ { \ if (Timings.IRQFlagChanging & IRQ_TRIGGER_NMI) \ { \ CPU.NMIPending = TRUE; \ Timings.NMITriggerPos = CPU.Cycles + 6; \ } \ if (Timings.IRQFlagChanging & IRQ_CLEAR_FLAG) \ ClearIRQ(); \ else if (Timings.IRQFlagChanging & IRQ_SET_FLAG) \ SetIRQ(); \ Timings.IRQFlagChanging = IRQ_NONE; \ } if (CPU.Flags & SCAN_KEYS_FLAG) { CPU.Flags &= ~SCAN_KEYS_FLAG; S9xMovieUpdate(); } for (;;) { if (CPU.NMIPending) { #ifdef DEBUGGER if (Settings.TraceHCEvent) S9xTraceFormattedMessage ("Comparing %d to %d\n", Timings.NMITriggerPos, CPU.Cycles); #endif if (Timings.NMITriggerPos <= CPU.Cycles) { CPU.NMIPending = FALSE; Timings.NMITriggerPos = 0xffff; if (CPU.WaitingForInterrupt) { CPU.WaitingForInterrupt = FALSE; Registers.PCw++; CPU.Cycles += TWO_CYCLES + ONE_DOT_CYCLE / 2; while (CPU.Cycles >= CPU.NextEvent) S9xDoHEventProcessing(); } CHECK_FOR_IRQ_CHANGE(); S9xOpcode_NMI(); } } if (CPU.Cycles >= Timings.NextIRQTimer) { #ifdef DEBUGGER S9xTraceMessage ("Timer triggered\n"); #endif S9xUpdateIRQPositions(false); CPU.IRQLine = TRUE; } if (CPU.IRQLine || CPU.IRQExternal) { if (CPU.WaitingForInterrupt) { CPU.WaitingForInterrupt = FALSE; Registers.PCw++; CPU.Cycles += TWO_CYCLES + ONE_DOT_CYCLE / 2; while (CPU.Cycles >= CPU.NextEvent) S9xDoHEventProcessing(); } if (!CheckFlag(IRQ)) { /* The flag pushed onto the stack is the new value */ CHECK_FOR_IRQ_CHANGE(); S9xOpcode_IRQ(); } } /* Change IRQ flag for instructions that set it only on last cycle */ CHECK_FOR_IRQ_CHANGE(); #ifdef DEBUGGER if ((CPU.Flags & BREAK_FLAG) && !(CPU.Flags & SINGLE_STEP_FLAG)) { for (int Break = 0; Break != 6; Break++) { if (S9xBreakpoint[Break].Enabled && S9xBreakpoint[Break].Bank == Registers.PB && S9xBreakpoint[Break].Address == Registers.PCw) { if (S9xBreakpoint[Break].Enabled == 2) S9xBreakpoint[Break].Enabled = TRUE; else CPU.Flags |= DEBUG_MODE_FLAG; } } } if (CPU.Flags & DEBUG_MODE_FLAG) break; if (CPU.Flags & TRACE_FLAG) S9xTrace(); if (CPU.Flags & SINGLE_STEP_FLAG) { CPU.Flags &= ~SINGLE_STEP_FLAG; CPU.Flags |= DEBUG_MODE_FLAG; } #endif if (CPU.Flags & SCAN_KEYS_FLAG) { #ifdef DEBUGGER if (!(CPU.Flags & FRAME_ADVANCE_FLAG)) #endif { S9xSyncSpeed(); } break; } uint8 Op; struct SOpcodes *Opcodes; if (CPU.PCBase) { Op = CPU.PCBase[Registers.PCw]; CPU.Cycles += CPU.MemSpeed; Opcodes = ICPU.S9xOpcodes; } else { Op = S9xGetByte(Registers.PBPC); OpenBus = Op; Opcodes = S9xOpcodesSlow; } if ((Registers.PCw & MEMMAP_MASK) + ICPU.S9xOpLengths[Op] >= MEMMAP_BLOCK_SIZE) { uint8 *oldPCBase = CPU.PCBase; CPU.PCBase = S9xGetBasePointer(ICPU.ShiftedPB + ((uint16) (Registers.PCw + 4))); if (oldPCBase != CPU.PCBase || (Registers.PCw & ~MEMMAP_MASK) == (0xffff & ~MEMMAP_MASK)) Opcodes = S9xOpcodesSlow; } Registers.PCw++; (*Opcodes[Op].S9xOpcode)(); if (Settings.SA1) S9xSA1MainLoop(); } S9xPackStatus(); }