From cb6ceafd73c848e286e0142741a2e1d94e06e739 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 19 Jul 2020 18:39:27 -0700 Subject: [PATCH] Make operand wrap length configurable Long operands, such as strings and bulk data, can span multiple lines. SourceGen wraps them at 64 characters, which is fine for assembly output but occasionally annoying on screen: if the operand column is wide enough to show the entire value, the comment column is pushed pretty far to the right. This change makes the width configurable, as 32/48/64 characters, with a pop-up in app settings. The assemblers are all wired to 64 characters, though we could make this configurable as well with an assembler-specific setting. Some things have moved around a bit in app settings. The Asm Config tab now comes last. Having it sandwiched in the middle of tabs that altered the on-screen display didn't make much sense. The Display Format is now explicitly for opcodes and operands, and is split into two columns. The left column is managed by the "quick set" feature, the right column is independent. --- Asm65/Formatter.cs | 18 ++ Asm65/StringOpFormatter.cs | 5 +- SourceGen/AppSettings.cs | 1 + SourceGen/AsmGen/AsmAcme.cs | 7 +- SourceGen/AsmGen/AsmCc65.cs | 7 +- SourceGen/AsmGen/AsmMerlin32.cs | 6 +- SourceGen/AsmGen/AsmTass64.cs | 7 +- SourceGen/AsmGen/GenCommon.cs | 2 + SourceGen/PseudoOp.cs | 8 +- SourceGen/RuntimeData/Help/settings.html | 106 ++++--- SourceGen/Tests/GenTest.cs | 1 + SourceGen/WpfGui/EditAppSettings.xaml | 360 ++++++++++++----------- SourceGen/WpfGui/EditAppSettings.xaml.cs | 55 +++- 13 files changed, 343 insertions(+), 240 deletions(-) diff --git a/Asm65/Formatter.cs b/Asm65/Formatter.cs index d676df2..6e28140 100644 --- a/Asm65/Formatter.cs +++ b/Asm65/Formatter.cs @@ -42,6 +42,10 @@ namespace Asm65 { /// fixed by keying off of the OpDef instead of OpDef.Opcode, but that's less convenient.) /// public class Formatter { + // Default wrap point for long operands. This potentially affects both on-screen + // display and source code generation. + private const int DEFAULT_OPERAND_WRAP_LEN = 64; + /// /// Various format configuration options. Fill one of these out and pass it to /// the Formatter constructor. @@ -80,6 +84,9 @@ namespace Asm65 { public DelimiterSet mCharDelimiters; public DelimiterSet mStringDelimiters; + // point at which we wrap long operands; zero uses default + public int mOperandWrapLen; + // miscellaneous public bool mSpacesBetweenBytes; // "20edfd" vs. "20 ed fd" public bool mCommaSeparatedDense; // "20edfd" vs. "$20,$ed,$fd" @@ -147,6 +154,10 @@ namespace Asm65 { if (mBoxLineCommentDelimiter == null) { mBoxLineCommentDelimiter = string.Empty; } + + if (mOperandWrapLen == 0) { + mOperandWrapLen = DEFAULT_OPERAND_WRAP_LEN; + } } } @@ -421,6 +432,13 @@ namespace Asm65 { get { return mFormatConfig.mExpressionMode; } } + /// + /// Point at which to wrap long operands, such as strings and dense hex. + /// + public int OperandWrapLen { + get { return mFormatConfig.mOperandWrapLen; } + } + /// /// Constructor. Initializes various fields based on the configuration. We want to diff --git a/Asm65/StringOpFormatter.cs b/Asm65/StringOpFormatter.cs index c32beae..36321c7 100644 --- a/Asm65/StringOpFormatter.cs +++ b/Asm65/StringOpFormatter.cs @@ -79,12 +79,11 @@ namespace Asm65 { /// Reference to text formatter. /// String delimiter values. /// How to format raw byte data. - /// Maximum line length. /// Character conversion delegate. public StringOpFormatter(Formatter formatter, Formatter.DelimiterDef delimiterDef, - RawOutputStyle byteStyle, int maxOperandLen, CharEncoding.Convert charConv) { + RawOutputStyle byteStyle, CharEncoding.Convert charConv) { mRawStyle = byteStyle; - mMaxOperandLen = maxOperandLen; + mMaxOperandLen = formatter.OperandWrapLen; CharConv = charConv; mDelimiterDef = delimiterDef; diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs index 8ddbefc..f662b12 100644 --- a/SourceGen/AppSettings.cs +++ b/SourceGen/AppSettings.cs @@ -56,6 +56,7 @@ namespace SourceGen { public const string FMT_UPPER_OPERAND_XY = "fmt-upper-operand-xy"; public const string FMT_ADD_SPACE_FULL_COMMENT = "fmt-add-space-full-comment"; public const string FMT_SPACES_BETWEEN_BYTES = "fmt-spaces-between-bytes"; + public const string FMT_OPERAND_WRAP_LEN = "fmt-operand-wrap-len"; public const string FMT_OPCODE_SUFFIX_ABS = "fmt-opcode-suffix-abs"; public const string FMT_OPCODE_SUFFIX_LONG = "fmt-opcode-suffix-long"; diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index 2248cbd..2224995 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -37,7 +37,6 @@ namespace SourceGen.AsmGen { // makefile rules. Since ".S" is pretty universal for assembly language sources, // I'm sticking with that. private const string ASM_FILE_SUFFIX = "_acme.S"; // must start with underscore - private const int MAX_OPERAND_LEN = 64; private const string CLOSE_PSEUDOPC = "} ;!pseudopc"; // IGenerator @@ -185,6 +184,7 @@ namespace SourceGen.AsmGen { private void SetFormatConfigValues(ref Formatter.FormatConfig config) { config.mSuppressImpliedAcc = true; + config.mOperandWrapLen = 64; config.mForceDirectOpcodeSuffix = "+1"; config.mForceAbsOpcodeSuffix = "+2"; config.mForceLongOpcodeSuffix = "+3"; @@ -455,7 +455,7 @@ namespace SourceGen.AsmGen { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; - int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte; + int maxPerLine = formatter.OperandWrapLen / formatter.CharsPerDenseByte; string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); for (int i = 0; i < length; i += maxPerLine) { @@ -648,8 +648,7 @@ namespace SourceGen.AsmGen { } StringOpFormatter stropf = new StringOpFormatter(SourceFormatter, - Formatter.DOUBLE_QUOTE_DELIM,StringOpFormatter.RawOutputStyle.CommaSep, - MAX_OPERAND_LEN, charConv); + Formatter.DOUBLE_QUOTE_DELIM,StringOpFormatter.RawOutputStyle.CommaSep, charConv); stropf.FeedBytes(data, offset, dfd.Length, leadingBytes, StringOpFormatter.ReverseMode.Forward); diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index 8d625bf..3e85f1d 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -32,7 +32,6 @@ namespace SourceGen.AsmGen { public class GenCc65 : IGenerator { private const string ASM_FILE_SUFFIX = "_cc65.S"; // must start with underscore private const string CFG_FILE_SUFFIX = "_cc65.cfg"; // ditto - private const int MAX_OPERAND_LEN = 64; // IGenerator public DisasmProject Project { get; private set; } @@ -181,6 +180,7 @@ namespace SourceGen.AsmGen { /// Configures the assembler-specific format items. /// private void SetFormatConfigValues(ref Formatter.FormatConfig config) { + config.mOperandWrapLen = 64; config.mForceDirectOpcodeSuffix = string.Empty; config.mForceAbsOpcodeSuffix = string.Empty; config.mForceLongOpcodeSuffix = string.Empty; @@ -486,7 +486,7 @@ namespace SourceGen.AsmGen { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; - int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte; + int maxPerLine = formatter.OperandWrapLen / formatter.CharsPerDenseByte; string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); for (int i = 0; i < length; i += maxPerLine) { @@ -709,8 +709,7 @@ namespace SourceGen.AsmGen { } StringOpFormatter stropf = new StringOpFormatter(SourceFormatter, - Formatter.DOUBLE_QUOTE_DELIM, StringOpFormatter.RawOutputStyle.CommaSep, - MAX_OPERAND_LEN, charConv); + Formatter.DOUBLE_QUOTE_DELIM, StringOpFormatter.RawOutputStyle.CommaSep, charConv); stropf.FeedBytes(data, offset, dfd.Length - trailingBytes, leadingBytes, StringOpFormatter.ReverseMode.Forward); diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 735c1b3..90ff780 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -32,7 +32,6 @@ namespace SourceGen.AsmGen { /// public class GenMerlin32 : IGenerator { private const string ASM_FILE_SUFFIX = "_Merlin32.S"; // must start with underscore - private const int MAX_OPERAND_LEN = 64; // IGenerator public DisasmProject Project { get; private set; } @@ -155,6 +154,7 @@ namespace SourceGen.AsmGen { /// Configures the assembler-specific format items. /// private void SetFormatConfigValues(ref Formatter.FormatConfig config) { + config.mOperandWrapLen = 64; config.mForceDirectOpcodeSuffix = string.Empty; config.mForceAbsOpcodeSuffix = ":"; config.mForceLongOpcodeSuffix = "l"; @@ -333,7 +333,7 @@ namespace SourceGen.AsmGen { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; - int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte; + int maxPerLine = formatter.OperandWrapLen / formatter.CharsPerDenseByte; string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); for (int i = 0; i < length; i += maxPerLine) { @@ -607,7 +607,7 @@ namespace SourceGen.AsmGen { StringOpFormatter stropf = new StringOpFormatter(SourceFormatter, new Formatter.DelimiterDef(delim), - StringOpFormatter.RawOutputStyle.DenseHex, MAX_OPERAND_LEN, charConv); + StringOpFormatter.RawOutputStyle.DenseHex, charConv); if (dfd.FormatType == FormatDescriptor.Type.StringDci) { // DCI is awkward because the character encoding flips on the last byte. Rather // than clutter up StringOpFormatter for this rare item, we just accept low/high diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 87f01c9..3496782 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -45,7 +45,6 @@ namespace SourceGen.AsmGen { private const string ASM_FILE_SUFFIX = "_64tass.S"; // must start with underscore private const string ASCII_ENC_NAME = "sg_ascii"; private const string HIGH_ASCII_ENC_NAME = "sg_hiascii"; - private const int MAX_OPERAND_LEN = 64; // IGenerator public DisasmProject Project { get; private set; } @@ -188,6 +187,7 @@ namespace SourceGen.AsmGen { config.mUpperOperandA = false; config.mUpperOperandS = false; config.mUpperOperandXY = false; + config.mOperandWrapLen = 64; config.mBankSelectBackQuote = true; @@ -524,7 +524,7 @@ namespace SourceGen.AsmGen { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; - int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte; + int maxPerLine = formatter.OperandWrapLen / formatter.CharsPerDenseByte; string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); for (int i = 0; i < length; i += maxPerLine) { @@ -744,8 +744,7 @@ namespace SourceGen.AsmGen { } StringOpFormatter stropf = new StringOpFormatter(SourceFormatter, - Formatter.DOUBLE_QUOTE_DELIM,StringOpFormatter.RawOutputStyle.CommaSep, - MAX_OPERAND_LEN, charConv); + Formatter.DOUBLE_QUOTE_DELIM,StringOpFormatter.RawOutputStyle.CommaSep, charConv); if (dfd.FormatType == FormatDescriptor.Type.StringDci) { // DCI is awkward because the character encoding flips on the last byte. Rather // than clutter up StringOpFormatter for this rare item, we just accept low/high diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index b622694..50e34fa 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -425,6 +425,8 @@ namespace SourceGen.AsmGen { settings.GetBool(AppSettings.FMT_SPACES_BETWEEN_BYTES, false); config.mAddSpaceLongComment = settings.GetBool(AppSettings.FMT_ADD_SPACE_FULL_COMMENT, true); + config.mOperandWrapLen = + settings.GetInt(AppSettings.FMT_OPERAND_WRAP_LEN, 0); config.mForceAbsOpcodeSuffix = settings.GetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, string.Empty); diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs index 1b1b148..1101456 100644 --- a/SourceGen/PseudoOp.cs +++ b/SourceGen/PseudoOp.cs @@ -33,8 +33,6 @@ namespace SourceGen { /// multiple lines. /// public class PseudoOp { - private const int MAX_OPERAND_LEN = 64; - /// /// One piece of the pseudo-instruction. /// @@ -278,7 +276,7 @@ namespace SourceGen { return 1; case FormatDescriptor.Type.Dense: { // no delimiter, two output bytes per input byte - int maxLen = MAX_OPERAND_LEN; + int maxLen = formatter.OperandWrapLen; int textLen = dfd.Length * formatter.CharsPerDenseByte; return (textLen + maxLen - 1) / maxLen; } @@ -367,7 +365,7 @@ namespace SourceGen { } break; case FormatDescriptor.Type.Dense: { - int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte; + int maxPerLine = formatter.OperandWrapLen / formatter.CharsPerDenseByte; offset += subIndex * maxPerLine; length -= subIndex * maxPerLine; if (length > maxPerLine) { @@ -547,7 +545,7 @@ namespace SourceGen { } StringOpFormatter stropf = new StringOpFormatter(formatter, delDef, - StringOpFormatter.RawOutputStyle.CommaSep, MAX_OPERAND_LEN, charConv); + StringOpFormatter.RawOutputStyle.CommaSep, charConv); stropf.FeedBytes(data, offset + hiddenLeadingBytes, dfd.Length - hiddenLeadingBytes - trailingBytes, 0, revMode); diff --git a/SourceGen/RuntimeData/Help/settings.html b/SourceGen/RuntimeData/Help/settings.html index 6669d26..2b30375 100644 --- a/SourceGen/RuntimeData/Help/settings.html +++ b/SourceGen/RuntimeData/Help/settings.html @@ -100,6 +100,65 @@ do not affect generated code, which must use the delimiter characters specified by the chosen assembler.

