예제 #1
0
        public void Spectrum48NtscVmCreationWorks()
        {
            // --- Act
            var sm = SpectrumVmFactory.CreateSpectrum48Ntsc();

            // --- Assert
            sm.ModelKey.ShouldBe(SpectrumModels.ZX_SPECTRUM_48);
            sm.EditionKey.ShouldBe(SpectrumModels.NTSC);
            sm.Cpu.ShouldNotBeNull();
            sm.Roms.ShouldNotBeNull();
            sm.RomCount.ShouldBe(1);
            sm.PagingInfo.ShouldNotBeNull();
            sm.RamBanks.ShouldNotBeNull();
            sm.RamBankCount.ShouldBe(0);
            sm.Keyboard.ShouldNotBeNull();
            sm.ScreenConfiguration.ShouldNotBeNull();
            sm.ScreenConfiguration.ScreenRenderingFrameTactCount.ShouldBe(59136);
            sm.ScreenBitmap.ShouldNotBeNull();
            sm.ScreenRenderingStatus.ShouldNotBeNull();
            sm.BeeperConfiguration.ShouldNotBeNull();
            sm.BeeperSamples.ShouldNotBeNull();
            sm.SoundConfiguration.ShouldBeNull();
            sm.AudioSamples.ShouldNotBeNull();
            sm.Breakpoints.ShouldNotBeNull();
            sm.MachineState.ShouldBe(VmState.None);
            sm.ExecutionCompletionReason.ShouldBe(ExecutionCompletionReason.None);
            sm.IsFirstStart.ShouldBeFalse();
            sm.IsFirstPause.ShouldBeFalse();
        }
예제 #2
0
        public async Task ResetOperationTrackingWorks()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.Breakpoints.AddBreakpoint(0x11CB);
            sm.StartDebug();
            await sm.CompletionTask;
            var pcBefore = sm.Cpu.PC;

            sm.ExecutionCompletionReason.ShouldBe(ExecutionCompletionReason.BreakpointReached);
            sm.MachineState.ShouldBe(VmState.Paused);

            // --- Act
            sm.Cpu.ResetOperationTracking();
            sm.Breakpoints.ClearAllBreakpoints();
            sm.Breakpoints.AddBreakpoint(0x11CE);
            sm.StartDebug();
            await sm.CompletionTask;
            var pcAfter = sm.Cpu.PC;

            // --- Assert
            pcBefore.ShouldBe((ushort)0x11CB);
            pcAfter.ShouldBe((ushort)0x11CE);
            sm.Cpu.OperationTrackingState.TouchedAny(0x0000, 0x11CA).ShouldBeFalse();
            sm.Cpu.OperationTrackingState[0x11CB].ShouldBeTrue();
            sm.Cpu.OperationTrackingState[0x11CC].ShouldBeTrue();
            sm.Cpu.OperationTrackingState[0x11CD].ShouldBeTrue();
            sm.Cpu.OperationTrackingState.TouchedAny(0x11CE, 0xFFFF).ShouldBeFalse();
        }
