public override void ComputeNextFrame(FrameBuffer frameBuffer) { base.ComputeNextFrame(frameBuffer); AssertDebug(CPU.Jammed || CPU.RunClocks <= 0 && (CPU.RunClocks % CPU.RunClocksMultiple) == 0); AssertDebug(CPU.Jammed || ((CPU.Clock + (ulong)(CPU.RunClocks / CPU.RunClocksMultiple)) % (114 * (ulong)FrameBuffer.Scanlines)) == 0); ulong startOfScanlineCpuClock = 0; Maria.StartFrame(); Cart.StartFrame(); for (var i = 0; i < FrameBuffer.Scanlines && !CPU.Jammed; i++) { AssertDebug(CPU.RunClocks <= 0 && (CPU.RunClocks % CPU.RunClocksMultiple) == 0); var newStartOfScanlineCpuClock = CPU.Clock + (ulong)(CPU.RunClocks / CPU.RunClocksMultiple); AssertDebug(startOfScanlineCpuClock == 0 || newStartOfScanlineCpuClock == startOfScanlineCpuClock + 114); startOfScanlineCpuClock = newStartOfScanlineCpuClock; CPU.RunClocks += (7 * CPU.RunClocksMultiple); var remainingRunClocks = (114 - 7) * CPU.RunClocksMultiple; CPU.Execute(); if (CPU.Jammed) { break; } if (CPU.EmulatorPreemptRequest) { Maria.DoDMAProcessing(); var remainingCpuClocks = 114 - (CPU.Clock - startOfScanlineCpuClock); CPU.Clock += remainingCpuClocks; CPU.RunClocks = 0; continue; } var dmaClocks = Maria.DoDMAProcessing(); // CHEAT: Ace of Aces: Title screen has a single scanline flicker without this. Maria DMA clock counting probably not 100% accurate. if (i == 203 && FrameBuffer.Scanlines == 262 /*NTSC*/ || i == 228 && FrameBuffer.Scanlines == 312 /*PAL*/) { if (dmaClocks == 152 && remainingRunClocks == 428 && (CPU.RunClocks == -4 || CPU.RunClocks == -8)) { dmaClocks -= 4; } } // Unsure exactly what to do if Maria DMA processing extends past the current scanline. // For now, throw away half remaining until we are within the current scanline. // KLAX initialization starts DMA without initializing the DLL data structure. // Maria processing then runs away causing an invalid CPU opcode to be executed that jams the machine. // So Maria must give up at some point, but not clear exactly how. // Anyway, this makes KLAX work without causing breakage elsewhere. while ((CPU.RunClocks + remainingRunClocks) < dmaClocks) { dmaClocks >>= 1; } // Assume the CPU waits until the next div4 boundary to proceed after DMA processing. if ((dmaClocks & 3) != 0) { dmaClocks += 4; dmaClocks -= (dmaClocks & 3); } CPU.Clock += (ulong)(dmaClocks / CPU.RunClocksMultiple); CPU.RunClocks -= dmaClocks; CPU.RunClocks += remainingRunClocks; CPU.Execute(); if (CPU.Jammed) { break; } if (CPU.EmulatorPreemptRequest) { var remainingCpuClocks = 114 - (CPU.Clock - startOfScanlineCpuClock); CPU.Clock += remainingCpuClocks; CPU.RunClocks = 0; } } Cart.EndFrame(); Maria.EndFrame(); }