Example #1
0
        /// <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);
        }