예제 #3
0
        public async Task InterruptExecutingIsInvoked()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.CachedVmStateFolder = STATE_FOLDER;
            await sm.StartAndRunToMain(true);

            var counter = 0;

            // --- Act
            sm.Cpu.InterruptExecuting += (s, e) => { counter++; };
            var entryAddress = sm.InjectCode(@"
                .org #8000
                ld a,2
                out (#fe),a
                halt
                halt
                halt
                ret
            ");

            sm.CallCode(entryAddress);
            await sm.CompletionTask;

            // --- Assert
            counter.ShouldBe(4);
        }
예제 #4
0
        public async Task InjectCodeWorksWithSpectrum48()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.CachedVmStateFolder = STATE_FOLDER;
            await sm.StartAndRunToMain(true);

            // --- Act
            var entryAddress = sm.InjectCode(@"
                .org #8000
                ld a,2
                out (#fe),a
                halt
                halt
                ret
            ");

            sm.CallCode(entryAddress);
            await sm.CompletionTask;

            // --- Assert
            sm.Cpu.A.ShouldBe((byte)2);
            foreach (var item in sm.ScreenBitmap[0])
            {
                item.ShouldBe((byte)0x02);
            }
        }
예제 #5
0
        public async Task PortWrittenIsInvoked()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.CachedVmStateFolder = STATE_FOLDER;
            await sm.StartAndRunToMain(true);

            var events = new List <AddressAndDataEventArgs>();

            // --- Act
            var entryAddress = sm.InjectCode(@"
                .org #8000
                ld a,5
                ld bc,#7FFE
                out (c),a
                ld a,#24
                out (#FE),a
                ret
            ");

            sm.Cpu.PortWritten += (s, e) => { events.Add(e); };
            sm.CallCode(entryAddress);
            await sm.CompletionTask;

            // --- Assert
            var sampleIndex = events.Count - 2; // -2 for the 2 read operation

            events[sampleIndex].Address.ShouldBe((ushort)0x7FFE);
            events[sampleIndex].Data.ShouldBe((byte)0x05);
            events[sampleIndex + 1].Address.ShouldBe((ushort)0x24FE);
            events[sampleIndex + 1].Data.ShouldBe((byte)0x24);
        }
예제 #6
0
        public async Task StartWorksAfterPause()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.Start();
            await sm.Pause();

            var stateBefore   = sm.MachineState;
            var eventCounter  = 0;
            var goesToRunning = false;

            sm.VmStateChanged += (s, e) =>
            {
                eventCounter++;
                if (eventCounter == 1)
                {
                    goesToRunning = e.OldState == VmState.Paused && e.NewState == VmState.Running;
                }
            };

            // --- Act
            sm.Start();
            var stateAfter = sm.MachineState;

            // --- Assert
            stateBefore.ShouldBe(VmState.Paused);
            stateAfter.ShouldBe(VmState.Running);
            sm.IsFirstStart.ShouldBeFalse();
            goesToRunning.ShouldBeTrue();
        }
예제 #7
0
        public async Task MemoryReadIsInvoked()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.CachedVmStateFolder = STATE_FOLDER;
            await sm.StartAndRunToMain(true);

            var events = new List <AddressAndDataEventArgs>();

            // --- Act
            var entryAddress = sm.InjectCode(@"
                .org #8000
                ld hl,#4000
                ld (hl),#AA
                ld a,(hl)
                inc hl
                ld (hl),#55
                ld a,(#4001)
                ret
            ");

            sm.Cpu.MemoryRead += (s, e) => { events.Add(e); };
            sm.CallCode(entryAddress);
            await sm.CompletionTask;

            // --- Assert
            var sampleIndex = events.Count - 2 - 2; // -2 for RET, -2 for the two memory reads

            events[sampleIndex].Address.ShouldBe((ushort)0x4000);
            events[sampleIndex].Data.ShouldBe((byte)0xAA);
            events[sampleIndex + 1].Address.ShouldBe((ushort)0x4001);
            events[sampleIndex + 1].Data.ShouldBe((byte)0x55);
        }
예제 #8
0
        public async Task StopAfterStartWorks()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.Start();
            var stateBefore    = sm.MachineState;
            var eventCounter   = 0;
            var goesToStopping = false;
            var goesToStopped  = false;

            sm.VmStateChanged += (s, e) =>
            {
                eventCounter++;
                if (eventCounter == 1)
                {
                    goesToStopping = e.OldState == VmState.Running && e.NewState == VmState.Stopping;
                }
                else if (eventCounter == 2)
                {
                    goesToStopped = e.OldState == VmState.Stopping && e.NewState == VmState.Stopped;
                }
            };

            // --- Act
            await sm.Stop();

            var stateAfter = sm.MachineState;

            // --- Assert
            stateBefore.ShouldBe(VmState.Running);
            stateAfter.ShouldBe(VmState.Stopped);
            goesToStopping.ShouldBeTrue();
            goesToStopped.ShouldBeTrue();
        }
예제 #9
0
        public async Task SpectrumP3EIn48ModeStartAndRunToMainWorksWithExistingStateFile()
        {
            // --- Arrange
            const string STATE_FILE = SpectrumVmStateFileManagerBase.SPECTRUM_P3_STARTUP_48;
            var          filename   = Path.Combine(STATE_FOLDER, STATE_FILE);

            if (File.Exists(filename))
            {
                File.Delete(filename);
            }
            var sm = SpectrumVmFactory.CreateSpectrumP3E();

            sm.CachedVmStateFolder = STATE_FOLDER;
            await sm.StartAndRunToMain(true);

            var creationTimeBefore = new FileInfo(filename).CreationTime;
            await sm.Stop();

            // --- Act
            await sm.StartAndRunToMain(true);

            // --- Assert
            var creationTimeAfter = new FileInfo(filename).CreationTime;

            sm.MachineState.ShouldBe(VmState.Paused);
            sm.Cpu.PC.ShouldBe(SpectrumVmStateFileManagerBase.SP48_MAIN_EXEC_ADDR);
            File.Exists(filename).ShouldBeTrue();
            creationTimeBefore.ShouldBe(creationTimeAfter);
        }
예제 #10
0
        public void CreateSpectrum48NtscTurboWorks()
        {
            // --- Act
            var sm = SpectrumVmFactory.CreateSpectrum48NtscTurbo();

            // --- Assert
            sm.ShouldNotBeNull();
            sm.ModelKey.ShouldBe(SpectrumModels.ZX_SPECTRUM_48);
            sm.EditionKey.ShouldBe(SpectrumModels.NTSC_2_X);
        }
예제 #11
0
        public void CreateSpectrumP3EWorks()
        {
            // --- Act
            var sm = SpectrumVmFactory.CreateSpectrumP3E();

            // --- Assert
            sm.ShouldNotBeNull();
            sm.ModelKey.ShouldBe(SpectrumModels.ZX_SPECTRUM_P3_E);
            sm.EditionKey.ShouldBe(SpectrumModels.PAL);
        }
예제 #12
0
        static async Task Main(string[] args)
        {
            Console.WriteLine("Creating ZX Spectrum 48K");
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            Console.WriteLine("Running to breakpoint 0x0001");
            sm.Breakpoints.AddBreakpoint(0x0001);
            sm.StartDebug();
            await sm.CompletionTask;

            Console.WriteLine($"Paused at breakpoint 0x{sm.Cpu.PC:X4}");
        }
예제 #13
0
        public async Task SoundSamplesCreatedForSpectrum128()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum128();

            // --- Act
            sm.RunUntilFrameCompletion();
            await sm.CompletionTask;

            // --- Assert
            sm.AudioSamples.Count.ShouldBeGreaterThanOrEqualTo(sm.SoundConfiguration.SamplesPerFrame);
            sm.AudioSamples.Count.ShouldBeLessThanOrEqualTo(sm.SoundConfiguration.SamplesPerFrame + 1);
        }
예제 #14
0
        public async Task AddressTrackingWorks1()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            // --- Act
            sm.Breakpoints.AddBreakpoint(0x0001);
            sm.StartDebug();
            await sm.CompletionTask;

            // --- Assert
            sm.Cpu.OperationTrackingState[0x0000].ShouldBeTrue();
            sm.Cpu.OperationTrackingState.TouchedAny(0x0001, 0xFFFF).ShouldBeFalse();
        }
