diff --git a/CommonUtil/Misc.cs b/CommonUtil/Misc.cs index 97e59b1..3bc7332 100644 --- a/CommonUtil/Misc.cs +++ b/CommonUtil/Misc.cs @@ -42,10 +42,10 @@ namespace CommonUtil { /// /// /// Usage: - /// AppDomain.CurrentDomain.UnhandledException += - /// new UnhandledExceptionEventHandler(CommonUtil.Misc.CrashReporter); + /// AppDomain.CurrentDomain.UnhandledException += + /// new UnhandledExceptionEventHandler(CommonUtil.Misc.CrashReporter); /// - /// Thanks: https://stackoverflow.com/a/21308327/294248 + /// Thanks: . /// public static void CrashReporter(object sender, UnhandledExceptionEventArgs e) { const string CRASH_PATH = @"CrashLog.txt"; @@ -80,7 +80,7 @@ namespace CommonUtil { /// faster than setting array elements individually. /// /// - /// From https://stackoverflow.com/a/18659408/294248 + /// From . /// /// Invokes Array.Copy() on overlapping elements. Other approaches involve using /// Buffer.BlockCopy or unsafe code. Apparently .NET Core has an Array.Fill(), but diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs index e700d0e..41fc265 100644 --- a/SourceGen/AppSettings.cs +++ b/SourceGen/AppSettings.cs @@ -124,7 +124,7 @@ namespace SourceGen { // Source generation settings. public const string SRCGEN_DEFAULT_ASM = "srcgen-default-asm"; public const string SRCGEN_ADD_IDENT_COMMENT = "srcgen-add-ident-comment"; - public const string SRCGEN_LONG_LABEL_NEW_LINE = "srcgen-long-label-new-line"; + public const string SRCGEN_LABEL_NEW_LINE = "srcgen-label-new-line"; public const string SRCGEN_SHOW_CYCLE_COUNTS = "srcgen-show-cycle-counts"; // Label file generation settings. @@ -291,21 +291,20 @@ namespace SourceGen { /// /// Retrieves an enumerated value setting. /// + /// Enumerated type. /// Setting name. - /// Enum type that the value is part of. /// Setting default value. /// The value found, or the default value if no setting with the specified - /// name exists, or the stored value is not a member of the specified enumerated - /// type. - public int GetEnum(string name, Type enumType, int defaultValue) { + /// name exists, or the stored value is not a member of the enumeration. + public T GetEnum(string name, T defaultValue) { if (!mSettings.TryGetValue(name, out string valueStr)) { return defaultValue; } try { - object o = Enum.Parse(enumType, valueStr); - return (int)o; + object o = Enum.Parse(typeof(T), valueStr); + return (T)o; } catch (ArgumentException ae) { - Debug.WriteLine("Failed to parse " + valueStr + " (enum " + enumType + "): " + + Debug.WriteLine("Failed to parse '" + valueStr + "' (enum " + typeof(T) + "): " + ae.Message); return defaultValue; } @@ -314,14 +313,20 @@ namespace SourceGen { /// /// Sets an enumerated setting. /// + /// + /// The value is output to the settings file as a string, rather than an integer, allowing + /// the correct handling even if the enumerated values are renumbered. + /// + /// Enumerated type. /// Setting name. - /// Enum type. /// Setting value (integer enum index). - public void SetEnum(string name, Type enumType, int value) { - string newVal = Enum.GetName(enumType, value); + public void SetEnum(string name, T value) { + if (value == null) { + throw new NotImplementedException("Can't handle a null-valued enum type"); + } + string newVal = Enum.GetName(typeof(T), value); if (newVal == null) { - // Shouldn't be possible if an enum value of the correct type is passed in. - Debug.WriteLine("Unable to get enum name type=" + enumType + " value=" + value); + Debug.WriteLine("Unable to get enum name type=" + typeof(T) + " value=" + value); return; } if (!mSettings.TryGetValue(name, out string oldValue) || oldValue != newVal) { diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index 24b3777..d3c56c0 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -62,9 +62,9 @@ namespace SourceGen.AsmGen { private string mWorkDirectory; /// - /// If set, long labels get their own line. + /// Influences whether labels are put on their own line. /// - private bool mLongLabelNewLine; + private GenCommon.LabelPlacement mLabelNewLine; /// /// Output column widths. @@ -198,7 +198,8 @@ namespace SourceGen.AsmGen { mFileNameBase = fileNameBase; Settings = settings; - mLongLabelNewLine = Settings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + mLabelNewLine = Settings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.SplitIfTooLong); AssemblerConfig config = AssemblerConfig.GetConfig(settings, AssemblerInfo.Id.Acme); @@ -667,7 +668,9 @@ namespace SourceGen.AsmGen { !string.Equals(opcode, sDataOpNames.EquDirective, StringComparison.InvariantCultureIgnoreCase)) { - if (mLongLabelNewLine && label.Length >= mColumnWidths[0]) { + if (mLabelNewLine == GenCommon.LabelPlacement.PreferSeparateLine || + (mLabelNewLine == GenCommon.LabelPlacement.SplitIfTooLong && + label.Length >= mColumnWidths[0])) { mOutStream.WriteLine(label); label = string.Empty; } diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index f64a5ae..5292529 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -57,9 +57,9 @@ namespace SourceGen.AsmGen { private string mWorkDirectory; /// - /// If set, long labels get their own line. + /// Influences whether labels are put on their own line. /// - private bool mLongLabelNewLine; + private GenCommon.LabelPlacement mLabelNewLine; /// /// Output column widths. @@ -188,7 +188,8 @@ namespace SourceGen.AsmGen { mFileNameBase = fileNameBase; Settings = settings; - mLongLabelNewLine = Settings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + mLabelNewLine = Settings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.SplitIfTooLong); AssemblerConfig config = AssemblerConfig.GetConfig(settings, AssemblerInfo.Id.Cc65); @@ -659,7 +660,9 @@ namespace SourceGen.AsmGen { StringComparison.InvariantCultureIgnoreCase)) { label += ':'; - if (mLongLabelNewLine && label.Length >= mColumnWidths[0]) { + if (mLabelNewLine == GenCommon.LabelPlacement.PreferSeparateLine || + (mLabelNewLine == GenCommon.LabelPlacement.SplitIfTooLong && + label.Length >= mColumnWidths[0])) { mOutStream.WriteLine(label); label = string.Empty; } diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 08d843a..da74dea 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -57,9 +57,9 @@ namespace SourceGen.AsmGen { private string mWorkDirectory; /// - /// If set, long labels get their own line. + /// Influences whether labels are put on their own line. /// - private bool mLongLabelNewLine; + private GenCommon.LabelPlacement mLabelNewLine; /// /// Output column widths. @@ -174,7 +174,8 @@ namespace SourceGen.AsmGen { mFileNameBase = fileNameBase; Settings = settings; - mLongLabelNewLine = Settings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + mLabelNewLine = Settings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.SplitIfTooLong); AssemblerConfig config = AssemblerConfig.GetConfig(settings, AssemblerInfo.Id.Merlin32); @@ -606,11 +607,14 @@ namespace SourceGen.AsmGen { // IGenerator public void OutputLine(string label, string opcode, string operand, string comment) { // Split long label, but not on EQU directives (confuses the assembler). - if (mLongLabelNewLine && label.Length >= mColumnWidths[0] && - !string.Equals(opcode, sDataOpNames.EquDirective, + if (!string.IsNullOrEmpty(label) && !string.Equals(opcode, sDataOpNames.EquDirective, StringComparison.InvariantCultureIgnoreCase)) { - mOutStream.WriteLine(label); - label = string.Empty; + if (mLabelNewLine == GenCommon.LabelPlacement.PreferSeparateLine || + (mLabelNewLine == GenCommon.LabelPlacement.SplitIfTooLong && + label.Length >= mColumnWidths[0])) { + mOutStream.WriteLine(label); + label = string.Empty; + } } mLineBuilder.Clear(); diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 5c02c34..7e685c2 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -73,9 +73,9 @@ namespace SourceGen.AsmGen { private string mWorkDirectory; /// - /// If set, long labels get their own line. + /// Influences whether labels are put on their own line. /// - private bool mLongLabelNewLine; + private GenCommon.LabelPlacement mLabelNewLine; /// /// Output column widths. @@ -201,7 +201,8 @@ namespace SourceGen.AsmGen { mFileNameBase = fileNameBase; Settings = settings; - mLongLabelNewLine = Settings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + mLabelNewLine = Settings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.SplitIfTooLong); AssemblerConfig config = AssemblerConfig.GetConfig(settings, AssemblerInfo.Id.Tass64); @@ -777,7 +778,9 @@ namespace SourceGen.AsmGen { !string.Equals(opcode, sDataOpNames.VarDirective, StringComparison.InvariantCultureIgnoreCase)) { - if (mLongLabelNewLine && label.Length >= mColumnWidths[0]) { + if (mLabelNewLine == GenCommon.LabelPlacement.PreferSeparateLine || + (mLabelNewLine == GenCommon.LabelPlacement.SplitIfTooLong && + label.Length >= mColumnWidths[0])) { mOutStream.WriteLine(label); label = string.Empty; } diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index 5754518..10f1abb 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -23,11 +23,19 @@ using Asm65; using CommonUtil; namespace SourceGen.AsmGen { + /// + /// Code common to all assembly source generators. + /// public class GenCommon { + public enum LabelPlacement { + Unknown = 0, + PreferSameLine, + SplitIfTooLong, + PreferSeparateLine, + } + /// /// Generates assembly source. - /// - /// This code is common to all generators. /// /// Reference to generator object (presumably the caller). /// Text output sink. diff --git a/SourceGen/Exporter.cs b/SourceGen/Exporter.cs index 1e2a48b..4f978df 100644 --- a/SourceGen/Exporter.cs +++ b/SourceGen/Exporter.cs @@ -550,10 +550,8 @@ namespace SourceGen { // but we're only doing this on the template file, which should be small. tmplStr = tmplStr.Replace("$ProjectName$", mProject.DataFileName); tmplStr = tmplStr.Replace("$AppVersion$", App.ProgramVersion.ToString()); - string expModeStr = ((Formatter.FormatConfig.ExpressionMode) - AppSettings.Global.GetEnum(AppSettings.FMT_EXPRESSION_MODE, - typeof(Formatter.FormatConfig.ExpressionMode), - (int)Formatter.FormatConfig.ExpressionMode.Unknown)).ToString(); + string expModeStr = AppSettings.Global.GetEnum(AppSettings.FMT_EXPRESSION_MODE, + Formatter.FormatConfig.ExpressionMode.Unknown).ToString(); tmplStr = tmplStr.Replace("$ExpressionStyle$", expModeStr); string dateStr = DateTime.Now.ToString("yyyy/MM/dd"); string timeStr = DateTime.Now.ToString("HH:mm:ss zzz"); diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index db2401e..59bb1e8 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -345,7 +345,8 @@ namespace SourceGen { settings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, "f:"); settings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, true); - settings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, true); + settings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + AsmGen.GenCommon.LabelPlacement.SplitIfTooLong); #if DEBUG settings.SetBool(AppSettings.DEBUG_MENU_ENABLED, true); @@ -1462,10 +1463,8 @@ namespace SourceGen { return; } - ClipLineFormat format = (ClipLineFormat)AppSettings.Global.GetEnum( - AppSettings.CLIP_LINE_FORMAT, - typeof(ClipLineFormat), - (int)ClipLineFormat.AssemblerSource); + ClipLineFormat format = AppSettings.Global.GetEnum(AppSettings.CLIP_LINE_FORMAT, + ClipLineFormat.AssemblerSource); int[] rightWidths = new int[] { 16, 6, 16, 80 }; diff --git a/SourceGen/SGTestData/20280-label-placement b/SourceGen/SGTestData/20280-label-placement new file mode 100644 index 0000000..236cc60 Binary files /dev/null and b/SourceGen/SGTestData/20280-label-placement differ diff --git a/SourceGen/SGTestData/20280-label-placement.dis65 b/SourceGen/SGTestData/20280-label-placement.dis65 new file mode 100644 index 0000000..ec1189a --- /dev/null +++ b/SourceGen/SGTestData/20280-label-placement.dis65 @@ -0,0 +1,175 @@ +### 6502bench SourceGen dis65 v1.0 ### +{ +"_ContentVersion":5, +"FileDataLength":25, +"FileDataCrc32":646072439, +"ProjectProps":{ +"CpuName":"6502", +"IncludeUndocumentedInstr":false, +"TwoByteBrk":false, +"EntryFlags":32702671, +"AutoLabelStyle":"Simple", +"AnalysisParams":{ +"AnalyzeUncategorizedData":true, +"DefaultTextScanMode":"LowHighAscii", +"MinCharsForString":4, +"SeekNearbyTargets":true, +"UseRelocData":false, +"SmartPlpHandling":false, +"SmartPlbHandling":true}, + +"PlatformSymbolFileIdentifiers":[], +"ExtensionScriptFileIdentifiers":[], +"ProjectSyms":{ +"__ENABLE_ALL_LABEL_NEWLINE":{ +"DataDescriptor":{ +"Length":1, +"Format":"NumericLE", +"SubFormat":"Decimal", +"SymbolRef":null}, + +"Comment":"", +"HasWidth":false, +"Direction":"ReadWrite", +"MultiMask":null, +"Label":"__ENABLE_ALL_LABEL_NEWLINE", +"Value":1, +"Source":"Project", +"Type":"ExternalAddr", +"LabelAnno":"None"}, + +"shortnm":{ +"DataDescriptor":{ +"Length":1, +"Format":"NumericLE", +"SubFormat":"Hex", +"SymbolRef":null}, + +"Comment":"short label", +"HasWidth":false, +"Direction":"ReadWrite", +"MultiMask":null, +"Label":"shortnm", +"Value":16384, +"Source":"Project", +"Type":"ExternalAddr", +"LabelAnno":"None"}, + +"SomewhatLongName":{ +"DataDescriptor":{ +"Length":1, +"Format":"NumericLE", +"SubFormat":"Hex", +"SymbolRef":null}, + +"Comment":"somewhat longer label", +"HasWidth":false, +"Direction":"ReadWrite", +"MultiMask":null, +"Label":"SomewhatLongName", +"Value":16385, +"Source":"Project", +"Type":"ExternalAddr", +"LabelAnno":"None"}}}, + +"AddressMap":[{ +"Offset":0, +"Addr":4096, +"Length":25, +"PreLabel":"", +"IsRelative":false}], +"TypeHints":[{ +"Low":0, +"High":0, +"Hint":"Code"}], +"StatusFlagOverrides":{ +}, + +"Comments":{ +}, + +"LongComments":{ +}, + +"Notes":{ +}, + +"UserLabels":{ +"9":{ +"Label":"data", +"Value":4105, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"20":{ +"Label":"shortb", +"Value":4116, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"10":{ +"Label":"BranchTargetLongName", +"Value":4106, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"24":{ +"Label":"done", +"Value":4120, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}}, + +"OperandFormats":{ +}, + +"LvTables":{ +"10":{ +"Variables":[{ +"DataDescriptor":{ +"Length":2, +"Format":"NumericLE", +"SubFormat":"Hex", +"SymbolRef":null}, + +"Comment":"local var with short name", +"HasWidth":true, +"Direction":"ReadWrite", +"MultiMask":null, +"Label":"ptr", +"Value":0, +"Source":"Variable", +"Type":"ExternalAddr", +"LabelAnno":"None"}, + +{ +"DataDescriptor":{ +"Length":2, +"Format":"NumericLE", +"SubFormat":"Hex", +"SymbolRef":null}, + +"Comment":"local var with longer name", +"HasWidth":true, +"Direction":"ReadWrite", +"MultiMask":null, +"Label":"PointerWithLongName", +"Value":2, +"Source":"Variable", +"Type":"ExternalAddr", +"LabelAnno":"None"}], +"ClearPrevious":false}}, + +"Visualizations":[], +"VisualizationAnimations":[], +"VisualizationSets":{ +}, + +"RelocList":{ +}, + +"DbrValues":{ +}} diff --git a/SourceGen/SGTestData/Expected/20280-label-placement_64tass.S b/SourceGen/SGTestData/Expected/20280-label-placement_64tass.S new file mode 100644 index 0000000..ef49d60 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20280-label-placement_64tass.S @@ -0,0 +1,28 @@ + .cpu "6502" +shortnm = $4000 ;short label +SomewhatLongName = $4001 ;somewhat longer label + +* = $1000 + lda shortnm + ldx SomewhatLongName + clc + bcc BranchTargetLongName + +data + .byte $cc + +ptr .var $00 ;local var with short name +PointerWithLongName .var $02 ;local var with longer name +BranchTargetLongName + sta ptr + stx PointerWithLongName + ldy data + lsr a + bcc shortb +shortb + nop + jmp done + +done + rts + diff --git a/SourceGen/SGTestData/Expected/20280-label-placement_acme.S b/SourceGen/SGTestData/Expected/20280-label-placement_acme.S new file mode 100644 index 0000000..6fa1f69 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20280-label-placement_acme.S @@ -0,0 +1,29 @@ + !cpu 6502 +shortnm = $4000 ;short label +SomewhatLongName = $4001 ;somewhat longer label + +* = $1000 + lda shortnm + ldx SomewhatLongName + clc + bcc BranchTargetLongName + +data + !byte $cc + + !zone Z00000a +.ptr = $00 ;local var with short name +.PointerWithLongName = $02 ;local var with longer name +BranchTargetLongName + sta .ptr + stx .PointerWithLongName + ldy data + lsr + bcc shortb +shortb + nop + jmp done + +done + rts + diff --git a/SourceGen/SGTestData/Expected/20280-label-placement_cc65.S b/SourceGen/SGTestData/Expected/20280-label-placement_cc65.S new file mode 100644 index 0000000..2b18133 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20280-label-placement_cc65.S @@ -0,0 +1,28 @@ + .setcpu "6502" +shortnm = $4000 ;short label +SomewhatLongName = $4001 ;somewhat longer label + + .org $1000 + lda shortnm + ldx SomewhatLongName + clc + bcc BranchTargetLongName + +data: + .byte $cc + +ptr .set $00 ;local var with short name +PointerWithLongName .set $02 ;local var with longer name +BranchTargetLongName: + sta ptr + stx PointerWithLongName + ldy data + lsr A + bcc shortb +shortb: + nop + jmp done + +done: + rts + diff --git a/SourceGen/SGTestData/Expected/20280-label-placement_cc65.cfg b/SourceGen/SGTestData/Expected/20280-label-placement_cc65.cfg new file mode 100644 index 0000000..80dbdea --- /dev/null +++ b/SourceGen/SGTestData/Expected/20280-label-placement_cc65.cfg @@ -0,0 +1,9 @@ +# 6502bench SourceGen generated linker script for 20280-label-placement +MEMORY { + MAIN: file=%O, start=%S, size=65536; +} +SEGMENTS { + CODE: load=MAIN, type=rw; +} +FEATURES {} +SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20280-label-placement_merlin32.S b/SourceGen/SGTestData/Expected/20280-label-placement_merlin32.S new file mode 100644 index 0000000..5246d87 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20280-label-placement_merlin32.S @@ -0,0 +1,27 @@ +shortnm equ $4000 ;short label +SomewhatLongName equ $4001 ;somewhat longer label + + org $1000 + lda shortnm + ldx SomewhatLongName + clc + bcc BranchTargetLongName + +data + dfb $cc + +]ptr equ $00 ;local var with short name +]PointerWithLongName equ $02 ;local var with longer name +BranchTargetLongName + sta ]ptr + stx ]PointerWithLongName + ldy data + lsr A + bcc shortb +shortb + nop + jmp done + +done + rts + diff --git a/SourceGen/SGTestData/README.md b/SourceGen/SGTestData/README.md index 2bf8417..ec1cdae 100644 --- a/SourceGen/SGTestData/README.md +++ b/SourceGen/SGTestData/README.md @@ -34,17 +34,19 @@ be opened as the project file. ### Overriding Settings ### All tests are run with a fixed set of app settings, so that the tests look -the same regardless of how the assemblers are configured. For example, -upper-case conversion is disabled, and cycle counts are not shown. +the same regardless of how the assemblers are configured in the app settings +file. For example, upper-case conversion is disabled, and cycle counts are +not shown. Sometimes a test will want to exercise one of these settings, so we need a way to tell the test harness to override the default. We do this by -creating project symbols. +creating project symbols: -| Name | Value | Description -| ---------------------- | ----- | -------------------------------------------| -| __ENABLE_LABEL_NEWLINE | any | Puts long labels on their own line | -| __ENABLE_CYCLE_COUNTS | any | Adds cycle count to end-of-line comments | +| Name | Value | Description +| -------------------------- | ----- | -------------------------------------------| +| __ENABLE_LABEL_NEWLINE | any | Puts long labels on their own line | +| __ENABLE_ALL_LABEL_NEWLINE | any | Puts all labels on their own line | +| __ENABLE_CYCLE_COUNTS | any | Adds cycle count to end-of-line comments | ### Execution ### @@ -74,7 +76,7 @@ the "retain output" box in the test harness, the directory and its contents will remain. This allows you to examine the outputs when investigating failures. -As a safety measure, the directory will NOT be removed if it contains files +As a safety measure, a directory will NOT be removed if it contains files that the test harness doesn't recognize. ### Updating Tests ### @@ -115,4 +117,3 @@ for a full explanation. Some test projects and data files for exercising the visualization generators. Not part of a formal test; load the projects and eyeball the results. - diff --git a/SourceGen/SGTestData/Source/20280-label-placement.S b/SourceGen/SGTestData/Source/20280-label-placement.S new file mode 100644 index 0000000..eff9a9a --- /dev/null +++ b/SourceGen/SGTestData/Source/20280-label-placement.S @@ -0,0 +1,32 @@ +; Copyright 2024 faddenSoft. All Rights Reserved. +; See the LICENSE.txt file for distribution terms (Apache 2.0). +; +; The symbol __ENABLE_ALL_LABEL_NEWLINE must be defined in the project +; symbols, so that labels are placed on their own lines whenever possible. +; +; Assembler: Merlin 32 + + org $1000 +shortnm equ $4000 +SomewhatLongName equ $4001 + + lda shortnm + ldx SomewhatLongName + clc + bcc BranchTargetLongName +data dfb $cc + +]ptr equ $00 +]PointerWithLongName equ $02 + +BranchTargetLongName + sta ]ptr + stx ]PointerWithLongName + ldy data + lsr A + bcc shortb +shortb nop + + jmp done + +done rts diff --git a/SourceGen/Tests/GenTest.cs b/SourceGen/Tests/GenTest.cs index 09fa24e..91488cb 100644 --- a/SourceGen/Tests/GenTest.cs +++ b/SourceGen/Tests/GenTest.cs @@ -430,7 +430,8 @@ namespace SourceGen.Tests { // Don't break lines with long labels. That way we can redefine "long" // without breaking our tests. (This is purely cosmetic.) - settings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + settings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.PreferSameLine); // This could be on or off. Off seems less distracting. settings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); @@ -455,17 +456,23 @@ namespace SourceGen.Tests { } /// - /// Applies app setting overrides that were specified in the project settings. + /// Applies app setting overrides that were specified in the project properties. /// private void ApplyProjectSettings(AppSettings settings, DisasmProject project) { // We could probably make this a more general mechanism, but that would strain // things a bit, since we need to know the settings name, bool/int/string, and // desired value. Easier to just have a set of named features. const string ENABLE_LABEL_NEWLINE = "__ENABLE_LABEL_NEWLINE"; + const string ENABLE_ALL_LABEL_NEWLINE = "__ENABLE_ALL_LABEL_NEWLINE"; const string ENABLE_CYCLE_COUNTS = "__ENABLE_CYCLE_COUNTS"; if (project.ProjectProps.ProjectSyms.ContainsKey(ENABLE_LABEL_NEWLINE)) { - settings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, true); + settings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.SplitIfTooLong); + } + if (project.ProjectProps.ProjectSyms.ContainsKey(ENABLE_ALL_LABEL_NEWLINE)) { + settings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + GenCommon.LabelPlacement.PreferSeparateLine); } if (project.ProjectProps.ProjectSyms.ContainsKey(ENABLE_CYCLE_COUNTS)) { settings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, true); diff --git a/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs b/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs index 8b552ef..10623b7 100644 --- a/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs +++ b/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs @@ -75,8 +75,7 @@ namespace SourceGen.Tools.WpfGui { public void Window_Loaded(object sender, RoutedEventArgs e) { // Restore chart mode setting. - ChartMode mode = (ChartMode)AppSettings.Global.GetEnum( - AppSettings.A2SC_MODE, typeof(ChartMode), (int)ChartMode.HiRes1_L); + ChartMode mode = AppSettings.Global.GetEnum(AppSettings.A2SC_MODE, ChartMode.HiRes1_L); int index = 0; for (int i = 0; i < ChartModeItems.Length; i++) { if (ChartModeItems[i].Mode == mode) { @@ -107,7 +106,7 @@ namespace SourceGen.Tools.WpfGui { return; } - AppSettings.Global.SetEnum(AppSettings.A2SC_MODE, typeof(ChartMode), (int)item.Mode); + AppSettings.Global.SetEnum(AppSettings.A2SC_MODE, item.Mode); string text; switch (item.Mode) { diff --git a/SourceGen/Tools/WpfGui/AsciiChart.xaml.cs b/SourceGen/Tools/WpfGui/AsciiChart.xaml.cs index f4ffdaa..57f9dea 100644 --- a/SourceGen/Tools/WpfGui/AsciiChart.xaml.cs +++ b/SourceGen/Tools/WpfGui/AsciiChart.xaml.cs @@ -55,8 +55,7 @@ namespace SourceGen.Tools.WpfGui { public void Window_Loaded(object sender, RoutedEventArgs e) { // Restore chart mode setting. - ChartMode mode = (ChartMode)AppSettings.Global.GetEnum( - AppSettings.ASCCH_MODE, typeof(ChartMode), (int)ChartMode.Standard); + ChartMode mode = AppSettings.Global.GetEnum(AppSettings.ASCCH_MODE, ChartMode.Standard); int index = 0; for (int i = 0; i < ChartModeItems.Length; i++) { if (ChartModeItems[i].Mode == mode) { @@ -87,7 +86,7 @@ namespace SourceGen.Tools.WpfGui { return; } - AppSettings.Global.SetEnum(AppSettings.ASCCH_MODE, typeof(ChartMode), (int)item.Mode); + AppSettings.Global.SetEnum(AppSettings.ASCCH_MODE, item.Mode); // // Draw box contents. diff --git a/SourceGen/Tools/WpfGui/HexDumpViewer.xaml.cs b/SourceGen/Tools/WpfGui/HexDumpViewer.xaml.cs index 0e98762..87c854f 100644 --- a/SourceGen/Tools/WpfGui/HexDumpViewer.xaml.cs +++ b/SourceGen/Tools/WpfGui/HexDumpViewer.xaml.cs @@ -111,8 +111,8 @@ namespace SourceGen.Tools.WpfGui { AsciiOnlyDump = AppSettings.Global.GetBool(AppSettings.HEXD_ASCII_ONLY, false); // Restore conv mode setting. - CharConvMode mode = (CharConvMode)AppSettings.Global.GetEnum( - AppSettings.HEXD_CHAR_CONV, typeof(CharConvMode), (int)CharConvMode.Ascii); + CharConvMode mode = + AppSettings.Global.GetEnum(AppSettings.HEXD_CHAR_CONV, CharConvMode.Ascii); int index = 0; for (int i = 0; i < CharConvItems.Length; i++) { if (CharConvItems[i].Mode == mode) { @@ -156,8 +156,7 @@ namespace SourceGen.Tools.WpfGui { // Keep app settings up to date. AppSettings.Global.SetBool(AppSettings.HEXD_ASCII_ONLY, mAsciiOnlyDump); - AppSettings.Global.SetEnum(AppSettings.HEXD_CHAR_CONV, typeof(CharConvMode), - (int)item.Mode); + AppSettings.Global.SetEnum(AppSettings.HEXD_CHAR_CONV, item.Mode); mFormatter = new Formatter(config); HexDumpLines.Reformat(mFormatter); diff --git a/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs b/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs index e5892c5..c737609 100644 --- a/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs +++ b/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs @@ -112,8 +112,8 @@ namespace SourceGen.Tools.WpfGui { public void Window_Loaded(object sender, RoutedEventArgs e) { // Restore chart settings. - CpuDef.CpuType type = (CpuDef.CpuType)AppSettings.Global.GetEnum( - AppSettings.INSTCH_MODE, typeof(CpuDef.CpuType), (int)CpuDef.CpuType.Cpu6502); + CpuDef.CpuType type = + AppSettings.Global.GetEnum(AppSettings.INSTCH_MODE, CpuDef.CpuType.Cpu6502); ShowUndocumented = AppSettings.Global.GetBool(AppSettings.INSTCH_SHOW_UNDOC, true); int index = 0; @@ -146,8 +146,7 @@ namespace SourceGen.Tools.WpfGui { } // Push current choice to settings. - AppSettings.Global.SetEnum(AppSettings.INSTCH_MODE, typeof(CpuDef.CpuType), - (int)item.Type); + AppSettings.Global.SetEnum(AppSettings.INSTCH_MODE, item.Type); AppSettings.Global.SetBool(AppSettings.INSTCH_SHOW_UNDOC, mShowUndocumented); // Populate the items source. diff --git a/SourceGen/WpfGui/EditAppSettings.xaml b/SourceGen/WpfGui/EditAppSettings.xaml index 30398de..7d57a49 100644 --- a/SourceGen/WpfGui/EditAppSettings.xaml +++ b/SourceGen/WpfGui/EditAppSettings.xaml @@ -820,10 +820,20 @@ limitations under the License. - - + + + + + + diff --git a/SourceGen/WpfGui/EditAppSettings.xaml.cs b/SourceGen/WpfGui/EditAppSettings.xaml.cs index c7fe5f7..1f0d622 100644 --- a/SourceGen/WpfGui/EditAppSettings.xaml.cs +++ b/SourceGen/WpfGui/EditAppSettings.xaml.cs @@ -31,6 +31,7 @@ using CommonUtil; using AssemblerInfo = SourceGen.AsmGen.AssemblerInfo; using AssemblerConfig = SourceGen.AsmGen.AssemblerConfig; using ExpressionMode = Asm65.Formatter.FormatConfig.ExpressionMode; +using LabelPlacement = SourceGen.AsmGen.GenCommon.LabelPlacement; namespace SourceGen.WpfGui { /// @@ -280,8 +281,8 @@ namespace SourceGen.WpfGui { UpperOperandXY = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_XY, false); Debug.Assert(clipboardFormatComboBox.Items.Count == sClipboardFormatItems.Length); - int clipIndex = mSettings.GetEnum(AppSettings.CLIP_LINE_FORMAT, - typeof(MainController.ClipLineFormat), 0); + int clipIndex = (int)mSettings.GetEnum(AppSettings.CLIP_LINE_FORMAT, + MainController.ClipLineFormat.AssemblerSource); if (clipIndex >= 0 && clipIndex < sClipboardFormatItems.Length) { // require Value == clipIndex because we're lazy and don't want to search Debug.Assert((int)sClipboardFormatItems[clipIndex].Value == clipIndex); @@ -414,8 +415,7 @@ namespace SourceGen.WpfGui { private void ClipboardFormatComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ClipboardFormatItem item = (ClipboardFormatItem)clipboardFormatComboBox.SelectedItem; - mSettings.SetEnum(AppSettings.CLIP_LINE_FORMAT, typeof(MainController.ClipLineFormat), - (int)item.Value); + mSettings.SetEnum(AppSettings.CLIP_LINE_FORMAT, item.Value); IsDirty = true; } @@ -757,14 +757,6 @@ namespace SourceGen.WpfGui { IsDirty = true; } } - public bool LongLabelNewLine { - get { return mSettings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); } - set { - mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, value); - OnPropertyChanged(); - IsDirty = true; - } - } public bool AddIdentComment { get { return mSettings.GetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false); } set { @@ -774,6 +766,58 @@ namespace SourceGen.WpfGui { } } + // label placement radio buttons + public bool LabelPlacement_PreferSameLine { + get { + LabelPlacement place = mSettings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + LabelPlacement.SplitIfTooLong); + return place == LabelPlacement.PreferSameLine; + } + set { + if (value) { + mSettings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + LabelPlacement.PreferSameLine); + LabelPlacementChanged(); + IsDirty = true; + } + } + } + public bool LabelPlacement_SplitIfTooLong { + get { + LabelPlacement place = mSettings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + LabelPlacement.SplitIfTooLong); + return place == LabelPlacement.SplitIfTooLong; + } + set { + if (value) { + mSettings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + LabelPlacement.SplitIfTooLong); + LabelPlacementChanged(); + IsDirty = true; + } + } + } + public bool LabelPlacement_PreferSeparateLine { + get { + LabelPlacement place = mSettings.GetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + LabelPlacement.SplitIfTooLong); + return place == LabelPlacement.PreferSeparateLine; + } + set { + if (value) { + mSettings.SetEnum(AppSettings.SRCGEN_LABEL_NEW_LINE, + LabelPlacement.PreferSeparateLine); + LabelPlacementChanged(); + IsDirty = true; + } + } + } + private void LabelPlacementChanged() { + OnPropertyChanged(nameof(LabelPlacement_PreferSameLine)); + OnPropertyChanged(nameof(LabelPlacement_SplitIfTooLong)); + OnPropertyChanged(nameof(LabelPlacement_PreferSeparateLine)); + } + private void Loaded_AsmConfig() { asmConfigComboBox.SelectedItem = AssemblerInfo.GetAssemblerInfo(mInitialAsmId); if (asmConfigComboBox.SelectedIndex < 0) { diff --git a/SourceGen/WpfGui/EditDataOperand.xaml.cs b/SourceGen/WpfGui/EditDataOperand.xaml.cs index d88981f..6eef077 100644 --- a/SourceGen/WpfGui/EditDataOperand.xaml.cs +++ b/SourceGen/WpfGui/EditDataOperand.xaml.cs @@ -293,8 +293,7 @@ namespace SourceGen.WpfGui { AnalyzeStringRanges(item.Mode); UpdateControls(); - AppSettings.Global.SetEnum(AppSettings.OPED_DEFAULT_STRING_ENCODING, - typeof(TextScanMode), (int)item.Mode); + AppSettings.Global.SetEnum(AppSettings.OPED_DEFAULT_STRING_ENCODING, item.Mode); } private void OkButton_Click(object sender, RoutedEventArgs e) { @@ -780,9 +779,8 @@ namespace SourceGen.WpfGui { // Get the previous mode selected in the combo box. If the format descriptor // doesn't specify a string, we'll use this. - TextScanMode textMode = (TextScanMode)AppSettings.Global.GetEnum( - AppSettings.OPED_DEFAULT_STRING_ENCODING, typeof(TextScanMode), - (int)TextScanMode.LowHighAscii); + TextScanMode textMode = AppSettings.Global.GetEnum( + AppSettings.OPED_DEFAULT_STRING_ENCODING, TextScanMode.LowHighAscii); if (dfd == null) { radioDefaultFormat.IsChecked = true; diff --git a/SourceGen/WpfGui/Export.xaml.cs b/SourceGen/WpfGui/Export.xaml.cs index f82453c..8de731c 100644 --- a/SourceGen/WpfGui/Export.xaml.cs +++ b/SourceGen/WpfGui/Export.xaml.cs @@ -235,8 +235,8 @@ namespace SourceGen.WpfGui { AsmCommentColWidth = colWidths[3]; } - TextMode mode = (TextMode)AppSettings.Global.GetEnum(AppSettings.EXPORT_TEXT_MODE, - typeof(TextMode), (int)TextMode.PlainText); + TextMode mode = AppSettings.Global.GetEnum(AppSettings.EXPORT_TEXT_MODE, + TextMode.PlainText); if (mode == TextMode.PlainText) { TextModePlain = true; } else { @@ -273,7 +273,7 @@ namespace SourceGen.WpfGui { } else { mode = TextMode.Csv; } - AppSettings.Global.SetEnum(AppSettings.EXPORT_TEXT_MODE, typeof(TextMode), (int)mode); + AppSettings.Global.SetEnum(AppSettings.EXPORT_TEXT_MODE, mode); } /// diff --git a/SourceGen/WpfGui/GenerateLabels.xaml.cs b/SourceGen/WpfGui/GenerateLabels.xaml.cs index 05862a3..f4ce2d2 100644 --- a/SourceGen/WpfGui/GenerateLabels.xaml.cs +++ b/SourceGen/WpfGui/GenerateLabels.xaml.cs @@ -55,17 +55,15 @@ namespace SourceGen.WpfGui { Owner = owner; DataContext = this; - Format = - (LabelFileGenerator.LabelFmt)AppSettings.Global.GetEnum(AppSettings.LABGEN_FORMAT, - typeof(LabelFileGenerator.LabelFmt), (int)LabelFileGenerator.LabelFmt.VICE); + Format = AppSettings.Global.GetEnum(AppSettings.LABGEN_FORMAT, + LabelFileGenerator.LabelFmt.VICE); UpdateFormats(); mIncludeAutoLabels = AppSettings.Global.GetBool(AppSettings.LABGEN_INCLUDE_AUTO, false); } private void OkButton_Click(object sender, RoutedEventArgs e) { // Save settings. - AppSettings.Global.SetEnum(AppSettings.LABGEN_FORMAT, - typeof(LabelFileGenerator.LabelFmt), (int)Format); + AppSettings.Global.SetEnum(AppSettings.LABGEN_FORMAT, Format); AppSettings.Global.SetBool(AppSettings.LABGEN_INCLUDE_AUTO, mIncludeAutoLabels); DialogResult = true; }