1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-29 10:50:28 +00:00

Two changes to "dense hex" bulk data formatting

(1) Added an option to limit the number of bytes per line.  This is
handy for things like bitmaps, where you might want to put (say) 3
or 8 bytes per line to reflect the structure.

(2) Added an application setting that determines whether the screen
listing shows Merlin/ACME dense hex (20edfd) or 64tass/cc65 hex bytes
($20,$ed,$fd).  Made the setting part of the assembler-driven display
definitions.  Updated 64tass+cc65 to use ".byte" as their dense hex
pseudo-op, and to use the updated formatter code.  No changes to
regression test output.

(Changes were requested in issue #42.)

Also, added a resize gripper to the bottom-right corner of the main
window.  (These seem to have generally fallen out of favor, but I
like having it there.)
This commit is contained in:
Andy McFadden 2019-12-10 17:41:00 -08:00
parent 13f7ae4b70
commit 071adb8e95
14 changed files with 200 additions and 76 deletions

View File

@ -82,6 +82,7 @@ namespace Asm65 {
// miscellaneous // miscellaneous
public bool mSpacesBetweenBytes; // "20edfd" vs. "20 ed fd" public bool mSpacesBetweenBytes; // "20edfd" vs. "20 ed fd"
public bool mCommaSeparatedDense; // "20edfd" vs. "$20,$ed,$fd"
// hex dumps // hex dumps
public bool mHexDumpAsciiOnly; // disallow non-ASCII chars in hex dumps? public bool mHexDumpAsciiOnly; // disallow non-ASCII chars in hex dumps?
@ -935,15 +936,47 @@ namespace Asm65 {
/// <returns>Formatted data string.</returns> /// <returns>Formatted data string.</returns>
public string FormatDenseHex(byte[] data, int offset, int length) { public string FormatDenseHex(byte[] data, int offset, int length) {
char[] hexChars = mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower; char[] hexChars = mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower;
char[] text = new char[length * 2]; char[] text;
for (int i = 0; i < length; i++) { if (mFormatConfig.mCommaSeparatedDense) {
byte val = data[offset + i]; text = new char[length * 4 - 1];
text[i * 2] = hexChars[val >> 4]; for (int i = 0; i < length; i++) {
text[i * 2 + 1] = hexChars[val & 0x0f]; byte val = data[offset + i];
text[i * 4] = '$';
text[i * 4 + 1] = hexChars[val >> 4];
text[i * 4 + 2] = hexChars[val & 0x0f];
if (i != length - 1) {
text[i * 4 + 3] = ',';
}
}
} else {
text = new char[length * 2];
for (int i = 0; i < length; i++) {
byte val = data[offset + i];
text[i * 2] = hexChars[val >> 4];
text[i * 2 + 1] = hexChars[val & 0x0f];
}
} }
return new string(text); return new string(text);
} }
/// <summary>
/// Returns the number of characters output for each byte when formatting dense hex.
/// </summary>
/// <remarks>
/// This isn't quite right, because you don't need a comma after the very last element
/// in the list for comma-separated values. Handling this correctly for multi-line
/// items is more trouble than it's worth though.
/// </remarks>
public int CharsPerDenseByte {
get {
if (mFormatConfig.mCommaSeparatedDense) {
return 4;
} else {
return 2;
}
}
}
/// <summary> /// <summary>
/// Formats up to 16 bytes of data into a single line hex dump, in this format: /// Formats up to 16 bytes of data into a single line hex dump, in this format:
/// <pre>012345: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef</pre> /// <pre>012345: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef</pre>

View File

@ -68,6 +68,7 @@ namespace SourceGen {
public const string FMT_STRING_DELIM = "fmt-string-delim"; public const string FMT_STRING_DELIM = "fmt-string-delim";
public const string FMT_NON_UNIQUE_LABEL_PREFIX = "fmt-non-unique-label-prefix"; public const string FMT_NON_UNIQUE_LABEL_PREFIX = "fmt-non-unique-label-prefix";
public const string FMT_LOCAL_VARIABLE_PREFIX = "fmt-local-variable-prefix"; public const string FMT_LOCAL_VARIABLE_PREFIX = "fmt-local-variable-prefix";
public const string FMT_COMMA_SEP_BULK_DATA = "fmt-comma-sep-bulk-data";
public const string CLIP_LINE_FORMAT = "clip-line-format"; public const string CLIP_LINE_FORMAT = "clip-line-format";
@ -101,6 +102,7 @@ namespace SourceGen {
// Operand edit settings. // Operand edit settings.
public const string OPED_DEFAULT_STRING_ENCODING = "oped-default-string-encoding"; public const string OPED_DEFAULT_STRING_ENCODING = "oped-default-string-encoding";
public const string OPED_DENSE_HEX_LIMIT = "oped-dense-hex-limit";
// Hex dump viewer settings. // Hex dump viewer settings.
public const string HEXD_ASCII_ONLY = "hexd-ascii-only"; public const string HEXD_ASCII_ONLY = "hexd-ascii-only";

View File

@ -195,6 +195,7 @@ namespace SourceGen.AsmGen {
config.mFullLineCommentDelimiterBase = ";"; config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = ";"; config.mBoxLineCommentDelimiter = ";";
config.mNonUniqueLabelPrefix = "@"; config.mNonUniqueLabelPrefix = "@";
config.mCommaSeparatedDense = false;
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common;
Formatter.DelimiterSet charSet = new Formatter.DelimiterSet(); Formatter.DelimiterSet charSet = new Formatter.DelimiterSet();
@ -453,7 +454,7 @@ namespace SourceGen.AsmGen {
private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) {
Formatter formatter = SourceFormatter; Formatter formatter = SourceFormatter;
byte[] data = Project.FileData; byte[] data = Project.FileData;
int maxPerLine = MAX_OPERAND_LEN / 2; int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte;
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense);
for (int i = 0; i < length; i += maxPerLine) { for (int i = 0; i < length; i += maxPerLine) {

View File

@ -114,7 +114,7 @@ namespace SourceGen.AsmGen {
//DefineBigData3 //DefineBigData3
//DefineBigData4 //DefineBigData4
{ "Fill", ".res" }, { "Fill", ".res" },
//Dense // no equivalent, use .byte with comma-separated args { "Dense", ".byte" }, // not really dense, just comma-separated bytes
//Junk //Junk
{ "StrGeneric", ".byte" }, { "StrGeneric", ".byte" },
//StrReverse //StrReverse
@ -190,6 +190,7 @@ namespace SourceGen.AsmGen {
config.mFullLineCommentDelimiterBase = ";"; config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = ";"; config.mBoxLineCommentDelimiter = ";";
config.mNonUniqueLabelPrefix = "@"; config.mNonUniqueLabelPrefix = "@";
config.mCommaSeparatedDense = true;
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Cc65; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Cc65;
Formatter.DelimiterSet charSet = new Formatter.DelimiterSet(); Formatter.DelimiterSet charSet = new Formatter.DelimiterSet();
@ -484,26 +485,37 @@ namespace SourceGen.AsmGen {
private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) {
Formatter formatter = SourceFormatter; Formatter formatter = SourceFormatter;
byte[] data = Project.FileData; byte[] data = Project.FileData;
StringBuilder sb = new StringBuilder(MAX_OPERAND_LEN); int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte;
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.DefineData1); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense);
for (int i = 0; i < length; i += maxPerLine) {
int maxPerLine = MAX_OPERAND_LEN / 4; int subLen = length - i;
int numChunks = (length + maxPerLine - 1) / maxPerLine; if (subLen > maxPerLine) {
for (int chunk = 0; chunk < numChunks; chunk++) { subLen = maxPerLine;
int chunkStart = chunk * maxPerLine;
int chunkEnd = Math.Min((chunk + 1) * maxPerLine, length);
for (int i = chunkStart; i < chunkEnd; i++) {
if (i != chunkStart) {
sb.Append(',');
}
sb.Append(formatter.FormatHexValue(data[offset + i], 2));
} }
string operandStr = formatter.FormatDenseHex(data, offset + i, subLen);
OutputLine(labelStr, opcodeStr, sb.ToString(), commentStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr);
labelStr = commentStr = string.Empty; labelStr = commentStr = string.Empty;
sb.Clear();
} }
//StringBuilder sb = new StringBuilder(MAX_OPERAND_LEN);
//int maxPerLine = MAX_OPERAND_LEN / 4;
//int numChunks = (length + maxPerLine - 1) / maxPerLine;
//for (int chunk = 0; chunk < numChunks; chunk++) {
// int chunkStart = chunk * maxPerLine;
// int chunkEnd = Math.Min((chunk + 1) * maxPerLine, length);
// for (int i = chunkStart; i < chunkEnd; i++) {
// if (i != chunkStart) {
// sb.Append(',');
// }
// sb.Append(formatter.FormatHexValue(data[offset + i], 2));
// }
// OutputLine(labelStr, opcodeStr, sb.ToString(), commentStr);
// labelStr = commentStr = string.Empty;
// sb.Clear();
//}
} }
/// <summary> /// <summary>

View File

@ -165,6 +165,7 @@ namespace SourceGen.AsmGen {
config.mFullLineCommentDelimiterBase = ";"; config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = string.Empty; config.mBoxLineCommentDelimiter = string.Empty;
config.mNonUniqueLabelPrefix = ":"; config.mNonUniqueLabelPrefix = ":";
config.mCommaSeparatedDense = false;
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Merlin; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Merlin;
Formatter.DelimiterSet charSet = new Formatter.DelimiterSet(); Formatter.DelimiterSet charSet = new Formatter.DelimiterSet();
@ -313,7 +314,7 @@ namespace SourceGen.AsmGen {
private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) {
Formatter formatter = SourceFormatter; Formatter formatter = SourceFormatter;
byte[] data = Project.FileData; byte[] data = Project.FileData;
int maxPerLine = MAX_OPERAND_LEN / 2; int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte;
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense);
for (int i = 0; i < length; i += maxPerLine) { for (int i = 0; i < length; i += maxPerLine) {

View File

@ -131,7 +131,7 @@ namespace SourceGen.AsmGen {
//DefineBigData3 //DefineBigData3
//DefineBigData4 //DefineBigData4
{ "Fill", ".fill" }, { "Fill", ".fill" },
//Dense // no equivalent, use .byte with comma-separated args { "Dense", ".byte" }, // not really dense, just comma-separated bytes
//Junk //Junk
{ "Align", ".align" }, { "Align", ".align" },
{ "StrGeneric", ".text" }, { "StrGeneric", ".text" },
@ -199,6 +199,7 @@ namespace SourceGen.AsmGen {
config.mFullLineCommentDelimiterBase = ";"; config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = ";"; config.mBoxLineCommentDelimiter = ";";
config.mNonUniqueLabelPrefix = ""; // should be '_', but that's a valid label char config.mNonUniqueLabelPrefix = ""; // should be '_', but that's a valid label char
config.mCommaSeparatedDense = true;
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common;
} }
@ -520,25 +521,18 @@ namespace SourceGen.AsmGen {
private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) {
Formatter formatter = SourceFormatter; Formatter formatter = SourceFormatter;
byte[] data = Project.FileData; byte[] data = Project.FileData;
StringBuilder sb = new StringBuilder(MAX_OPERAND_LEN); int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte;
string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.DefineData1); string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense);
for (int i = 0; i < length; i += maxPerLine) {
int maxPerLine = MAX_OPERAND_LEN / 4; int subLen = length - i;
int numChunks = (length + maxPerLine - 1) / maxPerLine; if (subLen > maxPerLine) {
for (int chunk = 0; chunk < numChunks; chunk++) { subLen = maxPerLine;
int chunkStart = chunk * maxPerLine;
int chunkEnd = Math.Min((chunk + 1) * maxPerLine, length);
for (int i = chunkStart; i < chunkEnd; i++) {
if (i != chunkStart) {
sb.Append(',');
}
sb.Append(formatter.FormatHexValue(data[offset + i], 2));
} }
string operandStr = formatter.FormatDenseHex(data, offset + i, subLen);
OutputLine(labelStr, opcodeStr, sb.ToString(), commentStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr);
labelStr = commentStr = string.Empty; labelStr = commentStr = string.Empty;
sb.Clear();
} }
} }

View File

@ -471,6 +471,8 @@ namespace SourceGen {
settings.GetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, string.Empty); settings.GetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, string.Empty);
mFormatterConfig.mLocalVariableLabelPrefix = mFormatterConfig.mLocalVariableLabelPrefix =
settings.GetString(AppSettings.FMT_LOCAL_VARIABLE_PREFIX, string.Empty); settings.GetString(AppSettings.FMT_LOCAL_VARIABLE_PREFIX, string.Empty);
mFormatterConfig.mCommaSeparatedDense =
settings.GetBool(AppSettings.FMT_COMMA_SEP_BULK_DATA, false);
string chrDelCereal = settings.GetString(AppSettings.FMT_CHAR_DELIM, null); string chrDelCereal = settings.GetString(AppSettings.FMT_CHAR_DELIM, null);
if (chrDelCereal != null) { if (chrDelCereal != null) {

View File

@ -276,7 +276,7 @@ namespace SourceGen {
case FormatDescriptor.Type.Dense: { case FormatDescriptor.Type.Dense: {
// no delimiter, two output bytes per input byte // no delimiter, two output bytes per input byte
int maxLen = MAX_OPERAND_LEN; int maxLen = MAX_OPERAND_LEN;
int textLen = dfd.Length * 2; int textLen = dfd.Length * formatter.CharsPerDenseByte;
return (textLen + maxLen - 1) / maxLen; return (textLen + maxLen - 1) / maxLen;
} }
default: default:
@ -364,7 +364,7 @@ namespace SourceGen {
} }
break; break;
case FormatDescriptor.Type.Dense: { case FormatDescriptor.Type.Dense: {
int maxPerLine = MAX_OPERAND_LEN / 2; int maxPerLine = MAX_OPERAND_LEN / formatter.CharsPerDenseByte;
offset += subIndex * maxPerLine; offset += subIndex * maxPerLine;
length -= subIndex * maxPerLine; length -= subIndex * maxPerLine;
if (length > maxPerLine) { if (length > maxPerLine) {

View File

@ -161,9 +161,10 @@ select both bytes in the editor. (If you click on the first item, then
Shift+double-click on the operand field of the last item, you can do Shift+double-click on the operand field of the last item, you can do
this very quickly.) The selection does not need to be contiguous: you this very quickly.) The selection does not need to be contiguous: you
can use Control+click to select scattered items.</p> can use Control+click to select scattered items.</p>
<p>If the range is discontiguous, or crosses a visual boundary <p>If the range is discontiguous, crosses a logical boundary
such as a change in address, a user-specified label, or a long comment such as a change in address or a user-specified label, or crosses a
or note, the selection will be split into smaller regions. A message at the visual boundary like a long comment, note, or visualization, the selection
will be split into smaller regions. A message at the
top of the dialog indicates how many bytes have been selected, and how top of the dialog indicates how many bytes have been selected, and how
many regions they have been divided into.</p> many regions they have been divided into.</p>
<p>(End-of-line comments do <i>not</i> split a region, and will <p>(End-of-line comments do <i>not</i> split a region, and will
@ -213,9 +214,10 @@ it requires that each individual string be entirely one or the other.</p>
<p>Due to fundamental limitations of the character set, C64 screen code <p>Due to fundamental limitations of the character set, C64 screen code
strings cannot be null terminated ($00 is '@').</p> strings cannot be null terminated ($00 is '@').</p>
<p>To avoid burying a label in the middle of a data item, contiguous areas <p>As noted earlier, to avoid burying elements such as labels in the middle
are split at labels. This can sometimes have unexpected effects. For of a data item, contiguous areas may be split into smaller regions. This
example, this can be formatted as two 16-bit words or one 32-bit word:</p> can sometimes have unexpected effects. For example, this can be formatted
as two 16-bit words or one 32-bit word:</p>
<pre> <pre>
.DD1 $01 .DD1 $01
.DD1 $ef .DD1 $ef
@ -223,7 +225,7 @@ example, this can be formatted as two 16-bit words or one 32-bit word:</p>
.DD1 $f0 .DD1 $f0
</pre> </pre>
<p>With a label in the middle, it be formatted as two 16-bit words, but <p>With a label in the middle, it can be formatted as two 16-bit words, but
not as a 32-bit word:</p> not as a 32-bit word:</p>
<pre> <pre>
.DD1 $01 .DD1 $01
@ -233,7 +235,7 @@ LABEL .DD1 $01
CODE LDA LABEL CODE LDA LABEL
</pre> </pre>
<p>If this is inconvenient, you can add a label at a 32-bit boundary, and <p>If this is undesirable, you can add a label at a 32-bit boundary, and
reference that instead:</p> reference that instead:</p>
<pre> <pre>
LABEL .DD1 $01 LABEL .DD1 $01

View File

@ -584,6 +584,10 @@ limitations under the License.
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
</StackPanel> </StackPanel>
<CheckBox DockPanel.Dock="Top" Margin="4,6,0,0"
Content="Use comma-separated format for bulk data"
IsChecked="{Binding CommaSeparatedBulkData}"/>
<GroupBox DockPanel.Dock="Bottom" Header="Quick Set" <GroupBox DockPanel.Dock="Bottom" Header="Quick Set"
HorizontalAlignment="Right" Padding="2,4"> HorizontalAlignment="Right" Padding="2,4">
<ComboBox Name="displayFmtQuickComboBox" DockPanel.Dock="Right" <ComboBox Name="displayFmtQuickComboBox" DockPanel.Dock="Right"

View File

@ -889,6 +889,19 @@ namespace SourceGen.WpfGui {
} }
} }
} }
private bool mCommaSeparatedBulkData;
public bool CommaSeparatedBulkData {
get { return mCommaSeparatedBulkData; }
set {
if (mCommaSeparatedBulkData != value) {
mCommaSeparatedBulkData = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.FMT_COMMA_SEP_BULK_DATA, value);
UpdateDisplayFormatQuickCombo();
IsDirty = true;
}
}
}
// prevent recursion // prevent recursion
private bool mSettingDisplayFmtCombo; private bool mSettingDisplayFmtCombo;
@ -935,11 +948,13 @@ namespace SourceGen.WpfGui {
public string OperandPrefixLong { get; private set; } public string OperandPrefixLong { get; private set; }
public string NonUniqueLabelPrefix { get; private set; } public string NonUniqueLabelPrefix { get; private set; }
public string LocalVarPrefix { get; private set; } public string LocalVarPrefix { get; private set; }
public bool CommaSeparatedBulkData { get; private set; }
public ExpressionMode ExpressionStyle { get; private set; } public ExpressionMode ExpressionStyle { get; private set; }
public DisplayFormatPreset(int id, string name, string opcSuffixAbs, public DisplayFormatPreset(int id, string name, string opcSuffixAbs,
string opcSuffixLong, string operPrefixAbs, string operPrefixLong, string opcSuffixLong, string operPrefixAbs, string operPrefixLong,
string nonUniqueLabelPrefix, string localVarPrefix, ExpressionMode expStyle) { string nonUniqueLabelPrefix, string localVarPrefix, bool commaSepBulkData,
ExpressionMode expStyle) {
Ident = id; Ident = id;
Name = name; Name = name;
OpcodeSuffixAbs = opcSuffixAbs; OpcodeSuffixAbs = opcSuffixAbs;
@ -948,6 +963,7 @@ namespace SourceGen.WpfGui {
OperandPrefixLong = operPrefixLong; OperandPrefixLong = operPrefixLong;
NonUniqueLabelPrefix = nonUniqueLabelPrefix; NonUniqueLabelPrefix = nonUniqueLabelPrefix;
LocalVarPrefix = localVarPrefix; LocalVarPrefix = localVarPrefix;
CommaSeparatedBulkData = commaSepBulkData;
ExpressionStyle = expStyle; ExpressionStyle = expStyle;
} }
} }
@ -958,10 +974,11 @@ namespace SourceGen.WpfGui {
DisplayPresets = new DisplayFormatPreset[AssemblerList.Count + 2]; DisplayPresets = new DisplayFormatPreset[AssemblerList.Count + 2];
DisplayPresets[0] = new DisplayFormatPreset(DisplayFormatPreset.ID_CUSTOM, DisplayPresets[0] = new DisplayFormatPreset(DisplayFormatPreset.ID_CUSTOM,
(string)FindResource("str_PresetCustom"), string.Empty, string.Empty, (string)FindResource("str_PresetCustom"), string.Empty, string.Empty,
string.Empty, string.Empty, string.Empty, string.Empty, ExpressionMode.Unknown); string.Empty, string.Empty, string.Empty, string.Empty, false,
ExpressionMode.Unknown);
DisplayPresets[1] = new DisplayFormatPreset(DisplayFormatPreset.ID_DEFAULT, DisplayPresets[1] = new DisplayFormatPreset(DisplayFormatPreset.ID_DEFAULT,
(string)FindResource("str_PresetDefault"), string.Empty, "l", "a:", "f:", (string)FindResource("str_PresetDefault"), string.Empty, "l", "a:", "f:",
string.Empty, string.Empty, ExpressionMode.Common); string.Empty, string.Empty, false, ExpressionMode.Common);
for (int i = 0; i < AssemblerList.Count; i++) { for (int i = 0; i < AssemblerList.Count; i++) {
AssemblerInfo asmInfo = AssemblerList[i]; AssemblerInfo asmInfo = AssemblerList[i];
AsmGen.IGenerator gen = AssemblerInfo.GetGenerator(asmInfo.AssemblerId); AsmGen.IGenerator gen = AssemblerInfo.GetGenerator(asmInfo.AssemblerId);
@ -973,7 +990,8 @@ namespace SourceGen.WpfGui {
asmInfo.Name, formatConfig.mForceAbsOpcodeSuffix, asmInfo.Name, formatConfig.mForceAbsOpcodeSuffix,
formatConfig.mForceLongOpcodeSuffix, formatConfig.mForceAbsOperandPrefix, formatConfig.mForceLongOpcodeSuffix, formatConfig.mForceAbsOperandPrefix,
formatConfig.mForceLongOperandPrefix, formatConfig.mNonUniqueLabelPrefix, formatConfig.mForceLongOperandPrefix, formatConfig.mNonUniqueLabelPrefix,
formatConfig.mLocalVariableLabelPrefix, formatConfig.mExpressionMode); formatConfig.mLocalVariableLabelPrefix, formatConfig.mCommaSeparatedDense,
formatConfig.mExpressionMode);
} }
} }
@ -996,6 +1014,8 @@ namespace SourceGen.WpfGui {
mSettings.GetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, string.Empty); mSettings.GetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, string.Empty);
LocalVarPrefix = LocalVarPrefix =
mSettings.GetString(AppSettings.FMT_LOCAL_VARIABLE_PREFIX, string.Empty); mSettings.GetString(AppSettings.FMT_LOCAL_VARIABLE_PREFIX, string.Empty);
CommaSeparatedBulkData =
mSettings.GetBool(AppSettings.FMT_COMMA_SEP_BULK_DATA, false);
string exprMode = mSettings.GetString(AppSettings.FMT_EXPRESSION_MODE, string.Empty); string exprMode = mSettings.GetString(AppSettings.FMT_EXPRESSION_MODE, string.Empty);
ExpressionMode mode; ExpressionMode mode;
@ -1053,6 +1073,7 @@ namespace SourceGen.WpfGui {
OperandPrefixLong = preset.OperandPrefixLong; OperandPrefixLong = preset.OperandPrefixLong;
NonUniqueLabelPrefix = preset.NonUniqueLabelPrefix; NonUniqueLabelPrefix = preset.NonUniqueLabelPrefix;
LocalVarPrefix = preset.LocalVarPrefix; LocalVarPrefix = preset.LocalVarPrefix;
CommaSeparatedBulkData = preset.CommaSeparatedBulkData;
SelectExpressionStyle(preset.ExpressionStyle); SelectExpressionStyle(preset.ExpressionStyle);
// dirty flag will be set by change watchers if one or more fields have changed // dirty flag will be set by change watchers if one or more fields have changed
@ -1078,6 +1099,7 @@ namespace SourceGen.WpfGui {
OperandPrefixLong == preset.OperandPrefixLong && OperandPrefixLong == preset.OperandPrefixLong &&
NonUniqueLabelPrefix == preset.NonUniqueLabelPrefix && NonUniqueLabelPrefix == preset.NonUniqueLabelPrefix &&
LocalVarPrefix == preset.LocalVarPrefix && LocalVarPrefix == preset.LocalVarPrefix &&
CommaSeparatedBulkData == preset.CommaSeparatedBulkData &&
expMode == preset.ExpressionStyle) { expMode == preset.ExpressionStyle) {
// match // match
displayFmtQuickComboBox.SelectedIndex = i; displayFmtQuickComboBox.SelectedIndex = i;

View File

@ -127,13 +127,20 @@ limitations under the License.
<TextBlock Text="Bulk Data" Margin="0,12,0,0"/> <TextBlock Text="Bulk Data" Margin="0,12,0,0"/>
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/> <Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>
<RadioButton Name="radioDenseHex" GroupName="Main" Content="Densely-_packed bytes" Margin="0,4,0,0" <RadioButton Name="radioDenseHex" GroupName="Main" Content="Densely-_packed bytes" Margin="0,4,0,0"
Checked="MainGroup_CheckedChanged"/> Checked="MainGroup_CheckedChanged"/>
<StackPanel Orientation="Horizontal" Margin="20,4,0,0">
<RadioButton Name="radioDenseHexLimited" GroupName="Main" Content="...with a limit of"
Checked="MainGroup_CheckedChanged"/>
<TextBox Width="40" Margin="7,-3,0,0" MaxLength="2"
Text="{Binding MaxDenseBytesPerLine, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="bytes per line" Margin="7,0,0,0"/>
</StackPanel>
<RadioButton Name="radioFill" GroupName="Main" Content="Area _filled with value" Margin="0,4,0,0" <RadioButton Name="radioFill" GroupName="Main" Content="Area _filled with value" Margin="0,4,0,0"
Checked="MainGroup_CheckedChanged"/> Checked="MainGroup_CheckedChanged"/>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<RadioButton Name="radioJunk" GroupName="Main" Content="_Junk bytes, end aligned to" Margin="0,4,0,0" <RadioButton Name="radioJunk" GroupName="Main" Content="_Junk bytes, end aligned to"
Checked="MainGroup_CheckedChanged"/> Checked="MainGroup_CheckedChanged"/>
<ComboBox Name="junkAlignComboBox" Margin="8,-1,0,0" Width="200" <ComboBox Name="junkAlignComboBox" Margin="8,-5,0,0" Width="200"
ItemsSource="{Binding JunkAlignmentItems}" DisplayMemberPath="Description"/> ItemsSource="{Binding JunkAlignmentItems}" DisplayMemberPath="Description"/>
</StackPanel> </StackPanel>

View File

@ -37,18 +37,6 @@ namespace SourceGen.WpfGui {
/// </summary> /// </summary>
public SortedList<int, FormatDescriptor> Results { get; private set; } public SortedList<int, FormatDescriptor> Results { get; private set; }
/// <summary>
/// Set to true when input is valid. Controls whether the OK button is enabled.
/// </summary>
public bool IsValid {
get { return mIsValid; }
set {
mIsValid = value;
OnPropertyChanged();
}
}
private bool mIsValid;
/// <summary> /// <summary>
/// Selected offsets. An otherwise contiguous range of offsets can be broken up /// Selected offsets. An otherwise contiguous range of offsets can be broken up
/// by user-specified labels and address discontinuities, so this needs to be /// by user-specified labels and address discontinuities, so this needs to be
@ -95,6 +83,35 @@ namespace SourceGen.WpfGui {
/// </summary> /// </summary>
private bool mPreferredFormatUnavailable; private bool mPreferredFormatUnavailable;
/// <summary>
/// Set to true when input is valid. Controls whether the OK button is enabled.
/// </summary>
public bool IsValid {
get { return mIsValid; }
set {
mIsValid = value;
OnPropertyChanged();
}
}
private bool mIsValid;
public int MaxDenseBytesPerLine {
get { return mMaxDenseBytesPerLine; }
set {
if (mMaxDenseBytesPerLine != value) {
mMaxDenseBytesPerLine = value;
OnPropertyChanged();
UpdateControls();
if (IsLoaded) {
// Set the radio button when text is typed.
radioDenseHexLimited.IsChecked = true;
}
}
}
}
private int mMaxDenseBytesPerLine;
/// <summary> /// <summary>
/// Text encoding combo box item. We use the same TextScanMode enum that the /// Text encoding combo box item. We use the same TextScanMode enum that the
/// uncategorized data analyzer uses. /// uncategorized data analyzer uses.
@ -142,6 +159,8 @@ namespace SourceGen.WpfGui {
mSelection = trs; mSelection = trs;
mFirstFormatDescriptor = firstDesc; mFirstFormatDescriptor = firstDesc;
MaxDenseBytesPerLine = AppSettings.Global.GetInt(AppSettings.OPED_DENSE_HEX_LIMIT, 8);
StringEncodingItems = new StringEncodingItem[] { StringEncodingItems = new StringEncodingItem[] {
new StringEncodingItem(Res.Strings.SCAN_LOW_ASCII, new StringEncodingItem(Res.Strings.SCAN_LOW_ASCII,
TextScanMode.LowAscii), TextScanMode.LowAscii),
@ -362,13 +381,23 @@ namespace SourceGen.WpfGui {
} }
} }
IsValid = isOk; IsValid = isOk;
// If dense hex with a limit is selected, check the value.
if (radioDenseHexLimited.IsChecked == true) {
if (MaxDenseBytesPerLine > 0) {
AppSettings.Global.SetInt(AppSettings.OPED_DENSE_HEX_LIMIT,
MaxDenseBytesPerLine);
} else {
IsValid = false;
}
}
} }
#region Setup #region Setup
/// <summary> /// <summary>
/// Determines the minimum and maximum alignment values, based on the sizes of the /// Determines the minimum and maximum alignment values, based on the sizes of the
/// regions and the address they end on. /// regions and the address they end on. Used for .align.
/// </summary> /// </summary>
/// <param name="min">Minimum allowed format, or None.</param> /// <param name="min">Minimum allowed format, or None.</param>
/// <param name="max">Maximum allowed format, or None.</param> /// <param name="max">Maximum allowed format, or None.</param>
@ -918,7 +947,6 @@ namespace SourceGen.WpfGui {
FormatDescriptor.Type type = FormatDescriptor.Type.Default; FormatDescriptor.Type type = FormatDescriptor.Type.Default;
FormatDescriptor.SubType subType = FormatDescriptor.SubType.None; FormatDescriptor.SubType subType = FormatDescriptor.SubType.None;
WeakSymbolRef symbolRef = null; WeakSymbolRef symbolRef = null;
int chunkLength = -1;
FormatDescriptor.SubType charSubType; FormatDescriptor.SubType charSubType;
CharEncoding.InclusionTest charTest; CharEncoding.InclusionTest charTest;
@ -1013,6 +1041,7 @@ namespace SourceGen.WpfGui {
} }
// Decode the main format. // Decode the main format.
int chunkLength = -1;
if (radioDefaultFormat.IsChecked == true) { if (radioDefaultFormat.IsChecked == true) {
// Default/None; note this would create a multi-byte Default format, which isn't // Default/None; note this would create a multi-byte Default format, which isn't
// really allowed. What we actually want to do is remove the explicit formatting // really allowed. What we actually want to do is remove the explicit formatting
@ -1033,7 +1062,7 @@ namespace SourceGen.WpfGui {
} else if (radio32BitLittle.IsChecked == true) { } else if (radio32BitLittle.IsChecked == true) {
type = FormatDescriptor.Type.NumericLE; type = FormatDescriptor.Type.NumericLE;
chunkLength = 4; chunkLength = 4;
} else if (radioDenseHex.IsChecked == true) { } else if (radioDenseHex.IsChecked == true || radioDenseHexLimited.IsChecked == true) {
type = FormatDescriptor.Type.Dense; type = FormatDescriptor.Type.Dense;
} else if (radioFill.IsChecked == true) { } else if (radioFill.IsChecked == true) {
type = FormatDescriptor.Type.Fill; type = FormatDescriptor.Type.Fill;
@ -1088,6 +1117,21 @@ namespace SourceGen.WpfGui {
case FormatDescriptor.Type.StringDci: case FormatDescriptor.Type.StringDci:
CreateDciStringEntries(rng.Low, rng.High, type, subType); CreateDciStringEntries(rng.Low, rng.High, type, subType);
break; break;
case FormatDescriptor.Type.Dense:
if (radioDenseHexLimited.IsChecked == true) {
int low = rng.Low;
while (low <= rng.High) {
int remaining = rng.High - low + 1;
int subLen = Math.Min(remaining, MaxDenseBytesPerLine);
CreateSimpleEntries(type, subType, -1, null, low, low + subLen - 1);
low += subLen;
chunkLength -= subLen;
}
} else {
CreateSimpleEntries(type, subType, chunkLength, null,
rng.Low, rng.High);
}
break;
default: default:
CreateSimpleEntries(type, subType, chunkLength, symbolRef, CreateSimpleEntries(type, subType, chunkLength, symbolRef,
rng.Low, rng.High); rng.Low, rng.High);

View File

@ -24,7 +24,7 @@ limitations under the License.
mc:Ignorable="d" mc:Ignorable="d"
Title="6502bench SourceGen" Title="6502bench SourceGen"
Icon="/SourceGen;component/Res/SourceGenIcon.ico" Icon="/SourceGen;component/Res/SourceGenIcon.ico"
Width="1200" Height="700" MinWidth="800" MinHeight="500" Width="1200" Height="700" MinWidth="800" MinHeight="500" ResizeMode="CanResizeWithGrip"
SourceInitialized="Window_SourceInitialized" SourceInitialized="Window_SourceInitialized"
Loaded="Window_Loaded" Loaded="Window_Loaded"
LocationChanged="Window_LocationChanged" LocationChanged="Window_LocationChanged"
@ -465,7 +465,7 @@ limitations under the License.
<TextBlock Grid.Column="0" Text="{Binding StatusBarText, FallbackValue=Ready}"/> <TextBlock Grid.Column="0" Text="{Binding StatusBarText, FallbackValue=Ready}"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center" <TextBlock Grid.Column="1" HorizontalAlignment="Center"
Text="{Binding ByteCountText, FallbackValue=30% code; 60% data; 10% junk}"/> Text="{Binding ByteCountText, FallbackValue=30% code; 60% data; 10% junk}"/>
<Button Grid.Column="2" Margin="4,0,6,0" Padding="4,0,4,0" <Button Grid.Column="2" Margin="4,0,20,0" Padding="4,0,4,0"
Content="{Binding MessageStatusText, FallbackValue=3 messages (1 warning/error)}" Content="{Binding MessageStatusText, FallbackValue=3 messages (1 warning/error)}"
Visibility="{Binding Path=CodeListVisibility}" Visibility="{Binding Path=CodeListVisibility}"
Click="MessageStatusButton_Click"/> Click="MessageStatusButton_Click"/>