/// <summary> /// Starts the virtual machine and runs it until the execution cycle is completed for /// a reason. /// </summary> /// <param name="cancellationToken">Cancellation token</param> /// <returns>The reason why the execution completed.</returns> public async Task RenderShadowScreen(CancellationToken cancellationToken) { var lastRunStart = _clockProvider.GetCounter(); var lastRenderFrameStart = lastRunStart; SpectrumVm.ShadowScreenDevice.FrameCount = SpectrumVm.ScreenDevice.FrameCount; SpectrumVm.ShadowScreenDevice.BorderColor = SpectrumVm.ScreenDevice.BorderColor; var frameCount = 0; while (!cancellationToken.IsCancellationRequested) { var lastFrameEnd = _clockProvider.GetCounter(); LastRenderFrameTicks = lastFrameEnd - lastRenderFrameStart; lastRenderFrameStart = lastFrameEnd; frameCount++; // --- Do additional task when render frame completed var screenTacts = SpectrumVm.ScreenConfiguration.ScreenRenderingFrameTactCount; SpectrumVm.ShadowScreenDevice.RenderScreen(0, screenTacts - 1); SpectrumVm.ShadowScreenDevice.OnNewFrame(); var renderFrameArgs = new RenderFrameEventArgs(SpectrumVm.ShadowScreenDevice.GetPixelBuffer()); RenderFrameCompleted?.Invoke(this, renderFrameArgs); if (renderFrameArgs.Cancel) { return; } var waitInTicks = lastRunStart + frameCount * _physicalFrameClockCount - _clockProvider.GetCounter() - _physicalFrameClockCount * 0.2; var waitInMs = 1000.0 * waitInTicks / _clockProvider.GetFrequency(); if (waitInMs > 0) { await Task.Delay((int)waitInMs, cancellationToken); if (cancellationToken.IsCancellationRequested) { return; } } else { await Task.Delay(1, cancellationToken); } } }
/// <summary> /// Starts the virtual machine and runs it until the execution cycle is completed for /// a reason. /// </summary> /// <param name="cancellationToken">Cancellation token</param> /// <param name="options">Virtual machine execution options</param> /// <returns>The reason why the execution completed.</returns> private async Task <ExecutionCompletionReason> StartAndRun(CancellationToken cancellationToken, ExecuteCycleOptions options) { LastExecutionContentionValue = ContentionAccumulated; var lastRunStart = _clockProvider.GetCounter(); var lastRenderFrameStart = lastRunStart; var frameCount = 0; var completed = false; while (!completed) { // --- Execute a single CPU Frame var lastCpuFrameStart = _clockProvider.GetCounter(); var cycleCompleted = SpectrumVm.ExecuteCycle(cancellationToken, options, true); LastCpuFrameTicks = _clockProvider.GetCounter() - lastCpuFrameStart; if (!cycleCompleted) { return(ExecutionCompletionReason.Cancelled); } // --- Check for emulated keys CpuFrameCount++; var hasEmulatedKey = SpectrumVm.KeyboardProvider?.EmulateKeyStroke(); if (hasEmulatedKey != true) { // --- Keyboard scan var keyStatusArgs = new KeyStatusEventArgs(); KeyScanning?.Invoke(this, keyStatusArgs); foreach (var keyStatus in keyStatusArgs.KeyStatusList) { SpectrumVm.KeyboardDevice.SetStatus(keyStatus); } } // --- Do additional tasks when CPU frame completed var cancelArgs = new CancelEventArgs(false); CpuFrameCompleted?.Invoke(this, cancelArgs); if (cancelArgs.Cancel) { return(ExecutionCompletionReason.Cancelled); } switch (SpectrumVm.ExecutionCompletionReason) { case ExecutionCompletionReason.TerminationPointReached: case ExecutionCompletionReason.BreakpointReached: case ExecutionCompletionReason.Halted: case ExecutionCompletionReason.Exception: completed = true; break; case ExecutionCompletionReason.RenderFrameCompleted: var lastFrameEnd = _clockProvider.GetCounter(); LastRenderFrameTicks = lastFrameEnd - lastRenderFrameStart; lastRenderFrameStart = lastFrameEnd; completed = options.EmulationMode == EmulationMode.UntilRenderFrameEnds; frameCount++; RenderFrameCount++; // --- Do additional task when render frame completed var renderFrameArgs = new RenderFrameEventArgs(SpectrumVm.ScreenDevice.GetPixelBuffer()); RenderFrameCompleted?.Invoke(this, renderFrameArgs); if (renderFrameArgs.Cancel) { return(ExecutionCompletionReason.Cancelled); } // --- Wait for the next render frame, unless completed if (!completed) { var waitInTicks = lastRunStart + frameCount * _physicalFrameClockCount - _clockProvider.GetCounter() - _physicalFrameClockCount * 0.2; var waitInMs = 1000.0 * waitInTicks / _clockProvider.GetFrequency(); if (waitInMs > 0) { await Task.Delay((int)waitInMs, cancellationToken); if (cancellationToken.IsCancellationRequested) { return(ExecutionCompletionReason.Cancelled); } } else { await Task.Delay(1, cancellationToken); } } break; } } // --- Done, pass back the reason of completing the run return(SpectrumVm.ExecutionCompletionReason); }