Compare commits

...

2 Commits

Author SHA1 Message Date
Andy McFadden 4322a0c231 Add option to put labels on separate lines
We currently have two options for assembly code output, selected by
a checkbox in the application settings: always put labels on the same
lines as the instruction or data operand, or split the labels onto
their own line if they were wider than the label text field.

This change adds a third option, which puts labels on their own line
whenever possible.  Assemblers don't generally allow this for variable
assignment pseudo-ops like "foo = $1000", but it's accepted for most
other situations.  This is a cosmetic change to the output, and will
not affect the generated code.

The old true/false app setting will be disregarded.  "Split if too
long" will be used by default.

Added test 20280-label-placement to exercise the "split whenever
allowed" behavior.

The "export" function has a similar option that has not been updated
(for no particular reason other than laziness).

Also, simplified the app settings GetEnum / SetEnum calls, which
can infer the enumerated type from the arguments.  This should not
impact behavior.
2024-04-21 16:26:42 -07:00
Andy McFadden e425237783 First cut at label file generation
Some debuggers allow you to import a list of labels for addresses.
This adds a command that generates a file full of labels in a
specific format.  Currently the VICE monitor format is supported.

(issue #151)
2024-04-20 16:37:08 -07:00
33 changed files with 766 additions and 99 deletions

View File

@ -42,10 +42,10 @@ namespace CommonUtil {
/// </summary>
/// <remarks>
/// Usage:
/// AppDomain.CurrentDomain.UnhandledException +=
/// new UnhandledExceptionEventHandler(CommonUtil.Misc.CrashReporter);
/// <code>AppDomain.CurrentDomain.UnhandledException +=
/// new UnhandledExceptionEventHandler(CommonUtil.Misc.CrashReporter);</code>
///
/// Thanks: https://stackoverflow.com/a/21308327/294248
/// Thanks: <see href="https://stackoverflow.com/a/21308327/294248"/>.
/// </remarks>
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.
/// </summary>
/// <remarks>
/// From https://stackoverflow.com/a/18659408/294248
/// From <see href="https://stackoverflow.com/a/18659408/294248"/>.
///
/// Invokes Array.Copy() on overlapping elements. Other approaches involve using
/// Buffer.BlockCopy or unsafe code. Apparently .NET Core has an Array.Fill(), but

View File

@ -124,9 +124,13 @@ 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.
public const string LABGEN_FORMAT = "labgen-format";
public const string LABGEN_INCLUDE_AUTO = "labgen-include-auto";
// Assembler settings prefix
public const string ASM_CONFIG_PREFIX = "asm-config-";
@ -287,21 +291,20 @@ namespace SourceGen {
/// <summary>
/// Retrieves an enumerated value setting.
/// </summary>
/// <typeparam name="T">Enumerated type.</typeparam>
/// <param name="name">Setting name.</param>
/// <param name="enumType">Enum type that the value is part of.</param>
/// <param name="defaultValue">Setting default value.</param>
/// <returns>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.</returns>
public int GetEnum(string name, Type enumType, int defaultValue) {
/// name exists, or the stored value is not a member of the enumeration.</returns>
public T GetEnum<T>(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;
}
@ -310,14 +313,20 @@ namespace SourceGen {
/// <summary>
/// Sets an enumerated setting.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <typeparam name="T">Enumerated type.</typeparam>
/// <param name="name">Setting name.</param>
/// <param name="enumType">Enum type.</param>
/// <param name="value">Setting value (integer enum index).</param>
public void SetEnum(string name, Type enumType, int value) {
string newVal = Enum.GetName(enumType, value);
public void SetEnum<T>(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) {

View File

@ -62,9 +62,9 @@ namespace SourceGen.AsmGen {
private string mWorkDirectory;
/// <summary>
/// If set, long labels get their own line.
/// Influences whether labels are put on their own line.
/// </summary>
private bool mLongLabelNewLine;
private GenCommon.LabelPlacement mLabelNewLine;
/// <summary>
/// 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;
}

View File

@ -57,9 +57,9 @@ namespace SourceGen.AsmGen {
private string mWorkDirectory;
/// <summary>
/// If set, long labels get their own line.
/// Influences whether labels are put on their own line.
/// </summary>
private bool mLongLabelNewLine;
private GenCommon.LabelPlacement mLabelNewLine;
/// <summary>
/// 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;
}

View File

@ -57,9 +57,9 @@ namespace SourceGen.AsmGen {
private string mWorkDirectory;
/// <summary>
/// If set, long labels get their own line.
/// Influences whether labels are put on their own line.
/// </summary>
private bool mLongLabelNewLine;
private GenCommon.LabelPlacement mLabelNewLine;
/// <summary>
/// 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();

View File

@ -73,9 +73,9 @@ namespace SourceGen.AsmGen {
private string mWorkDirectory;
/// <summary>
/// If set, long labels get their own line.
/// Influences whether labels are put on their own line.
/// </summary>
private bool mLongLabelNewLine;
private GenCommon.LabelPlacement mLabelNewLine;
/// <summary>
/// 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;
}

View File

@ -23,11 +23,19 @@ using Asm65;
using CommonUtil;
namespace SourceGen.AsmGen {
/// <summary>
/// Code common to all assembly source generators.
/// </summary>
public class GenCommon {
public enum LabelPlacement {
Unknown = 0,
PreferSameLine,
SplitIfTooLong,
PreferSeparateLine,
}
/// <summary>
/// Generates assembly source.
///
/// This code is common to all generators.
/// </summary>
/// <param name="gen">Reference to generator object (presumably the caller).</param>
/// <param name="sw">Text output sink.</param>

View File

@ -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");

View File

@ -0,0 +1,77 @@
/*
* Copyright 2019 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace SourceGen {
/// <summary>
/// Label file generator.
/// </summary>
public class LabelFileGenerator {
public enum LabelFmt {
Unknown = 0,
VICE,
}
private DisasmProject mProject;
private LabelFmt mFormat;
private bool mIncludeAutoLabels;
public LabelFileGenerator(DisasmProject project, LabelFmt format, bool includeAutoLabels) {
mProject = project;
mFormat = format;
mIncludeAutoLabels = includeAutoLabels;
}
public void Generate(StreamWriter outStream) {
List<Symbol> symList = new List<Symbol>();
foreach (Symbol sym in mProject.SymbolTable) {
bool include;
switch (sym.SymbolSource) {
case Symbol.Source.User:
case Symbol.Source.AddrPreLabel:
include = true;
break;
case Symbol.Source.Auto:
include = mIncludeAutoLabels;
break;
case Symbol.Source.Project:
case Symbol.Source.Platform:
case Symbol.Source.Variable:
default:
include = false;
break;
}
if (include) {
symList.Add(sym);
}
}
// Sort alphabetically. Not necessary, but it could make a "diff" easier to read.
symList.Sort((a, b) => Symbol.Compare(Symbol.SymbolSortField.Name, true, a, b));
// VICE format is "add_label <address> <label>", but may be abbreviated "al".
// We could also use ACME format ("labelname = $1234 ; Maybe a comment").
foreach (Symbol sym in symList) {
outStream.WriteLine("al " + sym.Value.ToString("x6") + " " + sym.Label);
}
}
}
}

View File

@ -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 };
@ -2557,6 +2556,47 @@ namespace SourceGen {
}
}
public void GenerateLabels() {
GenerateLabels dlg = new GenerateLabels(mMainWin);
if (dlg.ShowDialog() == false) {
return;
}
string ext;
string filter;
switch (dlg.Format) {
case LabelFileGenerator.LabelFmt.VICE:
ext = ".vs";
filter = "VICE commands (*.vs)|*.vs";
break;
default:
Debug.Assert(false, "bad format");
return;
}
SaveFileDialog fileDlg = new SaveFileDialog() {
Filter = filter,
FilterIndex = 1,
ValidateNames = true,
AddExtension = true, // doesn't add extension if non-ext file exists
FileName = "labels" + ext
};
if (fileDlg.ShowDialog() != true) {
return;
}
string pathName = Path.GetFullPath(fileDlg.FileName);
try {
using (StreamWriter writer = new StreamWriter(pathName, false)) {
LabelFileGenerator gen = new LabelFileGenerator(mProject,
dlg.Format, dlg.IncludeAutoLabels);
gen.Generate(writer);
}
} catch (Exception ex) {
MessageBox.Show("Error: " + ex.Message, "Failed", MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
public void Find() {
FindBox dlg = new FindBox(mMainWin, mFindString);
if (dlg.ShowDialog() == true) {

Binary file not shown.

View File

@ -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":{
}}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {}

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -79,6 +79,7 @@
<Compile Include="DailyTips.cs" />
<Compile Include="Exporter.cs" />
<Compile Include="FormattedOperandCache.cs" />
<Compile Include="LabelFileGenerator.cs" />
<Compile Include="LocalVariableLookup.cs" />
<Compile Include="MessageList.cs" />
<Compile Include="Sgec.cs" />
@ -188,6 +189,9 @@
<Compile Include="WpfGui\FormatAddressTable.xaml.cs">
<DependentUpon>FormatAddressTable.xaml</DependentUpon>
</Compile>
<Compile Include="WpfGui\GenerateLabels.xaml.cs">
<DependentUpon>GenerateLabels.xaml</DependentUpon>
</Compile>
<Compile Include="WpfGui\GotoBox.xaml.cs">
<DependentUpon>GotoBox.xaml</DependentUpon>
</Compile>
@ -413,6 +417,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="WpfGui\GenerateLabels.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="WpfGui\GotoBox.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -495,4 +503,4 @@
<Resource Include="Res\RedX.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View File

@ -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 {
}
/// <summary>
/// Applies app setting overrides that were specified in the project settings.
/// Applies app setting overrides that were specified in the project properties.
/// </summary>
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);

View File

@ -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) {

View File

@ -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.

View File

@ -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);

View File

@ -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.

View File

@ -820,10 +820,20 @@ limitations under the License.
<TextBlock Text="General code generation:" Margin="4,16,0,0"/>
<CheckBox Content="Show cycle counts in comments" Margin="4,8,0,0"
IsChecked="{Binding ShowCycleCountsAsm}"/>
<CheckBox Content="Put long labels on separate line" Margin="4,8,0,0"
IsChecked="{Binding LongLabelNewLine}"/>
<CheckBox Content="Identify assembler in output" Margin="4,8,0,0"
<CheckBox Content="Identify assembler in output" Margin="4,6,0,0"
IsChecked="{Binding AddIdentComment}"/>
<StackPanel Orientation="Vertical" Margin="10,6,0,0">
<TextBlock Text="Put labels on their own line..."/>
<RadioButton GroupName="labelLineGroup" Margin="0,4,0,0"
Content="Only when required"
IsChecked="{Binding LabelPlacement_PreferSameLine}"/>
<RadioButton GroupName="labelLineGroup" Margin="0,4,0,0"
Content="When the label is wider than the field"
IsChecked="{Binding LabelPlacement_SplitIfTooLong}"/>
<RadioButton GroupName="labelLineGroup" Margin="0,4,0,0"
Content="Whenever possible"
IsChecked="{Binding LabelPlacement_PreferSeparateLine}"/>
</StackPanel>
</StackPanel>
</TabItem>

View File

@ -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 {
/// <summary>
@ -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) {

View File

@ -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;

View File

@ -20,9 +20,9 @@ using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using CommonUtil;
using Microsoft.Win32;
namespace SourceGen.WpfGui {
/// <summary>
@ -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);
}
/// <summary>

View File

@ -0,0 +1,52 @@
<!--
Copyright 2024 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Window x:Class="SourceGen.WpfGui.GenerateLabels"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Generate Label File"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
<Grid Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Output Format" Padding="2,4">
<StackPanel>
<RadioButton Content="VICE label commands"
ToolTip="Load in VICE monitor with 'll' command."
IsChecked="{Binding Format_VICE}"/>
</StackPanel>
</GroupBox>
<CheckBox Grid.Row="1" Margin="0,4,0,0" Content="Include auto-generated labels"
IsChecked="{Binding IncludeAutoLabels}"/>
<StackPanel Grid.Row="2" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="0,8,0,0">
<Button Content="Generate" IsDefault="True" Width="70" Click="OkButton_Click"/>
<Button Content="Cancel" IsCancel="True" Width="70" Margin="4,0,0,0"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,71 @@
/*
* Copyright 2019 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
namespace SourceGen.WpfGui {
/// <summary>
/// Select parameters for label file generation.
/// </summary>
public partial class GenerateLabels : Window, INotifyPropertyChanged {
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public bool IncludeAutoLabels {
get { return mIncludeAutoLabels; }
set { mIncludeAutoLabels = value; OnPropertyChanged(); }
}
private bool mIncludeAutoLabels;
public LabelFileGenerator.LabelFmt Format { get; private set; }
public bool Format_VICE {
get { return Format == LabelFileGenerator.LabelFmt.VICE;}
set { Format = LabelFileGenerator.LabelFmt.VICE; UpdateFormats(); }
}
private void UpdateFormats() {
OnPropertyChanged(nameof(Format_VICE));
}
public GenerateLabels(Window owner) {
InitializeComponent();
Owner = owner;
DataContext = this;
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, Format);
AppSettings.Global.SetBool(AppSettings.LABGEN_INCLUDE_AUTO, mIncludeAutoLabels);
DialogResult = true;
}
}
}

View File

@ -136,6 +136,7 @@ limitations under the License.
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="FormatAddressTableCmd" Text="Format Address Table..."/>
<RoutedUICommand x:Key="GenerateLabelsCmd" Text="Generate Label File..."/>
<RoutedUICommand x:Key="GotoCmd" Text="Go To...">
<RoutedUICommand.InputGestures>
<KeyGesture>Ctrl+G</KeyGesture>
@ -284,6 +285,8 @@ limitations under the License.
CanExecute="CanFormatAsWord" Executed="FormatAsWordCmd_Executed"/>
<CommandBinding Command="{StaticResource FormatAddressTableCmd}"
CanExecute="CanFormatAddressTable" Executed="FormatAddressTableCmd_Executed"/>
<CommandBinding Command="{StaticResource GenerateLabelsCmd}"
CanExecute="IsProjectOpen" Executed="GenerateLabelsCmd_Executed"/>
<CommandBinding Command="{StaticResource GotoCmd}"
CanExecute="IsProjectOpen" Executed="GotoCmd_Executed"/>
<CommandBinding Command="{StaticResource GotoLastChangeCmd}"
@ -392,8 +395,8 @@ limitations under the License.
</MenuItem>
<Separator/>
<MenuItem Command="{StaticResource AssembleCmd}"/>
<!--<MenuItem Command="Print"/>-->
<MenuItem Command="{StaticResource ExportCmd}"/>
<MenuItem Command="{StaticResource GenerateLabelsCmd}"/>
<Separator/>
<MenuItem Command="{StaticResource ReloadExternalFilesCmd}"/>
<Separator/>

View File

@ -1332,6 +1332,10 @@ namespace SourceGen.WpfGui {
mMainCtrl.FormatAddressTable();
}
private void GenerateLabelsCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.GenerateLabels();
}
private void GotoCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.Goto();
}