1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-12 08:29:29 +00:00

Various improvements

The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable.  This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary.  The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.

Added an equality test to PseudoOpNames.  In LineListGen, don't
reset the line list if the names haven't actually changed.

Use a table lookup for C64 character conversions.  I figure that
should be faster than multiple conditionals on a modern x64 system.

Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).

Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.

Increased the width of text entry fields on the Pseudo-Op tab of app
settings.  The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc".  Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).

In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers.  It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run.  My guess is this has to do with changes to the built-in
malware scanner.  Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
This commit is contained in:
Andy McFadden 2019-08-17 11:14:05 -07:00
parent 268ad18067
commit 4902b89cf8
14 changed files with 341 additions and 221 deletions

View File

@ -193,17 +193,29 @@ namespace Asm65 {
public static bool IsExtendedC64Petscii(byte val) {
return sExtendedPetscii[val];
}
public static char ConvertC64Petscii(byte val) {
if ((val >= 0x20 && val <= 0x40) || val == 0x5b || val == 0x5d) {
return (char)val; // number/symbols, '[', ']'
} else if (val >= 0x41 && val <= 0x5a) {
return (char)(val + 0x20); // lower case
} else if (val >= 0xc1 && val <= 0xda) {
return (char)(val - 0x80); // upper case
} else {
Debug.Assert(!IsPrintableC64Petscii(val));
return UNPRINTABLE_CHAR;
private static char[] sPetsciiToUnicode = CreatePetsciiToUnicodeMap();
private static char[] CreatePetsciiToUnicodeMap() {
// There are performance arguments for doing this with and without a table. For
// x64 with fast memory and large caches, table seems reasonable.
char[] map = new char[256];
for (int val = 0; val < 256; val++) {
char ch;
if ((val >= 0x20 && val <= 0x40) || val == 0x5b || val == 0x5d) {
ch = (char)val; // number/symbols, '[', ']'
} else if (val >= 0x41 && val <= 0x5a) {
ch = (char)(val + 0x20); // lower case
} else if (val >= 0xc1 && val <= 0xda) {
ch = (char)(val - 0x80); // upper case
} else {
Debug.Assert(!IsPrintableC64Petscii((byte)val));
ch = UNPRINTABLE_CHAR;
}
map[val] = ch;
}
return map;
}
public static char ConvertC64Petscii(byte val) {
return sPetsciiToUnicode[val];
}
//
@ -243,19 +255,29 @@ namespace Asm65 {
public static bool IsExtendedC64ScreenCode(byte val) {
return sPrintableScreenCode[val];
}
public static char ConvertC64ScreenCode(byte val) {
if (val == 0x00 || val == 0x1b || val == 0x1d) {
return (char)(val + 0x40); // '@', '[', ']'
} else if (val >= 0x01 && val <= 0x1a) {
return (char)(val + 0x60); // lower case
} else if (val >= 0x20 && val <= 0x3f) {
return (char)(val); // numbers/symbols
} else if (val >= 0x41 && val <= 0x5a) {
return (char)(val); // upper case
} else {
Debug.Assert(!IsPrintableC64ScreenCode(val));
return UNPRINTABLE_CHAR;
private static char[] sScreenCodeToUnicode = CreateScreenCodeToUnicodeMap();
private static char[] CreateScreenCodeToUnicodeMap() {
char[] map = new char[256];
for (int val = 0; val < 256; val++) {
char ch;
if (val == 0x00 || val == 0x1b || val == 0x1d) {
ch = (char)(val + 0x40); // '@', '[', ']'
} else if (val >= 0x01 && val <= 0x1a) {
ch = (char)(val + 0x60); // lower case
} else if (val >= 0x20 && val <= 0x3f) {
ch = (char)(val); // numbers/symbols
} else if (val >= 0x41 && val <= 0x5a) {
ch = (char)(val); // upper case
} else {
Debug.Assert(!IsPrintableC64ScreenCode((byte)val));
ch = UNPRINTABLE_CHAR;
}
map[val] = ch;
}
return map;
}
public static char ConvertC64ScreenCode(byte val) {
return sScreenCodeToUnicode[val];
}
public static char ConvertLowAndHighC64ScreenCode(byte val) {
return ConvertC64ScreenCode((byte)(val & 0x7f));

View File

@ -103,32 +103,33 @@ namespace SourceGen.AsmGen {
// Pseudo-op string constants.
private static PseudoOp.PseudoOpNames sDataOpNames = new PseudoOp.PseudoOpNames() {
EquDirective = "=",
OrgDirective = "!pseudopc",
//RegWidthDirective // !al, !as, !rl, !rs
DefineData1 = "!byte",
DefineData2 = "!word",
DefineData3 = "!24",
DefineData4 = "!32",
//DefineBigData2
//DefineBigData3
//DefineBigData4
Fill = "!fill",
Dense = "!hex",
StrGeneric = "!text", // can use !xor for high ASCII
//StrReverse
//StrNullTerm
//StrLen8
//StrLen16
//StrDci
};
private static PseudoOp.PseudoOpNames sDataOpNames =
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" },
{ "OrgDirective", "!pseudopc" },
//RegWidthDirective // !al, !as, !rl, !rs
{ "DefineData1", "!byte" },
{ "DefineData2", "!word" },
{ "DefineData3", "!24" },
{ "DefineData4", "!32" },
//DefineBigData2
//DefineBigData3
//DefineBigData4
{ "Fill", "!fill" },
{ "Dense", "!hex" },
{ "StrGeneric", "!text" }, // can use !xor for high ASCII
//StrReverse
//StrNullTerm
//StrLen8
//StrLen16
//StrDci
});
// IGenerator
public void GetDefaultDisplayFormat(out PseudoOp.PseudoOpNames pseudoOps,
out Formatter.FormatConfig formatConfig) {
pseudoOps = sDataOpNames.GetCopy();
pseudoOps = sDataOpNames;
formatConfig = new Formatter.FormatConfig();
SetFormatConfigValues(ref formatConfig);
@ -374,7 +375,7 @@ namespace SourceGen.AsmGen {
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
if (opcodeStr == null) {
if (string.IsNullOrEmpty(opcodeStr)) {
// Nothing defined, output as comma-separated single-byte values.
GenerateShortSequence(offset, length, out opcodeStr, out operandStr);
} else {

View File

@ -100,32 +100,33 @@ namespace SourceGen.AsmGen {
// Pseudo-op string constants.
private static PseudoOp.PseudoOpNames sDataOpNames = new PseudoOp.PseudoOpNames() {
EquDirective = "=",
OrgDirective = ".org",
//RegWidthDirective // .a8, .a16, .i8, .i16
DefineData1 = ".byte",
DefineData2 = ".word",
DefineData3 = ".faraddr",
DefineData4 = ".dword",
DefineBigData2 = ".dbyt",
//DefineBigData3
//DefineBigData4
Fill = ".res",
//Dense // no equivalent, use .byte with comma-separated args
StrGeneric = ".byte",
//StrReverse
StrNullTerm = ".asciiz",
//StrLen8 // macro with .strlen?
//StrLen16
//StrDci
};
private static PseudoOp.PseudoOpNames sDataOpNames =
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" },
{ "OrgDirective", ".org" },
//RegWidthDirective // .a8, .a16, .i8, .i16
{ "DefineData1", ".byte" },
{ "DefineData2", ".word" },
{ "DefineData3", ".faraddr" },
{ "DefineData4", ".dword" },
{ "DefineBigData2", ".dbyt" },
//DefineBigData3
//DefineBigData4
{ "Fill", ".res" },
//Dense // no equivalent, use .byte with comma-separated args
{ "StrGeneric", ".byte" },
//StrReverse
{ "StrNullTerm", ".asciiz" },
//StrLen8 // macro with .strlen?
//StrLen16
//StrDci
});
// IGenerator
public void GetDefaultDisplayFormat(out PseudoOp.PseudoOpNames pseudoOps,
out Formatter.FormatConfig formatConfig) {
pseudoOps = sDataOpNames.GetCopy();
pseudoOps = sDataOpNames;
formatConfig = new Formatter.FormatConfig();
SetFormatConfigValues(ref formatConfig);
@ -401,7 +402,7 @@ namespace SourceGen.AsmGen {
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
if (opcodeStr == null) {
if ((string.IsNullOrEmpty(opcodeStr))) {
// Nothing defined, output as comma-separated single-byte values.
GenerateShortSequence(offset, length, out opcodeStr, out operandStr);
} else {

View File

@ -90,37 +90,35 @@ namespace SourceGen.AsmGen {
private CommonUtil.Version mAsmVersion = CommonUtil.Version.NO_VERSION;
// Semi-convenient way to hold all the interesting string constants in one place.
// Note the actual usage of the pseudo-op may not match what the main app does,
// e.g. RegWidthDirective behaves differently from "mx". I'm just trying to avoid
// having string constants scattered all over.
private static PseudoOp.PseudoOpNames sDataOpNames = new PseudoOp.PseudoOpNames() {
EquDirective = "equ",
OrgDirective = "org",
RegWidthDirective = "mx",
DefineData1 = "dfb",
DefineData2 = "dw",
DefineData3 = "adr",
DefineData4 = "adrl",
DefineBigData2 = "ddb",
//DefineBigData3
//DefineBigData4
Fill = "ds",
Dense = "hex",
StrGeneric = "asc",
StrReverse = "rev",
//StrNullTerm
StrLen8 = "str",
StrLen16 = "strl",
StrDci = "dci",
};
// Pseudo-op string constants.
private static PseudoOp.PseudoOpNames sDataOpNames =
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "equ" },
{ "OrgDirective", "org" },
//RegWidthDirective
{ "DefineData1", "dfb" },
{ "DefineData2", "dw" },
{ "DefineData3", "adr" },
{ "DefineData4", "adrl" },
{ "DefineBigData2", "ddb" },
//DefineBigData3
//DefineBigData4
{ "Fill", "ds" },
{ "Dense", "hex" },
{ "StrGeneric", "asc" },
{ "StrReverse", "rev" },
//StrNullTerm
{ "StrLen8", "str" },
{ "StrLen16", "strl" },
{ "StrDci", "dci" },
});
private const string REG_WIDTH_DIRECTIVE = "mx";
// IGenerator
public void GetDefaultDisplayFormat(out PseudoOp.PseudoOpNames pseudoOps,
out Formatter.FormatConfig formatConfig) {
pseudoOps = sDataOpNames.GetCopy();
pseudoOps.RegWidthDirective = string.Empty;
pseudoOps = sDataOpNames;
formatConfig = new Formatter.FormatConfig();
SetFormatConfigValues(ref formatConfig);
@ -249,7 +247,7 @@ namespace SourceGen.AsmGen {
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
if (opcodeStr == null) {
if ((string.IsNullOrEmpty(opcodeStr))) {
// Nothing defined, output as comma-separated single-byte values.
GenerateShortSequence(offset, length, out opcodeStr, out operandStr);
} else {
@ -413,8 +411,7 @@ namespace SourceGen.AsmGen {
// Assembler defaults to short regs, so we can skip this.
return;
}
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.RegWidthDirective),
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(REG_WIDTH_DIRECTIVE),
"%" + newM + newX, string.Empty);
}

View File

@ -111,33 +111,34 @@ namespace SourceGen.AsmGen {
// Pseudo-op string constants.
private static PseudoOp.PseudoOpNames sDataOpNames = new PseudoOp.PseudoOpNames() {
EquDirective = "=",
OrgDirective = ".logical",
//RegWidthDirective // .as, .al, .xs, .xl
DefineData1 = ".byte",
DefineData2 = ".word",
DefineData3 = ".long",
DefineData4 = ".dword",
//DefineBigData2
//DefineBigData3
//DefineBigData4
Fill = ".fill",
//Dense // no equivalent, use .byte with comma-separated args
StrGeneric = ".text",
//StrReverse
StrNullTerm = ".null",
StrLen8 = ".ptext",
//StrLen16
StrDci = ".shift"
};
private static PseudoOp.PseudoOpNames sDataOpNames =
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" },
{ "OrgDirective", ".logical" },
//RegWidthDirective // .as, .al, .xs, .xl
{ "DefineData1", ".byte" },
{ "DefineData2", ".word" },
{ "DefineData3", ".long" },
{ "DefineData4", ".dword" },
//DefineBigData2
//DefineBigData3
//DefineBigData4
{ "Fill", ".fill" },
//Dense // no equivalent, use .byte with comma-separated args
{ "StrGeneric", ".text" },
//StrReverse
{ "StrNullTerm", ".null" },
{ "StrLen8", ".ptext" },
//StrLen16
{ "StrDci", ".shift" }
});
private const string HERE_PSEUDO_OP = ".here";
// IGenerator
public void GetDefaultDisplayFormat(out PseudoOp.PseudoOpNames pseudoOps,
out Formatter.FormatConfig formatConfig) {
pseudoOps = sDataOpNames.GetCopy();
pseudoOps = sDataOpNames;
formatConfig = new Formatter.FormatConfig();
SetFormatConfigValues(ref formatConfig);
@ -165,7 +166,7 @@ namespace SourceGen.AsmGen {
}
/// <summary>
/// Configures the assembler-specific format items.
/// Configures the assembler-specific format items. May be called without a Project.
/// </summary>
private void SetFormatConfigValues(ref Formatter.FormatConfig config) {
// Must be lower case when --case-sensitive is used.
@ -187,27 +188,6 @@ namespace SourceGen.AsmGen {
config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = ";";
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common;
// Configure delimiters for single-character operands. The conversion mode we
// use is determined by the default text mode in the project properties.
Formatter.DelimiterSet charSet = new Formatter.DelimiterSet();
TextScanMode textMode = Project.ProjectProps.AnalysisParams.DefaultTextScanMode;
switch (textMode) {
case TextScanMode.C64Petscii:
charSet.Set(CharEncoding.Encoding.C64Petscii, Formatter.SINGLE_QUOTE_DELIM);
break;
case TextScanMode.C64ScreenCode:
charSet.Set(CharEncoding.Encoding.C64ScreenCode, Formatter.SINGLE_QUOTE_DELIM);
break;
case TextScanMode.LowAscii:
case TextScanMode.LowHighAscii:
default:
charSet.Set(CharEncoding.Encoding.Ascii, Formatter.SINGLE_QUOTE_DELIM);
charSet.Set(CharEncoding.Encoding.HighAscii,
new Formatter.DelimiterDef(string.Empty, '\'', '\'', " | $80"));
break;
}
config.mCharDelimiters = charSet;
}
// IGenerator
@ -221,6 +201,29 @@ namespace SourceGen.AsmGen {
Formatter.FormatConfig config = new Formatter.FormatConfig();
GenCommon.ConfigureFormatterFromSettings(Settings, ref config);
SetFormatConfigValues(ref config);
// Configure delimiters for single-character operands. The conversion mode we
// use is determined by the default text mode in the project properties.
Formatter.DelimiterSet charSet = new Formatter.DelimiterSet();
TextScanMode textMode = Project.ProjectProps.AnalysisParams.DefaultTextScanMode;
switch (textMode) {
case TextScanMode.C64Petscii:
charSet.Set(CharEncoding.Encoding.C64Petscii,
Formatter.SINGLE_QUOTE_DELIM);
break;
case TextScanMode.C64ScreenCode:
charSet.Set(CharEncoding.Encoding.C64ScreenCode,
Formatter.SINGLE_QUOTE_DELIM);
break;
case TextScanMode.LowAscii:
case TextScanMode.LowHighAscii:
default:
charSet.Set(CharEncoding.Encoding.Ascii, Formatter.SINGLE_QUOTE_DELIM);
charSet.Set(CharEncoding.Encoding.HighAscii,
new Formatter.DelimiterDef(string.Empty, '\'', '\'', " | $80"));
break;
}
config.mCharDelimiters = charSet;
SourceFormatter = new Formatter(config);
string msg = string.Format(Res.Strings.PROGRESS_GENERATING_FMT, pathName);
@ -387,7 +390,7 @@ namespace SourceGen.AsmGen {
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
if (opcodeStr == null) {
if ((string.IsNullOrEmpty(opcodeStr))) {
// Nothing defined, output as comma-separated single-byte values.
GenerateShortSequence(offset, length, out opcodeStr, out operandStr);
} else {

View File

@ -28,8 +28,9 @@ namespace SourceGen.AsmGen {
/// Returns some strings and format options for use in for the display list, configurable
/// through the app settings "quick set" feature. These are not used when generating
/// source code.
///
/// This may be called on an unconfigured IGenerator.
///
/// This may be called on an unconfigured IGenerator, so this should not expect to
/// have access to project properties.
/// </summary>
/// <param name="pseudoOps">Table of pseudo-op names.</param>
/// <param name="formatConfig">Format configuration.</param>

View File

@ -50,6 +50,7 @@ namespace SourceGen {
/// https://www.codeproject.com/Articles/34405/WPF-Data-Virtualization?msg=5635751
/// https://web.archive.org/web/20121216034305/http://www.zagstudio.com/blog/498
/// https://web.archive.org/web/20121107200359/http://www.zagstudio.com/blog/378
/// https://github.com/lvaleriu/Virtualization
/// </remarks>
public class DisplayList : IList<DisplayList.FormattedParts>, IList,
INotifyCollectionChanged, INotifyPropertyChanged {

View File

@ -32,9 +32,10 @@ namespace SourceGen {
/// There may be a large number of these, so try to keep the size down. These are usually
/// stored in lists, not arrays, so declaring as a struct wouldn't help with that.
///
/// The stringified names of the enum values are currently serialized into the project
/// file. DO NOT rename members of the enumeration without creating an upgrade path. If
/// new values are added, the project file version number should be incremented.
/// IMPORTANT: The stringified names of the enum values are currently serialized into
/// the project file. DO NOT rename members of the enumerations without creating an
/// upgrade path. If new values are added, the project file version number should
/// be incremented.
/// </summary>
public class FormatDescriptor {
/// <summary>

View File

@ -443,11 +443,16 @@ namespace SourceGen {
/// <summary>
/// Changes the pseudo-op name object. Clears the line list, instigating a
/// full re-render.
/// full re-render. If the new set is unchanged from the old set, nothing is done.
/// </summary>
/// <param name="opNames">Pseudo-op names.</param>
public void SetPseudoOpNames(PseudoOp.PseudoOpNames opNames) {
if (mPseudoOpNames == opNames) {
return;
}
mPseudoOpNames = opNames;
Debug.Assert(mPseudoOpNames == opNames);
mLineList.Clear();
mDisplayList.Clear();
}

View File

@ -386,7 +386,7 @@ namespace SourceGen {
/// Replaces the contents of the global settings object with the new settings,
/// then applies them to the project.
/// </summary>
/// <param name="settings"></param>
/// <param name="settings">New settings.</param>
public void SetAppSettings(AppSettings settings) {
AppSettings.Global.ReplaceSettings(settings);
ApplyAppSettings();
@ -450,6 +450,8 @@ namespace SourceGen {
}
// Update the formatter, and null out mOutputFormatterCpuDef to force a refresh
// of related items.
mOutputFormatter = new Formatter(mFormatterConfig);
mOutputFormatterCpuDef = null;
@ -1410,6 +1412,8 @@ namespace SourceGen {
EditAppSettings dlg = new EditAppSettings(owner, mMainWin, this,
initialTab, initialAsmId);
dlg.ShowDialog();
// The settings code calls SetAppSettings() directly whenever "Apply" is hit.
}
public void HandleCodeListDoubleClick(int row, int col) {

View File

@ -59,28 +59,89 @@ namespace SourceGen {
}
/// <summary>
/// Pseudo-op name collection. Name strings may be null.
/// Pseudo-op name collection. Instances are immutable.
/// </summary>
public class PseudoOpNames {
public string EquDirective { get; set; }
public string OrgDirective { get; set; }
public string RegWidthDirective { get; set; }
public string EquDirective { get; private set; }
public string OrgDirective { get; private set; }
public string RegWidthDirective { get; private set; }
public string DefineData1 { get; set; }
public string DefineData2 { get; set; }
public string DefineData3 { get; set; }
public string DefineData4 { get; set; }
public string DefineBigData2 { get; set; }
public string DefineBigData3 { get; set; }
public string DefineBigData4 { get; set; }
public string Fill { get; set; }
public string Dense { get; set; }
public string StrGeneric { get; set; }
public string StrReverse { get; set; }
public string StrLen8 { get; set; }
public string StrLen16 { get; set; }
public string StrNullTerm { get; set; }
public string StrDci { get; set; }
public string DefineData1 { get; private set; }
public string DefineData2 { get; private set; }
public string DefineData3 { get; private set; }
public string DefineData4 { get; private set; }
public string DefineBigData2 { get; private set; }
public string DefineBigData3 { get; private set; }
public string DefineBigData4 { get; private set; }
public string Fill { get; private set; }
public string Dense { get; private set; }
public string StrGeneric { get; private set; }
public string StrReverse { get; private set; }
public string StrLen8 { get; private set; }
public string StrLen16 { get; private set; }
public string StrNullTerm { get; private set; }
public string StrDci { get; private set; }
/// <summary>
/// Constructs an empty PseudoOp.
/// </summary>
public PseudoOpNames() : this(new Dictionary<string, string>()) {
}
/// <summary>
/// Constructor. Pass in a dictionary with name/value pairs. Unknown names
/// will be ignored, missing names will be assigned the empty string.
/// </summary>
/// <param name="dict">Dictionary of values.</param>
public PseudoOpNames(Dictionary<string, string> dict) {
foreach (PropertyInfo prop in GetType().GetProperties()) {
dict.TryGetValue(prop.Name, out string value);
if (value == null) {
value = string.Empty;
}
prop.SetValue(this, value);
}
}
public static bool operator ==(PseudoOpNames a, PseudoOpNames b) {
if (ReferenceEquals(a, b)) {
return true; // same object, or both null
}
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
return false; // one is null
}
return a.EquDirective == b.EquDirective &&
a.OrgDirective == b.OrgDirective &&
a.RegWidthDirective == b.RegWidthDirective &&
a.DefineData1 == b.DefineData1 &&
a.DefineData2 == b.DefineData2 &&
a.DefineData3 == b.DefineData3 &&
a.DefineData4 == b.DefineData4 &&
a.DefineBigData2 == b.DefineBigData2 &&
a.DefineBigData3 == b.DefineBigData3 &&
a.DefineBigData4 == b.DefineBigData4 &&
a.Fill == b.Fill &&
a.Dense == b.Dense &&
a.StrGeneric == b.StrGeneric &&
a.StrReverse == b.StrReverse &&
a.StrLen8 == b.StrLen8 &&
a.StrLen16 == b.StrLen16 &&
a.StrNullTerm == b.StrNullTerm &&
a.StrDci == b.StrDci;
}
public static bool operator !=(PseudoOpNames a, PseudoOpNames b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is PseudoOpNames && this == (PseudoOpNames)obj;
}
public override int GetHashCode() {
// should be enough
return (EquDirective == null ? 0 : EquDirective.GetHashCode()) ^
(OrgDirective == null ? 0 : OrgDirective.GetHashCode()) ^
(DefineData1 == null ? 0 : DefineData1.GetHashCode()) ^
(Fill == null ? 0 : Fill.GetHashCode());
}
public string GetDefineData(int width) {
switch (width) {
@ -101,11 +162,6 @@ namespace SourceGen {
}
}
public PseudoOpNames GetCopy() {
// Do it the lazy way.
return Deserialize(Serialize());
}
/// <summary>
/// Merges the non-null, non-empty strings in "other" into this instance.
/// </summary>
@ -127,13 +183,24 @@ namespace SourceGen {
// which means a lot of double-quote escaping. We could do something here
// that stored more nicely but it doesn't seem worth the effort.
JavaScriptSerializer ser = new JavaScriptSerializer();
return ser.Serialize(this);
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (PropertyInfo prop in GetType().GetProperties()) {
string value = (string)prop.GetValue(this);
if (!string.IsNullOrEmpty(value)) {
dict[prop.Name] = value;
}
}
return ser.Serialize(dict);
}
public static PseudoOpNames Deserialize(string cereal) {
JavaScriptSerializer ser = new JavaScriptSerializer();
try {
return ser.Deserialize<PseudoOpNames>(cereal);
Dictionary<string, string> dict =
ser.Deserialize<Dictionary<string, string>>(cereal);
return new PseudoOpNames(dict);
} catch (Exception ex) {
Debug.WriteLine("PseudoOpNames deserialization failed: " + ex.Message);
return new PseudoOpNames();
@ -142,34 +209,34 @@ namespace SourceGen {
}
/// <summary>
/// Returns a new PseudoOpNames instance with some reasonable defaults for on-screen
/// display.
/// Returns a PseudoOpNames instance with some reasonable defaults for on-screen display.
/// </summary>
public static PseudoOpNames DefaultPseudoOpNames {
get { return sDefaultPseudoOpNames.GetCopy(); }
get { return sDefaultPseudoOpNames; }
}
private static readonly PseudoOpNames sDefaultPseudoOpNames = new PseudoOpNames() {
EquDirective = ".eq",
OrgDirective = ".org",
RegWidthDirective = ".rwid",
private static readonly PseudoOpNames sDefaultPseudoOpNames =
new PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", ".eq" },
{ "OrgDirective", ".org" },
{ "RegWidthDirective", ".rwid" },
DefineData1 = ".dd1",
DefineData2 = ".dd2",
DefineData3 = ".dd3",
DefineData4 = ".dd4",
DefineBigData2 = ".dbd2",
DefineBigData3 = ".dbd3",
DefineBigData4 = ".dbd4",
Fill = ".fill",
Dense = ".bulk",
{ "DefineData1", ".dd1" },
{ "DefineData2", ".dd2" },
{ "DefineData3", ".dd3" },
{ "DefineData4", ".dd4" },
{ "DefineBigData2", ".dbd2" },
{ "DefineBigData3", ".dbd3" },
{ "DefineBigData4", ".dbd4" },
{ "Fill", ".fill" },
{ "Dense", ".bulk" },
StrGeneric = ".str",
StrReverse = ".rstr",
StrLen8 = ".l1str",
StrLen16 = ".l2str",
StrNullTerm = ".zstr",
StrDci = ".dstr",
};
{ "StrGeneric", ".str" },
{ "StrReverse", ".rstr" },
{ "StrLen8", ".l1str" },
{ "StrLen16", ".l2str" },
{ "StrNullTerm", ".zstr" },
{ "StrDci", ".dstr" }
});
/// <summary>

View File

@ -309,7 +309,14 @@ namespace SourceGen.Tests {
ReportProgress(" verify...");
timer.StartTask("Compare Binary to Expected");
FileInfo fi = new FileInfo(asmResults.OutputPathName);
if (fi.Length != project.FileData.Length) {
if (!fi.Exists) {
// This can happen if the assembler fails to generate output but doesn't
// report an error code (e.g. Merlin 32 in certain situations).
ReportErrMsg("asm output missing");
ReportFailure();
didFail = true;
continue;
} else if (fi.Length != project.FileData.Length) {
ReportErrMsg("asm output mismatch: length is " + fi.Length + ", expected " +
project.FileData.Length);
ReportFailure();

View File

@ -629,23 +629,23 @@ limitations under the License.
<TextBox Name="equDirectiveTextBox" Grid.Column="1" Grid.Row="0"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="defineData1TextBox" Grid.Column="1" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="defineBigData2TextBox" Grid.Column="1" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- second column -->
@ -660,19 +660,19 @@ limitations under the License.
<TextBox Name="orgDirectiveTextBox" Grid.Column="4" Grid.Row="0"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="defineData2TextBox" Grid.Column="4" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="strReverseTextBox" Grid.Column="4" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="strLen16TextBox" Grid.Column="4" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- third column -->
@ -687,19 +687,19 @@ limitations under the License.
<TextBox Name="regWidthDirectiveTextBox" Grid.Column="7" Grid.Row="0"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="defineData3TextBox" Grid.Column="7" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="fillTextBox" Grid.Column="7" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="strNullTermTextBox" Grid.Column="7" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- fourth column -->
@ -712,15 +712,15 @@ limitations under the License.
<TextBox Name="defineData4TextBox" Grid.Column="10" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="denseTextBox" Grid.Column="10" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBox Name="strDciTextBox" Grid.Column="10" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="8"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
</Grid>

View File

@ -30,6 +30,7 @@ using CommonUtil;
using AssemblerInfo = SourceGen.AsmGen.AssemblerInfo;
using AssemblerConfig = SourceGen.AsmGen.AssemblerConfig;
using ExpressionMode = Asm65.Formatter.FormatConfig.ExpressionMode;
using System.Windows.Input;
namespace SourceGen.WpfGui {
/// <summary>
@ -212,8 +213,16 @@ namespace SourceGen.WpfGui {
string stringCereal = stringSet.Serialize();
mSettings.SetString(AppSettings.FMT_STRING_DELIM, stringCereal);
mMainCtrl.SetAppSettings(mSettings);
AsmGen.AssemblerVersionCache.QueryVersions();
try {
// QueryVersions() can sometimes be slow under Win10 (mid 2019), possibly
// because of the built-in malware detection, so pop up a wait cursor.
Mouse.OverrideCursor = Cursors.Wait;
mMainCtrl.SetAppSettings(mSettings);
AsmGen.AssemblerVersionCache.QueryVersions();
} finally {
Mouse.OverrideCursor = null;
}
IsDirty = false;
}
@ -913,9 +922,9 @@ namespace SourceGen.WpfGui {
AssemblerInfo asmInfo = (AssemblerInfo)displayFmtQuickComboBox.SelectedItem;
AsmGen.IGenerator gen = AssemblerInfo.GetGenerator(asmInfo.AssemblerId);
PseudoOp.PseudoOpNames opNames;
PseudoOp.PseudoOpNames unused;
Asm65.Formatter.FormatConfig formatConfig;
gen.GetDefaultDisplayFormat(out opNames, out formatConfig);
gen.GetDefaultDisplayFormat(out unused, out formatConfig);
SetWidthDisamSettings(formatConfig.mForceAbsOpcodeSuffix,
formatConfig.mForceLongOpcodeSuffix,
@ -983,13 +992,14 @@ namespace SourceGen.WpfGui {
/// Exports values from text fields to a PseudoOpNames object.
/// </summary>
private PseudoOp.PseudoOpNames ExportPseudoOpNames() {
PseudoOp.PseudoOpNames opNames = new PseudoOp.PseudoOpNames();
Dictionary<string, string> dict = new Dictionary<string, string>();
for (int i = 0; i < mPseudoNameMap.Length; i++) {
// NOTE: PseudoOpNames must be a class (not a struct) or this will fail.
// SetValue() would be invoked on a boxed copy that is discarded afterward.
mPseudoNameMap[i].PropInfo.SetValue(opNames, mPseudoNameMap[i].TextBox.Text);
// Use TrimEnd() to remove invisible trailing spaces, and reduce a string
// that's nothing but blanks to empty.
dict[mPseudoNameMap[i].PropInfo.Name] = mPseudoNameMap[i].TextBox.Text.TrimEnd();
}
return opNames;
return new PseudoOp.PseudoOpNames(dict);
}
// Invoked when text is changed in any pseudo-op text box.