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:
Andy McFadden 2019-08-15 17:53:12 -07:00
parent 176e1ad6af
commit 7bbe5692bd
18 changed files with 560 additions and 211 deletions

View File

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

View File

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

View File

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

View File

@ -810,7 +810,7 @@ namespace SourceGen {
#endif
}
#region Static analyzer methods
#region Static analyzer methods
/// <summary>
/// Checks for a repeated run of the same byte.
@ -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,16 +912,17 @@ namespace SourceGen {
stringLen = 0;
expectedHiBit = -1;
} else {
if (expectedHiBit == -1) {
// First byte in string, set hi/lo expectation.
expectedHiBit = val & 0x80;
} else if ((val & 0x80) != expectedHiBit) {
// Mixed ASCII or non-ASCII, fail.
return -1;
if (limitHiBit) {
if (expectedHiBit == -1) {
// First byte in string, set hi/lo expectation.
expectedHiBit = val & 0x80;
} else if ((val & 0x80) != expectedHiBit) {
// 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++;
@ -913,16 +934,18 @@ 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;
}
}
@ -961,16 +983,18 @@ 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;
}
}
@ -1104,7 +1131,7 @@ namespace SourceGen {
return stringCount;
}
#endregion // Static analyzers
#endregion // Static analyzers
}
}

View File

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

View File

@ -393,6 +393,7 @@ namespace SourceGen {
descr += " ???";
break;
}
return descr;
}
switch (FormatSubType) {

View File

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

View File

@ -318,11 +318,19 @@ namespace SourceGen {
CharEncoding.Convert charConv;
switch (dfd.FormatSubType) {
case FormatDescriptor.SubType.Ascii:
charConv = CharEncoding.ConvertAscii;
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
charConv = CharEncoding.ConvertLowAndHighAscii;
} else {
charConv = CharEncoding.ConvertAscii;
}
delDef = delSet.Get(CharEncoding.Encoding.Ascii);
break;
case FormatDescriptor.SubType.HighAscii:
charConv = CharEncoding.ConvertHighAscii;
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:
charConv = CharEncoding.ConvertC64ScreenCode;
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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,46 +360,110 @@ 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) {
int asciiCount;
DataAnalysis.CountAsciiBytes(mFileData, rng.Low, rng.High,
out int lowAscii, out int highAscii, out int nonAscii);
if (highAscii > lowAscii) {
asciiCount = highAscii;
nonAscii += lowAscii;
} else {
asciiCount = lowAscii;
nonAscii += highAscii;
}
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.CountHighLowBytes(mFileData, rng.Low, rng.High, charTest,
out int lowAscii, out int highAscii, out int nonAscii);
if (highAscii > lowAscii) {
asciiCount = highAscii;
nonAscii += lowAscii;
} else {
asciiCount = lowAscii;
nonAscii += highAscii;
}
if (asciiCount >= 2 && asciiCount >= nonAscii * 2) {
// Looks good
mixedAsciiOkCount += asciiCount;
mixedAsciiNotCount += nonAscii;
if (asciiCount >= 2 && asciiCount >= nonAscii * 2) {
// Looks good
mixedCharOkCount += asciiCount;
mixedCharNotCount += nonAscii;
} else {
// Fail
radioStringMixed.IsEnabled = false;
radioStringMixedReverse.IsEnabled = false;
mixedCharOkCount = mixedCharNotCount = -1;
}
} else {
// Fail
radioStringMixed.IsEnabled = false;
radioStringMixedReverse.IsEnabled = false;
mixedAsciiOkCount = mixedAsciiNotCount = -1;
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,69 +985,118 @@ 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;
for (cur = low; cur <= high; cur++) {
byte val = mFileData[cur];
if (CommonUtil.TextUtil.IsHiLoAscii(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);
// start a new string
stringStart = cur;
if (subType == FormatDescriptor.SubType.ASCII_GENERIC) {
int highBit = 0;
for (cur = low; cur <= high; cur++) {
byte val = mFileData[cur];
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
CreateGenericStringOrByte(stringStart, cur - stringStart,
type, subType);
// start a new string
stringStart = cur;
} else {
// still in string, keep going
}
} else {
// still in string, keep going
// wasn't in a string, start one
stringStart = cur;
}
highBit = val & 0x80;
} else {
// not ASCII
if (stringStart >= 0) {
// was in a string, output it
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 {
// wasn't in a string, start one
stringStart = cur;
// not character
if (stringStart >= 0) {
// was in a string, output it
CreateGenericStringOrByte(stringStart, cur - stringStart,
type, subType);
stringStart = -1;
}
// output as single byte
CreateByteFD(cur, FormatDescriptor.SubType.Hex);
}
highBit = val & 0x80;
} else {
// not ASCII
if (stringStart >= 0) {
// was in a string, output it
CreateStringOrByte(stringStart, cur - stringStart, subType);
stringStart = -1;
}
// output as single byte
CreateByteFD(cur, FormatDescriptor.SubType.Hex);
}
}
if (stringStart >= 0) {
// close out the string
CreateStringOrByte(stringStart, cur - stringStart, subType);
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;
}
}

View File

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

View File

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

View File

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

View File

@ -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[] {