/// <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 DiscoveryProject.AddFileToProject(SpectNetPackage.Default.Options.DisassExportFolder, ExportParams.Filename, INVALID_FOLDER_MESSAGE, FILE_EXISTS_MESSAGE); }