/// <summary> /// Computes the next machine frame, updating contents of the provided <see cref="FrameBuffer"/>. /// </summary> /// <param name="frameBuffer">The framebuffer to contain the computed output.</param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException">frameBuffer is incompatible with machine.</exception> public virtual void ComputeNextFrame(FrameBuffer frameBuffer) { if (MachineHalt) { return; } InputState.CaptureInputState(); _FrameBuffer = frameBuffer; FrameNumber++; for (var i = 0; i < _FrameBuffer.SoundBufferByteLength; i++) { _FrameBuffer.SoundBuffer[i] = 0; } }
public override void ComputeNextFrame(FrameBuffer frameBuffer) { base.ComputeNextFrame(frameBuffer); TIA.StartFrame(); CPU.RunClocks = (FrameBuffer.Scanlines + 3) * 76; while (CPU.RunClocks > 0 && !CPU.Jammed) { if (TIA.WSYNCDelayClocks > 0) { CPU.Clock += (ulong)TIA.WSYNCDelayClocks / 3; CPU.RunClocks -= TIA.WSYNCDelayClocks / 3; TIA.WSYNCDelayClocks = 0; } if (TIA.EndOfFrame) { break; } CPU.Execute(); } TIA.EndFrame(); }
/// <summary> /// Create a <see cref="FrameBuffer"/> with compatible dimensions for this machine. /// </summary> public FrameBuffer CreateFrameBuffer() { var fb = new FrameBuffer(_VisiblePitch, _Scanlines); return fb; }
/// <summary> /// Computes the next machine frame, updating contents of the provided <see cref="FrameBuffer"/>. /// </summary> /// <param name="frameBuffer">The framebuffer to contain the computed output.</param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException">frameBuffer is incompatible with machine.</exception> public virtual void ComputeNextFrame(FrameBuffer frameBuffer) { if (MachineHalt) return; InputState.CaptureInputState(); _FrameBuffer = frameBuffer; FrameNumber++; for (var i = 0; i < _FrameBuffer.SoundBufferByteLength; i++) _FrameBuffer.SoundBuffer[i] = 0; }
public void ConnectToMachine(MachineBase m, EMU7800.Win.GameProgram g) { framebuffer = m.CreateFrameBuffer(); BufferWidth = framebuffer.VisiblePitch; BufferHeight = framebuffer.Scanlines; vidbuffer = new int[BufferWidth * BufferHeight]; uint newsamplerate = (uint)m.SoundSampleFrequency; if (newsamplerate != samplerate) { // really shouldn't happen (after init), but if it does, we're ready if (resampler != null) resampler.Dispose(); resampler = new SpeexResampler(3, newsamplerate, 44100, newsamplerate, 44100, null, null); samplerate = newsamplerate; dcfilter = DCFilter.DetatchedMode(256); } if (g.MachineType == MachineType.A7800PAL || g.MachineType == MachineType.A2600PAL) palette = TIATables.PALPalette; else palette = TIATables.NTSCPalette; }
/// <summary> /// Draw specified text at specified position using the specified foreground and background colors. /// </summary> /// <param name="frameBuffer"></param> /// <param name="text"></param> /// <param name="xoffset"></param> /// <param name="yoffset"></param> /// <param name="fore"></param> /// <param name="back"></param> /// <exception cref="ArgumentNullException">text must be non-null.</exception> public void DrawText(FrameBuffer frameBuffer, string text, int xoffset, int yoffset, byte fore, byte back) { if (text == null) { throw new ArgumentNullException("text"); } var textchars = text.ToUpper().ToCharArray(); for (var i = 0; i < text.Length + 1; i++) { for (var j = 0; j < 9; j++) { var pos = (j + yoffset) * frameBuffer.VisiblePitch + i * 5; for (var k = 0; k < 5; k++) { while (pos >= frameBuffer.VideoBufferByteLength) { pos -= frameBuffer.VideoBufferByteLength; } while (pos < 0) { pos += frameBuffer.VideoBufferByteLength; } frameBuffer.VideoBuffer[pos++] = back; } } } for (var i = 0; i < text.Length; i++) { var c = textchars[i]; uint fdata; switch (c) { case '/': case '\\': fdata = 0x0122448; break; case '(': fdata = 0x2488842; break; case ')': fdata = 0x4211124; break; case '.': fdata = 0x0000066; break; case ':': fdata = 0x0660660; break; case '-': fdata = 0x0007000; break; default: if (c >= 'A' && c <= 'Z') { fdata = AlphaFontData[c - 'A']; } else if (c >= '0' && c <= '9') { fdata = DigitFontData[c - '0']; } else { fdata = 0; } break; } var ypos = 8; for (var j = 0; j < 32; j++) { var xpos = j & 3; if (xpos == 0) { ypos--; } var pos = (ypos + yoffset) * frameBuffer.VisiblePitch + (4 - xpos) + xoffset; while (pos >= frameBuffer.VideoBufferByteLength) { pos -= frameBuffer.VideoBufferByteLength; } while (pos < 0) { pos += frameBuffer.VideoBufferByteLength; } if (((fdata >> j) & 1) != 0) { frameBuffer.VideoBuffer[pos] = fore; } } xoffset += 5; } }
/// <summary> /// Draw specified text at specified position using the specified foreground and background colors. /// </summary> /// <param name="frameBuffer"></param> /// <param name="text"></param> /// <param name="xoffset"></param> /// <param name="yoffset"></param> /// <param name="fore"></param> /// <param name="back"></param> /// <exception cref="ArgumentNullException">text must be non-null.</exception> public void DrawText(FrameBuffer frameBuffer, string text, int xoffset, int yoffset, byte fore, byte back) { if (text == null) throw new ArgumentNullException("text"); var textchars = text.ToUpper().ToCharArray(); for (var i = 0; i < text.Length + 1; i++) { for (var j = 0; j < 9; j++) { var pos = (j + yoffset) * frameBuffer.VisiblePitch + i * 5; for (var k = 0; k < 5; k++) { while (pos >= frameBuffer.VideoBufferByteLength) { pos -= frameBuffer.VideoBufferByteLength; } while (pos < 0) { pos += frameBuffer.VideoBufferByteLength; } frameBuffer.VideoBuffer[pos++] = back; } } } for (var i = 0; i < text.Length; i++) { var c = textchars[i]; uint fdata; switch (c) { case '/': case '\\': fdata = 0x0122448; break; case '(': fdata = 0x2488842; break; case ')': fdata = 0x4211124; break; case '.': fdata = 0x0000066; break; case ':': fdata = 0x0660660; break; case '-': fdata = 0x0007000; break; default: if (c >= 'A' && c <= 'Z') { fdata = AlphaFontData[c - 'A']; } else if (c >= '0' && c <= '9') { fdata = DigitFontData[c - '0']; } else { fdata = 0; } break; } var ypos = 8; for (var j = 0; j < 32; j++) { var xpos = j & 3; if (xpos == 0) { ypos--; } var pos = (ypos + yoffset) * frameBuffer.VisiblePitch + (4 - xpos) + xoffset; while (pos >= frameBuffer.VideoBufferByteLength) { pos -= frameBuffer.VideoBufferByteLength; } while (pos < 0) { pos += frameBuffer.VideoBufferByteLength; } if (((fdata >> j) & 1) != 0) { frameBuffer.VideoBuffer[pos] = fore; } } xoffset += 5; } }
/// <summary> /// Create a <see cref="FrameBuffer"/> with compatible dimensions for this machine. /// </summary> public FrameBuffer CreateFrameBuffer() { var fb = new FrameBuffer(_VisiblePitch, _Scanlines); return(fb); }
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(); }