예제 #15
0
        public async Task FirstSoundFrameShouldBeSilent()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            // --- Act
            sm.RunUntilFrameCompletion();
            await sm.CompletionTask;

            // --- Assert
            for (var i = 0; i < sm.BeeperSamples.Count; i++)
            {
                sm.BeeperSamples[i].ShouldBe(0.0f);
            }
        }
예제 #16
0
        public async Task PauseKeepsNonStartedState()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            // --- Act
            var stateBefore = sm.MachineState;
            await sm.Pause();

            var stateAfter = sm.MachineState;

            // --- Assert
            stateBefore.ShouldBe(VmState.None);
            stateAfter.ShouldBe(VmState.None);
        }
예제 #17
0
        public async Task MachineStopsAfterTimeout()
        {
            // --- Arrange
            var sm          = SpectrumVmFactory.CreateSpectrum48Pal();
            var stateBefore = sm.MachineState;

            // --- Act
            sm.TimeoutInMs = 10;
            sm.Start();
            await sm.CompletionTask;
            var stateAfter = sm.MachineState;

            // --- Assert
            stateBefore.ShouldBe(VmState.None);
            sm.ExecutionCompletionReason.ShouldBe(ExecutionCompletionReason.Timeout);
            stateAfter.ShouldBe(VmState.Paused);
        }
