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); }
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); } } }
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); }
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); }
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); } } }
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); } } }
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); } } }
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); }