Exemplo n.º 1
0
        /// <summary>
        /// Starts the machine in a background thread.
        /// </summary>
        /// <param name="options">Options to start the machine with.</param>
        /// <remarks>
        /// Reports completion when the machine starts executing its cycles. The machine can
        /// go into Paused or Stopped state, if the execution options allow, for example,
        /// when it runs to a predefined breakpoint.
        /// </remarks>
        public void Start(ExecuteCycleOptions options)
        {
            if (VmState == VmState.Running)
            {
                return;
            }

            // --- Prepare the machine to run
            IsFirstStart = VmState == VmState.None || VmState == VmState.Stopped;
            SpectrumVm.DebugInfoProvider?.PrepareBreakpoints();
            if (IsFirstStart)
            {
                SpectrumVm.Reset();
                SpectrumVm.Cpu.StackDebugSupport.ClearStepOutStack();
                SpectrumVm.DebugInfoProvider?.ResetHitCounts();
            }

            // --- Dispose the previous cancellation token, and create a new one
            CancellationTokenSource?.Dispose();
            CancellationTokenSource = new CancellationTokenSource();

            // --- Set up the task that runs the machine
            CompletionTask = new Task(async() =>
            {
                Cancelled            = false;
                ExecutionCycleResult = false;
                try
                {
                    ExecutionCycleResult = SpectrumVm.ExecuteCycle(CancellationTokenSource.Token, options);
                }
                catch (TaskCanceledException)
                {
                    Cancelled = true;
                }
                catch (Exception ex)
                {
                    ExecutionCycleException = ex;
                }

                // --- Conclude the execution task
                await ExecuteOnMainThread(() =>
                {
                    MoveToState(VmState == VmState.Stopping ||
                                VmState == VmState.Stopped ||
                                ExecutionCycleException != null
                            ? VmState.Stopped
                            : VmState.Paused);

                    if (ExecutionCycleException != null)
                    {
                        VmStoppedWithException?.Invoke(this, EventArgs.Empty);
                    }
                });
            });

            MoveToState(VmState.Running);
            CompletionTask.Start();
        }
