mirror of
https://github.com/fadden/6502bench.git
synced 2024-11-25 14:34:27 +00:00
Add C64 encodings to instruction and data operand editors
Both dialogs got a couple extra radio buttons for selection of single character operands. The data operand editor got a combo box that lets you specify how it scans for viable strings. Various string scanning methods were made more generic. This got a little strange with auto-detection of low/high ASCII, but that was mostly a matter of keeping the previous code around as a special case. Made C64 Screen Code DCI strings a thing that works.
This commit is contained in:
parent
176e1ad6af
commit
7bbe5692bd
@ -106,11 +106,12 @@ namespace Asm65 {
|
||||
return IsExtendedAscii((byte)(val & 0x7f));
|
||||
}
|
||||
public static char ConvertLowAndHighAscii(byte val) {
|
||||
if (IsPrintableAscii(val) || IsPrintableHighAscii(val)) {
|
||||
return (char)(val & 0x7f);
|
||||
} else {
|
||||
return UNPRINTABLE_CHAR;
|
||||
}
|
||||
//if (IsPrintableAscii(val) || IsPrintableHighAscii(val)) {
|
||||
// return (char)(val & 0x7f);
|
||||
//} else {
|
||||
// return UNPRINTABLE_CHAR;
|
||||
//}
|
||||
return ConvertAscii((byte)(val & 0x7f));
|
||||
}
|
||||
|
||||
//
|
||||
@ -256,5 +257,8 @@ namespace Asm65 {
|
||||
return UNPRINTABLE_CHAR;
|
||||
}
|
||||
}
|
||||
public static char ConvertLowAndHighC64ScreenCode(byte val) {
|
||||
return ConvertC64ScreenCode((byte)(val & 0x7f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,12 +152,10 @@ namespace Asm65 {
|
||||
new Dictionary<CharEncoding.Encoding, DelimiterDef>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the specified DelimiterDef, or a default if not found.
|
||||
/// Returns the specified DelimiterDef, or null if not found.
|
||||
/// </summary>
|
||||
public DelimiterDef Get(CharEncoding.Encoding enc) {
|
||||
if (!mDelimiters.TryGetValue(enc, out DelimiterDef def)) {
|
||||
return DOUBLE_QUOTE_DELIM;
|
||||
}
|
||||
mDelimiters.TryGetValue(enc, out DelimiterDef def);
|
||||
return def;
|
||||
}
|
||||
public void Set(CharEncoding.Encoding enc, DelimiterDef def) {
|
||||
@ -528,6 +526,10 @@ namespace Asm65 {
|
||||
}
|
||||
|
||||
DelimiterDef delimDef = mFormatConfig.mCharDelimiters.Get(enc);
|
||||
if (delimDef == null) {
|
||||
return FormatHexValue(value, 2);
|
||||
}
|
||||
|
||||
string fmt = delimDef.FormatStr;
|
||||
Debug.Assert(fmt != null);
|
||||
|
||||
|
@ -68,6 +68,9 @@ namespace SourceGen {
|
||||
|
||||
public const string CLIP_LINE_FORMAT = "clip-line-format";
|
||||
|
||||
// Main project view settings.
|
||||
public const string PRVW_RECENT_PROJECT_LIST = "prvw-recent-project-list";
|
||||
|
||||
// Symbol-list window options.
|
||||
public const string SYMWIN_SHOW_USER = "symwin-show-user";
|
||||
public const string SYMWIN_SHOW_AUTO = "symwin-show-auto";
|
||||
@ -91,6 +94,9 @@ namespace SourceGen {
|
||||
public const string CDLV_FONT_FAMILY = "cdlv-font-family";
|
||||
public const string CDLV_FONT_SIZE = "cdlv-font-size";
|
||||
|
||||
// Operand edit settings.
|
||||
public const string OPED_DEFAULT_STRING_ENCODING = "oped-default-string-encoding";
|
||||
|
||||
// Hex dump viewer settings.
|
||||
public const string HEXD_ASCII_ONLY = "hexd-ascii-only";
|
||||
public const string HEXD_CHAR_CONV = "hexd-char-conv1";
|
||||
@ -105,9 +111,6 @@ namespace SourceGen {
|
||||
public const string SRCGEN_LONG_LABEL_NEW_LINE = "srcgen-long-label-new-line";
|
||||
public const string SRCGEN_SHOW_CYCLE_COUNTS = "srcgen-show-cycle-counts";
|
||||
|
||||
// Main project view settings.
|
||||
public const string PRVW_RECENT_PROJECT_LIST = "prvw-recent-project-list";
|
||||
|
||||
// Assembler settings prefix
|
||||
public const string ASM_CONFIG_PREFIX = "asm-config-";
|
||||
|
||||
|
@ -837,43 +837,63 @@ namespace SourceGen {
|
||||
/// <param name="fileData">Raw data.</param>
|
||||
/// <param name="start">Offset of first byte in range.</param>
|
||||
/// <param name="end">Offset of last byte in range</param>
|
||||
/// <param name="lowAscii">Set to the number of low-ASCII bytes found.</param>
|
||||
/// <param name="highAscii">Set to the number of high-ASCII bytes found.</param>
|
||||
/// <param name="nonAscii">Set to the number of non-ASCII bytes found.</param>
|
||||
public static void CountAsciiBytes(byte[] fileData, int start, int end,
|
||||
out int lowAscii, out int highAscii, out int nonAscii) {
|
||||
lowAscii = highAscii = nonAscii = 0;
|
||||
/// <param name="charTest">Character test delegate. Must match on both high and
|
||||
/// low characters.</param>
|
||||
/// <param name="lowVal">Set to the number of low-range characters found.</param>
|
||||
/// <param name="highVal">Set to the number of high-range characters found.</param>
|
||||
/// <param name="nonChar">Set to the number of non-character bytes found.</param>
|
||||
public static void CountHighLowBytes(byte[] fileData, int start, int end,
|
||||
CharEncoding.InclusionTest charTest,
|
||||
out int lowVal, out int highVal, out int nonChar) {
|
||||
lowVal = highVal = nonChar = 0;
|
||||
|
||||
for (int i = start; i <= end; i++) {
|
||||
byte val = fileData[i];
|
||||
if (val < 0x20) {
|
||||
nonAscii++;
|
||||
} else if (val < 0x7f) {
|
||||
lowAscii++;
|
||||
} else if (val < 0xa0) {
|
||||
nonAscii++;
|
||||
} else if (val < 0xff) {
|
||||
highAscii++;
|
||||
if (!charTest(val)) {
|
||||
nonChar++;
|
||||
} else if ((val & 0x80) == 0) {
|
||||
lowVal++;
|
||||
} else {
|
||||
nonAscii++;
|
||||
highVal++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the number of bytes that match the character test.
|
||||
/// </summary>
|
||||
/// <param name="fileData">Raw data.</param>
|
||||
/// <param name="start">Offset of first byte in range.</param>
|
||||
/// <param name="end">Offset of last byte in range.</param>
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
/// <returns>Number of matching characters.</returns>
|
||||
public static int CountCharacterBytes(byte[] fileData, int start, int end,
|
||||
CharEncoding.InclusionTest charTest) {
|
||||
int count = 0;
|
||||
for (int i = start; i <= end; i++) {
|
||||
if (charTest(fileData[i])) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the number of null-terminated strings in the buffer.
|
||||
///
|
||||
/// Zero-length strings are allowed but not included in the count.
|
||||
///
|
||||
/// Each string must be either high-ASCII or low-ASCII, not a mix.
|
||||
///
|
||||
/// If any bad data is found, the scan aborts and returns -1.
|
||||
/// </summary>
|
||||
/// <param name="fileData">Raw data.</param>
|
||||
/// <param name="start">Offset of first byte in range.</param>
|
||||
/// <param name="end">Offset of last byte in range.</param>
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
/// <param name="limitHiBit">If set, the high bit in all character must be the
|
||||
/// same. Used to enforce a single encoding when "low or high ASCII" is used.</param>
|
||||
/// <returns>Number of strings found, or -1 if bad data identified.</returns>
|
||||
public static int RecognizeNullTerminatedStrings(byte[] fileData, int start, int end) {
|
||||
public static int RecognizeNullTerminatedStrings(byte[] fileData, int start, int end,
|
||||
CharEncoding.InclusionTest charTest, bool limitHiBit) {
|
||||
// Quick test.
|
||||
if (fileData[end] != 0x00) {
|
||||
return -1;
|
||||
@ -892,6 +912,7 @@ namespace SourceGen {
|
||||
stringLen = 0;
|
||||
expectedHiBit = -1;
|
||||
} else {
|
||||
if (limitHiBit) {
|
||||
if (expectedHiBit == -1) {
|
||||
// First byte in string, set hi/lo expectation.
|
||||
expectedHiBit = val & 0x80;
|
||||
@ -899,9 +920,9 @@ namespace SourceGen {
|
||||
// Mixed ASCII or non-ASCII, fail.
|
||||
return -1;
|
||||
}
|
||||
val &= 0x7f;
|
||||
if (val < 0x20 || val == 0x7f) {
|
||||
// Non-ASCII, fail.
|
||||
}
|
||||
if (!charTest(val)) {
|
||||
// Not a matching character, fail.
|
||||
return -1;
|
||||
}
|
||||
stringLen++;
|
||||
@ -914,15 +935,17 @@ namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Counts strings prefixed with an 8-bit length.
|
||||
///
|
||||
/// Each string must be either high-ASCII or low-ASCII, not a mix.
|
||||
///
|
||||
/// Zero-length strings are allowed but not counted.
|
||||
/// </summary>
|
||||
/// <param name="fileData">Raw data.</param>
|
||||
/// <param name="start">Offset of first byte in range.</param>
|
||||
/// <param name="end">Offset of last byte in range.</param>
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
/// <param name="limitHiBit">If set, the high bit in all character must be the
|
||||
/// same. Used to enforce a single encoding when "low or high ASCII" is used.</param>
|
||||
/// <returns>Number of strings found, or -1 if bad data identified.</returns>
|
||||
public static int RecognizeLen8Strings(byte[] fileData, int start, int end) {
|
||||
public static int RecognizeLen8Strings(byte[] fileData, int start, int end,
|
||||
CharEncoding.InclusionTest charTest, bool limitHiBit) {
|
||||
int posn = start;
|
||||
int remaining = end - start + 1;
|
||||
int stringCount = 0;
|
||||
@ -944,13 +967,12 @@ namespace SourceGen {
|
||||
|
||||
while (strLen-- != 0) {
|
||||
byte val = fileData[posn++];
|
||||
if ((val & 0x80) != expectedHiBit) {
|
||||
if (limitHiBit && (val & 0x80) != expectedHiBit) {
|
||||
// Mixed ASCII, fail.
|
||||
return -1;
|
||||
}
|
||||
val &= 0x7f;
|
||||
if (val < 0x20 || val == 0x7f) {
|
||||
// Non-ASCII, fail.
|
||||
if (!charTest(val)) {
|
||||
// Not a matching character, fail.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -962,15 +984,17 @@ namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Counts strings prefixed with a 16-bit length.
|
||||
///
|
||||
/// Each string must be either high-ASCII or low-ASCII, not a mix.
|
||||
///
|
||||
/// Zero-length strings are allowed but not counted.
|
||||
/// </summary>
|
||||
/// <param name="fileData">Raw data.</param>
|
||||
/// <param name="start">Offset of first byte in range.</param>
|
||||
/// <param name="end">Offset of last byte in range.</param>
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
/// <param name="limitHiBit">If set, the high bit in all character must be the
|
||||
/// same. Used to enforce a single encoding when "low or high ASCII" is used.</param>
|
||||
/// <returns>Number of strings found, or -1 if bad data identified.</returns>
|
||||
public static int RecognizeLen16Strings(byte[] fileData, int start, int end) {
|
||||
public static int RecognizeLen16Strings(byte[] fileData, int start, int end,
|
||||
CharEncoding.InclusionTest charTest, bool limitHiBit) {
|
||||
int posn = start;
|
||||
int remaining = end - start + 1;
|
||||
int stringCount = 0;
|
||||
@ -998,13 +1022,12 @@ namespace SourceGen {
|
||||
|
||||
while (strLen-- != 0) {
|
||||
byte val = fileData[posn++];
|
||||
if ((val & 0x80) != expectedHiBit) {
|
||||
if (limitHiBit && (val & 0x80) != expectedHiBit) {
|
||||
// Mixed ASCII, fail.
|
||||
return -1;
|
||||
}
|
||||
val &= 0x7f;
|
||||
if (val < 0x20 || val == 0x7f) {
|
||||
// Non-ASCII, fail.
|
||||
if (!charTest(val)) {
|
||||
// Not a matching character, fail.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1020,11 +1043,16 @@ namespace SourceGen {
|
||||
/// Each string must be at least two bytes. To reduce false-positives, we require
|
||||
/// that all strings have the same hi/lo pattern.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Not useful for C64Petscii, which mixes high/low characters.
|
||||
/// </remarks>
|
||||
/// <param name="fileData">Raw data.</param>
|
||||
/// <param name="start">Offset of first byte in range.</param>
|
||||
/// <param name="end">Offset of last byte in range.</param>
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
/// <returns>Number of strings found, or -1 if bad data identified.</returns>
|
||||
public static int RecognizeDciStrings(byte[] fileData, int start, int end) {
|
||||
public static int RecognizeDciStrings(byte[] fileData, int start, int end,
|
||||
CharEncoding.InclusionTest charTest) {
|
||||
int expectedHiBit = fileData[start] & 0x80;
|
||||
int stringCount = 0;
|
||||
int stringLen = 0;
|
||||
@ -1048,9 +1076,8 @@ namespace SourceGen {
|
||||
stringLen++;
|
||||
}
|
||||
|
||||
val &= 0x7f;
|
||||
if (val < 0x20 || val == 0x7f) {
|
||||
// Non-ASCII, fail.
|
||||
if (!charTest((byte)(val & 0x7f))) {
|
||||
// Not a matching character, fail.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -106,8 +106,8 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
/// <param name="e">Argument from SelectionChanged event.</param>
|
||||
public void SelectionChanged(SelectionChangedEventArgs e) {
|
||||
Debug.WriteLine("SelectionChanged event: Add=" + e.AddedItems.Count +
|
||||
" Rem=" + e.RemovedItems.Count);
|
||||
//Debug.WriteLine("SelectionChanged event: Add=" + e.AddedItems.Count +
|
||||
// " Rem=" + e.RemovedItems.Count);
|
||||
foreach (DisplayList.FormattedParts parts in e.AddedItems) {
|
||||
Debug.Assert(parts.ListIndex >= 0 && parts.ListIndex < mSelection.Length);
|
||||
this[parts.ListIndex] = true;
|
||||
|
@ -393,6 +393,7 @@ namespace SourceGen {
|
||||
descr += " ???";
|
||||
break;
|
||||
}
|
||||
return descr;
|
||||
}
|
||||
|
||||
switch (FormatSubType) {
|
||||
|
@ -2359,7 +2359,7 @@ namespace SourceGen {
|
||||
/// is selected.</param>
|
||||
public SelectionState UpdateSelectionState() {
|
||||
int selCount = mMainWin.CodeListView_GetSelectionCount();
|
||||
Debug.WriteLine("UpdateSelectionState: selCount=" + selCount);
|
||||
//Debug.WriteLine("UpdateSelectionState: selCount=" + selCount);
|
||||
|
||||
SelectionState state = new SelectionState();
|
||||
|
||||
|
@ -318,11 +318,19 @@ namespace SourceGen {
|
||||
CharEncoding.Convert charConv;
|
||||
switch (dfd.FormatSubType) {
|
||||
case FormatDescriptor.SubType.Ascii:
|
||||
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
||||
charConv = CharEncoding.ConvertLowAndHighAscii;
|
||||
} else {
|
||||
charConv = CharEncoding.ConvertAscii;
|
||||
}
|
||||
delDef = delSet.Get(CharEncoding.Encoding.Ascii);
|
||||
break;
|
||||
case FormatDescriptor.SubType.HighAscii:
|
||||
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
||||
charConv = CharEncoding.ConvertLowAndHighAscii;
|
||||
} else {
|
||||
charConv = CharEncoding.ConvertHighAscii;
|
||||
}
|
||||
delDef = delSet.Get(CharEncoding.Encoding.HighAscii);
|
||||
break;
|
||||
case FormatDescriptor.SubType.C64Petscii:
|
||||
@ -330,7 +338,11 @@ namespace SourceGen {
|
||||
delDef = delSet.Get(CharEncoding.Encoding.C64Petscii);
|
||||
break;
|
||||
case FormatDescriptor.SubType.C64Screen:
|
||||
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
||||
charConv = CharEncoding.ConvertLowAndHighC64ScreenCode;
|
||||
} else {
|
||||
charConv = CharEncoding.ConvertC64ScreenCode;
|
||||
}
|
||||
delDef = delSet.Get(CharEncoding.Encoding.C64ScreenCode);
|
||||
break;
|
||||
default:
|
||||
@ -340,13 +352,17 @@ namespace SourceGen {
|
||||
break;
|
||||
}
|
||||
|
||||
if (delDef == null) {
|
||||
delDef = Formatter.DOUBLE_QUOTE_DELIM;
|
||||
}
|
||||
|
||||
switch (dfd.FormatType) {
|
||||
case FormatDescriptor.Type.StringGeneric:
|
||||
// Generic character data.
|
||||
popcode = opNames.StrGeneric;
|
||||
break;
|
||||
case FormatDescriptor.Type.StringReverse:
|
||||
// High or low ASCII, full width specified by formatter. Show characters
|
||||
// Character data, full width specified by formatter. Show characters
|
||||
// in reverse order.
|
||||
popcode = opNames.StrReverse;
|
||||
revMode = StringOpFormatter.ReverseMode.FullReverse;
|
||||
@ -377,10 +393,8 @@ namespace SourceGen {
|
||||
popcode = opNames.StrLen16;
|
||||
break;
|
||||
case FormatDescriptor.Type.StringDci:
|
||||
// High or low ASCII, with high bit on last byte flipped. Only useful
|
||||
// for ASCII strings.
|
||||
// High bit on last byte is flipped.
|
||||
popcode = opNames.StrDci;
|
||||
charConv = CharEncoding.ConvertLowAndHighAscii;
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
|
@ -112,6 +112,10 @@ limitations under the License.
|
||||
<system:String x:Key="str_RuntimeDirNotFoundCaption">RuntimeData Not Found</system:String>
|
||||
<system:String x:Key="str_SaveBeforeAsm">Please save your project before assembling. The generated source code will be placed in the same directory as the project file.</system:String>
|
||||
<system:String x:Key="str_SaveBeforeAsmCaption">Save Project First</system:String>
|
||||
<system:String x:Key="str_ScanLowAscii">Plain ASCII</system:String>
|
||||
<system:String x:Key="str_ScanLowHighAscii">Low or High ASCII</system:String>
|
||||
<system:String x:Key="str_ScanC64Petscii">C64 PETSCII</system:String>
|
||||
<system:String x:Key="str_ScanC64ScreenCode">C64 Screen Code</system:String>
|
||||
<system:String x:Key="str_SetupSystemSummaryFmt">{1} CPU @ {2} MHz</system:String>
|
||||
<system:String x:Key="str_ShowCol">Show</system:String>
|
||||
<system:String x:Key="str_StatusReady">Ready</system:String>
|
||||
|
@ -205,6 +205,14 @@ namespace SourceGen.Res {
|
||||
(string)Application.Current.FindResource("str_SaveBeforeAsm");
|
||||
public static string SAVE_BEFORE_ASM_CAPTION =
|
||||
(string)Application.Current.FindResource("str_SaveBeforeAsmCaption");
|
||||
public static string SCAN_LOW_ASCII =
|
||||
(string)Application.Current.FindResource("str_ScanLowAscii");
|
||||
public static string SCAN_LOW_HIGH_ASCII =
|
||||
(string)Application.Current.FindResource("str_ScanLowHighAscii");
|
||||
public static string SCAN_C64_PETSCII =
|
||||
(string)Application.Current.FindResource("str_ScanC64Petscii");
|
||||
public static string SCAN_C64_SCREEN_CODE =
|
||||
(string)Application.Current.FindResource("str_ScanC64ScreenCode");
|
||||
public static string SETUP_SYSTEM_SUMMARY_FMT =
|
||||
(string)Application.Current.FindResource("str_SetupSystemSummaryFmt");
|
||||
public static string SHOW_COL =
|
||||
|
@ -1,7 +1,7 @@
|
||||
; Copyright 2019 faddenSoft. All Rights Reserved.
|
||||
; See the LICENSE.txt file for distribution terms (Apache 2.0).
|
||||
;
|
||||
; Assembler: ACME (good PETSCII/screen code support)
|
||||
; Assembler: ACME (for the PETSCII/screen code support)
|
||||
|
||||
!cpu 65816
|
||||
* = $1000
|
||||
@ -28,6 +28,8 @@
|
||||
lda #$7f ;EDIT: force to PETSCII
|
||||
lda #$7f ;EDIT: force to screen code
|
||||
|
||||
lda #$0d ;verify the instruction operand editor only allows C64SC
|
||||
|
||||
; Single letter in a 16-bit immediate
|
||||
rep #$30
|
||||
!al
|
||||
@ -63,7 +65,7 @@
|
||||
; Start with the basics
|
||||
!byte $80
|
||||
!text "low ASCII str"
|
||||
!byte $80
|
||||
; !byte $80 ; let them run together to test scan / dialog behavior
|
||||
!xor $80 {
|
||||
!text "high ASCII str"
|
||||
}
|
||||
|
@ -512,6 +512,9 @@ namespace SourceGen.WpfGui {
|
||||
private void ImportDelimiters(Formatter.DelimiterSet delSet, DelimiterTextBoxes[] boxarr) {
|
||||
foreach (DelimiterTextBoxes boxes in boxarr) {
|
||||
Formatter.DelimiterDef def = delSet.Get(boxes.mEncoding);
|
||||
if (def == null) {
|
||||
def = Formatter.DOUBLE_QUOTE_DELIM;
|
||||
}
|
||||
boxes.mPrefix.Text = def.Prefix;
|
||||
boxes.mOpen.Text = "" + def.OpenDelim;
|
||||
boxes.mClose.Text = "" + def.CloseDelim;
|
||||
|
@ -33,8 +33,8 @@ limitations under the License.
|
||||
<system:String x:Key="str_SingleGroup">Select data format ({0} bytes selected):</system:String>
|
||||
<system:String x:Key="str_MultiGroup">Select data format ({0} bytes selected in {1} groups):</system:String>
|
||||
|
||||
<system:String x:Key="str_StringMixed">Mixed ASCII ({0} bytes) and non-ASCII ({1} bytes)</system:String>
|
||||
<system:String x:Key="str_StringMixedReverse">Reversed ASCII ({0} bytes) and non-ASCII ({1} bytes)</system:String>
|
||||
<system:String x:Key="str_StringMixed">Mixed character ({0} bytes) and non-character ({1} bytes)</system:String>
|
||||
<system:String x:Key="str_StringMixedReverse">Reversed character ({0} bytes) and non-character ({1} bytes)</system:String>
|
||||
<system:String x:Key="str_StringNullTerm">Null-terminated strings ({0})</system:String>
|
||||
<system:String x:Key="str_StringLen8">Strings prefixed with 8-bit length ({0})</system:String>
|
||||
<system:String x:Key="str_StringLen16">Strings prefixed with 16-bit length ({0})</system:String>
|
||||
@ -64,29 +64,44 @@ limitations under the License.
|
||||
<RadioButton Name="radio32BitLittle" GroupName="Main" Content="32-bit words, little-endian" Margin="0,4,0,0"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
</StackPanel>
|
||||
<GroupBox Name="simpleDisplayAsGroupBox" Grid.Column="1" Header="Display As..." Margin="16,0,0,0">
|
||||
<GroupBox Name="simpleDisplayAsGroupBox" Grid.Column="1" Header="Display As..." Margin="16,0,0,0"
|
||||
Padding="2,4">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Column="0" Grid.Row="0">
|
||||
<RadioButton Name="radioSimpleDataHex" GroupName="Display" Content="Hex"
|
||||
Margin="0,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<RadioButton Name="radioSimpleDataDecimal" GroupName="Display" Content="Decimal"
|
||||
Margin="0,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
Margin="0,4,18,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<RadioButton Name="radioSimpleDataBinary" GroupName="Display" Content="Binary"
|
||||
Margin="0,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<RadioButton Name="radioSimpleDataAscii" GroupName="Display" Content="ASCII"
|
||||
<RadioButton Name="radioSimpleDataAddress" GroupName="Display" Content="_Address"
|
||||
Margin="0,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<RadioButton Name="radioSimpleDataAddress" GroupName="Display" Content="_Address"
|
||||
Margin="12,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<StackPanel Grid.Column="1" Grid.Row="0">
|
||||
<RadioButton Name="radioSimpleDataAscii" GroupName="Display"
|
||||
Content="ASCII (low or high) character"
|
||||
Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<RadioButton Name="radioSimpleDataPetscii" GroupName="Display"
|
||||
Content="C64 PETSCII character"
|
||||
Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<RadioButton Name="radioSimpleDataScreenCode" GroupName="Display"
|
||||
Content="C64 Screen character"
|
||||
Checked="SimpleDisplay_CheckedChanged"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2">
|
||||
<RadioButton Name="radioSimpleDataSymbolic" GroupName="Display" Content="_Symbolic reference"
|
||||
Margin="12,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<TextBox Name="symbolEntryTextBox" Margin="32,4,0,0" TextChanged="SymbolEntryTextBox_TextChanged"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="32,4,0,0"
|
||||
Margin="0,4,0,0" Checked="SimpleDisplay_CheckedChanged"/>
|
||||
<TextBox Name="symbolEntryTextBox" Margin="20,4,0,0" TextChanged="SymbolEntryTextBox_TextChanged"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="20,4,0,0"
|
||||
IsEnabled="{Binding ElementName=radioSimpleDataSymbolic, Path=IsChecked}">
|
||||
<RadioButton Name="radioSymbolPartLow" GroupName="Part" Content="Low"/>
|
||||
<RadioButton Name="radioSymbolPartHigh" GroupName="Part" Content="High" Margin="8,0,0,0"/>
|
||||
@ -106,6 +121,12 @@ limitations under the License.
|
||||
|
||||
<TextBlock Text="String" Margin="0,12,0,0"/>
|
||||
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Character encoding:" Margin="0,7,0,0"/>
|
||||
<ComboBox Name="stringEncodingComboBox" Width="150" Margin="8,4,0,4"
|
||||
ItemsSource="{Binding StringEncodingItems}" DisplayMemberPath="Name"
|
||||
SelectionChanged="StringEncodingComboBox_SelectionChanged"/>
|
||||
</StackPanel>
|
||||
<RadioButton Name="radioStringMixed" GroupName="Main" Margin="0,4,0,0"
|
||||
Content="{StaticResource str_StringMixed}"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
|
@ -21,7 +21,9 @@ using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
using Asm65;
|
||||
using CommonUtil;
|
||||
using TextScanMode = SourceGen.ProjectProperties.AnalysisParameters.TextScanMode;
|
||||
|
||||
namespace SourceGen.WpfGui {
|
||||
/// <summary>
|
||||
@ -77,18 +79,27 @@ namespace SourceGen.WpfGui {
|
||||
/// </summary>
|
||||
private Asm65.Formatter mFormatter;
|
||||
|
||||
///// <summary>
|
||||
///// Set this during initial control configuration, so we know to ignore the CheckedChanged
|
||||
///// events.
|
||||
///// </summary>
|
||||
//private bool mIsInitialSetup;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if, during the initial setup, the format defined by FirstFormatDescriptor
|
||||
/// was unavailable.
|
||||
/// </summary>
|
||||
private bool mPreferredFormatUnavailable;
|
||||
|
||||
/// <summary>
|
||||
/// Text encoding combo box item. We use the same TextScanMode enum that the
|
||||
/// uncategorized data analyzer uses.
|
||||
/// </summary>
|
||||
public class StringEncodingItem {
|
||||
public string Name { get; private set; }
|
||||
public TextScanMode Mode { get; private set; }
|
||||
|
||||
public StringEncodingItem(string name, TextScanMode mode) {
|
||||
Name = name;
|
||||
Mode = mode;
|
||||
}
|
||||
}
|
||||
public StringEncodingItem[] StringEncodingItems { get; private set; }
|
||||
|
||||
// INotifyPropertyChanged implementation
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
|
||||
@ -107,6 +118,17 @@ namespace SourceGen.WpfGui {
|
||||
mFormatter = formatter;
|
||||
mSelection = trs;
|
||||
mFirstFormatDescriptor = firstDesc;
|
||||
|
||||
StringEncodingItems = new StringEncodingItem[] {
|
||||
new StringEncodingItem(Res.Strings.SCAN_LOW_ASCII,
|
||||
TextScanMode.LowAscii),
|
||||
new StringEncodingItem(Res.Strings.SCAN_LOW_HIGH_ASCII,
|
||||
TextScanMode.LowHighAscii),
|
||||
new StringEncodingItem(Res.Strings.SCAN_C64_PETSCII,
|
||||
TextScanMode.C64Petscii),
|
||||
new StringEncodingItem(Res.Strings.SCAN_C64_SCREEN_CODE,
|
||||
TextScanMode.C64ScreenCode),
|
||||
};
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
||||
@ -116,6 +138,9 @@ namespace SourceGen.WpfGui {
|
||||
// Disable any radio buttons that won't work.
|
||||
AnalyzeRanges();
|
||||
|
||||
// This gets invoked a bit later, from the "selection changed" callback.
|
||||
//AnalyzeStringRanges(TextScanMode.LowHighAscii);
|
||||
|
||||
// Configure the dialog from the FormatDescriptor, if one is available.
|
||||
Debug.WriteLine("First FD: " + mFirstFormatDescriptor);
|
||||
SetControlsFromDescriptor(mFirstFormatDescriptor);
|
||||
@ -169,6 +194,36 @@ namespace SourceGen.WpfGui {
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string encoding combo box to an item that matches the specified mode. If
|
||||
/// the mode can't be found, an arbitrary entry will be chosen.
|
||||
/// </summary>
|
||||
private void SetStringEncoding(TextScanMode mode) {
|
||||
StringEncodingItem choice = null;
|
||||
foreach (StringEncodingItem item in StringEncodingItems) {
|
||||
if (item.Mode == mode) {
|
||||
choice = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (choice == null) {
|
||||
choice = StringEncodingItems[1];
|
||||
}
|
||||
stringEncodingComboBox.SelectedItem = choice;
|
||||
}
|
||||
|
||||
private void StringEncodingComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (!IsLoaded) {
|
||||
return;
|
||||
}
|
||||
StringEncodingItem item = (StringEncodingItem)stringEncodingComboBox.SelectedItem;
|
||||
AnalyzeStringRanges(item.Mode);
|
||||
UpdateControls();
|
||||
|
||||
AppSettings.Global.SetEnum(AppSettings.OPED_DEFAULT_STRING_ENCODING,
|
||||
typeof(TextScanMode), (int)item.Mode);
|
||||
}
|
||||
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
||||
CreateDescriptorListFromControls();
|
||||
FormatDescriptor.DebugDumpSortedList(Results);
|
||||
@ -207,7 +262,14 @@ namespace SourceGen.WpfGui {
|
||||
bool focusOnSymbol = !simpleDisplayAsGroupBox.IsEnabled && wantStyle;
|
||||
simpleDisplayAsGroupBox.IsEnabled = wantStyle;
|
||||
if (wantStyle) {
|
||||
radioSimpleDataAscii.IsEnabled = IsRawAsciiCompatible(simpleWidth, isBigEndian);
|
||||
// Because this covers multiple items in a data area, we allow the
|
||||
// "extended" set, which includes some control characters.
|
||||
radioSimpleDataAscii.IsEnabled = IsCompatibleWithCharSet(simpleWidth,
|
||||
isBigEndian, CharEncoding.IsExtendedLowOrHighAscii);
|
||||
radioSimpleDataPetscii.IsEnabled = IsCompatibleWithCharSet(simpleWidth,
|
||||
isBigEndian, CharEncoding.IsExtendedC64Petscii);
|
||||
radioSimpleDataScreenCode.IsEnabled = IsCompatibleWithCharSet(simpleWidth,
|
||||
isBigEndian, CharEncoding.IsExtendedC64ScreenCode);
|
||||
}
|
||||
|
||||
// Enable the symbolic reference entry box if the "display as" group is enabled.
|
||||
@ -263,13 +325,6 @@ namespace SourceGen.WpfGui {
|
||||
|
||||
IEnumerator<TypedRangeSet.TypedRange> iter = mSelection.RangeListIterator;
|
||||
|
||||
int mixedAsciiOkCount = 0;
|
||||
int mixedAsciiNotCount = 0;
|
||||
int nullTermStringCount = 0;
|
||||
int len8StringCount = 0;
|
||||
int len16StringCount = 0;
|
||||
int dciStringCount = 0;
|
||||
|
||||
// For each range, check to see if the data within qualifies for the various
|
||||
// options. If any of them fail to meet the criteria, the option is disabled
|
||||
// for all ranges.
|
||||
@ -277,7 +332,7 @@ namespace SourceGen.WpfGui {
|
||||
TypedRangeSet.TypedRange rng = iter.Current;
|
||||
Debug.WriteLine("Testing [" + rng.Low + ", " + rng.High + "]");
|
||||
|
||||
// Start with the easy ones. Single-byte and dense are always enabled.
|
||||
// Note single-byte and dense are always enabled.
|
||||
|
||||
int count = rng.High - rng.Low + 1;
|
||||
Debug.Assert(count > 0);
|
||||
@ -305,18 +360,68 @@ namespace SourceGen.WpfGui {
|
||||
} else {
|
||||
radioFill.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analyzes the selection to see which string formatting options are suitable.
|
||||
/// Disables radio buttons and updates labels.
|
||||
///
|
||||
/// Call this when the character encoding selection changes.
|
||||
/// </summary>
|
||||
private void AnalyzeStringRanges(TextScanMode scanMode) {
|
||||
Debug.WriteLine("Analyzing string ranges");
|
||||
Debug.Assert(IsLoaded);
|
||||
|
||||
int mixedCharOkCount = 0;
|
||||
int mixedCharNotCount = 0;
|
||||
int nullTermStringCount = 0;
|
||||
int len8StringCount = 0;
|
||||
int len16StringCount = 0;
|
||||
int dciStringCount = 0;
|
||||
|
||||
CharEncoding.InclusionTest charTest;
|
||||
switch (scanMode) {
|
||||
case TextScanMode.LowAscii:
|
||||
charTest = CharEncoding.IsExtendedAscii;
|
||||
break;
|
||||
case TextScanMode.LowHighAscii:
|
||||
charTest = CharEncoding.IsExtendedLowOrHighAscii;
|
||||
break;
|
||||
case TextScanMode.C64Petscii:
|
||||
charTest = CharEncoding.IsExtendedC64Petscii;
|
||||
break;
|
||||
case TextScanMode.C64ScreenCode:
|
||||
charTest = CharEncoding.IsExtendedC64ScreenCode;
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
charTest = CharEncoding.IsExtendedAscii;
|
||||
break;
|
||||
}
|
||||
|
||||
radioStringMixed.IsEnabled = true;
|
||||
radioStringMixedReverse.IsEnabled = true;
|
||||
radioStringNullTerm.IsEnabled = (scanMode != TextScanMode.C64ScreenCode);
|
||||
radioStringLen8.IsEnabled = true;
|
||||
radioStringLen16.IsEnabled = true;
|
||||
radioStringDci.IsEnabled = (scanMode != TextScanMode.C64Petscii);
|
||||
|
||||
IEnumerator<TypedRangeSet.TypedRange> iter = mSelection.RangeListIterator;
|
||||
while (iter.MoveNext()) {
|
||||
TypedRangeSet.TypedRange rng = iter.Current;
|
||||
Debug.WriteLine("Testing [" + rng.Low + ", " + rng.High + "]");
|
||||
|
||||
// See if there's enough string data to make it worthwhile. We use an
|
||||
// arbitrary threshold of 2+ ASCII characters, and require twice as many
|
||||
// ASCII as non-ASCII. We arbitrarily require the strings to be either
|
||||
// high or low ASCII, and treat the other as non-ASCII. (We could relax
|
||||
// this -- we generate separate items for each string and non-ASCII chunk --
|
||||
// but I'm trying to hide the option when the buffer doesn't really seem
|
||||
// to be holding strings. Could replace with some sort of minimum string
|
||||
// length requirement?)
|
||||
// arbitrary threshold of 2+ printable characters, and require twice as many
|
||||
// printable as non-printable.
|
||||
if (radioStringMixed.IsEnabled) {
|
||||
if (scanMode == TextScanMode.LowHighAscii) {
|
||||
// We use a special test that counts low, high, and non-ASCII.
|
||||
// Whichever form of ASCII has the highest count is the winner, and
|
||||
// the loser is counted as non-ASCII.
|
||||
int asciiCount;
|
||||
DataAnalysis.CountAsciiBytes(mFileData, rng.Low, rng.High,
|
||||
DataAnalysis.CountHighLowBytes(mFileData, rng.Low, rng.High, charTest,
|
||||
out int lowAscii, out int highAscii, out int nonAscii);
|
||||
if (highAscii > lowAscii) {
|
||||
asciiCount = highAscii;
|
||||
@ -328,23 +433,37 @@ namespace SourceGen.WpfGui {
|
||||
|
||||
if (asciiCount >= 2 && asciiCount >= nonAscii * 2) {
|
||||
// Looks good
|
||||
mixedAsciiOkCount += asciiCount;
|
||||
mixedAsciiNotCount += nonAscii;
|
||||
mixedCharOkCount += asciiCount;
|
||||
mixedCharNotCount += nonAscii;
|
||||
} else {
|
||||
// Fail
|
||||
radioStringMixed.IsEnabled = false;
|
||||
radioStringMixedReverse.IsEnabled = false;
|
||||
mixedAsciiOkCount = mixedAsciiNotCount = -1;
|
||||
mixedCharOkCount = mixedCharNotCount = -1;
|
||||
}
|
||||
} else {
|
||||
int matchCount = DataAnalysis.CountCharacterBytes(mFileData,
|
||||
rng.Low, rng.High, charTest);
|
||||
int missCount = (rng.High - rng.Low + 1) - matchCount;
|
||||
if (matchCount >= 2 && matchCount >= missCount * 2) {
|
||||
mixedCharOkCount += matchCount;
|
||||
mixedCharNotCount += missCount;
|
||||
} else {
|
||||
// Fail
|
||||
radioStringMixed.IsEnabled = false;
|
||||
radioStringMixedReverse.IsEnabled = false;
|
||||
mixedCharOkCount = mixedCharNotCount = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for null-terminated strings. Zero-length strings are allowed, but
|
||||
// not counted -- we want to have some actual character data. Individual
|
||||
// strings need to be entirely high-ASCII or low-ASCII, but not all strings
|
||||
// ASCII strings need to be entirely high-ASCII or low-ASCII, but not all strings
|
||||
// in a region have to be the same.
|
||||
if (radioStringNullTerm.IsEnabled) {
|
||||
int strCount = DataAnalysis.RecognizeNullTerminatedStrings(mFileData,
|
||||
rng.Low, rng.High);
|
||||
rng.Low, rng.High, charTest, scanMode == TextScanMode.LowHighAscii);
|
||||
if (strCount > 0) {
|
||||
nullTermStringCount += strCount;
|
||||
} else {
|
||||
@ -355,7 +474,8 @@ namespace SourceGen.WpfGui {
|
||||
|
||||
// Check for strings prefixed with an 8-bit length.
|
||||
if (radioStringLen8.IsEnabled) {
|
||||
int strCount = DataAnalysis.RecognizeLen8Strings(mFileData, rng.Low, rng.High);
|
||||
int strCount = DataAnalysis.RecognizeLen8Strings(mFileData, rng.Low, rng.High,
|
||||
charTest, scanMode == TextScanMode.LowHighAscii);
|
||||
if (strCount > 0) {
|
||||
len8StringCount += strCount;
|
||||
} else {
|
||||
@ -366,7 +486,8 @@ namespace SourceGen.WpfGui {
|
||||
|
||||
// Check for strings prefixed with a 16-bit length.
|
||||
if (radioStringLen16.IsEnabled) {
|
||||
int strCount = DataAnalysis.RecognizeLen16Strings(mFileData, rng.Low, rng.High);
|
||||
int strCount = DataAnalysis.RecognizeLen16Strings(mFileData, rng.Low, rng.High,
|
||||
charTest, scanMode == TextScanMode.LowHighAscii);
|
||||
if (strCount > 0) {
|
||||
len16StringCount += strCount;
|
||||
} else {
|
||||
@ -375,10 +496,11 @@ namespace SourceGen.WpfGui {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for DCI strings. All strings within a single range must have the
|
||||
// Check for DCI strings. All strings within the entire range must have the
|
||||
// same "polarity", e.g. low ASCII terminated by high ASCII.
|
||||
if (radioStringDci.IsEnabled) {
|
||||
int strCount = DataAnalysis.RecognizeDciStrings(mFileData, rng.Low, rng.High);
|
||||
int strCount = DataAnalysis.RecognizeDciStrings(mFileData, rng.Low, rng.High,
|
||||
charTest);
|
||||
if (strCount > 0) {
|
||||
dciStringCount += strCount;
|
||||
} else {
|
||||
@ -390,15 +512,16 @@ namespace SourceGen.WpfGui {
|
||||
|
||||
// Update the dialog with string and character counts, summed across all regions.
|
||||
|
||||
string fmt;
|
||||
const string UNSUP_STR = "xx";
|
||||
fmt = (string)FindResource("str_StringMixed");
|
||||
string revfmt = (string)FindResource("str_StringMixedReverse");
|
||||
if (mixedAsciiOkCount > 0) {
|
||||
if (mixedCharOkCount > 0) {
|
||||
Debug.Assert(radioStringMixed.IsEnabled);
|
||||
radioStringMixed.Content = string.Format(fmt,
|
||||
mixedAsciiOkCount, mixedAsciiNotCount);
|
||||
mixedCharOkCount, mixedCharNotCount);
|
||||
radioStringMixedReverse.Content = string.Format(revfmt,
|
||||
mixedAsciiOkCount, mixedAsciiNotCount);
|
||||
mixedCharOkCount, mixedCharNotCount);
|
||||
} else {
|
||||
Debug.Assert(!radioStringMixed.IsEnabled);
|
||||
radioStringMixed.Content = string.Format(fmt, UNSUP_STR, UNSUP_STR);
|
||||
@ -440,32 +563,40 @@ namespace SourceGen.WpfGui {
|
||||
Debug.Assert(!radioStringDci.IsEnabled);
|
||||
radioStringDci.Content = string.Format(fmt, UNSUP_STR);
|
||||
}
|
||||
|
||||
// If this invalidated the selected item, reset to Default.
|
||||
if ((radioStringMixed.IsChecked == true && !radioStringMixed.IsEnabled) ||
|
||||
(radioStringMixedReverse.IsChecked == true && !radioStringMixedReverse.IsEnabled) ||
|
||||
(radioStringNullTerm.IsChecked == true && !radioStringNullTerm.IsEnabled) ||
|
||||
(radioStringLen8.IsChecked == true && !radioStringLen8.IsEnabled) ||
|
||||
(radioStringLen8.IsChecked == true && !radioStringLen8.IsEnabled) ||
|
||||
(radioStringDci.IsChecked == true && !radioStringDci.IsEnabled)) {
|
||||
|
||||
Debug.WriteLine("Previous selection invalidated");
|
||||
radioDefaultFormat.IsChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the data in the buffer can be represented as ASCII values.
|
||||
/// Determines whether the data in the buffer can be represented as character values.
|
||||
/// Using ".DD1 'A'" for 0x41 is obvious, but we also allow ".DD2 'A'" for
|
||||
/// 0x41 0x00. 16-bit character constants are more likely as intermediate
|
||||
/// operands, but could be found in data areas.
|
||||
///
|
||||
/// High and low ASCII are allowed, and may be freely mixed.
|
||||
///
|
||||
/// Testing explicitly is probably excessive, and possibly counter-productive if
|
||||
/// the user is trying to flag an area that is a mix of ASCII and non-ASCII and
|
||||
/// just wants hex for the rest, but we'll give it a try.
|
||||
/// </summary>
|
||||
/// <param name="wordWidth">Number of bytes per character.</param>
|
||||
/// <param name="isBigEndian">Word endian-ness.</param>
|
||||
/// <returns>True if data in all regions can be represented as high or low ASCII.</returns>
|
||||
private bool IsRawAsciiCompatible(int wordWidth, bool isBigEndian) {
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
/// <returns>True if data in all regions can be represented as a character.</returns>
|
||||
private bool IsCompatibleWithCharSet(int wordWidth, bool isBigEndian,
|
||||
CharEncoding.InclusionTest charTest) {
|
||||
IEnumerator<TypedRangeSet.TypedRange> iter = mSelection.RangeListIterator;
|
||||
while (iter.MoveNext()) {
|
||||
TypedRangeSet.TypedRange rng = iter.Current;
|
||||
Debug.Assert(((rng.High - rng.Low + 1) / wordWidth) * wordWidth ==
|
||||
rng.High - rng.Low + 1);
|
||||
for (int i = rng.Low; i <= rng.High; i += wordWidth) {
|
||||
int val = RawData.GetWord(mFileData, rng.Low, wordWidth, isBigEndian);
|
||||
if (val < 0x20 || (val >= 0x7f && val < 0xa0) || val >= 0xff) {
|
||||
int val = RawData.GetWord(mFileData, i, wordWidth, isBigEndian);
|
||||
if (val != (byte)val || !charTest((byte)val)) {
|
||||
// bad value, fail
|
||||
return false;
|
||||
}
|
||||
@ -485,11 +616,22 @@ namespace SourceGen.WpfGui {
|
||||
radioSimpleDataHex.IsChecked = true;
|
||||
radioSymbolPartLow.IsChecked = true;
|
||||
|
||||
// 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);
|
||||
|
||||
if (dfd == null) {
|
||||
radioDefaultFormat.IsChecked = true;
|
||||
SetStringEncoding(textMode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dfd.IsString) {
|
||||
textMode = TextScanModeFromDescriptor(dfd);
|
||||
}
|
||||
|
||||
RadioButton preferredFormat;
|
||||
|
||||
switch (dfd.FormatType) {
|
||||
@ -529,11 +671,14 @@ namespace SourceGen.WpfGui {
|
||||
break;
|
||||
case FormatDescriptor.SubType.Ascii:
|
||||
case FormatDescriptor.SubType.HighAscii:
|
||||
case FormatDescriptor.SubType.C64Petscii:
|
||||
case FormatDescriptor.SubType.C64Screen:
|
||||
// TODO(petscii): update UI
|
||||
radioSimpleDataAscii.IsChecked = true;
|
||||
break;
|
||||
case FormatDescriptor.SubType.C64Petscii:
|
||||
radioSimpleDataPetscii.IsChecked = true;
|
||||
break;
|
||||
case FormatDescriptor.SubType.C64Screen:
|
||||
radioSimpleDataScreenCode.IsChecked = true;
|
||||
break;
|
||||
case FormatDescriptor.SubType.Address:
|
||||
radioSimpleDataAddress.IsChecked = true;
|
||||
break;
|
||||
@ -601,6 +746,24 @@ namespace SourceGen.WpfGui {
|
||||
mPreferredFormatUnavailable = true;
|
||||
radioDefaultFormat.IsChecked = true;
|
||||
}
|
||||
|
||||
SetStringEncoding(textMode);
|
||||
}
|
||||
|
||||
private TextScanMode TextScanModeFromDescriptor(FormatDescriptor dfd) {
|
||||
Debug.Assert(dfd.IsString);
|
||||
switch (dfd.FormatSubType) {
|
||||
case FormatDescriptor.SubType.Ascii:
|
||||
case FormatDescriptor.SubType.HighAscii:
|
||||
return TextScanMode.LowHighAscii;
|
||||
case FormatDescriptor.SubType.C64Petscii:
|
||||
return TextScanMode.C64Petscii;
|
||||
case FormatDescriptor.SubType.C64Screen:
|
||||
return TextScanMode.C64ScreenCode;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
return TextScanMode.LowHighAscii;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Setup
|
||||
@ -623,6 +786,33 @@ namespace SourceGen.WpfGui {
|
||||
WeakSymbolRef symbolRef = null;
|
||||
int chunkLength = -1;
|
||||
|
||||
FormatDescriptor.SubType charSubType;
|
||||
CharEncoding.InclusionTest charTest;
|
||||
StringEncodingItem item = (StringEncodingItem)stringEncodingComboBox.SelectedItem;
|
||||
switch (item.Mode) {
|
||||
case TextScanMode.LowAscii:
|
||||
charSubType = FormatDescriptor.SubType.Ascii;
|
||||
charTest = CharEncoding.IsExtendedAscii;
|
||||
break;
|
||||
case TextScanMode.LowHighAscii:
|
||||
charSubType = FormatDescriptor.SubType.ASCII_GENERIC;
|
||||
charTest = CharEncoding.IsExtendedLowOrHighAscii;
|
||||
break;
|
||||
case TextScanMode.C64Petscii:
|
||||
charSubType = FormatDescriptor.SubType.C64Petscii;
|
||||
charTest = CharEncoding.IsExtendedC64Petscii;
|
||||
break;
|
||||
case TextScanMode.C64ScreenCode:
|
||||
charSubType = FormatDescriptor.SubType.C64Screen;
|
||||
charTest = CharEncoding.IsExtendedC64ScreenCode;
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
charSubType = FormatDescriptor.SubType.ASCII_GENERIC;
|
||||
charTest = CharEncoding.IsExtendedLowOrHighAscii;
|
||||
break;
|
||||
}
|
||||
|
||||
// Decode the "display as" panel, if it's relevant.
|
||||
if (radioSimpleDataHex.IsEnabled) {
|
||||
if (radioSimpleDataHex.IsChecked == true) {
|
||||
@ -632,8 +822,11 @@ namespace SourceGen.WpfGui {
|
||||
} else if (radioSimpleDataBinary.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.Binary;
|
||||
} else if (radioSimpleDataAscii.IsChecked == true) {
|
||||
// TODO(petscii): add PETSCII buttons
|
||||
subType = FormatDescriptor.SubType.ASCII_GENERIC;
|
||||
} else if (radioSimpleDataPetscii.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.C64Petscii;
|
||||
} else if (radioSimpleDataScreenCode.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.C64Screen;
|
||||
} else if (radioSimpleDataAddress.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.Address;
|
||||
} else if (radioSimpleDataSymbolic.IsChecked == true) {
|
||||
@ -683,26 +876,23 @@ namespace SourceGen.WpfGui {
|
||||
} else if (radioFill.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.Fill;
|
||||
} else if (radioStringMixed.IsChecked == true) {
|
||||
// TODO(petscii): encoding format will come from a combo box; that determines
|
||||
// the subType and the arg to the string-creation functions, which use the
|
||||
// appropriate char encoding methods to break up the strings
|
||||
type = FormatDescriptor.Type.StringGeneric;
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
subType = charSubType;
|
||||
} else if (radioStringMixedReverse.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.StringReverse;
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
subType = charSubType;
|
||||
} else if (radioStringNullTerm.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.StringNullTerm;
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
subType = charSubType;
|
||||
} else if (radioStringLen8.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.StringL8;
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
subType = charSubType;
|
||||
} else if (radioStringLen16.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.StringL16;
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
subType = charSubType;
|
||||
} else if (radioStringDci.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.StringDci;
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
subType = charSubType;
|
||||
} else {
|
||||
Debug.Assert(false);
|
||||
// default/none
|
||||
@ -715,11 +905,12 @@ namespace SourceGen.WpfGui {
|
||||
while (iter.MoveNext()) {
|
||||
TypedRangeSet.TypedRange rng = iter.Current;
|
||||
|
||||
// TODO(petscii): handle encoding on all four calls
|
||||
switch (type) {
|
||||
case FormatDescriptor.Type.StringGeneric:
|
||||
CreateMixedStringEntries(rng.Low, rng.High, type, subType, charTest);
|
||||
break;
|
||||
case FormatDescriptor.Type.StringReverse:
|
||||
CreateMixedStringEntries(rng.Low, rng.High, type, subType);
|
||||
CreateMixedStringEntries(rng.Low, rng.High, type, subType, charTest);
|
||||
break;
|
||||
case FormatDescriptor.Type.StringNullTerm:
|
||||
CreateCStringEntries(rng.Low, rng.High, type, subType);
|
||||
@ -794,25 +985,35 @@ namespace SourceGen.WpfGui {
|
||||
|
||||
/// <summary>
|
||||
/// Creates one or more FormatDescriptor entries for the specified range, adding them
|
||||
/// to the Results list.
|
||||
/// to the Results list. Runs of character data are output as generic strings, while any
|
||||
/// non-character data is output as individual bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the only string create function that accepts a mix of valid and invalid
|
||||
/// characters.
|
||||
/// </remarks>
|
||||
/// <param name="low">Offset of first byte in range.</param>
|
||||
/// <param name="high">Offset of last byte in range.</param>
|
||||
/// <param name="type">String type (Generic or Reverse).</param>
|
||||
/// <param name="subType">String sub-type.</param>
|
||||
/// <param name="charTest">Character test delegate.</param>
|
||||
private void CreateMixedStringEntries(int low, int high, FormatDescriptor.Type type,
|
||||
FormatDescriptor.SubType subType) {
|
||||
FormatDescriptor.SubType subType, CharEncoding.InclusionTest charTest) {
|
||||
int stringStart = -1;
|
||||
int highBit = 0;
|
||||
int cur;
|
||||
|
||||
if (subType == FormatDescriptor.SubType.ASCII_GENERIC) {
|
||||
int highBit = 0;
|
||||
for (cur = low; cur <= high; cur++) {
|
||||
byte val = mFileData[cur];
|
||||
if (CommonUtil.TextUtil.IsHiLoAscii(val)) {
|
||||
if (charTest(val)) {
|
||||
// is ASCII
|
||||
if (stringStart >= 0) {
|
||||
// was in a string
|
||||
if (highBit != (val & 0x80)) {
|
||||
// end of string due to high bit flip, output
|
||||
CreateStringOrByte(stringStart, cur - stringStart, subType);
|
||||
CreateGenericStringOrByte(stringStart, cur - stringStart,
|
||||
type, subType);
|
||||
// start a new string
|
||||
stringStart = cur;
|
||||
} else {
|
||||
@ -827,36 +1028,75 @@ namespace SourceGen.WpfGui {
|
||||
// not ASCII
|
||||
if (stringStart >= 0) {
|
||||
// was in a string, output it
|
||||
CreateStringOrByte(stringStart, cur - stringStart, subType);
|
||||
CreateGenericStringOrByte(stringStart, cur - stringStart,
|
||||
type, subType);
|
||||
stringStart = -1;
|
||||
}
|
||||
// output as single byte
|
||||
CreateByteFD(cur, FormatDescriptor.SubType.Hex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (cur = low; cur <= high; cur++) {
|
||||
byte val = mFileData[cur];
|
||||
if (charTest(val)) {
|
||||
// is character
|
||||
if (stringStart < 0) {
|
||||
// mark this as the start of the string
|
||||
stringStart = cur;
|
||||
}
|
||||
} else {
|
||||
// not character
|
||||
if (stringStart >= 0) {
|
||||
// close out the string
|
||||
CreateStringOrByte(stringStart, cur - stringStart, subType);
|
||||
// was in a string, output it
|
||||
CreateGenericStringOrByte(stringStart, cur - stringStart,
|
||||
type, subType);
|
||||
stringStart = -1;
|
||||
}
|
||||
// output as single byte
|
||||
CreateByteFD(cur, FormatDescriptor.SubType.Hex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (stringStart >= 0) {
|
||||
// close out the string
|
||||
CreateGenericStringOrByte(stringStart, cur - stringStart, type, subType);
|
||||
}
|
||||
}
|
||||
|
||||
private FormatDescriptor.SubType ResolveAsciiGeneric(int offset,
|
||||
FormatDescriptor.SubType subType) {
|
||||
if (subType == FormatDescriptor.SubType.ASCII_GENERIC) {
|
||||
if ((mFileData[offset] & 0x80) != 0) {
|
||||
subType = FormatDescriptor.SubType.HighAscii;
|
||||
} else {
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
}
|
||||
}
|
||||
return subType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a format descriptor for ASCII data. If the data is only one byte long,
|
||||
/// a single-byte ASCII char item is emitted instead.
|
||||
/// Creates a format descriptor for character data. If the data is only one byte long,
|
||||
/// a single-byte character item is emitted instead.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset of first byte.</param>
|
||||
/// <param name="length">Length of string.</param>
|
||||
/// <param name="subType">String sub-type.</param>
|
||||
private void CreateStringOrByte(int offset, int length, FormatDescriptor.SubType subType) {
|
||||
/// <param name="type">String type (Generic or Reverse).</param>
|
||||
/// <param name="subType">String sub-type. If set to ASCII_GENERIC, this will
|
||||
/// refine the sub-type.</param>
|
||||
private void CreateGenericStringOrByte(int offset, int length,
|
||||
FormatDescriptor.Type type, FormatDescriptor.SubType subType) {
|
||||
Debug.Assert(length > 0);
|
||||
subType = ResolveAsciiGeneric(offset, subType);
|
||||
if (length == 1) {
|
||||
// Single byte, output as single char rather than 1-byte string. We use the
|
||||
// same encoding as the rest of the string.
|
||||
CreateByteFD(offset, subType);
|
||||
} else {
|
||||
FormatDescriptor dfd;
|
||||
dfd = FormatDescriptor.Create(length,
|
||||
FormatDescriptor.Type.StringGeneric, subType);
|
||||
dfd = FormatDescriptor.Create(length, type, subType);
|
||||
Results.Add(offset, dfd);
|
||||
}
|
||||
}
|
||||
@ -886,7 +1126,7 @@ namespace SourceGen.WpfGui {
|
||||
if (mFileData[i] == 0x00) {
|
||||
// End of string. Zero-length strings are allowed.
|
||||
FormatDescriptor dfd = FormatDescriptor.Create(
|
||||
i - startOffset + 1, type, subType);
|
||||
i - startOffset + 1, type, ResolveAsciiGeneric(startOffset, subType));
|
||||
Results.Add(startOffset, dfd);
|
||||
startOffset = i + 1;
|
||||
} else {
|
||||
@ -917,7 +1157,8 @@ namespace SourceGen.WpfGui {
|
||||
length++;
|
||||
}
|
||||
// Zero-length strings are allowed.
|
||||
FormatDescriptor dfd = FormatDescriptor.Create(length, type, subType);
|
||||
FormatDescriptor dfd = FormatDescriptor.Create(length, type,
|
||||
ResolveAsciiGeneric(i, subType));
|
||||
Results.Add(i, dfd);
|
||||
i += length;
|
||||
}
|
||||
@ -948,8 +1189,9 @@ namespace SourceGen.WpfGui {
|
||||
if ((val & 0x80) == endMask) {
|
||||
// found the end of a string
|
||||
int length = (i - stringStart) + 1;
|
||||
FormatDescriptor dfd = FormatDescriptor.Create(length, type, subType);
|
||||
Results.Add(stringStart < i ? stringStart : i, dfd);
|
||||
FormatDescriptor dfd = FormatDescriptor.Create(length, type,
|
||||
ResolveAsciiGeneric(stringStart, subType));
|
||||
Results.Add(stringStart, dfd);
|
||||
stringStart = i + 1;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,11 @@ limitations under the License.
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<RadioButton Name="binaryButton" GroupName="Format" Content="Binary" Margin="0,2,0,0"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<RadioButton Name="asciiButton" GroupName="Format" Content="ASCII character" Margin="0,2,0,0"
|
||||
<RadioButton Name="asciiButton" GroupName="Format" Content="ASCII (low or high) character" Margin="0,2,0,0"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<RadioButton Name="petsciiButton" GroupName="Format" Content="C64 PETSCII character" Margin="0,2,0,0"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<RadioButton Name="screenCodeButton" GroupName="Format" Content="C64 Screen character" Margin="0,2,0,0"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
|
@ -171,8 +171,19 @@ namespace SourceGen.WpfGui {
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
||||
mIsInitialSetup = true;
|
||||
|
||||
// Can this be represented as high or low ASCII?
|
||||
asciiButton.IsEnabled = CommonUtil.TextUtil.IsHiLoAscii(mOperandValue);
|
||||
// Can this be represented as a character? We only allow the printable set
|
||||
// here, not the extended set (which includes control characters).
|
||||
if (mOperandValue == (byte) mOperandValue) {
|
||||
asciiButton.IsEnabled =
|
||||
CharEncoding.IsPrintableLowOrHighAscii((byte)mOperandValue);
|
||||
petsciiButton.IsEnabled =
|
||||
CharEncoding.IsPrintableC64Petscii((byte)mOperandValue);
|
||||
screenCodeButton.IsEnabled =
|
||||
CharEncoding.IsPrintableC64ScreenCode((byte)mOperandValue);
|
||||
} else {
|
||||
asciiButton.IsEnabled = petsciiButton.IsEnabled = screenCodeButton.IsEnabled =
|
||||
false;
|
||||
}
|
||||
|
||||
// Configure the dialog from the FormatDescriptor, if one is available.
|
||||
SetControlsFromDescriptor(FormatDescriptor);
|
||||
@ -475,9 +486,14 @@ namespace SourceGen.WpfGui {
|
||||
break;
|
||||
case FormatDescriptor.SubType.Ascii:
|
||||
case FormatDescriptor.SubType.HighAscii:
|
||||
// TODO(petscii): encoding
|
||||
asciiButton.IsChecked = true;
|
||||
break;
|
||||
case FormatDescriptor.SubType.C64Petscii:
|
||||
petsciiButton.IsChecked = true;
|
||||
break;
|
||||
case FormatDescriptor.SubType.C64Screen:
|
||||
screenCodeButton.IsChecked = true;
|
||||
break;
|
||||
case FormatDescriptor.SubType.Symbol:
|
||||
Debug.Assert(dfd.HasSymbol);
|
||||
symbolButton.IsChecked = true;
|
||||
@ -555,12 +571,15 @@ namespace SourceGen.WpfGui {
|
||||
} else if (binaryButton.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.Binary;
|
||||
} else if (asciiButton.IsChecked == true) {
|
||||
// TODO(petscii): encoding
|
||||
if (mOperandValue > 0x7f) {
|
||||
subType = FormatDescriptor.SubType.HighAscii;
|
||||
} else {
|
||||
subType = FormatDescriptor.SubType.Ascii;
|
||||
}
|
||||
} else if (petsciiButton.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.C64Petscii;
|
||||
} else if (screenCodeButton.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.C64Screen;
|
||||
} else if (symbolButton.IsChecked == true) {
|
||||
subType = FormatDescriptor.SubType.Symbol;
|
||||
} else {
|
||||
|
@ -38,11 +38,6 @@ limitations under the License.
|
||||
<system:String x:Key="str_65C02">WDC W65C02S</system:String>
|
||||
<system:String x:Key="str_65816">WDC W65C816S</system:String>
|
||||
|
||||
<system:String x:Key="str_LowAscii">Plain ASCII</system:String>
|
||||
<system:String x:Key="str_LowHighAscii">Plain or High ASCII</system:String>
|
||||
<system:String x:Key="str_C64Petscii">C64 PETSCII</system:String>
|
||||
<system:String x:Key="str_C64ScreenCode">C64 Screen Code</system:String>
|
||||
|
||||
<system:String x:Key="str_DisableStringScan">None (disabled)</system:String>
|
||||
|
||||
<system:String x:Key="str_AutoLabelSimple">Simple ("L1234")</system:String>
|
||||
|
@ -97,13 +97,13 @@ namespace SourceGen.WpfGui {
|
||||
new CpuItem((string)FindResource("str_65816"), CpuDef.CpuType.Cpu65816),
|
||||
};
|
||||
DefaultTextScanModeItems = new DefaultTextScanMode[] {
|
||||
new DefaultTextScanMode((string)FindResource("str_LowAscii"),
|
||||
new DefaultTextScanMode(Res.Strings.SCAN_LOW_ASCII,
|
||||
TextScanMode.LowAscii),
|
||||
new DefaultTextScanMode((string)FindResource("str_LowHighAscii"),
|
||||
new DefaultTextScanMode(Res.Strings.SCAN_LOW_HIGH_ASCII,
|
||||
TextScanMode.LowHighAscii),
|
||||
new DefaultTextScanMode((string)FindResource("str_C64Petscii"),
|
||||
new DefaultTextScanMode(Res.Strings.SCAN_C64_PETSCII,
|
||||
TextScanMode.C64Petscii),
|
||||
new DefaultTextScanMode((string)FindResource("str_C64ScreenCode"),
|
||||
new DefaultTextScanMode(Res.Strings.SCAN_C64_SCREEN_CODE,
|
||||
TextScanMode.C64ScreenCode),
|
||||
};
|
||||
MinCharsItems = new MinCharsItem[] {
|
||||
|
Loading…
Reference in New Issue
Block a user