Exemplo n.º 1
0
        public void InterruptIsNotRereaised()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);
            idev.CheckForInterrupt(TEST_INT_TACT);

            // --- Act
            vm.Cpu.StateFlags &= Z80StateFlags.InvInt;
            idev.CheckForInterrupt(TEST_INT_TACT + 1);

            // --- Assert
            idev.InterruptRaised.ShouldBeTrue();
            idev.InterruptRevoked.ShouldBeFalse();
            (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.None);
            idev.FrameCount.ShouldBe(1);
        }
Exemplo n.º 2
0
        public void InterruptIsRaisedWithinAllowedRange()
        {
            for (var tact = TEST_INT_TACT; tact <= TEST_INT_TACT + InterruptDevice.LONGEST_OP_TACTS; tact++)
            {
                // --- Arrange
                var vm   = new SpectrumAdvancedTestMachine();
                var idev = new InterruptDevice(TEST_INT_TACT);
                idev.OnAttachedToVm(vm);

                // --- Act/Assert
                idev.CheckForInterrupt(tact);
                idev.InterruptRaised.ShouldBeTrue();
                idev.InterruptRevoked.ShouldBeFalse();
                idev.FrameCount.ShouldBe(1);
                (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.Int);
            }
        }
Exemplo n.º 3
0
        public void InterruptNotRaisedTooEarly()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);

            // --- Act/Assert
            for (var tact = 0; tact < TEST_INT_TACT; tact++)
            {
                idev.CheckForInterrupt(tact);
                idev.InterruptRaised.ShouldBeFalse();
                idev.InterruptRevoked.ShouldBeFalse();
                idev.FrameCount.ShouldBe(0);
                (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.None);
            }
        }