+

Display Format

+ +

These options change the way the code list looks on screen. They +do not affect generated code.

+ +

The +operand width disambiguator +strings are used when the width of an instruction operand is unclear. +You may specify values for all of them or none of them.

+ +

Different assemblers have different ways of forming expressions. +Sometimes the rules allow expressions to be written simply, other times +explicit grouping with parenthesis is required. Select whichever style +you are most comfortable with.

+ +

Non-unique labels are identified with a prefix character, typically +'@' or ':'. The default is '@', but you can configure it to any character +that isn't valid for the start of a label. (64tass uses '_' for locals, +but that's a valid label start character, and so isn't allowed here.) +The setting affects label editing as well as display.

+ +

If you would like your local variables to be shown with a prefix +character, you can set it in the "local variable prefix" box.

+ +

The "quick set" pop-up configures the fields on the left side of the +tab to match the conventions of the specified assembler. Select your +preferred assembler in the combo box to set the fields. The setting +automatically switches to "custom" when you edit a field. +(64tass and ACME use the "common" +expression style, cc65 and Merlin 32 have their own unique styles.)

+ +

The "comma-separated format for bulk data" determines whether large +blocks of hex look like ABC123 or +$AB,$C1,$23. The former reduces the number of lines +required, the latter is more readable.

+