Exemplo n.º 2
0
        /// <summary>
        /// Starts the machine in a background thread with the specified options.
        /// </summary>
        /// <remarks>
        /// Reports completion when the machine starts executing its cycles. The machine can
        /// go into Paused or Stopped state, if the execution options allow, for example,
        /// when it runs to a predefined breakpoint.
        /// </remarks>
        public void StartWithOptions(ExecuteCycleOptions options)
        {
            if (MachineState == VmState.Running)
            {
                return;
            }

            // --- Prepare the machine to run
            IsFirstStart = MachineState == VmState.None || MachineState == VmState.Stopped;
            SpectrumVm.DebugInfoProvider?.PrepareBreakpoints();
            MachineState = VmState.Starting;
            if (IsFirstStart)
            {
                SpectrumVm.Reset();
                SpectrumVm.Cpu.StackDebugSupport.Reset();
                SpectrumVm.DebugInfoProvider?.ResetHitCounts();
                CpuFrameCount    = 0;
                RenderFrameCount = 0;
            }

            // --- Dispose the previous cancellation token, and create a new one
            _cancellationTokenSource?.Dispose();
            _cancellationTokenSource = new CancellationTokenSource();

            // --- Set up the task that runs the machine
            MachineState = VmState.Running;
            try
            {
                _completionTask = StartAndRun(_cancellationTokenSource.Token, options);
                _completionTask.GetAwaiter().OnCompleted(async() =>
                {
                    await WaitForPause();
                });
            }
            catch (TaskCanceledException)
            {
                ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
            }
            catch (Exception ex)
            {
                ExceptionRaised?.Invoke(this, new VmExceptionArgs(ex));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Builds the machine that can be started
        /// </summary>
        protected virtual void BuildMachine()
        {
            if (SpectrumVm == null)
            {
                MoveToState(VmState.BuildingMachine);

                if (StartupConfiguration == null)
                {
                    throw new InvalidOperationException("You must provide a startup configuration for " +
                                                        "the virtual machine, it cannot be null");
                }

                // --- Create the machine on first start
                SpectrumVm = new Spectrum48(StartupConfiguration.DeviceData, this);
                SpectrumVm.ScreenDevice.FrameCompleted +=
                    (s, e) => VmScreenRefreshed?.Invoke(s,
                                                        new VmScreenRefreshedEventArgs(SpectrumVm.ScreenDevice.GetPixelBuffer()));
            }

            // --- We either provider out DebugInfoProvider, or use
            // --- the default one
            if (StartupConfiguration.DebugInfoProvider == null)
            {
                StartupConfiguration.DebugInfoProvider = SpectrumVm.DebugInfoProvider;
            }
            else
            {
                SpectrumVm.DebugInfoProvider = StartupConfiguration.DebugInfoProvider;
            }
            // --- Set up stack debug support
            if (StartupConfiguration.StackDebugSupport != null)
            {
                SpectrumVm.Cpu.StackDebugSupport = StartupConfiguration.StackDebugSupport;
                StartupConfiguration.StackDebugSupport.Reset();
            }

            // --- At this point we have a Spectrum VM.
            // --- Let's reset it
            SpectrumVm.Reset();
        }
Exemplo n.º 4
0
        /// <summary>
        /// Starts the virtual machine with the provided options
        /// </summary>
        /// <param name="options">The execution cycle options to start with</param>
        public void StartVm(ExecuteCycleOptions options)
        {
            if (VmState == VmState.Running)
            {
                return;
            }

            IsFirstStart = VmState == VmState.None ||
                           VmState == VmState.BuildingMachine ||
                           VmState == VmState.Stopped;
            if (IsFirstStart)
            {
                EnsureMachine();
            }

            // --- We allow this event to execute something on the main thread
            // --- right before the execution cycle starts
            MoveToState(VmState.BeforeRun);

            // --- Dispose the previous cancellation token, and create a new one
            CancellationTokenSource?.Dispose();
            CancellationTokenSource = new CancellationTokenSource();

            // --- We use the completion source to sign that the VM's execution cycle is done
            _vmStarterCompletionSource = new TaskCompletionSource <bool>();
            _executionCompletionSource = new TaskCompletionSource <bool>();

            // --- Get the exception of the execution cycle
            Exception exDuringRun = null;

            // --- Allow the controller to save its current scheduler context
            SaveMainContext();
            SpectrumVm.DebugInfoProvider?.PrepareBreakpoints();

            Task.Factory.StartNew(ExecutionAction,
                                  CancellationTokenSource.Token,
                                  TaskCreationOptions.LongRunning,
                                  TaskScheduler.Current);

            // --- Execute the VM cycle
            async void ExecutionAction()
            {
                try
                {
                    SpectrumVm.ExecuteCycle(CancellationTokenSource.Token, options);
                }
                catch (TaskCanceledException)
                {
                }
                catch (Exception ex)
                {
                    exDuringRun = ex;
                }

                // --- Forget about the cancellation token
                CancellationTokenSource?.Dispose();
                CancellationTokenSource = null;

                // --- Go back to the main thread before setting up new state
                await ExecuteOnMainThread(() =>
                {
                    // --- Calculate next state
                    MoveToState(VmState == VmState.Stopping || exDuringRun != null
                        ? VmState.Stopped
                        : VmState.Paused);

                    // --- Conclude the execution task
                    if (exDuringRun == null)
                    {
                        _executionCompletionSource.SetResult(true);
                    }
                    else
                    {
                        _executionCompletionSource.SetException(exDuringRun);
                        OnVmStoppedWithException(exDuringRun);
                    }
                });
            }
        }
Exemplo n.º 5
0
        /// <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);
        }