예제 #18
0
        public async Task MachineRaisesFrameCompletedEvents()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            // --- Act
            sm.TimeoutInMs = 200;
            var counter = 0;

            sm.VmFrameCompleted += (s, e) => { counter++; };
            sm.Start();
            await sm.CompletionTask;

            // --- Assert
            counter.ShouldBeGreaterThanOrEqualTo(9);
            counter.ShouldBeLessThanOrEqualTo(11);
        }
예제 #19
0
        public async Task StopKeepsStoppedState()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.Start();
            await sm.Stop();

            // --- Act
            var stateBefore = sm.MachineState;
            await sm.Stop();

            var stateAfter = sm.MachineState;

            // --- Assert
            stateBefore.ShouldBe(VmState.Stopped);
            stateAfter.ShouldBe(VmState.Stopped);
        }
예제 #20
0
        public async Task StartDebugWorks()
        {
            // --- Arrange
            var sm          = SpectrumVmFactory.CreateSpectrum48Pal();
            var stateBefore = sm.MachineState;

            // --- Act
            sm.Breakpoints.AddBreakpoint(0x0001);
            sm.StartDebug();
            await sm.CompletionTask;
            var stateAfter = sm.MachineState;

            // --- Assert
            stateBefore.ShouldBe(VmState.None);
            stateAfter.ShouldBe(VmState.Paused);
            sm.RunsInDebugMode.ShouldBeTrue();
            sm.Cpu.PC.ShouldBe((ushort)0x0001);
            sm.ExecutionCompletionReason.ShouldBe(ExecutionCompletionReason.BreakpointReached);
        }
예제 #21
0
        public async Task Spectrum128In48ModeStartAndRunToMainWorksWithNoStateFile()
        {
            // --- Arrange
            const string STATE_FILE = SpectrumVmStateFileManagerBase.SPECTRUM_128_STARTUP_48;
            var          filename   = Path.Combine(STATE_FOLDER, STATE_FILE);

            if (File.Exists(filename))
            {
                File.Delete(filename);
            }
            var sm = SpectrumVmFactory.CreateSpectrum128();

            sm.CachedVmStateFolder = STATE_FOLDER;

            // --- Act
            await sm.StartAndRunToMain(true);

            // --- Assert
            sm.MachineState.ShouldBe(VmState.Paused);
            sm.Cpu.PC.ShouldBe(SpectrumVmStateFileManagerBase.SP48_MAIN_EXEC_ADDR);
            File.Exists(filename).ShouldBeTrue();
        }
예제 #22
0
        public async Task SpectrumP3EStartAndRunToMainWorksWithNoStateFile()
        {
            // --- Arrange
            const string STATE_FILE = SpectrumVmStateFileManagerBase.SPECTRUM_P3_STARTUP_P3;
            var          filename   = Path.Combine(STATE_FOLDER, STATE_FILE);

            if (File.Exists(filename))
            {
                File.Delete(filename);
            }
            var sm = SpectrumVmFactory.CreateSpectrumP3E();

            sm.CachedVmStateFolder = STATE_FOLDER;

            // --- Act
            await sm.StartAndRunToMain();

            // --- Assert
            sm.MachineState.ShouldBe(VmState.Paused);
            sm.Cpu.PC.ShouldBe(SpectrumVmStateFileManagerBase.SPP3_RETURN_TO_EDITOR);
            File.Exists(filename).ShouldBeTrue();
        }
