protected override async Task ExecuteAsync() { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (AddVmStateParameterDialog(out var vm)) { return; } // --- Save the file into the vmstate folder var filename = Path.Combine(HostPackage.Options.VmStateSaveFileFolder, vm.Filename); filename = Path.ChangeExtension(filename, ".vmstate"); var spectrum = HostPackage.EmulatorViewModel.Machine.SpectrumVm; var state = spectrum.GetVmState(HostPackage.Solution.ActiveProject.ModelName); var folder = Path.GetDirectoryName(filename); if (folder != null && !Directory.Exists(folder)) { Directory.CreateDirectory(folder); } File.WriteAllText(filename, state); SpectrumProject.AddFileToProject(HostPackage.Options.VmStateProjectFolder, filename, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); }
/// <summary> /// The virtual machine has just saved a file. /// </summary> private void MachineOnLeftSaveMode(object sender, SaveModeEventArgs e) { Dispatcher.Invoke(() => { SpectrumProject.AddFileToProject( SpectNetPackage.Default.Options.TapeFolder, e.FileName, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); }); }
/// <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> /// 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> /// Exports the disassembly using the specified disassembler object /// </summary> /// <param name="disassembler">Disassembler set up for annotations</param> public void ExportDisassembly(Z80Disassembler disassembler) { // --- Create the disassembly output if (!ushort.TryParse(ExportParams.StartAddress, out var startAddress)) { return; } if (!ushort.TryParse(ExportParams.EndAddress, out var endAddress)) { return; } var output = disassembler.Disassemble(startAddress, endAddress); var equs = new Dictionary <string, ushort>(); var items = new List <DisassemblyItemViewModel>(); var addresses = new HashSet <ushort>(); // --- Create the set of addresses that may be referred through labels foreach (var outputItem in output.OutputItems) { addresses.Add(outputItem.Address); } // --- Collect all external labels and symbols foreach (var outputItem in output.OutputItems) { items.Add(new DisassemblyItemViewModel(ParentViewModel, outputItem)); if (outputItem.HasLabelSymbol) { // --- Check if it is an external label if (outputItem.SymbolValue >= startAddress && outputItem.SymbolValue <= endAddress) { continue; } if (ParentViewModel.GetLabel(outputItem.SymbolValue, out var extLabel)) { equs[extLabel] = outputItem.SymbolValue; } } // --- Check if literal replacement else if (ParentViewModel.GetLiteralReplacement(outputItem.Address, out var literal)) { equs[literal] = outputItem.SymbolValue; } } // --- Create the exported contents item by item var contents = new StringBuilder(4000); // --- Export the origin contents.AppendLine($"{InstructionIndent}.org #{startAddress:X4}"); contents.AppendLine(); // --- Create .EQU lines for external labels and symbols contents.AppendLine($"{CommentBegins}External symbols{CommentEnds}"); foreach (var symbolKey in equs.Keys.OrderBy(k => k)) { if (ExportParams.HangingLabels) { contents.AppendLine($"{symbolKey}:"); contents.AppendLine($"{InstructionIndent}.equ #{equs[symbolKey]:X4}"); } else { contents.AppendLine($"{symbolKey}: .equ #{equs[symbolKey]:X4}"); } } contents.AppendLine(); // --- Iterate output items foreach (var item in items) { var lineContents = new StringBuilder(200); // --- Handle prefix comment if (item.HasPrefixComment) { contents.AppendLine(); OutputComment(item.PrefixCommentFormatted, contents, MaxLineLength - CommentCharCount, ""); } // --- Handle label if (!string.IsNullOrEmpty(item.LabelFormatted)) { if (ExportParams.HangingLabels) { // --- Hanging label to a separate line contents.AppendLine($"{item.LabelFormatted}"); lineContents.Append(InstructionIndent); } else { // ---Label goes to the instruction line lineContents.Append($"{item.LabelFormatted} "); } } else { // --- Instruction only lineContents.Append(InstructionIndent); } // --- Instruction part: take care labels that cannot be accessed through instructions var instruction = item.InstructionFormatted; var instrItem = item.Item; if (item.Item.HasLabelSymbol && !addresses.Contains(item.Item.SymbolValue)) { if (instrItem.SymbolValue >= startAddress && instrItem.SymbolValue <= endAddress || !ParentViewModel.GetLabel(instrItem.SymbolValue, out _)) { // --- Internal or external label without associated symbol // --- Change the disassembly label name to the corresponding address var addressString = $"#{instrItem.SymbolValue:X4}"; instruction = instrItem.Instruction.Substring(0, instrItem.TokenPosition) + addressString + instrItem.Instruction.Substring(instrItem.TokenPosition + instrItem.TokenLength); } } lineContents.Append(instruction); // --- Handle line comment if (!string.IsNullOrEmpty(item.CommentFormatted)) { var maxCommentLength = MaxLineLength - CommentCharCount - lineContents.Length - 1; if (maxCommentLength < 20) { // --- Comment does not fit into this line contents.AppendLine(lineContents.ToString()); OutputComment(item.CommentFormatted, contents, MaxLineLength - CommentCharCount - InstructionIndent.Length, InstructionIndent); } else { // --- Comment fits into this line lineContents.Append(" "); OutputComment(item.CommentFormatted, contents, maxCommentLength, lineContents.ToString()); } } else { // --- Output the remainder of the line if (lineContents.Length > 0) { contents.AppendLine(lineContents.ToString()); } } } // --- Save the file try { var dirName = Path.GetDirectoryName(ExportParams.Filename); if (!string.IsNullOrEmpty(dirName) && !Directory.Exists(dirName)) { Directory.CreateDirectory(dirName); } File.WriteAllText(ExportParams.Filename, contents.ToString()); } catch (Exception ex) { VsxDialogs.Show($"Error while exporting to file {ExportParams.Filename}: {ex.Message}", "Export disassembly error.", MessageBoxButton.OK, VsxMessageBoxIcon.Error); return; } if (!ExportParams.AddToProject) { return; } // --- Step #6: Add the saved item to the project // --- Check path segment names SpectrumProject.AddFileToProject(SpectNetPackage.Default.Options.DisassExportFolder, ExportParams.Filename, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); }
/// <summary> /// Override this method to define the async command body te execute on the /// background thread /// </summary> protected override Task ExecuteAsync() { SpectrumProject?.SetDefaultAnnotationItem(this); return(Task.FromResult(0)); }
/// <summary> /// Compiles the Z80 code file /// </summary> protected override async Task ExecuteAsync() { // --- Prepare the appropriate file to export Success = true; // --- Step #1: Compile the code if (!await CompileCode()) { Success = false; return; } // --- Step #2: Check for zero code length if (Output.Segments.Sum(s => s.EmittedCode.Count) == 0) { VsxDialogs.Show("The length of the compiled code is 0, " + "so there is no code to export.", "No code to export."); Success = false; return; } // --- Step #3: Collect export parameters from the UI await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (DisplayExportParameterDialog(out var vm)) { return; } // --- Step #4: Execute pre-export event var codeManager = SpectNetPackage.Default.CodeManager; PreexportError = null; PostexportError = null; var eventOutput = await codeManager.RunPreExportEvent(ItemFullPath, vm.Filename); if (eventOutput != null) { PreexportError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); Success = false; return; } if (vm.Format == ExportFormat.IntelHex) { // --- Step #5: Export to Intel format SaveIntelHexFile(vm.Filename, Output); } else { // --- Step #5: Check screen file again var useScreenFile = !string.IsNullOrEmpty(vm.ScreenFile) && vm.ScreenFile.Trim().Length > 0; if (useScreenFile && !CommonTapeFilePlayer.CheckScreenFile(vm.ScreenFile)) { VsxDialogs.Show("The specified screen file cannot be read as a ZX Spectrum compatible screen file.", "Screen file error.", MessageBoxButton.OK, VsxMessageBoxIcon.Error); Success = false; return; } // --- Step #6: Create code segments var codeBlocks = CreateTapeBlocks(vm.Name, vm.SingleBlock); List <byte[]> screenBlocks = null; if (useScreenFile) { screenBlocks = CreatScreenBlocks(vm.ScreenFile); } // --- Step #7: Create Auto Start header block, if required var blocksToSave = new List <byte[]>(); if (!ushort.TryParse(vm.StartAddress, out var startAddress)) { startAddress = (ushort)ExportStartAddress; } if (vm.AutoStartEnabled) { var autoStartBlocks = CreateAutoStartBlock( vm.Name, useScreenFile, vm.AddPause0, vm.Border, startAddress, vm.ApplyClear ? Output.Segments.Min(s => s.StartAddress) : (ushort?)null); blocksToSave.AddRange(autoStartBlocks); } // --- Step #8: Save all the blocks if (screenBlocks != null) { blocksToSave.AddRange(screenBlocks); } blocksToSave.AddRange(codeBlocks); SaveDataBlocks(vm, blocksToSave); if (vm.AddToProject) { // --- Step #9: Add the saved item to the project // --- Check path segment names SpectrumProject.AddFileToProject(SpectNetPackage.Default.Options.TapeFolder, vm.Filename, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); } } // --- Run post-export event // --- Execute post-build event eventOutput = await codeManager.RunPostExportEvent(ItemFullPath, vm.Filename); if (eventOutput != null) { PostexportError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); Success = false; } }
/// <summary> /// Compiles the Z80 code file /// </summary> protected override async Task ExecuteAsync() { // --- Prepare the appropriate file to export Success = true; // --- Step #1: Compile the code if (!await CompileCode()) { Success = false; return; } // --- Step #2: Check for zero code length if (Output.Segments.Sum(s => s.EmittedCode.Count) == 0) { VsxDialogs.Show("The length of the compiled code is 0, " + "so there is no code to export.", "No code to export."); Success = false; return; } // --- Step #3: Collect export parameters from the UI await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (DisplayExportParameterDialog(out var vm)) { return; } // --- Step #4: Execute pre-export event var codeManager = SpectNetPackage.Default.CodeManager; PreexportError = null; PostexportError = null; var eventOutput = await codeManager.RunPreExportEvent(ItemFullPath, vm.Filename); if (eventOutput != null) { PreexportError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); Success = false; return; } vm.Name = Path.GetFileNameWithoutExtension(ItemPath) ?? "MyCode"; var result = ExportCompiledCode(Output, vm); if (result != 0) { VsxDialogs.Show("The specified screen file cannot be read as a ZX Spectrum compatible screen file.", "Screen file error.", MessageBoxButton.OK, VsxMessageBoxIcon.Error); Success = false; return; } if (vm.AddToProject) { // --- Step #9: Add the saved item to the project // --- Check path segment names SpectrumProject.AddFileToProject(SpectNetPackage.Default.Options.TapeFolder, vm.Filename, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); } // --- Run post-export event // --- Execute post-build event eventOutput = await codeManager.RunPostExportEvent(ItemFullPath, vm.Filename); if (eventOutput != null) { PostexportError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); Success = false; } }