Long operands, such as strings and bulk data, are wrapped to a new +line after a certain number of characters. Use the pop-up to configure +the value. Larger values can make the code easier to read, but smaller +values allow you to shrink the width of the operand column in the +on-screen listing, moving the comment field closer in.

+ + +

Pseudo-Op

+ +

These options change the way the code list looks on screen. Assembler +directives and data pseudo-opcodes will use these values. This does +not affect generated source code, which always matches the conventions +of the target assembler.

+ +

Enter the string you want to use for the various data formats. If +a field is left blank, a default value is used.

+ +

The "quick set" pop-up configures the fields on this tab to match +the conventions of the specified assembler. Select your preferred assembler +in the combo box to set the fields. The setting automatically switches to +"custom" when you edit a field.

+ + +

Asm Config

These settings configure cross-assemblers and modify assembly source @@ -150,53 +209,6 @@ the source file what the intended target assembler is, or what options are required to process the file correctly.

-

Display Format

- -

These options change the way the code list looks on screen. They -do not affect generated code.

- -

The -operand width disambiguator -strings are used when the width of an instruction operand is unclear. -You may specify values for all of them or none of them.

- -

Different assemblers have different ways of forming expressions. -Sometimes the rules allow expressions to be written simply, other times -explicit grouping with parenthesis is required. Select whichever style -you are most comfortable with.