예제 #23
0
        public async Task RunningSpectrumP3EDoesNotAllowStratAndRunToMain()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrumP3E();

            sm.CachedVmStateFolder = STATE_FOLDER;
            sm.Start();

            // --- Act/Assert
            try
            {
                await sm.StartAndRunToMain(true);
            }
            catch (InvalidOperationException)
            {
                return;
            }
            finally
            {
                await sm.Stop();
            }
            Assert.Fail("Expected InvalidOperationException");
        }
예제 #24
0
 public void TestInitialize()
 {
     SpectrumVmFactory.Reset();
     SpectrumVmFactory.RegisterDefaultProviders();
 }
예제 #25
0
        public async Task OperationExecutedIsInvoked()
        {
            // --- Arrange
            var sm = SpectrumVmFactory.CreateSpectrum48Pal();

            sm.CachedVmStateFolder = STATE_FOLDER;
            await sm.StartAndRunToMain(true);

            var events = new List <Z80InstructionExecutionEventArgs>();

            // --- Act
            var entryAddress = sm.InjectCode(@"
                .org #8000
                di;              8000: No prefix
                bit 3,a;         8001: CB prefix
                ld a,(ix+2);     8003: DD prefix
                ld a,(iy+6);     8006: FD prefix
                bit 2,(ix+2);    8009: DD CB prefixes
                bit 2,(iy+6);    800D: FD CB prefixes
                in d,(c);        8011: ED prefix
                ret
            ");

            sm.Cpu.OperationExecuted += (s, e) => { events.Add(e); };
            sm.CallCode(entryAddress);
            await sm.CompletionTask;

            // --- Assert
            var sampleIndex = events.Count - 1 - 7; // -1 for the RET operation, -7 for the 7 operations
            var op          = events[sampleIndex];

            op.PcBefore.ShouldBe((ushort)0x8000);
            op.Instruction.SequenceEqual(new byte[] { 0xF3 }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0xF3);
            op.PcAfter.ShouldBe((ushort)0x8001);

            op = events[sampleIndex + 1];
            op.PcBefore.ShouldBe((ushort)0x8001);
            op.Instruction.SequenceEqual(new byte[] { 0xCB, 0x5F }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0x5F);
            op.PcAfter.ShouldBe((ushort)0x8003);

            op = events[sampleIndex + 2];
            op.PcBefore.ShouldBe((ushort)0x8003);
            op.Instruction.SequenceEqual(new byte[] { 0xDD, 0x7E, 0x02 }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0x7E);
            op.PcAfter.ShouldBe((ushort)0x8006);

            op = events[sampleIndex + 3];
            op.PcBefore.ShouldBe((ushort)0x8006);
            op.Instruction.SequenceEqual(new byte[] { 0xFD, 0x7E, 0x06 }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0x7E);
            op.PcAfter.ShouldBe((ushort)0x8009);

            op = events[sampleIndex + 4];
            op.PcBefore.ShouldBe((ushort)0x8009);
            op.Instruction.SequenceEqual(new byte[] { 0xDD, 0xCB, 0x56, 0x02 }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0x56);
            op.PcAfter.ShouldBe((ushort)0x800D);

            op = events[sampleIndex + 5];
            op.PcBefore.ShouldBe((ushort)0x800D);
            op.Instruction.SequenceEqual(new byte[] { 0xFD, 0xCB, 0x56, 0x06 }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0x56);
            op.PcAfter.ShouldBe((ushort)0x8011);

            op = events[sampleIndex + 6];
            op.PcBefore.ShouldBe((ushort)0x8011);
            op.Instruction.SequenceEqual(new byte[] { 0xED, 0x50 }).ShouldBeTrue();
            op.OpCode.ShouldBe((byte)0x50);
            op.PcAfter.ShouldBe((ushort)0x8013);
        }