public void EnabledInterruptIsRaised()
        {
            // --- Arrange
            var pars     = new ScreenConfiguration();
            var pixels   = new TestPixelRenderer(pars);
            var spectrum = new SpectrumAdvancedTestMachine(pars, pixels);

            // --- We render the screen while the interrupt is enabled
            spectrum.InitCode(new byte[]
            {
                0xED, 0x56,       // IM 1
                0xFB,             // EI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x00, 0x0A, // LD BC,$0A00
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0x76              // HALT
            });
            var startTime = spectrum.Clock.GetCounter();

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // === Display some extra information about the duration of the frame execution
            var duration = (spectrum.Clock.GetCounter() - startTime)
                           / (double)spectrum.Clock.GetFrequency();

            Console.WriteLine("Frame execution time: {0} second", duration);

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800F);

            // --- The instructions above take 66599 tacts while reaching the HALT operation
            // --- However, an interrupt is generated, and because of IM 1, the RST 38 is
            // --- invoked. It checks to keyboard status in 1034 tacts.
            // --- When HALT is reached, the CPU tact count is 67633.
            spectrum.Cpu.Tacts.ShouldBeGreaterThanOrEqualTo(67553L);
        }
示例#2
0
        public void NoInCContentionValueOnPort0XfeIsAppliedWhileLeftDisplayEdgeIsReached()
        {
            // --- Arrange
            const int EXPECTED_CONTENTION = 0;
            var       screenConfig        = new ScreenConfiguration(SpectrumModels.ZxSpectrum48Pal.Screen);
            var       pixels   = new TestPixelRenderer(SpectrumModels.ZxSpectrum48Pal.Screen);
            var       spectrum = new SpectrumAdvancedTestMachine(pixels);

            var tactToReach = screenConfig.FirstDisplayLine * screenConfig.ScreenLineTime
                              + screenConfig.FirstPixelTactInLine - 4;

            InitMachineInCWithNops(tactToReach, 0xFE, spectrum);

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // --- Assert
            spectrum.Cpu.Tacts.ShouldBe(EXPECTED_CONTENTION + tactToReach + 4); // +4 for HALT
        }
        public void SettingBorderValueDoesNotChangeInvisibleScreenArea()
        {
            // --- Arrange
            var pars     = new ScreenConfiguration();
            var pixels   = new TestPixelRenderer(pars);
            var spectrum = new SpectrumAdvancedTestMachine(pars, pixels);

            // --- Because we execute only 451 CPU tacts, rendering does not
            // --- reach the visible border area
            spectrum.InitCode(new byte[]
            {
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x10, 0x00, // LD BC,$0010
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0xFB,             // EI
                0x76              // HALT
            });
            ((IScreenDeviceTestSupport)spectrum.ScreenDevice).FillScreenBuffer(0xFF);

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800E);
            spectrum.Cpu.Tacts.ShouldBe(451L);
            pixels.IsFrameReady.ShouldBeFalse();

            pixels.SetPixelMemory(spectrum.ScreenDevice.GetPixelBuffer());
            for (var row = 0; row < spectrum.ScreenDevice.ScreenConfiguration.ScreenLines; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0xFF);
                }
            }
        }
示例#4
0
        public void StepOverWorksWithMultipleSteps()
        {
            // --- Arrange
            var pars          = new ScreenConfiguration();
            var pixels        = new TestPixelRenderer(pars);
            var spectrum      = new SpectrumAdvancedTestMachine(pars, pixels);
            var debugProvider = new TestDebugInfoProvider();

            spectrum.SetDebugInfoProvider(debugProvider);

            // --- We render the screen while the interrupt is disabled
            spectrum.InitCode(new byte[]
            {
                0x3E, 0x10,       // LD A,$10
                0x87,             // ADD A,A
                0x47,             // LD B,A
                0x4F,             // LD C,A
            });
            var regs = spectrum.Cpu.Registers;

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));
            var pc1 = regs.PC;

            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));
            var pc2 = regs.PC;

            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));
            var pc3 = regs.PC;

            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));

            // --- Assert
            regs.A.ShouldBe((byte)0x20);
            regs.B.ShouldBe((byte)0x20);
            regs.C.ShouldBe((byte)0x20);
            pc1.ShouldBe((ushort)0x8002);
            pc2.ShouldBe((ushort)0x8003);
            pc3.ShouldBe((ushort)0x8004);
            regs.PC.ShouldBe((ushort)0x8005);
        }
        public void DisabledInterruptIsNotRaised()
        {
            // --- Arrange
            var pars     = new ScreenConfiguration();
            var pixels   = new TestPixelRenderer(pars);
            var spectrum = new SpectrumAdvancedTestMachine(pars, pixels);

            // --- We render the screen while the interrupt is disabled
            spectrum.InitCode(new byte[]
            {
                0xED, 0x56,       // IM 1
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x00, 0x0A, // LD BC,$0A00
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0x76              // HALT
            });
            var startTime = spectrum.Clock.GetCounter();

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // === Display some extra information about the duration of the frame execution
            var duration = (spectrum.Clock.GetCounter() - startTime)
                           / (double)spectrum.Clock.GetFrequency();

            Console.WriteLine("Frame execution time: {0} second", duration);

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800F);

            spectrum.Cpu.Tacts.ShouldBeGreaterThanOrEqualTo(66599L);
        }