Exemplo n.º 4
0
        public void InterruptIsNotRaisedTooLate()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);

            // --- Act/Assert
            var lateTact = TEST_INT_TACT + InterruptDevice.LONGEST_OP_TACTS + 1;

            for (var tact = lateTact; tact < lateTact + 10; tact++)
            {
                idev.CheckForInterrupt(tact);
                idev.InterruptRaised.ShouldBeFalse();
                idev.InterruptRevoked.ShouldBeTrue();
                idev.FrameCount.ShouldBe(0);
                (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.None);
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// The main execution cycle of the Spectrum VM
        /// </summary>
        /// <param name="token">Cancellation token</param>
        /// <param name="options">Execution options</param>
        /// <return>True, if the cycle completed; false, if it has been cancelled</return>
        public bool ExecuteCycle(CancellationToken token, ExecuteCycleOptions options)
        {
            ExecuteCycleOptions          = options;
            ExecutionCompletionReason    = ExecutionCompletionReason.None;
            LastExecutionStartTact       = Cpu.Tacts;
            LastExecutionContentionValue = ContentionAccumulated;

            // --- We use these variables to calculate wait time at the end of the frame
            var cycleStartTime  = Clock.GetCounter();
            var cycleStartTact  = Cpu.Tacts;
            var cycleFrameCount = 0;

            // --- We use this variable to check whether to stop in Debug mode
            var executedInstructionCount = -1;

            // --- Loop #1: The main cycle that goes on until cancelled
            while (!token.IsCancellationRequested)
            {
                if (_frameCompleted)
                {
                    // --- This counter helps us to calculate where we are in the frame after
                    // --- each CPU operation cycle
                    LastFrameStartCpuTick = Cpu.Tacts - Overflow;

                    // --- Notify devices to start a new frame
                    OnNewFrame();
                    LastRenderedUlaTact = Overflow;
                    _frameCompleted     = false;
                }

                // --- Loop #2: The physical frame cycle that goes on while CPU and ULA
                // --- processes everything whithin a physical frame (0.019968 second)
                while (!_frameCompleted)
                {
                    // --- Check for leaving maskable interrupt mode
                    if (RunsInMaskableInterrupt)
                    {
                        if (Cpu.Registers.PC == 0x0052)
                        {
                            // --- We leave the maskable interrupt mode when the
                            // --- current instruction completes
                            RunsInMaskableInterrupt = false;
                        }
                    }

                    // --- Check debug mode when a CPU instruction has been entirelly executed
                    if (!Cpu.IsInOpExecution)
                    {
                        // --- Check for cancellation
                        if (token.IsCancellationRequested)
                        {
                            ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
                            return(false);
                        }

                        // --- The next instruction is about to be executed
                        executedInstructionCount++;

                        // --- Check for timeout
                        if (options.TimeoutTacts > 0 &&
                            cycleStartTact + options.TimeoutTacts < Cpu.Tacts)
                        {
                            ExecutionCompletionReason = ExecutionCompletionReason.Timeout;
                            return(false);
                        }

                        // --- Check for reaching the termination point
                        if (options.EmulationMode == EmulationMode.UntilExecutionPoint)
                        {
                            if (options.TerminationPoint < 0x4000)
                            {
                                // --- ROM & address must match
                                if (options.TerminationRom == MemoryDevice.GetSelectedRomIndex() &&
                                    options.TerminationPoint == Cpu.Registers.PC)
                                {
                                    // --- We reached the termination point within ROM
                                    ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                                    return(true);
                                }
                            }
                            else if (options.TerminationPoint == Cpu.Registers.PC)
                            {
                                // --- We reached the termination point within RAM
                                ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                                return(true);
                            }
                        }

                        // --- Check for entering maskable interrupt mode
                        if (Cpu.MaskableInterruptModeEntered)
                        {
                            RunsInMaskableInterrupt = true;
                        }

                        // --- Check for a debugging stop point
                        if (options.EmulationMode == EmulationMode.Debugger)
                        {
                            if (IsDebugStop(options, executedInstructionCount))
                            {
                                // --- At this point, the cycle should be stopped because of debugging reasons
                                // --- The screen should be refreshed
                                ScreenDevice.OnFrameCompleted();
                                ExecutionCompletionReason = ExecutionCompletionReason.BreakpointReached;
                                return(true);
                            }
                        }
                    }

                    // --- Check for interrupt signal generation
                    InterruptDevice.CheckForInterrupt(CurrentFrameTact);

                    // --- Run a single Z80 instruction
                    Cpu.ExecuteCpuCycle();
                    _lastBreakpoint = null;

                    // --- Run a rendering cycle according to the current CPU tact count
                    var lastTact = CurrentFrameTact;
                    ScreenDevice.RenderScreen(LastRenderedUlaTact + 1, lastTact);
                    LastRenderedUlaTact = lastTact;

                    // --- Exit if the emulation mode specifies so
                    if (options.EmulationMode == EmulationMode.UntilHalt &&
                        (Cpu.StateFlags & Z80StateFlags.Halted) != 0)
                    {
                        ExecutionCompletionReason = ExecutionCompletionReason.Halted;
                        return(true);
                    }

                    // --- Notify each CPU-bound device that the current operation has been completed
                    foreach (var device in _cpuBoundDevices)
                    {
                        device.OnCpuOperationCompleted();
                    }

                    // --- Decide whether this frame has been completed
                    _frameCompleted = !Cpu.IsInOpExecution && CurrentFrameTact >= _frameTacts;
                } // -- End Loop #2

                // --- A physical frame has just been completed. Take care about screen refresh
                cycleFrameCount++;
                FrameCount++;

                // --- Notify devices that the current frame completed
                OnFrameCompleted();

                // --- Exit if the emulation mode specifies so
                if (options.EmulationMode == EmulationMode.UntilFrameEnds)
                {
                    ExecutionCompletionReason = ExecutionCompletionReason.FrameCompleted;
                    return(true);
                }

                // --- Wait while the frame time ellapses
                if (!ExecuteCycleOptions.FastVmMode)
                {
                    var nextFrameCounter = cycleStartTime + cycleFrameCount * PhysicalFrameClockCount;
                    Clock.WaitUntil((long)nextFrameCounter, token);
                }

                // --- Start a new frame and carry on
                Overflow = CurrentFrameTact % _frameTacts;
            } // --- End Loop #1

            // --- The cycle has been interrupted by cancellation
            ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
            return(false);
        }
Exemplo n.º 6
0
        /// <summary>
        /// The main execution cycle of the Spectrum VM
        /// </summary>
        /// <param name="token">Cancellation token</param>
        /// <param name="options">Execution options</param>
        /// <param name="completeOnCpuFrame">The cycle should complete on CPU frame completion</param>
        /// <return>True, if the cycle completed; false, if it has been cancelled</return>
        public bool ExecuteCycle(CancellationToken token, ExecuteCycleOptions options, bool completeOnCpuFrame = false)
        {
            ExecuteCycleOptions       = options;
            ExecutionCompletionReason = ExecutionCompletionReason.None;
            LastExecutionStartTact    = Cpu.Tacts;

            // --- We use this variables to check whether to stop in Debug mode
            var executedInstructionCount = -1;
            var entryStepOutDepth        = Cpu.StackDebugSupport.StepOutStackDepth;

            // --- Check if we're just start running the next frame
            if (HasFrameCompleted)
            {
                // --- This counter helps us to calculate where we are in the frame after
                // --- each CPU operation cycle
                LastFrameStartCpuTick = Cpu.Tacts - Overflow;

                // --- Notify devices to start a new frame
                OnNewFrame();
                LastRenderedUlaTact = Overflow;
                HasFrameCompleted   = false;
            }

            // --- The physical frame cycle that goes on while CPU and ULA
            // --- processes everything within a physical frame (0.019968 second)
            while (!HasFrameCompleted)
            {
                // --- Check debug mode when a CPU instruction has been entirely executed
                if (!Cpu.IsInOpExecution)
                {
                    // --- The next instruction is about to be executed
                    executedInstructionCount++;

                    // --- Check for cancellation
                    if (token.IsCancellationRequested)
                    {
                        ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
                        return(false);
                    }

                    // --- Check for CPU frame completion
                    if (completeOnCpuFrame && Cpu.Tacts > LastExecutionStartTact + CPU_FRAME)
                    {
                        ExecutionCompletionReason = ExecutionCompletionReason.CpuFrameCompleted;
                        return(true);
                    }

                    // --- Check for several termination modes
                    switch (options.EmulationMode)
                    {
                    // --- Check for reaching the termination point
                    case EmulationMode.UntilExecutionPoint when options.TerminationPoint < 0x4000:
                    {
                        // --- ROM & address must match
                        if (options.TerminationRom == MemoryDevice.GetSelectedRomIndex() &&
                            options.TerminationPoint == Cpu.Registers.PC)
                        {
                            // --- We reached the termination point within ROM
                            ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                            return(true);
                        }

                        break;
                    }

                    // --- Check if we reached the termination point within RAM
                    case EmulationMode.UntilExecutionPoint
                        when options.TerminationPoint == Cpu.Registers.PC:

                        ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                        return(true);

                    // --- Check for a debugging stop point
                    case EmulationMode.Debugger
                        when IsDebugStop(options, executedInstructionCount, entryStepOutDepth) &&
                        DebugConditionSatisfied():

                        // --- At this point, the cycle should be stopped because of debugging reasons
                        // --- The screen should be refreshed
                        ExecutionCompletionReason = ExecutionCompletionReason.BreakpointReached;

                        return(true);
                    }
                }

                // --- Check for interrupt signal generation
                InterruptDevice.CheckForInterrupt(CurrentFrameTact);

                // --- Run a single Z80 instruction
                Cpu.ExecuteCpuCycle();
                _lastBreakpoint = null;

                // --- Run a rendering cycle according to the current CPU tact count
                var lastTact = CurrentFrameTact;
                ScreenDevice.RenderScreen(LastRenderedUlaTact + 1, lastTact);
                LastRenderedUlaTact = lastTact;

                // --- Exit if the CPU is HALTed and the emulation mode specifies so
                if (options.EmulationMode == EmulationMode.UntilHalt &&
                    (Cpu.StateFlags & Z80StateFlags.Halted) != 0)
                {
                    ExecutionCompletionReason = ExecutionCompletionReason.Halted;
                    return(true);
                }

                // --- Notify each CPU-bound device that the current operation has been completed
                foreach (var device in _cpuBoundDevices)
                {
                    device.OnCpuOperationCompleted();
                }

                // --- Decide whether this frame has been completed
                HasFrameCompleted = !Cpu.IsInOpExecution && CurrentFrameTact >= FrameTacts;
            }

            // --- A physical frame has just been completed. Take care about screen refresh
            FrameCount++;
            Overflow = CurrentFrameTact % FrameTacts;

            // --- Notify devices that the current frame completed
            OnFrameCompleted();

            // --- We exit the cycle when the render frame has completed
            ExecutionCompletionReason = ExecutionCompletionReason.RenderFrameCompleted;
            return(true);
        }
Exemplo n.º 7
0
        /// <summary>
        /// The main execution cycle of the Spectrum VM
        /// </summary>
        /// <param name="token">Cancellation token</param>
        /// <param name="options">Execution options</param>
        /// <return>True, if the cycle completed; false, if it has been cancelled</return>
        public bool ExecuteCycle(CancellationToken token, ExecuteCycleOptions options)
        {
            ExecuteCycleOptions = options;

            // --- We use these variables to calculate wait time at the end of the frame
            var cycleStartTime  = Clock.GetCounter();
            var cycleFrameCount = 0;

            // --- We use this variable to check whether to stop in Debug mode
            var executedInstructionCount = -1;

            // --- Loop #1: The main cycle that goes on until cancelled
            while (!token.IsCancellationRequested)
            {
                if (_frameCompleted)
                {
                    // --- This counter helps us to calculate where we are in the frame after
                    // --- each CPU operation cycle
                    LastFrameStartCpuTick = Cpu.Tacts - Overflow;

                    // --- Notify devices to start a new frame
                    OnNewFrame();
                    LastRenderedUlaTact = Overflow;
                    _frameCompleted     = false;
                }

                // --- We use this timestamp for debug information calculation
                var currentFrameStartCounter = Clock.GetCounter();

                DebugInfoProvider.CpuTime             = 0;
                DebugInfoProvider.ScreenRenderingTime = 0;

                // --- Loop #2: The physical frame cycle that goes on while CPU and ULA
                // --- processes everything whithin a physical frame (0.019968 second)
                while (!_frameCompleted)
                {
                    // --- Check debug mode when a CPU instruction has been entirelly executed
                    if (!Cpu.IsInOpExecution)
                    {
                        // --- Check for cancellation
                        if (token.IsCancellationRequested)
                        {
                            return(false);
                        }

                        // --- The next instruction is about to be executed
                        executedInstructionCount++;

                        // --- Check for reaching the termination point
                        if (options.EmulationMode == EmulationMode.UntilExecutionPoint &&
                            options.TerminationPoint == Cpu.Registers.PC)
                        {
                            // --- We reached the termination point
                            return(true);
                        }

                        // --- Check for a debugging stop point
                        if (options.EmulationMode == EmulationMode.Debugger)
                        {
                            if (IsDebugStop(options.DebugStepMode, executedInstructionCount))
                            {
                                // --- At this point, the cycle should be stopped because of debugging reasons
                                // --- The screen should be refreshed
                                ScreenDevice.OnFrameCompleted();
                                return(true);
                            }
                        }
                    }

                    // --- Check for interrupt signal generation
                    InterruptDevice.CheckForInterrupt(CurrentFrameTact);

                    // --- Run a single Z80 instruction
                    var cpuStart = Clock.GetCounter();
                    Cpu.ExecuteCpuCycle();
                    DebugInfoProvider.CpuTime += Clock.GetCounter() - cpuStart;

                    // --- Run a rendering cycle according to the current CPU tact count
                    var lastTact    = CurrentFrameTact;
                    var renderStart = Clock.GetCounter();
                    ScreenDevice.RenderScreen(LastRenderedUlaTact + 1, lastTact);
                    DebugInfoProvider.ScreenRenderingTime += Clock.GetCounter() - renderStart;
                    LastRenderedUlaTact = lastTact;

                    // --- Exit if the emulation mode specifies so
                    if (options.EmulationMode == EmulationMode.UntilHalt &&
                        (Cpu.StateFlags & Z80StateFlags.Halted) != 0)
                    {
                        return(true);
                    }

                    // --- Notify each CPU-bound device that the current operation has been completed
                    foreach (var device in _cpuBoundDevices)
                    {
                        device.OnCpuOperationCompleted();
                    }

                    // --- Decide whether this frame has been completed
                    _frameCompleted = !Cpu.IsInOpExecution && CurrentFrameTact >= _frameTacts;
                } // -- End Loop #2

                // --- A physical frame has just been completed. Take care about screen refresh
                cycleFrameCount++;
                FrameCount++;

                // --- Calculate debug information
                DebugInfoProvider.FrameTime   = Clock.GetCounter() - currentFrameStartCounter;
                DebugInfoProvider.UtilityTime = DebugInfoProvider.FrameTime - DebugInfoProvider.CpuTime
                                                - DebugInfoProvider.ScreenRenderingTime;
                DebugInfoProvider.CpuTimeInMs             = DebugInfoProvider.CpuTime / (double)Clock.GetFrequency() * 1000;
                DebugInfoProvider.ScreenRenderingTimeInMs =
                    DebugInfoProvider.ScreenRenderingTime / (double)Clock.GetFrequency() * 1000;
                DebugInfoProvider.UtilityTimeInMs =
                    DebugInfoProvider.UtilityTime / (double)Clock.GetFrequency() * 1000;
                DebugInfoProvider.FrameTimeInMs = DebugInfoProvider.FrameTime / (double)Clock.GetFrequency() * 1000;

                // --- Notify devices that the current frame completed
                OnFrameCompleted();

                // --- Exit if the emulation mode specifies so
                if (options.EmulationMode == EmulationMode.UntilFrameEnds)
                {
                    return(true);
                }

                // --- Wait while the frame time ellapses
                var nextFrameCounter = cycleStartTime + cycleFrameCount * PhysicalFrameClockCount;
                Clock.WaitUntil((long)nextFrameCounter, token);

                // --- Start a new frame and carry on
                Overflow = CurrentFrameTact % _frameTacts;
            } // --- End Loop #1

            // --- The cycle has been inerrupted by cancellation
            return(false);
        }