/// <summary> /// Compiles the Z80 code file /// </summary> protected override async Task ExecuteAsync() { // --- Prepare the appropriate file to compile/run CodeInjected = false; // --- Step #1: Compile the code var success = await Task.Run(() => CompileCode()); if (!success) { return; } // --- Step #2: Check machine compatibility var modelName = SpectNetPackage.Default.Solution.ActiveProject?.ModelName; SpectrumModelType modelType; if (Output.ModelType == null) { switch (modelName) { case SpectrumModels.ZX_SPECTRUM_48: modelType = SpectrumModelType.Spectrum48; break; case SpectrumModels.ZX_SPECTRUM_128: modelType = SpectrumModelType.Spectrum128; break; case SpectrumModels.ZX_SPECTRUM_P3_E: modelType = SpectrumModelType.SpectrumP3; break; case SpectrumModels.ZX_SPECTRUM_NEXT: modelType = SpectrumModelType.Next; break; default: modelType = SpectrumModelType.Spectrum48; break; } } else { modelType = Output.ModelType.Value; } // --- We may display dialogs, so we go back to the main thread await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (!SpectrumModels.IsModelCompatibleWith(modelName, modelType)) { VsxDialogs.Show("The model type defined in the code is not compatible with the " + "Spectum virtual machine of this project.", "Cannot run code."); return; } // --- Step #3: Check for zero code length if (Output.Segments.Sum(s => s.EmittedCode.Count) == 0) { VsxDialogs.Show("The lenght of the compiled code is 0, " + "so there is no code to inject into the virtual machine and run.", "No code to run."); return; } // --- Step #4: Check non-zero displacements var options = HostPackage.Options; if (Output.Segments.Any(s => (s.Displacement ?? 0) != 0) && options.ConfirmNonZeroDisplacement) { var answer = VsxDialogs.Show("The compiled code contains non-zero displacement" + "value, so the displaced code may fail. Are you sure you want to run the code?", "Non-zero displacement found", MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1); if (answer == VsxDialogResult.No) { return; } } var vm = HostPackage.EmulatorViewModel; var pane = OutputWindow.GetPane <SpectrumVmOutputPane>(); HostPackage.ShowToolWindow <SpectrumEmulatorToolWindow>(); // --- Step #5: Prepare the machine to be in the appropriate mode if (IsInInjectMode) { if (vm.MachineState == VmState.Running || vm.MachineState != VmState.Paused && vm.MachineState != VmState.Stopped && vm.MachineState != VmState.None) { VsxDialogs.Show("To inject the code into the virtual machine, please pause it first.", "The virtual machine is running"); return; } } else { var stopped = await SpectrumVmManager.StopSpectrumVmAsync(options.ConfirmMachineRestart); if (!stopped) { return; } } // --- Step #6: Start the virtual machine and run it to the injection point if (vm.MachineState == VmState.Stopped || vm.MachineState == VmState.None) { await pane.WriteLineAsync("Starting the virtual machine in code injection mode."); // --- Use specific startup for each model. var started = true; try { switch (modelName) { case SpectrumModels.ZX_SPECTRUM_48: await VmStateFileManager.SetSpectrum48StartupState(); break; case SpectrumModels.ZX_SPECTRUM_128: if (modelType == SpectrumModelType.Spectrum48) { await VmStateFileManager.SetSpectrum128In48StartupState(); } else { await VmStateFileManager.SetSpectrum128In128StartupState(); } break; case SpectrumModels.ZX_SPECTRUM_P3_E: if (modelType == SpectrumModelType.Spectrum48) { await VmStateFileManager.SetSpectrumP3In48StartupState(); } else { await VmStateFileManager.SetSpectrumP3InP3StartupState(); } break; case SpectrumModels.ZX_SPECTRUM_NEXT: // --- Implement later return; default: // --- Implement later return; } } catch (Exception) { started = false; } if (!started) { return; } } // --- Step #7: Inject the code into the memory, and force // --- new disassembly await pane.WriteLineAsync("Injecting code into the Spectrum virtual machine."); HostPackage.CodeManager.InjectCodeIntoVm(Output); CodeInjected = true; // --- Step #8: Jump to execute the code var continuationPoint = GetContinuationAddress(); if (continuationPoint.HasValue) { vm.Machine.SpectrumVm.Cpu.Registers.PC = continuationPoint.Value; await pane.WriteLineAsync($"Resuming code execution at address {vm.Machine.SpectrumVm.Cpu.Registers.PC:X4}."); } vm.MemViewPoint = (ushort)MemoryStartAddress; vm.DisAssViewPoint = (ushort)DisassemblyStartAddress; vm.StackDebugSupport.ClearStepOutStack(); GetAffectedItem(out var hierarchy, out var itemId); var ext = string.Empty; if (hierarchy is IVsProject project) { project.GetMkDocument(itemId, out var itemFullPath); ext = Path.GetExtension(itemFullPath) ?? string.Empty; } if (ext.ToLower() == ".zxbas") { // --- Push the MAIN_EXECUTION loop address to the stack var memDevice = vm.Machine.SpectrumVm.MemoryDevice; var spValue = vm.Machine.SpectrumVm.Cpu.Registers.SP; var mainExec = VmStateFileManager.SP48_MAIN_EXEC_ADDR; memDevice.Write((ushort)(spValue - 1), (byte)(mainExec >> 8)); memDevice.Write((ushort)(spValue - 2), (byte)mainExec); vm.Machine.SpectrumVm.Cpu.Registers.SP -= 2; } ResumeVm(); }
/// <summary> /// Checks if the current Spectrum model of the project in compatible with the /// specified model type /// </summary> /// <param name="type">Model type</param> /// <returns> /// True, if the project's Spectrum model is compatible with the specified one; /// otherwise, false /// </returns> public static bool IsCurrentModelCompatibleWith(SpectrumModelType type) { var modelName = Default.CodeDiscoverySolution?.CurrentProject?.ModelName; return(SpectrumModels.IsModelCompatibleWith(modelName, type)); }