diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs
index f9309d7..3c25e5d 100644
--- a/SourceGen/AppSettings.cs
+++ b/SourceGen/AppSettings.cs
@@ -115,6 +115,18 @@ namespace SourceGen {
// Assembler settings prefix
public const string ASM_CONFIG_PREFIX = "asm-config-";
+ // Text/HTML export settings.
+ public const string EXPORT_INCLUDE_NOTES = "export-include-notes";
+ public const string EXPORT_SHOW_OFFSET = "export-show-offset";
+ public const string EXPORT_SHOW_ADDR = "export-show-addr";
+ public const string EXPORT_SHOW_BYTES = "export-show-bytes";
+ public const string EXPORT_SHOW_FLAGS = "export-show-flags";
+ public const string EXPORT_SHOW_ATTR = "export-show-attr";
+ public const string EXPORT_COL_WIDTHS = "export-col-widths";
+ public const string EXPORT_TEXT_MODE = "export-text-mode";
+ public const string EXPORT_SELECTION_ONLY = "export-selection-only";
+ public const string EXPORT_INCLUDE_SYMTAB = "export-include-symtab";
+
// Internal debugging features.
public const string DEBUG_MENU_ENABLED = "debug-menu-enabled";
diff --git a/SourceGen/Exporter.cs b/SourceGen/Exporter.cs
index fb8a97c..06a7b93 100644
--- a/SourceGen/Exporter.cs
+++ b/SourceGen/Exporter.cs
@@ -14,16 +14,53 @@
* limitations under the License.
*/
using System;
-using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Text;
using Asm65;
using CommonUtil;
-using ClipLineFormat = SourceGen.MainController.ClipLineFormat;
namespace SourceGen {
+ ///
+ /// Source code export functions.
+ ///
+ ///
+ /// The five columns on the left (offset, address, bytes, flags, attributes) are optional,
+ /// and have a fixed width. The four columns on the right (label, opcode, operand, comment)
+ /// are mandatory, and have configurable widths.
+ ///
public class Exporter {
+ ///
+ /// Optional selection specifier. If null, the entire file is included.
+ ///
+ public DisplayListSelection Selection { get; set; }
+
+ ///
+ /// Should notes be included in the output?
+ ///
+ public bool IncludeNotes { get; set; }
+
+ ///
+ /// Bit flags, used to indicate which of the optional columns are active.
+ ///
+ [FlagsAttribute]
+ public enum ActiveColumnFlags {
+ None = 0,
+ Offset = 1,
+ Address = 1 << 1,
+ Bytes = 1 << 2,
+ Flags = 1 << 3,
+ Attr = 1 << 4,
+
+ ALL = 0x7f
+ }
+
+ ///
+ /// Flags indicating active optional columns.
+ ///
+ private ActiveColumnFlags mLeftFlags;
+
///
/// Project reference.
///
@@ -39,117 +76,282 @@ namespace SourceGen {
///
private Formatter mFormatter;
+ ///
+ /// The cumulative width of the columns determines the end point.
+ ///
+ private int[] mColEnd;
+
+ private enum Col {
+ Offset = 0,
+ Address = 1,
+ Bytes = 2,
+ Flags = 3,
+ Attr = 4,
+ Label = 5,
+ Opcode = 6,
+ Operand = 7,
+ Comment = 8,
+ COUNT // number of elements, must be last
+ }
+
///
/// Constructor.
///
- public Exporter(DisasmProject project, LineListGen codeLineList, Formatter formatter) {
+ public Exporter(DisasmProject project, LineListGen codeLineList, Formatter formatter,
+ ActiveColumnFlags leftFlags, int[] rightWidths) {
mProject = project;
mCodeLineList = codeLineList;
mFormatter = formatter;
+ mLeftFlags = leftFlags;
+
+ ConfigureColumns(leftFlags, rightWidths);
+ }
+
+ private void ConfigureColumns(ActiveColumnFlags leftFlags, int[] rightWidths) {
+ mColEnd = new int[(int)Col.COUNT];
+ int total = 0;
+ int width;
+
+ // offset "+123456"
+ if ((leftFlags & ActiveColumnFlags.Offset) != 0) {
+ total = mColEnd[(int)Col.Offset] = total + 7 + 1;
+ } else {
+ mColEnd[(int)Col.Offset] = total;
+ }
+
+ // address "1234:" or "12/4567:"
+ if ((leftFlags & ActiveColumnFlags.Address) != 0) {
+ width = mProject.CpuDef.HasAddr16 ? 5 : 8;
+ total = mColEnd[(int)Col.Address] = total + width + 1;
+ } else {
+ mColEnd[(int)Col.Address] = total;
+ }
+
+ // bytes "12345678+" or "12 45 78 01+"
+ if ((leftFlags & ActiveColumnFlags.Bytes) != 0) {
+ // A limit of 8 gets us 4 bytes from dense display ("20edfd60") and 3 if spaces
+ // are included ("20 ed fd") with no excess. We want to increase it to 11 so
+ // we can always show 4 bytes. Add one for a trailing "+".
+ width = mFormatter.Config.mSpacesBetweenBytes ? 12 : 9;
+ total = mColEnd[(int)Col.Bytes] = total + width + 1;
+ } else {
+ mColEnd[(int)Col.Bytes] = total;
+ }
+
+ // flags "NVMXDIZC" or "NVMXDIZC E"
+ if ((leftFlags & ActiveColumnFlags.Flags) != 0) {
+ width = mProject.CpuDef.HasEmuFlag ? 10 : 8;
+ total = mColEnd[(int)Col.Flags] = total + width + 1;
+ } else {
+ mColEnd[(int)Col.Flags] = total;
+ }
+
+ // attributes "@H!#>"
+ if ((leftFlags & ActiveColumnFlags.Attr) != 0) {
+ total = mColEnd[(int)Col.Attr] = total + 5 + 1;
+ } else {
+ mColEnd[(int)Col.Attr] = total;
+ }
+
+ total = mColEnd[(int)Col.Label] = total + rightWidths[0];
+ total = mColEnd[(int)Col.Opcode] = total + rightWidths[1];
+ total = mColEnd[(int)Col.Operand] = total + rightWidths[2];
+ total = mColEnd[(int)Col.Comment] = total + rightWidths[3];
+
+ //Debug.WriteLine("Export col ends:");
+ //for (int i = 0; i < (int)Col.COUNT; i++) {
+ // Debug.WriteLine(" " + i + "(" + ((Col)i) + ") " + mColEnd[i]);
+ //}
}
///
/// Converts the selected lines to formatted text.
///
- /// Set of lines to select.
- /// Line format.
/// Result; holds text of all selected lines.
/// Result; holds text of all selected lines, in CSV format.
- public void SelectionToText(DisplayListSelection selection, ClipLineFormat lineFormat,
- bool addCsv, out string fullText, out string csvText) {
-
- StringBuilder plainText = new StringBuilder(selection.Count * 50);
- StringBuilder sb = new StringBuilder(100);
+ public void SelectionToString(bool addCsv, out string fullText, out string csvText) {
+ StringBuilder sb = new StringBuilder(128);
+ StringBuilder plainText = new StringBuilder(Selection.Count * 50);
StringBuilder csv = null;
if (addCsv) {
- csv = new StringBuilder(selection.Count * 40);
+ csv = new StringBuilder(Selection.Count * 40);
}
- int addrAdj = mProject.CpuDef.HasAddr16 ? 6 : 9;
- int disAdj = 0;
- int bytesWidth = 0;
- if (lineFormat == MainController.ClipLineFormat.Disassembly) {
- // A limit of 8 gets us 4 bytes from dense display ("20edfd60") and 3 if spaces
- // are included ("20 ed fd") with no excess. We want to increase it to 11 so
- // we can always show 4 bytes.
- bytesWidth = (mFormatter.Config.mSpacesBetweenBytes ? 11 : 8);
- disAdj = addrAdj + bytesWidth + 2;
- }
-
- // Walking through the selected indices can be slow for a large file, so we
- // run through the full list and pick out the selected items with our parallel
- // structure. (I'm assuming that "select all" will be a common precursor.)
- foreach (int index in selection) {
- LineListGen.Line line = mCodeLineList[index];
- DisplayList.FormattedParts parts = mCodeLineList.GetFormattedParts(index);
- switch (line.LineType) {
- case LineListGen.Line.Type.Code:
- case LineListGen.Line.Type.Data:
- case LineListGen.Line.Type.EquDirective:
- case LineListGen.Line.Type.RegWidthDirective:
- case LineListGen.Line.Type.OrgDirective:
- case LineListGen.Line.Type.LocalVariableTable:
- if (lineFormat == ClipLineFormat.Disassembly) {
- if (!string.IsNullOrEmpty(parts.Addr)) {
- sb.Append(parts.Addr);
- sb.Append(": ");
- }
-
- // Shorten the "...".
- string bytesStr = parts.Bytes;
- if (bytesStr != null && bytesStr.Length > bytesWidth) {
- bytesStr = bytesStr.Substring(0, bytesWidth) + "+";
- }
- TextUtil.AppendPaddedString(sb, bytesStr, disAdj);
- }
- TextUtil.AppendPaddedString(sb, parts.Label, disAdj + 9);
- TextUtil.AppendPaddedString(sb, parts.Opcode, disAdj + 9 + 8);
- TextUtil.AppendPaddedString(sb, parts.Operand, disAdj + 9 + 8 + 11);
- if (string.IsNullOrEmpty(parts.Comment)) {
- // Trim trailing spaces off opcode or operand.
- TextUtil.TrimEnd(sb);
- } else {
- sb.Append(parts.Comment);
- }
- sb.Append("\r\n");
- break;
- case LineListGen.Line.Type.LongComment:
- if (lineFormat == ClipLineFormat.Disassembly) {
- TextUtil.AppendPaddedString(sb, string.Empty, disAdj);
- }
- sb.Append(parts.Comment);
- sb.Append("\r\n");
- break;
- case LineListGen.Line.Type.Note:
- // don't include notes
- break;
- case LineListGen.Line.Type.Blank:
- sb.Append("\r\n");
- break;
- default:
- Debug.Assert(false);
- break;
+ for (int lineIndex = 0; lineIndex < mCodeLineList.Count; lineIndex++) {
+ if (!Selection[lineIndex]) {
+ continue;
+ }
+ if (GenerateTextLine(lineIndex, sb)) {
+ plainText.Append(sb.ToString());
+ plainText.Append("\r\n");
}
- plainText.Append(sb);
sb.Clear();
-
if (addCsv) {
- csv.Append(TextUtil.EscapeCSV(parts.Offset)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Addr)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Bytes)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Flags)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Attr)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Label)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Opcode)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Operand)); csv.Append(',');
- csv.Append(TextUtil.EscapeCSV(parts.Comment));
+ GenerateCsvLine(lineIndex, sb);
+ csv.Append(sb.ToString());
csv.Append("\r\n");
+ sb.Clear();
}
}
fullText = plainText.ToString();
- csvText = csv.ToString();
+ if (addCsv) {
+ csvText = csv.ToString();
+ } else {
+ csvText = null;
+ }
+ }
+
+ ///
+ /// Generates a full listing and writes it to the specified file.
+ ///
+ /// Full path to output file.
+ /// Output as Comma Separated Values rather than plain text.
+ public void OutputToText(string pathName, bool asCsv) {
+ // Generate UTF-8 text. For plain text we omit the byte-order mark, for CSV
+ // it appears to be meaningful (tested w/ very old version of Excel).
+ using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(asCsv))) {
+ StringBuilder sb = new StringBuilder(128);
+ for (int lineIndex = 0; lineIndex < mCodeLineList.Count; lineIndex++) {
+ if (Selection != null && !Selection[lineIndex]) {
+ continue;
+ }
+
+ if (asCsv) {
+ GenerateCsvLine(lineIndex, sb);
+ sw.WriteLine(sb.ToString());
+ } else {
+ if (GenerateTextLine(lineIndex, sb)) {
+ sw.WriteLine(sb.ToString());
+ }
+ }
+ sb.Clear();
+ }
+ }
+ }
+
+ ///
+ /// Generates a line of plain text output. The line will not have EOL markers added.
+ ///
+ /// Index of line to output.
+ /// String builder to append text to. Must be cleared before
+ /// calling here. (This is a minor optimization.)
+ private bool GenerateTextLine(int index, StringBuilder sb) {
+ Debug.Assert(sb.Length == 0);
+
+ // Width of "bytes" field, without '+' or trailing space.
+ int bytesWidth = mColEnd[(int)Col.Bytes] - mColEnd[(int)Col.Address] - 2;
+
+ LineListGen.Line line = mCodeLineList[index];
+ DisplayList.FormattedParts parts = mCodeLineList.GetFormattedParts(index);
+ switch (line.LineType) {
+ case LineListGen.Line.Type.Code:
+ case LineListGen.Line.Type.Data:
+ case LineListGen.Line.Type.EquDirective:
+ case LineListGen.Line.Type.RegWidthDirective:
+ case LineListGen.Line.Type.OrgDirective:
+ case LineListGen.Line.Type.LocalVariableTable:
+ if (parts.IsLongComment) {
+ // This happens for long comments embedded in LV tables.
+ if (mColEnd[(int)Col.Attr] != 0) {
+ TextUtil.AppendPaddedString(sb, string.Empty, mColEnd[(int)Col.Attr]);
+ }
+ sb.Append(parts.Comment);
+ break;
+ }
+
+ if ((mLeftFlags & ActiveColumnFlags.Offset) != 0) {
+ TextUtil.AppendPaddedString(sb, parts.Offset,
+ mColEnd[(int)Col.Offset]);
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Address) != 0) {
+ string str;
+ if (!string.IsNullOrEmpty(parts.Addr)) {
+ str = parts.Addr + ":";
+ } else {
+ str = string.Empty;
+ }
+ TextUtil.AppendPaddedString(sb, str, mColEnd[(int)Col.Address]);
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Bytes) != 0) {
+ // Shorten the "...".
+ string bytesStr = parts.Bytes;
+ if (bytesStr != null && bytesStr.Length > bytesWidth) {
+ bytesStr = bytesStr.Substring(0, bytesWidth) + "+";
+ }
+ TextUtil.AppendPaddedString(sb, bytesStr, mColEnd[(int)Col.Bytes]);
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Flags) != 0) {
+ TextUtil.AppendPaddedString(sb, parts.Flags, mColEnd[(int)Col.Flags]);
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Attr) != 0) {
+ TextUtil.AppendPaddedString(sb, parts.Attr, mColEnd[(int)Col.Attr]);
+ }
+ TextUtil.AppendPaddedString(sb, parts.Label, mColEnd[(int)Col.Label]);
+ TextUtil.AppendPaddedString(sb, parts.Opcode, mColEnd[(int)Col.Opcode]);
+ TextUtil.AppendPaddedString(sb, parts.Operand, mColEnd[(int)Col.Operand]);
+ if (string.IsNullOrEmpty(parts.Comment)) {
+ // Trim trailing spaces off opcode or operand. Would be more efficient
+ // to just not generate the spaces, but this is simpler and we're not
+ // in a hurry.
+ TextUtil.TrimEnd(sb);
+ } else {
+ sb.Append(parts.Comment);
+ }
+ break;
+ case LineListGen.Line.Type.LongComment:
+ case LineListGen.Line.Type.Note:
+ if (line.LineType == LineListGen.Line.Type.Note && !IncludeNotes) {
+ return false;
+ }
+ if (mColEnd[(int)Col.Attr] != 0) {
+ // Long comments aren't the left-most field, so pad it out.
+ TextUtil.AppendPaddedString(sb, string.Empty, mColEnd[(int)Col.Attr]);
+ }
+ sb.Append(parts.Comment);
+ break;
+ case LineListGen.Line.Type.Blank:
+ break;
+ default:
+ Debug.Assert(false);
+ break;
+ }
+ return true;
+ }
+
+ private void GenerateCsvLine(int index, StringBuilder sb) {
+ LineListGen.Line line = mCodeLineList[index];
+ DisplayList.FormattedParts parts = mCodeLineList.GetFormattedParts(index);
+
+ if ((mLeftFlags & ActiveColumnFlags.Offset) != 0) {
+ sb.Append(TextUtil.EscapeCSV(parts.Offset)); sb.Append(',');
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Address) != 0) {
+ sb.Append(TextUtil.EscapeCSV(parts.Addr)); sb.Append(',');
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Bytes) != 0) {
+ sb.Append(TextUtil.EscapeCSV(parts.Bytes)); sb.Append(',');
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Flags) != 0) {
+ sb.Append(TextUtil.EscapeCSV(parts.Flags)); sb.Append(',');
+ }
+ if ((mLeftFlags & ActiveColumnFlags.Attr) != 0) {
+ sb.Append(TextUtil.EscapeCSV(parts.Attr)); sb.Append(',');
+ }
+ if (parts.IsLongComment) {
+ // put the comment in the Label column
+ sb.Append(TextUtil.EscapeCSV(parts.Comment)); sb.Append(",,,");
+ } else {
+ sb.Append(TextUtil.EscapeCSV(parts.Label)); sb.Append(',');
+ sb.Append(TextUtil.EscapeCSV(parts.Opcode)); sb.Append(',');
+ sb.Append(TextUtil.EscapeCSV(parts.Operand)); sb.Append(',');
+ sb.Append(TextUtil.EscapeCSV(parts.Comment));
+ }
+ }
+
+ public void OutputToHtml(string pathName, bool overwriteCss) {
+ Debug.WriteLine("HTML"); // TODO
}
}
}
diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs
index 1ce2a83..8c434aa 100644
--- a/SourceGen/LineListGen.cs
+++ b/SourceGen/LineListGen.cs
@@ -1256,9 +1256,17 @@ namespace SourceGen {
int cycles = mProject.CpuDef.GetCycles(op.Opcode, attr.StatusFlags,
attr.BranchTaken, branchCross);
if (cycles > 0) {
- eolComment = cycles.ToString() + " " + eolComment;
+ if (!string.IsNullOrEmpty(eolComment)) {
+ eolComment = cycles.ToString() + " " + eolComment;
+ } else {
+ eolComment = cycles.ToString();
+ }
} else {
- eolComment = (-cycles).ToString() + "+ " + eolComment;
+ if (!string.IsNullOrEmpty(eolComment)) {
+ eolComment = (-cycles).ToString() + "+ " + eolComment;
+ } else {
+ eolComment = (-cycles).ToString() + "+";
+ }
}
}
string commentStr = mFormatter.FormatEolComment(eolComment);
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 27039ac..73e4fbe 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -185,7 +185,8 @@ namespace SourceGen {
public enum ClipLineFormat {
Unknown = -1,
AssemblerSource = 0,
- Disassembly = 1
+ Disassembly = 1,
+ AllColumns = 2
}
///
@@ -1291,14 +1292,29 @@ namespace SourceGen {
///
public void CopyToClipboard() {
DisplayListSelection selection = mMainWin.CodeDisplayList.SelectedIndices;
+ if (selection.Count == 0) {
+ Debug.WriteLine("Selection is empty!");
+ return;
+ }
+
ClipLineFormat format = (ClipLineFormat)AppSettings.Global.GetEnum(
AppSettings.CLIP_LINE_FORMAT,
typeof(ClipLineFormat),
(int)ClipLineFormat.AssemblerSource);
- Exporter eport = new Exporter(mProject, CodeLineList, mOutputFormatter);
- eport.SelectionToText(selection, format, true,
- out string fullText, out string csvText);
+ int[] rightWidths = new int[] { 9, 8, 11, 100 };
+
+ Exporter.ActiveColumnFlags colFlags = Exporter.ActiveColumnFlags.None;
+ if (format == ClipLineFormat.Disassembly) {
+ colFlags |= Exporter.ActiveColumnFlags.Address |
+ Exporter.ActiveColumnFlags.Bytes;
+ } else if (format == ClipLineFormat.AllColumns) {
+ colFlags = Exporter.ActiveColumnFlags.ALL;
+ }
+ Exporter eport = new Exporter(mProject, CodeLineList, mOutputFormatter,
+ colFlags, rightWidths);
+ eport.Selection = selection;
+ eport.SelectionToString(true, out string fullText, out string csvText);
DataObject dataObject = new DataObject();
dataObject.SetText(fullText.ToString());
@@ -1954,12 +1970,45 @@ namespace SourceGen {
}
public void Export() {
- Export dlg = new Export(mMainWin);
+ string outName;
+ if (string.IsNullOrEmpty(mProjectPathName)) {
+ outName = Path.GetFileName(mDataPathName);
+ } else {
+ outName = Path.GetFileName(mProjectPathName);
+ }
+
+ Export dlg = new Export(mMainWin, outName);
if (dlg.ShowDialog() == false) {
return;
}
- // TODO
+ int[] rightWidths = new int[] {
+ dlg.AsmLabelColWidth, dlg.AsmOpcodeColWidth,
+ dlg.AsmOperandColWidth, dlg.AsmCommentColWidth
+ };
+ Exporter eport = new Exporter(mProject, CodeLineList, mOutputFormatter,
+ dlg.ColFlags, rightWidths);
+ eport.IncludeNotes = dlg.IncludeNotes;
+ if (dlg.SelectionOnly) {
+ DisplayListSelection selection = mMainWin.CodeDisplayList.SelectedIndices;
+ if (selection.Count == 0) {
+ // no selection == select all
+ selection = null;
+ }
+ eport.Selection = selection;
+ }
+
+ try {
+ Mouse.OverrideCursor = Cursors.Wait;
+
+ if (dlg.GenType == WpfGui.Export.GenerateFileType.Html) {
+ eport.OutputToHtml(dlg.PathName, dlg.OverwriteCss);
+ } else {
+ eport.OutputToText(dlg.PathName, dlg.TextModeCsv);
+ }
+ } finally {
+ Mouse.OverrideCursor = null;
+ }
}
public void Find() {
diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml
index 42ae788..ebd26e7 100644
--- a/SourceGen/Res/Strings.xaml
+++ b/SourceGen/Res/Strings.xaml
@@ -30,6 +30,7 @@ limitations under the License.
Assembled output does not match: offset +{0:x6} has value ${1:x2}, expected ${2:x2}.
Assembled output does not match: length is {0}, expected {1}.
Expected output file wasn't created
+ All Columns
Assembler Source
Disassembly
6502bench SourceGen v{0}
@@ -64,9 +65,12 @@ limitations under the License.
Symbol files and extension scripts must live in the application runtime directory ({0}) or project directory ({1}).
File {2} lives elsewhere.
File Not In Runtime Directory
All files (*.*)|*.*
- C# Source Files(*.cs)|*.cs
- SourceGen projects(*.dis65)|*.dis65
+ C# Source Files (*.cs)|*.cs
+ CSV files (*.csv)|*.csv
+ SourceGen projects (*.dis65)|*.dis65
+ HTML files (*.html)|*.html
SourceGen symbols (*.sym65)|*.sym65
+ Text files (*.txt)|*.txt
File is {0:N1} KB of raw data.
Find reached the starting point of the search.
Find...
diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs
index ee9cc53..36903ff 100644
--- a/SourceGen/Res/Strings.xaml.cs
+++ b/SourceGen/Res/Strings.xaml.cs
@@ -53,6 +53,8 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_DefaultC64PetsciiDelimPat");
public static string DEFAULT_C64_SCREEN_CODE_DELIM_PAT =
(string)Application.Current.FindResource("str_DefaultC64ScreenCodeDelimPat");
+ public static string CLIPFORMAT_ALL_COLUMNS =
+ (string)Application.Current.FindResource("str_ClipformatAllColumns");
public static string CLIPFORMAT_ASSEMBLER_SOURCE =
(string)Application.Current.FindResource("str_ClipformatAssemblerSource");
public static string CLIPFORMAT_DISASSEMBLY =
@@ -111,10 +113,16 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_FileFilterAll");
public static string FILE_FILTER_CS =
(string)Application.Current.FindResource("str_FileFilterCs");
+ public static string FILE_FILTER_CSV =
+ (string)Application.Current.FindResource("str_FileFilterCsv");
public static string FILE_FILTER_DIS65 =
(string)Application.Current.FindResource("str_FileFilterDis65");
+ public static string FILE_FILTER_HTML =
+ (string)Application.Current.FindResource("str_FileFilterHtml");
public static string FILE_FILTER_SYM65 =
(string)Application.Current.FindResource("str_FileFilterSym65");
+ public static string FILE_FILTER_TEXT =
+ (string)Application.Current.FindResource("str_FileFilterText");
public static string FILE_INFO_FMT =
(string)Application.Current.FindResource("str_FileInfoFmt");
public static string FIND_REACHED_START =
diff --git a/SourceGen/WpfGui/EditAppSettings.xaml.cs b/SourceGen/WpfGui/EditAppSettings.xaml.cs
index a845c7a..0be7aec 100644
--- a/SourceGen/WpfGui/EditAppSettings.xaml.cs
+++ b/SourceGen/WpfGui/EditAppSettings.xaml.cs
@@ -247,7 +247,9 @@ namespace SourceGen.WpfGui {
new ClipboardFormatItem(Res.Strings.CLIPFORMAT_ASSEMBLER_SOURCE,
MainController.ClipLineFormat.AssemblerSource),
new ClipboardFormatItem(Res.Strings.CLIPFORMAT_DISASSEMBLY,
- MainController.ClipLineFormat.Disassembly)
+ MainController.ClipLineFormat.Disassembly),
+ new ClipboardFormatItem(Res.Strings.CLIPFORMAT_ALL_COLUMNS,
+ MainController.ClipLineFormat.AllColumns)
};
// ItemsSource for combo box
public ClipboardFormatItem[] ClipboardFormatItems {
diff --git a/SourceGen/WpfGui/Export.xaml b/SourceGen/WpfGui/Export.xaml
index 25285b7..6bc33ae 100644
--- a/SourceGen/WpfGui/Export.xaml
+++ b/SourceGen/WpfGui/Export.xaml
@@ -23,7 +23,8 @@ limitations under the License.
mc:Ignorable="d"
Title="Export Source"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
- ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
+ ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
+ Loaded="Window_Loaded">
@@ -32,12 +33,15 @@ limitations under the License.
-
+
-
-
-
+
+
+
+
+
+
@@ -82,20 +86,23 @@ limitations under the License.
+
+
+
-
-
+
+
-
-
+
+
@@ -107,9 +114,9 @@ limitations under the License.
+ IsEnabled="{Binding IsValid}" Click="GenerateHtmlButton_Click"/>
+ IsEnabled="{Binding IsValid}" Click="GenerateTextButton_Click"/>
diff --git a/SourceGen/WpfGui/Export.xaml.cs b/SourceGen/WpfGui/Export.xaml.cs
index 6ca5fac..6a1b143 100644
--- a/SourceGen/WpfGui/Export.xaml.cs
+++ b/SourceGen/WpfGui/Export.xaml.cs
@@ -16,15 +16,85 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
+using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
+using CommonUtil;
+using Microsoft.Win32;
+
namespace SourceGen.WpfGui {
///
/// Export selection dialog.
///
public partial class Export : Window, INotifyPropertyChanged {
+ ///
+ /// Indicates which type of file the user wants to generate.
+ ///
+ public enum GenerateFileType {
+ Unknown = 0,
+ Text,
+ Html
+ }
+
+ ///
+ /// Result: type of file to generate.
+ ///
+ public GenerateFileType GenType { get; private set; }
+
+ ///
+ /// Result: full pathname of output file.
+ ///
+ public string PathName { get; private set; }
+
+ ///
+ /// Result: flags indicating which of the optional columns should be shown.
+ ///
+ public Exporter.ActiveColumnFlags ColFlags { get; private set; }
+
+ private bool mIncludeNotes;
+ public bool IncludeNotes {
+ get { return mIncludeNotes; }
+ set { mIncludeNotes = value; OnPropertyChanged(); }
+ }
+
+ private bool mShowOffset;
+ public bool ShowOffset {
+ get { return mShowOffset; }
+ set { mShowOffset = value; OnPropertyChanged(); }
+ }
+
+ private bool mShowAddress;
+ public bool ShowAddress {
+ get { return mShowAddress; }
+ set { mShowAddress = value; OnPropertyChanged(); }
+ }
+
+ private bool mShowBytes;
+ public bool ShowBytes {
+ get { return mShowBytes; }
+ set { mShowBytes = value; OnPropertyChanged(); }
+ }
+
+ private bool mShowFlags;
+ public bool ShowFlags {
+ get { return mShowFlags; }
+ set { mShowFlags = value; OnPropertyChanged(); }
+ }
+
+ private bool mShowAttrs;
+ public bool ShowAttr {
+ get { return mShowAttrs; }
+ set { mShowAttrs = value; OnPropertyChanged(); }
+ }
+
+ private bool mSelectionOnly;
+ public bool SelectionOnly {
+ get { return mSelectionOnly; }
+ set { mSelectionOnly = value; OnPropertyChanged(); }
+ }
+
//
// Numeric input fields, bound directly to TextBox.Text. These rely on a TextChanged
// field to update the IsValid flag, because the "set" method is only called when the
@@ -71,6 +141,36 @@ namespace SourceGen.WpfGui {
}
}
+ private bool mTextModePlain;
+ public bool TextModePlain {
+ get { return mTextModePlain; }
+ set { mTextModePlain = value; OnPropertyChanged(); }
+ }
+
+ private bool mTextModeCsv;
+ public bool TextModeCsv {
+ get { return mTextModeCsv; }
+ set { mTextModeCsv = value; OnPropertyChanged(); }
+ }
+
+ private bool mIncludeSymbolTable;
+ public bool IncludeSymbolTable {
+ get { return mIncludeSymbolTable; }
+ set { mIncludeSymbolTable = value; OnPropertyChanged(); }
+ }
+
+ private bool mOverwriteCss;
+ public bool OverwriteCss {
+ get { return mOverwriteCss; }
+ set { mOverwriteCss = value; OnPropertyChanged(); }
+ }
+
+ private enum TextMode {
+ Unknown = 0,
+ PlainText,
+ Csv
+ }
+
///
/// Valid flag, used to enable the "generate" buttons.
///
@@ -80,23 +180,96 @@ namespace SourceGen.WpfGui {
}
private bool mIsValid;
-
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
- public Export(Window owner) {
+ ///
+ /// Filename of project file.
+ ///
+ string mProjectFileName;
+
+
+ public Export(Window owner, string projectFileName) {
InitializeComponent();
Owner = owner;
DataContext = this;
+
+ mProjectFileName = projectFileName;
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
- // TODO
+ IncludeNotes = AppSettings.Global.GetBool(AppSettings.EXPORT_INCLUDE_NOTES, false);
+ ShowOffset = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_OFFSET, false);
+ ShowAddress = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_ADDR, false);
+ ShowBytes = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_BYTES, false);
+ ShowFlags = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_FLAGS, false);
+ ShowAttr = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_ATTR, false);
+ SelectionOnly = AppSettings.Global.GetBool(AppSettings.EXPORT_SELECTION_ONLY, false);
+ IncludeSymbolTable = AppSettings.Global.GetBool(AppSettings.EXPORT_INCLUDE_SYMTAB,
+ false);
+
+ int[] colWidths = new int[] { 9, 8, 11, 72 }; // 100-col output
+ string colStr = AppSettings.Global.GetString(AppSettings.EXPORT_COL_WIDTHS, null);
+ try {
+ if (!string.IsNullOrEmpty(colStr)) {
+ colWidths = TextUtil.DeserializeIntArray(colStr);
+ if (colWidths.Length != 4) {
+ throw new InvalidOperationException("Bad length " + colWidths.Length);
+ }
+ }
+ } finally {
+ AsmLabelColWidth = colWidths[0];
+ AsmOpcodeColWidth = colWidths[1];
+ AsmOperandColWidth = colWidths[2];
+ AsmCommentColWidth = colWidths[3];
+ }
+
+ TextMode mode = (TextMode)AppSettings.Global.GetEnum(AppSettings.EXPORT_TEXT_MODE,
+ typeof(TextMode), (int)TextMode.PlainText);
+ if (mode == TextMode.PlainText) {
+ TextModePlain = true;
+ } else {
+ TextModeCsv = true;
+ }
}
+ ///
+ /// Saves the settings to the global settings object.
+ ///
+ private void SaveSettings() {
+ AppSettings.Global.SetBool(AppSettings.EXPORT_INCLUDE_NOTES, IncludeNotes);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_OFFSET, ShowOffset);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_ADDR, ShowAddress);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_BYTES, ShowBytes);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_FLAGS, ShowFlags);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_ATTR, ShowAttr);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_SELECTION_ONLY, SelectionOnly);
+ AppSettings.Global.SetBool(AppSettings.EXPORT_INCLUDE_SYMTAB, IncludeSymbolTable);
+ int[] colWidths = new int[] {
+ AsmLabelColWidth, AsmOpcodeColWidth, AsmOperandColWidth, AsmCommentColWidth
+ };
+ string cereal = TextUtil.SerializeIntArray(colWidths);
+ AppSettings.Global.SetString(AppSettings.EXPORT_COL_WIDTHS, cereal);
+ // OverwriteCss is not saved, since there's generally no reason to replace it.
+ // Forcing the user to check it every time is essentially the same as popping
+ // up an "are you sure you want to overwrite" dialog, but less annoying for the
+ // common case.
+
+ TextMode mode;
+ if (TextModePlain) {
+ mode = TextMode.PlainText;
+ } else {
+ mode = TextMode.Csv;
+ }
+ AppSettings.Global.SetEnum(AppSettings.EXPORT_TEXT_MODE, typeof(TextMode), (int)mode);
+ }
+
+ ///
+ /// Updates the state of the UI.
+ ///
private void UpdateControls() {
bool isValid = true;
@@ -108,8 +281,69 @@ namespace SourceGen.WpfGui {
IsValid = isValid;
}
+ ///
+ /// Called whenever something is typed in one of the column width entry boxes.
+ ///
+ ///
+ /// We need this because we're using validated int fields rather than strings. The
+ /// "set" call doesn't fire if the user types garbage.
+ ///
private void AsmColWidthTextBox_TextChanged(object sender, TextChangedEventArgs e) {
UpdateControls();
}
+
+ private void GenerateHtmlButton_Click(object sender, RoutedEventArgs e) {
+ GenType = GenerateFileType.Html;
+ Finish(Res.Strings.FILE_FILTER_HTML, ".html");
+ }
+
+ private void GenerateTextButton_Click(object sender, RoutedEventArgs e) {
+ GenType = GenerateFileType.Text;
+ if (TextModeCsv) {
+ Finish(Res.Strings.FILE_FILTER_CSV, ".csv");
+ } else {
+ Finish(Res.Strings.FILE_FILTER_TEXT, ".txt");
+ }
+ }
+
+ ///
+ /// Handles a click on one of the "generate" buttons.
+ ///
+ private void Finish(string fileFilter, string fileExt) {
+ Debug.Assert(mProjectFileName == Path.GetFileName(mProjectFileName));
+ string initialName = Path.GetFileNameWithoutExtension(mProjectFileName) + fileExt;
+
+ SaveFileDialog fileDlg = new SaveFileDialog() {
+ Filter = fileFilter + "|" + Res.Strings.FILE_FILTER_ALL,
+ FilterIndex = 1,
+ ValidateNames = true,
+ AddExtension = true, // doesn't add extension if non-ext file exists
+ FileName = initialName
+ };
+ if (fileDlg.ShowDialog() != true) {
+ return;
+ }
+ PathName = Path.GetFullPath(fileDlg.FileName);
+
+ ColFlags = Exporter.ActiveColumnFlags.None;
+ if (ShowOffset) {
+ ColFlags |= Exporter.ActiveColumnFlags.Offset;
+ }
+ if (ShowAddress) {
+ ColFlags |= Exporter.ActiveColumnFlags.Address;
+ }
+ if (ShowBytes) {
+ ColFlags |= Exporter.ActiveColumnFlags.Bytes;
+ }
+ if (ShowFlags) {
+ ColFlags |= Exporter.ActiveColumnFlags.Flags;
+ }
+ if (ShowAttr) {
+ ColFlags |= Exporter.ActiveColumnFlags.Attr;
+ }
+
+ SaveSettings();
+ DialogResult = true;
+ }
}
}