示例#6
0
        public void StepOverWorksWithRst()
        {
            // --- Arrange
            var pars          = new ScreenConfiguration();
            var pixels        = new TestPixelRenderer(pars);
            var spectrum      = new SpectrumAdvancedTestMachine(pars, pixels);
            var debugProvider = new TestDebugInfoProvider();

            spectrum.SetDebugInfoProvider(debugProvider);

            // --- We render the screen while the interrupt is disabled
            spectrum.InitCode(new byte[]
            {
                0xF3,       // DI
                0x16, 0x06, // LD D,6
                0xFF,       // RST $38
                0x7A,       // LD A,D
                0x76        // HALT
            });
            var regs = spectrum.Cpu.Registers;

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));
            var pc1 = regs.PC;

            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));
            var pc2 = regs.PC;

            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.Debugger, DebugStepMode.StepOver));

            // --- Assert
            regs.D.ShouldBe((byte)0x06);
            pc1.ShouldBe((ushort)0x8003);
            pc2.ShouldBe((ushort)0x8004);
            regs.PC.ShouldBe((ushort)0x8005);
        }
示例#7
0
        public void RenderingTenScreenFramesWorksAsExpected()
        {
            // --- Arrange
            var pixels   = new TestPixelRenderer(SpectrumModels.ZxSpectrum48Pal.Screen);
            var spectrum = new SpectrumAdvancedTestMachine(pixels);

            // === The same as RenderingScreenWithPatternWorks1 test case, but waits
            // === while the full frame is rendered and a new frame is started.

            // --- We render the screen with pixels with color index
            // --- of 0x09 and 0x0A in a chequered pattern
            spectrum.InitCode(new byte[]
            {
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x75, 0x0A, // LD BC,$0A75
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0x76              // HALT
            });

            for (var addr = 0x4000; addr < 0x5800; addr++)
            {
                spectrum.WriteSpectrumMemory((ushort)addr, (byte)((addr & 0x0100) == 0 ? 0x55 : 0xAA));
            }
            for (var addr = 0x5800; addr < 0x5B00; addr++)
            {
                spectrum.WriteSpectrumMemory((ushort)addr, 0x51);
            }
            var startTime = spectrum.Clock.GetCounter();

            // --- Act
            // === Be aware of EmulationMode.UntilNextFrame
            for (var i = 0; i < 10; i++)
            {
                spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilFrameEnds));
            }

            // === Display some extra information about the duration of the frame execution
            var duration = (spectrum.Clock.GetCounter() - startTime)
                           / (double)spectrum.Clock.GetFrequency();

            Console.WriteLine("Frame execution time: {0} second", duration);

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800D);
            pixels.IsFrameReady.ShouldBeTrue();

            // === The full frame's tact time is used
            spectrum.Cpu.Tacts.ShouldBeGreaterThanOrEqualTo(spectrum.ScreenDevice.ScreenConfiguration.ScreenRenderingFrameTactCount * 10);

            // === The full time should not exceed the 10*frame time + the longest Z80 instruction length,
            // === which is 23
            spectrum.Cpu.Tacts.ShouldBeLessThanOrEqualTo(spectrum.ScreenDevice.ScreenConfiguration.ScreenRenderingFrameTactCount * 10 + 23);

            // --- The top 48 border rows should be set to 0x05
            pixels.SetPixelMemory(spectrum.ScreenDevice.GetPixelBuffer());
            for (var row = 0; row < 48; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- Display rows should have a border value of 0x05 and a pixel value of 0x00
            for (var row = 48; row < 48 + 192; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.BorderLeftPixels; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
                for (var column = spectrum.ScreenDevice.ScreenConfiguration.BorderLeftPixels;
                     column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth - spectrum.ScreenDevice.ScreenConfiguration.BorderRightPixels;
                     column++)
                {
                    var expectedColor = (row + column) % 2 == 1 ? 0x09 : 0x0A;
                    pixels[row, column].ShouldBe((byte)expectedColor);
                }
                for (var column = spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth - spectrum.ScreenDevice.ScreenConfiguration.BorderRightPixels;
                     column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth;
                     column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- The bottom 48 border rows should be set to 0x05
            for (var row = 48 + 192; row < spectrum.ScreenDevice.ScreenConfiguration.ScreenLines; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }
        }
示例#8
0
        public void RenderingScreenWithPatternWorks1()
        {
            // --- Arrange
            var pixels   = new TestPixelRenderer(SpectrumModels.ZxSpectrum48Pal.Screen);
            var spectrum = new SpectrumAdvancedTestMachine(pixels);

            // --- We render the screen with pixels with color index
            // --- of 0x09 and 0x0A in a chequered pattern
            spectrum.InitCode(new byte[]
            {
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x75, 0x0A, // LD BC,$0A75
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0x76              // HALT
            });

            for (var addr = 0x4000; addr < 0x5800; addr++)
            {
                spectrum.WriteSpectrumMemory((ushort)addr, (byte)((addr & 0x0100) == 0 ? 0x55 : 0xAA));
            }
            for (var addr = 0x5800; addr < 0x5B00; addr++)
            {
                spectrum.WriteSpectrumMemory((ushort)addr, 0x51);
            }

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800D);
            spectrum.Cpu.Tacts.ShouldBe(69633L);
            pixels.IsFrameReady.ShouldBeFalse();

            // --- The top 48 border rows should be set to 0x05
            pixels.SetPixelMemory(spectrum.ScreenDevice.GetPixelBuffer());
            for (var row = 0; row < 48; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- Display rows should have a border value of 0x05 and a pixel value of 0x00
            for (var row = 48; row < 48 + 192; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.BorderLeftPixels; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
                for (var column = spectrum.ScreenDevice.ScreenConfiguration.BorderLeftPixels;
                     column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth - spectrum.ScreenDevice.ScreenConfiguration.BorderRightPixels;
                     column++)
                {
                    var expectedColor = (row + column) % 2 == 1  ? 0x09 : 0x0A;
                    pixels[row, column].ShouldBe((byte)expectedColor);
                }
                for (var column = spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth - spectrum.ScreenDevice.ScreenConfiguration.BorderRightPixels;
                     column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth;
                     column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- The bottom 48 border rows should be set to 0x05
            for (var row = 48 + 192; row < spectrum.ScreenDevice.ScreenConfiguration.ScreenLines; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }
        }
示例#9
0
        public void RenderingScreenWithEmptyPixelsWorks()
        {
            // --- Arrange
            var pixels   = new TestPixelRenderer(SpectrumModels.ZxSpectrum48Pal.Screen);
            var spectrum = new SpectrumAdvancedTestMachine(pixels);

            // --- Because we execute 69637 CPU tacts, rendering set all border
            // --- pixels to 5 + screen pixels to 0
            spectrum.InitCode(new byte[]
            {
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x75, 0x0A, // LD BC,$0A75
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0x76              // HALT
            });

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800D);
            spectrum.Cpu.Tacts.ShouldBe(69633L);
            pixels.IsFrameReady.ShouldBeFalse();

            // --- The top 48 border rows should be set to 0x05
            pixels.SetPixelMemory(spectrum.ScreenDevice.GetPixelBuffer());
            for (var row = 0; row < 48; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- Display rows should have a border value of 0x05 and a pixel value of 0x00
            for (var row = 48; row < 48 + 192; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.BorderLeftPixels; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
                for (var column = spectrum.ScreenDevice.ScreenConfiguration.BorderLeftPixels;
                     column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth - spectrum.ScreenDevice.ScreenConfiguration.BorderRightPixels;
                     column++)
                {
                    pixels[row, column].ShouldBe((byte)0x00);
                }
                for (var column = spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth - spectrum.ScreenDevice.ScreenConfiguration.BorderRightPixels;
                     column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth;
                     column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- The bottom 48 border rows should be set to 0x05
            for (var row = 48 + 192; row < spectrum.ScreenDevice.ScreenConfiguration.ScreenLines; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }
        }
示例#10
0
        public void SettingBorderValueChangesBorderArea3()
        {
            // --- Arrange
            var pixels   = new TestPixelRenderer(SpectrumModels.ZxSpectrum48Pal.Screen);
            var spectrum = new SpectrumAdvancedTestMachine(pixels);

            // --- Because we execute 14413 CPU tacts, rendering reaches
            // --- all top border rows + the left border of the first
            // --- display row, and sets the first 28 pixels to 0.

            spectrum.InitCode(new byte[]
            {
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x29, 0x02, // LD BC,$0229
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0xFB,             // EI
                0x76              // HALT
            });
            ((IScreenDeviceTestSupport)spectrum.ScreenDevice).FillScreenBuffer(0xDC);

            // --- Act
            spectrum.ExecuteCycle(CancellationToken.None, new ExecuteCycleOptions(EmulationMode.UntilHalt));

            // --- Assert
            var regs = spectrum.Cpu.Registers;

            regs.PC.ShouldBe((ushort)0x800E);
            spectrum.Cpu.Tacts.ShouldBe(14413L);
            pixels.IsFrameReady.ShouldBeFalse();

            // --- The top 48 border rows should be set to 0x05
            pixels.SetPixelMemory(spectrum.ScreenDevice.GetPixelBuffer());
            for (var row = 0; row < 48; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0x05);
                }
            }

            // --- The left border of row 48 should be set to 0x05
            for (var column = 0; column < 48; column++)
            {
                pixels[48, column].ShouldBe((byte)0x05);
            }

            // --- The first 12 pixels of the first display row (48) should be set to 0
            for (var column = 48; column < 156; column++)
            {
                pixels[48, column].ShouldBe((byte)0x00);
            }

            // --- The other pixels of the first display row (48) should be intact
            for (var column = 156; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
            {
                pixels[48, column].ShouldBe((byte)0xDC);
            }

            // --- All the other screen bytes should be intact (0xDC)
            for (var row = 49; row < spectrum.ScreenDevice.ScreenConfiguration.ScreenLines; row++)
            {
                for (var column = 0; column < spectrum.ScreenDevice.ScreenConfiguration.ScreenWidth; column++)
                {
                    pixels[row, column].ShouldBe((byte)0xDC);
                }
            }
        }
        public void ExecutionCyleWorksWithCancellation()
        {
            // --- Arrange
            var pars     = new ScreenConfiguration();
            var pixels   = new TestPixelRenderer(pars);
            var spectrum = new SpectrumAdvancedTestMachine(pars, pixels);

            // === The same as RenderingScreenWithPatternWorks1 test case, but waits
            // === while the full frame is rendered and a new frame is started.

            // --- We render the screen with pixels with color index
            // --- of 0x09 and 0x0A in a chequered pattern
            spectrum.InitCode(new byte[]
            {
                0xF3,             // DI
                0x3E, 0x05,       // LD A,$05
                0xD3, 0xFE,       // OUT ($FE),A
                0x01, 0x75, 0x0A, // LD BC,$0A75
                0x0B,             // DECLB: DEC BC
                0x78,             // LD A,B
                0xB1,             // OR C
                0x20, 0xFB,       // JR NZ,DECLB
                0xFB,             // EI
                0x76              // HALT
            });

            for (var addr = 0x4000; addr < 0x5800; addr++)
            {
                spectrum.WriteSpectrumMemory((ushort)addr, (byte)((addr & 0x0100) == 0 ? 0x55 : 0xAA));
            }
            for (var addr = 0x5800; addr < 0x5B00; addr++)
            {
                spectrum.WriteSpectrumMemory((ushort)addr, 0x51);
            }
            var counter          = spectrum.Clock.GetCounter();
            var cancellationTime = counter + spectrum.Clock.GetFrequency() / 100000; // 0.01ms

            var startTime          = spectrum.Clock.GetCounter();
            var cancellationSource = new CancellationTokenSource();

            // --- Act
            // === We wait up to two frames
            Task.WaitAll(
                Task.Run(() => spectrum.ExecuteCycle(cancellationSource.Token, new ExecuteCycleOptions(EmulationMode.UntilFrameEnds)),
                         cancellationSource.Token),
                Task.Run(() =>
            {
                spectrum.Clock.WaitUntil(cancellationTime, cancellationSource.Token);
                cancellationSource.Cancel();
            }, cancellationSource.Token));

            // === Display some extra information about the duration of the frame execution
            var duration = (spectrum.Clock.GetCounter() - startTime)
                           / (double)spectrum.Clock.GetFrequency();

            Console.WriteLine("Frame execution time: {0} second", duration);

            // --- Assert
            // === Only a part of the frame's tact time is used
            spectrum.Cpu.Tacts.ShouldBeLessThan(spectrum.ScreenDevice.ScreenConfiguration.UlaFrameTactCount);
        }