public void ProcessMicBitAbortsInPassiveMode() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = 0x0038; td.SetTapeMode(); // --- Act td.ProcessMicBit(true); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Passive); td.SavePhase.ShouldBe(SavePhase.None); }
public void ProcessMicBitWorksInSaveModeWithBitChange() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); // --- Act td.ProcessMicBit(false); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.None); }
public void GetEarBitsReturnTrueInLoadModeWithNoPlayer() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.LoadBytesRoutineAddress; td.SetTapeMode(); // --- Act var bit = td.GetEarBit(0); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Load); bit.ShouldBeTrue(); }
public void GetEarBitsReturnTrueInPassiveMode() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = 0x0038; td.SetTapeMode(); // --- Act var bit = td.GetEarBit(0); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Passive); bit.ShouldBeTrue(); }
public void CreateTapeFileIsInvokedWhenEnteringSaveMode() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var provider = new FakeTapeProvider(); var td = new TapeDevice(provider, provider); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; // --- Act td.SetTapeMode(); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); provider.CreateTapeFileInvoked.ShouldBeTrue(); }
public void ProcessMicBitAbortsInLoadMode() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = vm.RomInfo.LoadBytesRoutineAddress; td.SetTapeMode(); // --- Act td.ProcessMicBit(true); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Load); td.SavePhase.ShouldBe(SavePhase.None); }
public void SetTapeModeLeavesLoadModeWhenEof() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(new EmptyTapeContentProvider()); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.LoadBytesRoutineAddress; td.SetTapeMode(); var before = td.CurrentMode; // --- Act td.SetTapeMode(); // --- Assert before.ShouldBe(TapeOperationMode.Load); td.CurrentMode.ShouldBe(TapeOperationMode.Passive); }
public void SetTapeModeInvokesEnteredLoadModeEvent() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.LoadBytesRoutineAddress; var invoked = false; td.EnteredLoadMode += (sender, args) => { invoked = true; }; // --- Act td.SetTapeMode(); // --- Assert invoked.ShouldBeTrue(); }
public void SaveTzxBlockIsCalledWhenCompletingDataBlock() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var provider = new FakeTapeProvider(); var td = new TapeDevice(provider, provider); td.OnAttachedToVm(vm); var testData = new byte[] { 0x90, 0x02, 0x05, 0xAA, 0xFF, 0x63 }; // --- Act EmitFullDataBlock(vm, td, testData); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.None); provider.SaveTzxBlockInvoked.ShouldBeTrue(); }
public void ProcessMicBitFailsWithLongBit1Pulse() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); (var debugCpu, var tacts, var pulse) = EmitHeaderWithSync(vm, td); // --- Act tacts += TapeDataBlockPlayer.BIT_1_PL + TapeDevice.SAVE_PULSE_TOLERANCE + 1; debugCpu.SetTacts(tacts); td.ProcessMicBit(pulse); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.Error); }
public void ProcessMicBitFailsWithLongPilotPulse() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var debugCpu = vm.Cpu as IZ80CpuTestSupport; // --- Act debugCpu.SetTacts(TapeDataBlockPlayer.PILOT_PL + TapeDevice.SAVE_PULSE_TOLERANCE + 1); td.ProcessMicBit(false); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.Error); }
public void SetTapeModeLeavesSaveModeWhenError() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var before = td.CurrentMode; // --- Act vm.Cpu.Registers.PC = TapeDevice.ERROR_ROM_ADDRESS; td.SetTapeMode(); // --- Assert before.ShouldBe(TapeOperationMode.Save); td.CurrentMode.ShouldBe(TapeOperationMode.Passive); }
public void ProcessMicBitCatchesFirstPilotPulse() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var debugCpu = vm.Cpu as IZ80CpuTestSupport; // --- Act debugCpu.SetTacts(TapeDataBlockPlayer.PILOT_PL); td.ProcessMicBit(false); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.Pilot); td.PilotPulseCount.ShouldBe(1); }
public void ProcessMicBitFailsWithShortTermSyncPulse() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); var testData = new byte[] { 0x90, 0x02, 0x05, 0xAA, 0xFF, 0x63 }; (var debugCpu, var tacts, var pulse) = EmitHeaderAndData(vm, td, testData); // --- Act tacts += TapeDataBlockPlayer.TERM_SYNC - TapeDevice.SAVE_PULSE_TOLERANCE - 1; debugCpu.SetTacts(tacts); td.ProcessMicBit(pulse); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.Error); }
public void SetTapeModeInvokesLeftSaveModeWhenError() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var invoked = false; td.LeftSaveMode += (sender, args) => { invoked = true; }; // --- Act vm.Cpu.Registers.PC = TapeDevice.ERROR_ROM_ADDRESS; td.SetTapeMode(); // --- Assert invoked.ShouldBeTrue(); }
public void SetTapeModeLeavesSaveModeAfterSilence() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var before = td.CurrentMode; // --- Act var debugCpu = vm.Cpu as IZ80CpuTestSupport; debugCpu.SetTacts(2 * TapeDevice.SAVE_STOP_SILENCE); td.SetTapeMode(); // --- Assert before.ShouldBe(TapeOperationMode.Save); td.CurrentMode.ShouldBe(TapeOperationMode.Passive); }
public void SetNameIsNotCalledWithInvalidHeader() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var provider = new FakeTapeProvider(); var td = new TapeDevice(provider, provider); td.OnAttachedToVm(vm); var testData = new byte[] { 0x42, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20 }; // --- Act EmitFullDataBlock(vm, td, testData); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.None); provider.SuggestedName.ShouldBeNull(); }
public void ProcessMicBitWorksWithBit1Pulse() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); (var debugCpu, var tacts, var pulse) = EmitHeaderWithSync(vm, td); // --- Act tacts += TapeDataBlockPlayer.BIT_1_PL; debugCpu.SetTacts(tacts); td.ProcessMicBit(pulse); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.Data); td.BitOffset.ShouldBe(0); td.DataByte.ShouldBe((byte)0); td.PrevDataPulse.ShouldBe(MicPulseType.Bit1); }
public void ProcessMicBitWorksWithDataBytes() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); var testData = new byte[] { 0x90, 0x02, 0x05, 0xAA, 0xFF, 0x63 }; // --- Act EmitHeaderAndData(vm, td, testData); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.Data); td.DataLength.ShouldBe(testData.Length); for (var i = 0; i < testData.Length; i++) { td.DataBuffer[i].ShouldBe(testData[i]); } }
/// <summary> /// Writes a byte of data to a specified port address /// </summary> /// <param name="port"></param> /// <param name="value"></param> public override void WritePort(ushort port, byte value) { // process IO contention ContendPortAddress(port); // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address if ((port & 0x0001) != 0) { return; } // store the last OUT byte LastULAOutByte = value; /* * Bit 7 6 5 4 3 2 1 0 +-------------------------------+ | | | | E | M | Border | +-------------------------------+ */ // Border - LSB 3 bits hold the border colour if (ULADevice.borderColour != (value & BORDER_BIT)) { // border value has changed - update the screen buffer ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } ULADevice.borderColour = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); // Tape mic processing (not implemented yet) //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); }
public void SetTapeModeInvokesLedtSaveModeEventAfterSilence() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var invoked = false; td.LeftSaveMode += (sender, args) => { invoked = true; }; // --- Act var debugCpu = vm.Cpu as IZ80CpuTestSupport; debugCpu.SetTacts(2 * TapeDevice.SAVE_STOP_SILENCE); td.SetTapeMode(); // --- Assert invoked.ShouldBeTrue(); }
public void SetNameIsCalledAtFirstDataBlock() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var saveProvider = new FakeSaveToTapeProvider(); var td = new TapeDevice(saveProvider); td.OnAttachedToVm(vm); var testData = new byte[] { 0x00, 0x00, 0x42, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x00, 0x6F, 0x80, 0x4F, 0x00, 0xC3 }; // --- Act EmitFullDataBlock(vm, td, testData); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.None); saveProvider.SuggestedName.ShouldBe("Border"); }
public void FinalizeTapeFileIsInvokedWhenLeavingSaveMode() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var saveProvider = new FakeSaveToTapeProvider(); var td = new TapeDevice(saveProvider); td.OnAttachedToVm(vm); vm.Cpu.Registers.PC = td.SaveBytesRoutineAddress; td.SetTapeMode(); var before = td.CurrentMode; // --- Act var debugCpu = vm.Cpu as IZ80CpuTestSupport; debugCpu.SetTacts(2 * TapeDevice.SAVE_STOP_SILENCE); td.SetTapeMode(); // --- Assert before.ShouldBe(TapeOperationMode.Save); td.CurrentMode.ShouldBe(TapeOperationMode.Passive); saveProvider.FinalizeTapeFileInvoked.ShouldBeTrue(); }
/// <summary> /// Writes a byte of data to a specified port address /// </summary> public override void WritePort(ushort port, byte value) { // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address if ((port & 0x0001) != 0) { return; } LastFe = value; // store the last OUT byte LastULAOutByte = value; /* * Bit 7 6 5 4 3 2 1 0 +-------------------------------+ | | | | E | M | Border | +-------------------------------+ */ // Border - LSB 3 bits hold the border colour if (ULADevice.BorderColor != (value & BORDER_BIT)) { //ULADevice.RenderScreen((int)CurrentFrameCycle); ULADevice.BorderColor = value & BORDER_BIT; } // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0, _renderSound); // Tape TapeDevice.WritePort(port, value); // Tape mic processing (not implemented yet) //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); }
public void ProcessMicBitWorksWithTermSyncPulse() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); var testData = new byte[] { 0x90, 0x02, 0x05, 0xAA, 0xFF, 0x63 }; (var debugCpu, var tacts, var pulse) = EmitHeaderAndData(vm, td, testData); // --- Act tacts += TapeDataBlockPlayer.TERM_SYNC; debugCpu.SetTacts(tacts); td.ProcessMicBit(pulse); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.None); for (var i = 0; i < testData.Length; i++) { td.DataBuffer[i].ShouldBe(testData[i]); } }
public void ProcessMicBitWorksWithMultipleDataBlock() { // --- Arrange var vm = new SpectrumTapeDeviceTestMachine(); var td = new TapeDevice(null, null); td.OnAttachedToVm(vm); var testData = new byte[] { 0x90, 0x02, 0x05, 0xAA, 0xFF, 0x63 }; // --- Act (var debugCpu, var tacts, var pulse) = EmitFullDataBlock(vm, td, testData); tacts += TapeDataBlockPlayer.PILOT_PL * 5; debugCpu.SetTacts(tacts); td.ProcessMicBit(pulse); (debugCpu, tacts, pulse) = EmitFullDataBlock(vm, td, testData); tacts += TapeDataBlockPlayer.PILOT_PL * 5; debugCpu.SetTacts(tacts); td.ProcessMicBit(pulse); // --- Assert td.CurrentMode.ShouldBe(TapeOperationMode.Save); td.SavePhase.ShouldBe(SavePhase.None); td.DataBlockCount.ShouldBe(2); }
/// <summary> /// Writes a byte of data to a specified port address /// </summary> /// <param name="port"></param> /// <param name="value"></param> public override void WritePort(ushort port, byte value) { // process IO contention ContendPortAddress(port); // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte BitArray bits = new BitArray(new byte[] { value }); // Check whether the low bit is reset bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; AYDevice.WritePort(port, value); UPDDiskDevice.WritePort(port, value); // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set if (port == 0x7ffd) { if (!PagingDisabled) { // bits 0, 1, 2 select the RAM page var rp = value & 0x07; if (rp < 8) { RAMPaged = rp; } // bit 3 controls shadow screen SHADOWPaged = bits[3]; // Bit 5 set signifies that paging is disabled until next reboot PagingDisabled = bits[5]; // portbit 4 is the LOW BIT of the ROM selection ROMlow = bits[4]; } } // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set if (port == 0x1ffd) { if (!PagingDisabled) { if (!bits[0]) { // special paging is not enabled - get the ROMpage high byte ROMhigh = bits[2]; // set the special paging mode flag SpecialPagingMode = false; } else { // special paging is enabled // this is decided based on combinations of bits 1 & 2 // Config 0 = Bit1-0 Bit2-0 // Config 1 = Bit1-1 Bit2-0 // Config 2 = Bit1-0 Bit2-1 // Config 3 = Bit1-1 Bit2-1 BitArray confHalfNibble = new BitArray(2); confHalfNibble[0] = bits[1]; confHalfNibble[1] = bits[2]; // set special paging configuration PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); // set the special paging mode flag SpecialPagingMode = true; } } // bit 4 is the printer port strobe PrinterPortStrobe = bits[4]; } // Only even addresses address the ULA if (lowBitReset) { // store the last OUT byte LastULAOutByte = value; /* * Bit 7 6 5 4 3 2 1 0 +-------------------------------+ | | | | E | M | Border | +-------------------------------+ */ // Border - LSB 3 bits hold the border colour if (ULADevice.borderColour != (value & BORDER_BIT)) { ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } ULADevice.borderColour = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } LastULAOutByte = value; }
/// <summary> /// Reads a byte of data from a specified port address /// </summary> /// <param name="port"></param> /// <returns></returns> public override byte ReadPort(ushort port) { bool deviceAddressed = true; // process IO contention ContendPortAddress(port); int result = 0xFF; // check AY if (AYDevice.ReadPort(port, ref result)) { return((byte)result); } // Kempston joystick input takes priority over all other input // if this is detected just return the kempston byte if ((port & 0xe0) == 0 || (port & 0x20) == 0) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) { return((byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine); } InputRead = true; } else if (UPDDiskDevice.ReadPort(port, ref result)) { return((byte)result); } else { if (KeyboardDevice.ReadPort(port, ref result)) { // not a lagframe InputRead = true; // process tape INs TapeDevice.ReadPort(port, ref result); } else { deviceAddressed = false; } } if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle long _tStates = CurrentFrameCycle - 1; // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) { result = 0xff; } else { if (ULADevice.floatingBusTable[_tStates] < 0) { result = 0xff; } else { result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); } } } return((byte)result); }
public void SyncState(Serializer ser) { ser.BeginSection("ZXMachine"); ser.Sync("FrameCompleted", ref FrameCompleted); ser.Sync("OverFlow", ref OverFlow); ser.Sync("FrameCount", ref FrameCount); ser.Sync("_frameCycles", ref _frameCycles); ser.Sync("inputRead", ref inputRead); ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick); ser.Sync("LastULAOutByte", ref LastULAOutByte); ser.Sync("ROM0", ref ROM0, false); ser.Sync("ROM1", ref ROM1, false); ser.Sync("ROM2", ref ROM2, false); ser.Sync("ROM3", ref ROM3, false); ser.Sync("RAM0", ref RAM0, false); ser.Sync("RAM1", ref RAM1, false); ser.Sync("RAM2", ref RAM2, false); ser.Sync("RAM3", ref RAM3, false); ser.Sync("RAM4", ref RAM4, false); ser.Sync("RAM5", ref RAM5, false); ser.Sync("RAM6", ref RAM6, false); ser.Sync("RAM7", ref RAM7, false); ser.Sync("ROMPaged", ref ROMPaged); ser.Sync("SHADOWPaged", ref SHADOWPaged); ser.Sync("RAMPaged", ref RAMPaged); ser.Sync("PagingDisabled", ref PagingDisabled); ser.Sync("SpecialPagingMode", ref SpecialPagingMode); ser.Sync("PagingConfiguration", ref PagingConfiguration); ser.Sync("ROMhigh", ref ROMhigh); ser.Sync("ROMlow", ref ROMlow); ser.Sync("LastContendedReadByte", ref LastContendedReadByte); KeyboardDevice.SyncState(ser); BuzzerDevice.SyncState(ser); TapeBuzzer.SyncState(ser); ULADevice.SyncState(ser); CPUMon.SyncState(ser); if (AYDevice != null) { AYDevice.SyncState(ser); ((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig; } ser.Sync("tapeMediaIndex", ref tapeMediaIndex); if (ser.IsReader) { IsLoadState = true; TapeMediaIndex = tapeMediaIndex; IsLoadState = false; } TapeDevice.SyncState(ser); ser.Sync("diskMediaIndex", ref diskMediaIndex); if (ser.IsReader) { IsLoadState = true; DiskMediaIndex = diskMediaIndex; IsLoadState = false; } if (UPDDiskDevice != null) { UPDDiskDevice.SyncState(ser); } ser.EndSection(); }
/// <summary> /// Executes a single frame /// </summary> public virtual void ExecuteFrame(bool render, bool renderSound) { ULADevice.FrameEnd = false; ULADevice.ULACycleCounter = CurrentFrameCycle; InputRead = false; _render = render; _renderSound = renderSound; FrameCompleted = false; if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) { TapeDevice.StartFrame(); } if (_renderSound) { if (AYDevice != null) { AYDevice.StartFrame(); } } PollInput(); for (;;) { // run the CPU Monitor cycle CPUMon.ExecuteCycle(); // cycle the tape device if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) { TapeDevice.TapeCycle(); } // has frame end been reached? if (ULADevice.FrameEnd) { break; } } OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength; // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; ULADevice.LastTState = 0; if (AYDevice != null) { AYDevice.EndFrame(); } FrameCount++; if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) { TapeDevice.EndFrame(); } FrameCompleted = true; // is this a lag frame? Spectrum.IsLagFrame = !InputRead; // FDC debug if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) { // only write UPD log every second if (FrameCount % 10 == 0) { System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog); UPDDiskDevice.dLog = new System.Collections.Generic.List <string>(); //System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); } } }