- -

Non-unique labels are identified with a prefix character, typically -'@' or ':'. The default is '@', but you can configure it to any character -that isn't valid for the start of a label. (64tass uses '_' for locals, -but that's a valid label start character, and so isn't allowed here.) -The setting affects label editing as well as display.

- -

If you would like your local variables to be shown with a prefix -character, you can set it in the "local variable prefix" box.

- -

The "quick set" pop-up configures the fields on this tab to match -the conventions of the specified assembler. Select your preferred assembler -in the combo box to set the fields. The setting automatically switches to -"custom" when you edit a field. (64tass and ACME use the "common" -expression style, cc65 and Merlin 32 have their own unique styles.)

- - -

Pseudo-Op

- -

These options change the way the code list looks on screen. Assembler -directives and data pseudo-opcodes will use these values. This does -not affect generated source code, which always matches the conventions -of the target assembler.

- -

Enter the string you want to use for the various data formats. If -a field is left blank, a default value is used.

- -

The "quick set" pop-up configures the fields on this tab to match -the conventions of the specified assembler. Select your preferred assembler -in the combo box to set the fields. The setting automatically switches to -"custom" when you edit a field.

- -

Project Properties

Project properties are stored in the .dis65 project file. diff --git a/SourceGen/Tests/GenTest.cs b/SourceGen/Tests/GenTest.cs index d2fe0d7..7d8106e 100644 --- a/SourceGen/Tests/GenTest.cs +++ b/SourceGen/Tests/GenTest.cs @@ -389,6 +389,7 @@ namespace SourceGen.Tests { settings.SetBool(AppSettings.FMT_UPPER_OPERAND_S, true); settings.SetBool(AppSettings.FMT_UPPER_OPERAND_XY, false); settings.SetBool(AppSettings.FMT_ADD_SPACE_FULL_COMMENT, false); + settings.SetInt(AppSettings.FMT_OPERAND_WRAP_LEN, 64); // Don't show the assembler ident line. You can make a case for this being // mandatory, since the generated code is only guaranteed to work with the diff --git a/SourceGen/WpfGui/EditAppSettings.xaml b/SourceGen/WpfGui/EditAppSettings.xaml index 691f590..16f11b9 100644 --- a/SourceGen/WpfGui/EditAppSettings.xaml +++ b/SourceGen/WpfGui/EditAppSettings.xaml @@ -129,10 +129,10 @@ limitations under the License. - + @@ -422,180 +422,120 @@ limitations under the License. - - - - - - - - - - - - - - - - - - - -