/// <summary> /// Exports the list to the specified file /// </summary> /// <param name="filename"></param> public void ExportToFile(string filename) { var listing = new StringBuilder(4096); foreach (var line in ProgramLines) { listing.Append(line.LineNo); listing.Append(" "); listing.AppendLine(line.Text); } try { var dirName = Path.GetDirectoryName(filename); if (!string.IsNullOrEmpty(dirName) && !Directory.Exists(dirName)) { Directory.CreateDirectory(dirName); } File.WriteAllText(filename, listing.ToString()); } catch (Exception ex) { VsxDialogs.Show($"Error while exporting to file {filename}: {ex.Message}", "Export BASIC listing error.", MessageBoxButton.OK, VsxMessageBoxIcon.Error); return; } }
/// <summary> /// Stops the Spectrum VM, displays confirmation, if required /// </summary> /// <returns>Tru, if start confirmed; otherwise, false</returns> public static async Task<bool> StopSpectrumVmAsync(bool needConfirm) { var vm = SpectNetPackage.Default.EmulatorViewModel; var machineState = vm.MachineState; if (machineState == VmState.Running || machineState == VmState.Paused) { if (needConfirm) { var answer = VsxDialogs.Show("Are you sure, you want to restart " + "the ZX Spectrum virtual machine?", "The ZX Spectrum virtual machine is running", MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1); if (answer == VsxDialogResult.No) { return false; } } // --- Stop the machine and allow 50ms to stop. await vm.Machine.Stop(); if (vm.MachineState == VmState.Stopped) return true; const string MESSAGE = "The ZX Spectrum virtual machine did not stop."; var pane = OutputWindow.GetPane<SpectrumVmOutputPane>(); await pane.WriteLineAsync(MESSAGE); VsxDialogs.Show(MESSAGE, "Unexpected issue", MessageBoxButton.OK, VsxMessageBoxIcon.Error); return false; } return true; }
/// <summary> /// Compiles the Z80 code file /// </summary> protected override async Task ExecuteAsync() { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); VmStateLoaded = false; GetItem(out var hierarchy, out var itemId); if (!(hierarchy is IVsProject project)) { return; } project.GetMkDocument(itemId, out var itemFullPath); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var vm = Package.MachineViewModel; // --- Prepare the machine to be in the appropriate mode if (vm.MachineState == VmState.Stopped || vm.MachineState == VmState.None) { vm.Start(); await vm.Pause(); } if (vm.MachineState != VmState.Paused) { VsxDialogs.Show("To load state file into the virtual machine, please pause it first.", "The virtual machine is running"); return; } Package.ShowToolWindow <SpectrumEmulatorToolWindow>(); Package.StateFileManager.LoadVmStateFile(itemFullPath); vm.Machine.ForceScreenRefresh(); VmStateLoaded = true; }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override void FinallyOnMainThread() { base.FinallyOnMainThread(); if (Package.Options.ConfirmCodeCompile && Output.ErrorCount == 0) { VsxDialogs.Show("The code has been successfully compiled."); } }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override Task FinallyOnMainThreadAsync() { if (VmStateLoaded && Package.Options.ConfirmVmStateLoad) { VsxDialogs.Show("The VM state file has been loaded."); } return(Task.FromResult(0)); }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override void FinallyOnMainThread() { Package.TestManager.CompilatioInProgress = false; if (Package.Options.ConfirmTestCompile && Output.ErrorCount == 0) { VsxDialogs.Show("The unit test code has been successfully compiled."); } }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override void FinallyOnMainThread() { base.FinallyOnMainThread(); if (!IsCancelled && Package.Options.ConfirmCodeExport && Output.ErrorCount == 0) { VsxDialogs.Show("The code has been exported."); } }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override async Task FinallyOnMainThreadAsync() { await base.FinallyOnMainThreadAsync(); if (HostPackage.Options.ConfirmCodeCompile && Output.ErrorCount == 0) { VsxDialogs.Show("The code has been successfully compiled."); } }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override Task FinallyOnMainThreadAsync() { Package.TestManager.CompilatioInProgress = false; if (Package.Options.ConfirmTestCompile && Output.ErrorCount == 0) { VsxDialogs.Show("The unit test code has been successfully compiled."); } return(Task.FromResult(0)); }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override async Task FinallyOnMainThreadAsync() { await base.FinallyOnMainThreadAsync(); if (Success && !IsCancelled && Package.Options.ConfirmCodeExport && Output.ErrorCount == 0) { VsxDialogs.Show("The code has been exported."); } }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override void FinallyOnMainThread() { Package.CodeManager.CompilatioInProgress = false; var options = Package.Options; if (options.ConfirmCodeStart) { VsxDialogs.Show("The code has been started."); } }
protected override async Task ExecuteAsync() { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var folder = HostPackage.Options.VmStateSaveFileFolder; var filename = VsxDialogs.FileOpen(VMSTATE_FILTER, folder); if (filename == null) { return; } // --- Stop the virtual machine, provided it runs var options = HostPackage.Options; var pane = OutputWindow.GetPane <SpectrumVmOutputPane>(); var vm = HostPackage.EmulatorViewModel; var machineState = vm.MachineState; if ((machineState == VmState.Running || machineState == VmState.Paused)) { if (options.ConfirmMachineRestart) { var answer = VsxDialogs.Show("Are you sure, you want to restart " + "the ZX Spectrum virtual machine?", "The ZX Spectum virtual machine is running", MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1); if (answer == VsxDialogResult.No) { return; } } // --- Stop the machine and allow 50ms to stop. await HostPackage.EmulatorViewModel.Machine.Stop(); await Task.Delay(50); if (vm.MachineState != VmState.Stopped) { const string MESSAGE = "The ZX Spectrum virtual machine did not stop."; await pane.WriteLineAsync(MESSAGE); VsxDialogs.Show(MESSAGE, "Unexpected issue", MessageBoxButton.OK, VsxMessageBoxIcon.Error); return; } } // --- Load the file and keep it paused VmStateFileManager.LoadVmStateFile(filename); HostPackage.EmulatorViewModel.ForceScreenRefresh(); HostPackage.EmulatorViewModel.ForcePauseVmAfterStateRestore(); }
/// <summary> /// Creates the compilation list file as set up in the package options /// </summary> /// <param name="hierarchy">Hierarchy object</param> /// <param name="itemId">Identifier of item to compile</param> private void CreateCompilationListFile(IVsHierarchy hierarchy, uint itemId) { var options = HostPackage.Options; if (!options.GenerateCompilationList || options.GenerateForCompileOnly && !(this is CompileCodeCommand)) { return; } // --- Create list file name if (!(hierarchy is IVsProject project)) { return; } project.GetMkDocument(itemId, out var itemFullPath); var codeFile = Path.GetFileNameWithoutExtension(itemFullPath); var suffix = string.IsNullOrWhiteSpace(options.CompilationFileSuffix) ? string.Empty : DateTime.Now.ToString(options.CompilationFileSuffix); var listFilename = $"{codeFile}{suffix}{options.CompilationFileExtension ?? ".list"}"; var listFolder = string.IsNullOrWhiteSpace(options.CompilationFileFolder) ? LIST_TMP_FOLDER : options.CompilationFileFolder; // -- Make sure list folder exists if (!Directory.Exists(listFolder)) { Directory.CreateDirectory(listFolder); } // --- Save the list file var listContents = CreateListFileContents(); var fullListFileName = Path.Combine(listFolder, listFilename); try { File.WriteAllText(fullListFileName, listContents); } catch { VsxDialogs.Show($"Error when writing list file {fullListFileName}. " + "The file name may contain invalid characters, or you miss file permissions.", "Error when creating list file.", MessageBoxButton.OK, VsxMessageBoxIcon.Error); return; } if (options.AddCompilationToProject) { SpectrumProject.AddFileToProject(HostPackage.Options.CompilationProjectFolder, fullListFileName, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE, false); } }
/// <summary> /// Save data blocks /// </summary> /// <param name="vm">Export parameters</param> /// <param name="blocksToSave">Collection of data blocks to save</param> private static void SaveDataBlocks(ExportZ80ProgramViewModel vm, IEnumerable <byte[]> blocksToSave) { try { // --- Create directory var dirName = Path.GetDirectoryName(vm.Filename); if (dirName != null && !Directory.Exists(dirName)) { Directory.CreateDirectory(dirName); } // --- Save data blocks if (vm.Format == ExportFormat.Tzx) { using (var writer = new BinaryWriter(File.Create(vm.Filename))) { var header = new TzxHeader(); header.WriteTo(writer); foreach (var block in blocksToSave) { var tzxBlock = new TzxStandardSpeedDataBlock { Data = block, DataLength = (ushort)block.Length }; tzxBlock.WriteTo(writer); } } } else { using (var writer = new BinaryWriter(File.Create(vm.Filename))) { foreach (var block in blocksToSave) { writer.Write((ushort)block.Length); writer.Write(block); } } } } catch (Exception ex) { VsxDialogs.Show($"An error has been raised while exporting a program: {ex.Message}", "Errorr when exporting file"); } }
/// <summary> /// Override this method to define the async command body te execute on the /// background thread /// </summary> protected override async Task ExecuteAsync() { if (!(Package.MachineViewModel.SpectrumVm.FloppyDevice is FloppyDevice floppyDevice)) { return; } try { await InsertFloppyAsync(floppyDevice, ItemPath); } catch (Exception) { VsxDialogs.Show("Cannot mount this virtual floppy disk file. It might be corrupt, or is inaccessible.", "Error inserting floppy", MessageBoxButton.OK, VsxMessageBoxIcon.Exclamation); } }
/// <summary> /// Tests if the compiler is available. /// </summary> /// <returns>True, if the compiler is installed, and so available.</returns> public async Task <bool> IsAvailable() { var runner = new ZxbRunner(SpectNetPackage.Default.Options.ZxbPath, 10000); try { await runner.RunAsync(new ZxbOptions()); } catch (Exception ex) { VsxDialogs.Show(string.Format(ZXB_NOT_FOUND_MESSAGE, ex.Message), "Error when running ZXB", MessageBoxButton.OK, VsxMessageBoxIcon.Exclamation); return(false); } return(true); }
protected override void OnExecute() { var tw = Package.GetToolWindow <TapeFileExplorerToolWindow>(); var vm = tw.Content.Vm; var filename = VsxDialogs.FileOpen(TZX_FILTER, vm.LatestPath); if (filename == null) { return; } vm.FileName = filename; vm.LatestPath = Path.GetDirectoryName(filename); vm.ReadFrom(filename); vm.Loaded = true; tw.Content.EditorControl.Vm = vm; }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override Task FinallyOnMainThread() { base.FinallyOnMainThread(); if (IsInInjectMode) { if (Package.Options.ConfirmInjectCode && CodeInjected) { VsxDialogs.Show("The code has been injected."); } } else { if (Package.Options.ConfirmCodeStart && Output.ErrorCount > 0) { VsxDialogs.Show("The code has been started."); } } return(Task.FromResult(0)); }
/// <summary> /// Override this method to define the action to execute on the main /// thread of Visual Studio -- finally /// </summary> protected override async Task FinallyOnMainThreadAsync() { await base.FinallyOnMainThreadAsync(); if (IsInInjectMode) { if (HostPackage.Options.ConfirmInjectCode && CodeInjected) { VsxDialogs.Show("The code has been injected."); } } else { if (HostPackage.Options.ConfirmCodeStart && Output.ErrorCount > 0) { VsxDialogs.Show("The code has been started."); } } }
protected override Task ExecuteOnMainThreadAsync() { var folder = HostPackage.Options.VmStateSaveFileFolder; var filename = VsxDialogs.FileSave(VMSTATE_FILTER, folder); if (filename != null) { var spectrum = HostPackage.EmulatorViewModel.Machine.SpectrumVm; var state = spectrum.GetVmState(HostPackage.Solution.ActiveProject.ModelName); folder = Path.GetDirectoryName(filename); if (folder != null && !Directory.Exists(folder)) { Directory.CreateDirectory(folder); } File.WriteAllText(filename, state); } return(Task.FromResult(0)); }
protected override void OnExecute() { var folder = Package.Options.VmStateSaveFileFolder; var filename = VsxDialogs.FileSave(VMSTATE_FILTER, folder); if (filename == null) { return; } var spectrum = Package.MachineViewModel.SpectrumVm; var state = spectrum.GetVmState(Package.CodeDiscoverySolution.CurrentProject.ModelName); folder = Path.GetDirectoryName(filename); if (folder != null && !Directory.Exists(folder)) { Directory.CreateDirectory(folder); } File.WriteAllText(filename, state); }
/// <summary> /// Sets the active project to the current project file /// </summary> protected async override Task ExecuteAsync() { // --- Collect export parameters from the UI await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (DisplayCreateVfddDialog(out var vm)) { return; } // --- Create a temporary file string fullPath; try { var filename = Path.ChangeExtension(Path.GetFileName(vm.Filename), ".vfdd"); var userPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); fullPath = Path.Combine(Path.Combine(userPath, "SpectNetFloppies"), filename); VirtualFloppyFile.CreateSpectrumFloppyFile(fullPath, vm.Format); } catch (Exception ex) { VsxDialogs.Show($"Error: {ex.Message}", "Error creating virtual floppy disk file"); return; } // --- Add the temp file to the project SpectrumProject.AddFileToProject(SpectNetPackage.Default.Options.VfddFolder, fullPath, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); // --- Remove the temp file try { File.Delete(fullPath); } catch (Exception) { // --- This exception is intentionally ignored } }
/// <summary> /// Exports the memory of the specified virtual machine /// </summary> /// <param name="spectrumVm"></param> public void ExportMemory(ISpectrumVm spectrumVm) { // --- Parse addresses if (!ushort.TryParse(ExportParams.StartAddress, out var startAddress)) { return; } if (!ushort.TryParse(ExportParams.EndAddress, out var endAddress)) { return; } try { var contents = spectrumVm.MemoryDevice.CloneMemory() .Skip(startAddress) .Take(endAddress - startAddress + 1) .ToArray(); var dirName = Path.GetDirectoryName(ExportParams.Filename); if (!string.IsNullOrEmpty(dirName) && !Directory.Exists(dirName)) { Directory.CreateDirectory(dirName); } File.WriteAllBytes(ExportParams.Filename, contents); } catch (Exception ex) { VsxDialogs.Show($"Error while exporting to file {ExportParams.Filename}: {ex.Message}", "Export disassembly error.", MessageBoxButton.OK, VsxMessageBoxIcon.Error); } if (!ExportParams.AddToProject) { return; } SpectrumProject.AddFileToProject(SpectNetPackage.Default.Options.MemoryExportFolder, ExportParams.Filename, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); }
/// <summary> /// Waits while the Spectrum virtual machine starts and reaches its termination point /// </summary> /// <returns>True, if started within timeout; otherwise, false</returns> private async Task <bool> WaitStart() { const int TIME_OUT_IN_SECONDS = 5; var counter = 0; while (Package.MachineViewModel.VmState != VmState.Paused && counter < TIME_OUT_IN_SECONDS * 10) { await Task.Delay(100); counter++; } if (Package.MachineViewModel.VmState != VmState.Paused) { var pane = OutputWindow.GetPane <SpectrumVmOutputPane>(); var message = $"The ZX Spectrum virtual machine did not start within {TIME_OUT_IN_SECONDS} seconds."; pane.WriteLine(message); VsxDialogs.Show(message, "Unexpected issue", MessageBoxButton.OK, VsxMessageBoxIcon.Error); Package.MachineViewModel.StopVm(); return(false); } return(true); }
protected override Task ExecuteAsync() { var tw = SpectNetPackage.Default.GetToolWindow <TapeFileExplorerToolWindow>(); var vm = tw.Content.Vm; var filename = VsxDialogs.FileOpen(TZX_FILTER, vm.LatestPath); if (filename == null) { return(Task.FromResult(0)); } vm.FileName = filename; vm.LatestPath = Path.GetDirectoryName(filename); vm.ReadFrom(filename); vm.Loaded = true; tw.Content.EditorControl.Vm = vm; if (vm.Blocks.Count > 0) { vm.Blocks[0].IsSelected = true; } vm.BlockSelectedCommand.Execute(null); return(Task.FromResult(0)); }
/// <summary> /// Loads the specified .vmstate file /// </summary> /// <param name="stateFile">Full name of the .vmstate file</param> public void LoadVmStateFile(string stateFile) { var state = File.ReadAllText(stateFile); try { Package.MachineViewModel.SpectrumVm.SetVmState(state, Package.CodeDiscoverySolution.CurrentProject.ModelName); Package.MachineViewModel.SpectrumVm.BeeperDevice.Reset(); Package.MachineViewModel.SpectrumVm.BeeperProvider.Reset(); var pane = OutputWindow.GetPane <SpectrumVmOutputPane>(); pane.WriteLine($"Forcing Paused state from {Package.MachineViewModel.VmState}"); Package.MachineViewModel.ForcePauseVmAfterStateRestore(); } catch (InvalidVmStateException e) { VsxDialogs.Show(e.OriginalMessage, "Error loading virtual machine state"); } catch (Exception e) { VsxDialogs.Show($"Unexpected error: {e.Message}", "Error loading virtual machine state"); } }
/// <summary> /// Stops the Spectrum VM, displays confirmation, if required /// </summary> /// <returns>Tru, if start confirmed; otherwise, false</returns> public async Task <bool> StopSpectrumVm(bool needConfirm) { var vm = Package.MachineViewModel; var machineState = vm.VmState; if (machineState == VmState.Running || machineState == VmState.Paused) { if (needConfirm) { var answer = VsxDialogs.Show("Are you sure, you want to restart " + "the ZX Spectrum virtual machine?", "The ZX Spectum virtual machine is running", MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1); if (answer == VsxDialogResult.No) { return(false); } } // --- Stop the machine and allow 50ms to stop. Package.MachineViewModel.StopVm(); await Package.MachineViewModel.MachineController.CompletionTask; if (vm.VmState == VmState.Stopped) { return(true); } const string MESSAGE = "The ZX Spectrum virtual machine did not stop."; var pane = OutputWindow.GetPane <SpectrumVmOutputPane>(); pane.WriteLine(MESSAGE); VsxDialogs.Show(MESSAGE, "Unexpected issue", MessageBoxButton.OK, VsxMessageBoxIcon.Error); return(false); } return(true); }
/// <summary> /// Updates the RAM annotation file according to changes /// </summary> private void OnAnnotationFileChanged(object sender, EventArgs eventArgs) { var project = SpectNetPackage.Default.ActiveProject; var annFile = project?.DefaultAnnotationItem ?? project?.AnnotationProjectItems?.FirstOrDefault(); RamBankAnnotations.Clear(); if (annFile == null) { return; } RamBankAnnotationFile = annFile.Filename; try { var disAnn = File.ReadAllText(annFile.Filename); DisassemblyAnnotation.DeserializeBankAnnotations(disAnn, out var annotations); RamBankAnnotations = annotations; } catch (Exception ex) { VsxDialogs.Show(ex.Message, "Error loading annotation file", MessageBoxButton.OK, VsxMessageBoxIcon.Error); } }
/// <summary> /// Adds the exported file to the project structure /// </summary> /// <param name="projectFolder">Project folder to add the file</param> /// <param name="filename">Filename to add to the project</param> /// <param name="invFolderMessage">Message to display when folder name is invalid</param> /// <param name="fileExistsMessage">Message to display when file already exists</param> /// <param name="confirmOverwrite">When a file exists, the user must confirm overwriting it</param> public static void AddFileToProject(string projectFolder, string filename, string invFolderMessage = null, string fileExistsMessage = null, bool confirmOverwrite = true) { var folderSegments = projectFolder.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); foreach (var segment in folderSegments) { bool valid; try { valid = !Path.IsPathRooted(segment); } catch { valid = false; } if (!valid) { VsxDialogs.Show(invFolderMessage ?? DEFAULT_INV_FOLDER_MESSAGE, "Invalid characters in path"); return; } } // --- Obtain the project and its items var project = SpectNetPackage.Default.ActiveRoot; var projectItems = project.ProjectItems; var currentIndex = 0; var find = true; while (currentIndex < folderSegments.Length) { // --- Find or create folder segments var segment = folderSegments[currentIndex]; if (find) { // --- We are in "find" mode var found = false; // --- Search for the folder segment foreach (ProjectItem projItem in projectItems) { var folder = projItem.Properties.Item("FolderName").Value?.ToString(); if (string.Compare(folder, segment, StringComparison.InvariantCultureIgnoreCase) == 0) { // --- We found the folder, we'll go no with search within this segment projectItems = projItem.ProjectItems; found = true; break; } } if (!found) { // --- Move to "create" mode find = false; } } if (!find) { // --- We're in create mode, add and locate the new folder segment var found = false; projectItems.AddFolder(segment); var parent = projectItems.Parent; if (parent is Project projectType) { projectItems = projectType.ProjectItems; } else if (parent is ProjectItem itemType) { projectItems = itemType.ProjectItems; } foreach (ProjectItem projItem in projectItems) { var folder = projItem.Properties.Item("FolderName").Value?.ToString(); if (string.Compare(folder, segment, StringComparison.InvariantCultureIgnoreCase) == 0) { // --- We found the folder, we'll go no with search within this segment projectItems = projItem.ProjectItems; found = true; break; } } if (!found) { // --- This should not happen... VsxDialogs.Show($"The folder segment {segment} could not be created.", "Adding project item failed"); return; } } // --- Move to the next segment currentIndex++; } // --- Check if that filename exists within the project folder var tempFile = Path.GetFileName(filename); ProjectItem toDelete = null; foreach (ProjectItem projItem in projectItems) { var file = Path.GetFileName(projItem.FileNames[0]); if (string.Compare(file, tempFile, StringComparison.InvariantCultureIgnoreCase) == 0) { if (confirmOverwrite) { var answer = VsxDialogs.Show(fileExistsMessage ?? DEFAULT_FILE_EXISTS_MESSAGE, "File already exists", MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1); if (answer == VsxDialogResult.No) { return; } } toDelete = projItem; break; } } // --- Remove existing file toDelete?.Delete(); // --- Add the item to the appropriate item projectItems.AddFromFileCopy(filename); // --- Refresh the solution's content SpectNetPackage.Default.ActiveProject.CollectItems(); }
/// <summary> /// Compiles the Z80 code file /// </summary> protected override async Task ExecuteAsync() { // --- Prepare the appropriate file to compile/run GetCodeItem(out var hierarchy, out var itemId); // --- Step #1: Compile the code if (!CompileCode(hierarchy, itemId)) { return; } // --- Step #2: Check machine compatibility var modelName = SpectNetPackage.Default.CodeDiscoverySolution?.CurrentProject?.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; } if (!SpectNetPackage.IsCurrentModelCompatibleWith(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 = Package.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; } } // --- Step #5: Stop the virtual machine if required await SwitchToMainThreadAsync(); Package.ShowToolWindow <SpectrumEmulatorToolWindow>(); var pane = OutputWindow.GetPane <SpectrumVmOutputPane>(); var vm = Package.MachineViewModel; var stopped = await Package.CodeManager.StopSpectrumVm(options.ConfirmMachineRestart); if (!stopped) { return; } // --- Step #6: Start the virtual machine so that later we can load the program pane.WriteLine("Starting the virtual machine in code injection mode."); // --- Use specific startup for each model. bool started = true; try { switch (modelName) { case SpectrumModels.ZX_SPECTRUM_48: await Package.StateFileManager.SetSpectrum48StartupState(); break; case SpectrumModels.ZX_SPECTRUM_128: if (modelType == SpectrumModelType.Spectrum48) { await Package.StateFileManager.SetSpectrum128In48StartupState(); } else { await Package.StateFileManager.SetSpectrum128In128StartupState(); } break; case SpectrumModels.ZX_SPECTRUM_P3_E: if (modelType == SpectrumModelType.Spectrum48) { await Package.StateFileManager.SetSpectrumP3In48StartupState(); } else { await Package.StateFileManager.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 pane.WriteLine("Injecting code into the Spectrum virtual machine."); Package.CodeManager.InjectCodeIntoVm(Output); // --- Step #8: Jump to execute the code var continuationPoint = GetContinuationAddress(); if (continuationPoint.HasValue) { vm.SpectrumVm.Cpu.Registers.PC = continuationPoint.Value; pane.WriteLine($"Resuming code execution at address {vm.SpectrumVm.Cpu.Registers.PC:X4}."); } ResumeVm(); }