diff --git a/Asm65/Number.cs b/Asm65/Number.cs
index 67adc84..4741516 100644
--- a/Asm65/Number.cs
+++ b/Asm65/Number.cs
@@ -58,6 +58,33 @@ namespace Asm65 {
return true;
}
+ ///
+ /// Parses a hexadecimal integer. Leading '$' or "0x" will be ignored.
+ ///
+ /// Trim whitespace before calling here.
+ ///
+ /// String to parse.
+ /// Integer value of string.
+ /// True if the parsing was successful.
+ public static bool TryParseIntHex(string str, out int val) {
+ if (str.Length == 0) {
+ val = 0;
+ return false;
+ }
+ if (str[0] == '$') {
+ str = str.Substring(1);
+ }
+ // leading "0x" will be handled by Convert
+
+ try {
+ val = Convert.ToInt32(str, 16);
+ } catch (Exception) {
+ val = 0;
+ return false;
+ }
+ return true;
+ }
+
///
/// Parses a long integer in a variety of formats (hex, decimal, binary). We allow
/// hex to be identified with a leading '$' as well as "0x".
diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs
index 08cbeca..d2b2a4a 100644
--- a/SourceGen/CodeAnalysis.cs
+++ b/SourceGen/CodeAnalysis.cs
@@ -1224,16 +1224,73 @@ namespace SourceGen {
///
/// Data Bank Register value.
///
- ///
- /// This is primarily a value from $00-ff, but we also want to encode the B=K special
- /// mode.
- ///
- public enum DbrValue : short {
- // $00-ff is bank number
- Unknown = -1, // unknown / do-nothing
- ProgramBankReg = -2 // set B=K
+ public class DbrValue {
+ public const short UNKNOWN = -1;
+ public const short USE_PBR = -2;
+
+ ///
+ /// If true, ignore Bank, use Program Bank Register instead.
+ ///
+ public bool FollowPbr;
+
+ ///
+ /// Bank number (0-255).
+ ///
+ public byte Bank { get; private set; }
+
+ public enum Source { Unknown = 0, User, Auto, Smart };
+ ///
+ /// From whence this value originates.
+ ///
+ public Source ValueSource { get; private set; }
+
+ ///
+ /// Representation of the object state as a short integer. 0-255 specifies the
+ /// bank, while negative values are used for special conditions.
+ ///
+ public short AsShort {
+ get {
+ if (FollowPbr) {
+ return USE_PBR;
+ } else {
+ return Bank;
+ }
+ }
+ }
+
+ public DbrValue(bool followPbr, byte bank, Source source) {
+ FollowPbr = followPbr;
+ Bank = bank;
+ ValueSource = source;
+ }
+
+ public override string ToString() {
+ return "DBR:" + (FollowPbr ? "K" : "$" + Bank.ToString("x2"));
+ }
+
+ public static bool operator ==(DbrValue a, DbrValue b) {
+ if (ReferenceEquals(a, b)) {
+ return true; // same object, or both null
+ }
+ if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
+ return false; // one is null
+ }
+ // All fields must be equal.
+ return a.Bank == b.Bank && a.FollowPbr == b.FollowPbr &&
+ a.ValueSource == b.ValueSource;
+ }
+ public static bool operator !=(DbrValue a, DbrValue b) {
+ return !(a == b);
+ }
+ public override bool Equals(object obj) {
+ return obj is Symbol && this == (DbrValue)obj;
+ }
+ public override int GetHashCode() {
+ return Bank + (FollowPbr ? 0x100 : 0);
+ }
}
+
///
/// Determines the value of the Data Bank Register (DBR, register 'B') for relevant
/// instructions, and updates the Anattrib OperandOffset value.
@@ -1244,21 +1301,21 @@ namespace SourceGen {
short[] bval = new short[mAnattribs.Length];
// Initialize all entries to "unknown".
- Misc.Memset(bval, (short)DbrValue.Unknown);
+ Misc.Memset(bval, DbrValue.UNKNOWN);
// Set B=K every time we cross an address boundary and the program bank changes.
- DbrValue prevBank = DbrValue.Unknown;
+ short prevBank = DbrValue.UNKNOWN;
foreach (AddressMap.AddressMapEntry ent in mAddrMap) {
- int mapBank = ent.Addr >> 16;
- if (mapBank != (int)prevBank) {
- bval[ent.Offset] = (short)mapBank;
- prevBank = (DbrValue)mapBank;
+ short mapBank = (short)(ent.Addr >> 16);
+ if (mapBank != prevBank) {
+ bval[ent.Offset] = mapBank;
+ prevBank = mapBank;
}
}
// Apply the user-specified values.
foreach (KeyValuePair kvp in userValues) {
- bval[kvp.Key] = (short)kvp.Value;
+ bval[kvp.Key] = kvp.Value.AsShort;
}
// Run through the file, looking for PHK/PLB pairs. When we find one, set an
@@ -1270,19 +1327,19 @@ namespace SourceGen {
// Run through file, updating instructions as needed.
- short curVal = (short)DbrValue.Unknown;
+ short curVal = DbrValue.UNKNOWN;
for (int offset = 0; offset < mAnattribs.Length; offset++) {
- if (bval[offset] != (short)DbrValue.Unknown) {
+ if (bval[offset] != DbrValue.UNKNOWN) {
curVal = bval[offset];
}
if (!mAnattribs[offset].UsesDataBankReg) {
continue;
}
Debug.Assert(mAnattribs[offset].IsInstructionStart);
- Debug.Assert(curVal != (short)DbrValue.Unknown);
+ Debug.Assert(curVal != DbrValue.UNKNOWN);
int bank;
- if (curVal == (short)DbrValue.ProgramBankReg) {
+ if (curVal == DbrValue.USE_PBR) {
bank = mAnattribs[offset].Address >> 16;
} else {
Debug.Assert(curVal >= 0 && curVal < 256);
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index f618a59..5daee47 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -2120,15 +2120,13 @@ namespace SourceGen {
break;
case UndoableChange.ChangeType.SetDataBank: {
// If there's no entry, treat it as an entry with value = Unknown.
- if (!DbrValues.TryGetValue(offset, out CodeAnalysis.DbrValue current)) {
- current = CodeAnalysis.DbrValue.Unknown;
- }
+ DbrValues.TryGetValue(offset, out CodeAnalysis.DbrValue current);
if (current != (CodeAnalysis.DbrValue)oldValue) {
Debug.WriteLine("GLITCH: old DBR value mismatch (" +
current + " vs " + oldValue + ")");
Debug.Assert(false);
}
- if ((CodeAnalysis.DbrValue)newValue == CodeAnalysis.DbrValue.Unknown) {
+ if (newValue == null) {
DbrValues.Remove(offset);
} else {
DbrValues[offset] = (CodeAnalysis.DbrValue)newValue;
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index f315839..62f3a0f 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -1873,19 +1873,16 @@ namespace SourceGen {
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
int offset = CodeLineList[selIndex].FileOffset;
- CodeAnalysis.DbrValue curValue;
- if (!mProject.DbrValues.TryGetValue(offset, out curValue)) {
- curValue = CodeAnalysis.DbrValue.Unknown;
- }
+ // Get current user-specified value, or null.
+ mProject.DbrValues.TryGetValue(offset, out CodeAnalysis.DbrValue curValue);
- EditDataBank dlg = new EditDataBank(mMainWin, mProject.AddrMap, mFormatter, curValue);
+ EditDataBank dlg = new EditDataBank(mMainWin, mProject, mFormatter, curValue);
if (dlg.ShowDialog() != true) {
return;
}
if (dlg.Result != curValue) {
- Debug.WriteLine("Changing DBR at +" + offset.ToString("x6") + " to $" +
- ((int)(dlg.Result)).ToString("x2"));
+ Debug.WriteLine("Changing DBR at +" + offset.ToString("x6") + " to $" + dlg.Result);
UndoableChange uc =
UndoableChange.CreateDataBankChange(offset, curValue, dlg.Result);
ChangeSet cs = new ChangeSet(uc);
diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs
index 4438e03..c8634c6 100644
--- a/SourceGen/ProjectFile.cs
+++ b/SourceGen/ProjectFile.cs
@@ -401,6 +401,18 @@ namespace SourceGen {
}
}
}
+ public class SerDbrValue {
+ // Skip the ValueSource property; should always be User in project file.
+ public bool FollowPbr;
+ public byte Bank;
+
+ public SerDbrValue() { }
+ public SerDbrValue(CodeAnalysis.DbrValue dbrValue) {
+ FollowPbr = dbrValue.FollowPbr;
+ Bank = dbrValue.Bank;
+ Debug.Assert(dbrValue.ValueSource == CodeAnalysis.DbrValue.Source.User);
+ }
+ }
// Fields are serialized to/from JSON. DO NOT change the field names.
public int _ContentVersion { get; set; }
@@ -420,6 +432,7 @@ namespace SourceGen {
public List VisualizationAnimations { get; set; }
public Dictionary VisualizationSets { get; set; }
public Dictionary RelocList { get; set; }
+ public Dictionary DbrValues { get; set; }
///
/// Serializes a DisasmProject into an augmented JSON string.
@@ -534,6 +547,13 @@ namespace SourceGen {
spf.RelocList.Add(kvp.Key.ToString(), kvp.Value);
}
+ // We could output the value as a short, using DbrValue.AsShort, but it doesn't
+ // save much space and could make life harder down the road.
+ spf.DbrValues = new Dictionary(proj.DbrValues.Count);
+ foreach (KeyValuePair kvp in proj.DbrValues) {
+ spf.DbrValues.Add(kvp.Key.ToString(), new SerDbrValue(kvp.Value));
+ }
+
JavaScriptSerializer ser = new JavaScriptSerializer();
string cereal = ser.Serialize(spf);
sb.Append(cereal);
@@ -854,6 +874,19 @@ namespace SourceGen {
}
}
+ // Deserialize data bank register values. This was added in v1.7.
+ if (spf.DbrValues != null) {
+ foreach (KeyValuePair kvp in spf.DbrValues) {
+ if (!ParseValidateKey(kvp.Key, spf.FileDataLength,
+ Res.Strings.PROJECT_FIELD_DBR_VALUE, report, out int intKey)) {
+ continue;
+ }
+ CodeAnalysis.DbrValue newDbr = new CodeAnalysis.DbrValue(kvp.Value.FollowPbr,
+ kvp.Value.Bank, CodeAnalysis.DbrValue.Source.User);
+ proj.DbrValues.Add(intKey, newDbr);
+ }
+ }
+
return true;
}
diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml
index ffddd09..fbd2e03 100644
--- a/SourceGen/Res/Strings.xaml
+++ b/SourceGen/Res/Strings.xaml
@@ -152,6 +152,7 @@ limitations under the License.
Executing assembler...
Generating {0}...
comment
+ DBR value
long comment
local variable table
note
diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs
index 3b3317c..f7d16e1 100644
--- a/SourceGen/Res/Strings.xaml.cs
+++ b/SourceGen/Res/Strings.xaml.cs
@@ -285,6 +285,8 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_ProgressGeneratingFmt");
public static string PROJECT_FIELD_COMMENT =
(string)Application.Current.FindResource("str_ProjectFieldComment");
+ public static string PROJECT_FIELD_DBR_VALUE =
+ (string)Application.Current.FindResource("str_ProjectFieldDbrValue");
public static string PROJECT_FIELD_LONG_COMMENT =
(string)Application.Current.FindResource("str_ProjectFieldLongComment");
public static string PROJECT_FIELD_LV_TABLE =
diff --git a/SourceGen/WpfGui/EditDataBank.xaml b/SourceGen/WpfGui/EditDataBank.xaml
index e1ec974..daa6b76 100644
--- a/SourceGen/WpfGui/EditDataBank.xaml
+++ b/SourceGen/WpfGui/EditDataBank.xaml
@@ -20,12 +20,17 @@ limitations under the License.
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGen.WpfGui"
+ xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="Set Data Bank"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
ContentRendered="Window_ContentRendered">
+
+ (other)
+
+
@@ -33,25 +38,25 @@ limitations under the License.
+
-
-
+
-
+
-
-
-
-
+
+
-
+
diff --git a/SourceGen/WpfGui/EditDataBank.xaml.cs b/SourceGen/WpfGui/EditDataBank.xaml.cs
index 255b6c8..eb664eb 100644
--- a/SourceGen/WpfGui/EditDataBank.xaml.cs
+++ b/SourceGen/WpfGui/EditDataBank.xaml.cs
@@ -19,7 +19,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;
-
+using System.Windows.Controls;
using Asm65;
using CommonUtil;
@@ -38,13 +38,14 @@ namespace SourceGen.WpfGui {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
- private AddressMap mAddrMap;
+ private DisasmProject mProject;
private Formatter mFormatter;
+ private bool mSettingComboBox;
private string mDataBankStr;
public string DataBankStr {
get { return mDataBankStr; }
- set { mDataBankStr = value; OnPropertyChanged(); }
+ set { mDataBankStr = value; OnPropertyChanged(); UpdateControls(); }
}
private bool mIsValid;
@@ -53,50 +54,64 @@ namespace SourceGen.WpfGui {
set { mIsValid = value; OnPropertyChanged(); }
}
- public class BankLabel {
- public CodeAnalysis.DbrValue Bank { get; private set; }
+ public class BankLabelItem : IComparable {
+ //public CodeAnalysis.DbrValue Bank { get; private set; }
+ public byte Bank { get; private set; }
public string Label { get; private set; }
- public BankLabel(CodeAnalysis.DbrValue bank, string label) {
+ public BankLabelItem(byte bank, string label) {
Bank = bank;
Label = label;
}
+
+ public int CompareTo(BankLabelItem other) {
+ return Bank - other.Bank;
+ }
}
- public List BankLabels { get; private set; } = new List();
+ public List BankLabels { get; private set; } = new List();
///
/// Constructor.
///
/// Parent window.
- public EditDataBank(Window owner, AddressMap addrMap, Formatter formatter,
+ /// Disassembly project.
+ /// Text formatter.
+ /// Current value, or null if none set.
+ public EditDataBank(Window owner, DisasmProject proj, Formatter formatter,
CodeAnalysis.DbrValue curValue) {
InitializeComponent();
Owner = owner;
DataContext = this;
- mAddrMap = addrMap;
+ mProject = proj;
mFormatter = formatter;
- if (curValue == CodeAnalysis.DbrValue.ProgramBankReg) {
- DataBankStr = PROG_BANK_STR;
- } else if (curValue == CodeAnalysis.DbrValue.Unknown) {
- DataBankStr = string.Empty;
- } else if ((int)curValue >= 0 && (int)curValue <= 255) {
- // Format as address rather than hexvalue so we don't get leading '$'.
- DataBankStr = formatter.FormatAddress((int)curValue, false);
- } else {
- Debug.Assert(false, "invalid DBR value " + curValue);
- DataBankStr = string.Empty;
+ PopulateComboBox();
+ DataBankStr = DbrValueToString(curValue); // sets combo box
+ IsValid = true;
+ }
+
+ private void PopulateComboBox() {
+ // Entry #0 is always the "other" option.
+ string otherStr = (string)FindResource("str_OtherBank");
+ BankLabels.Add(new BankLabelItem(0, otherStr));
+
+ bool[] done = new bool[256];
+ foreach (AddressMap.AddressMapEntry ent in mProject.AddrMap) {
+ byte bank = (byte)(ent.Addr >> 16);
+ if (done[bank]) {
+ continue;
+ }
+ done[bank] = true;
+
+ Anattrib attr = mProject.GetAnattrib(ent.Offset);
+ string label = (attr.Symbol != null) ? attr.Symbol.Label : string.Empty;
+ BankLabels.Add(new BankLabelItem(bank,
+ mFormatter.FormatHexValue(bank, 2) + " " + label));
}
- // TODO: combo box
- BankLabels.Add(new BankLabel((CodeAnalysis.DbrValue)1, "(other)"));
- BankLabels.Add(new BankLabel((CodeAnalysis.DbrValue)1, "$02 FirstBankLabel"));
- BankLabels.Add(new BankLabel((CodeAnalysis.DbrValue)1, "$88 FancyBank"));
- bankCombo.SelectedIndex = 0;
-
- IsValid = true; // TODO: validate
+ BankLabels.Sort();
}
private void Window_ContentRendered(object sender, EventArgs e) {
@@ -105,31 +120,86 @@ namespace SourceGen.WpfGui {
}
private void OkButton_Click(object sender, RoutedEventArgs e) {
- Result = GetValue(DataBankStr);
+ Result = StringToDbrValue(DataBankStr);
DialogResult = true;
}
+ private void UpdateControls() {
+ CodeAnalysis.DbrValue dbrVal = StringToDbrValue(DataBankStr);
+ IsValid = (string.IsNullOrEmpty(DataBankStr) || dbrVal != null);
+ SetComboBoxSelection(dbrVal);
+ }
+
+ ///
+ /// Sets the selected item in the combo box based on the value in the text edit box.
+ ///
+ private void SetComboBoxSelection(CodeAnalysis.DbrValue dbrVal) {
+ mSettingComboBox = true; // recursion guard
+
+ //CodeAnalysis.DbrValue dbrVal = StringToDbrValue(DataBankStr);
+ int index = 0;
+ if (dbrVal != null && !dbrVal.FollowPbr) {
+ // skip first entry
+ for (int i = 1; i < BankLabels.Count; i++) {
+ if (BankLabels[i].Bank == dbrVal.Bank) {
+ index = i;
+ break;
+ }
+ }
+ }
+ bankCombo.SelectedIndex = index;
+
+ mSettingComboBox = false;
+ }
+
+ ///
+ /// Reacts to a combo box selection change.
+ ///
+ private void bankCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) {
+ if (mSettingComboBox) {
+ // Selection changed programatically, don't update text edit box.
+ return;
+ }
+ if (bankCombo.SelectedIndex == 0) {
+ // "other", don't update text edit box
+ return;
+ }
+ BankLabelItem item = (BankLabelItem)bankCombo.SelectedItem;
+
+ DataBankStr = mFormatter.FormatHexValue(item.Bank, 2);
+ }
+
///
/// Converts a DBR value string to a value.
///
/// String to convert.
- /// DBR value.
- private static CodeAnalysis.DbrValue GetValue(string valueStr) {
- if (valueStr == PROG_BANK_STR) {
- return CodeAnalysis.DbrValue.ProgramBankReg;
+ /// DBR value, or null if invalid.
+ private static CodeAnalysis.DbrValue StringToDbrValue(string valueStr) {
+ valueStr = valueStr.Trim();
+ if (valueStr.Equals(PROG_BANK_STR, StringComparison.InvariantCultureIgnoreCase)) {
+ return new CodeAnalysis.DbrValue(true, 0, CodeAnalysis.DbrValue.Source.User);
} else {
- // Try to parse as 1- or 2-digit hex value.
- try {
- int val = Convert.ToInt32(valueStr, 16);
- if (val < 0 || val > 255) {
- // invalid value
- return CodeAnalysis.DbrValue.Unknown;
- }
- return (CodeAnalysis.DbrValue)val;
- } catch (Exception ex) {
- Debug.WriteLine("Result parse failed: " + ex.Message);
- return CodeAnalysis.DbrValue.Unknown;
+ if (!Number.TryParseIntHex(valueStr, out int val)) {
+ Debug.WriteLine("Unable to parse '" + valueStr + "' as hex value");
+ return null;
}
+ if (val != (byte)val) {
+ Debug.WriteLine("Val " + val + " out of range of byte");
+ return null;
+ }
+
+ return new CodeAnalysis.DbrValue(false, (byte)val,
+ CodeAnalysis.DbrValue.Source.User);
+ }
+ }
+
+ private string DbrValueToString(CodeAnalysis.DbrValue value) {
+ if (value == null) {
+ return string.Empty;
+ } else if (value.FollowPbr) {
+ return PROG_BANK_STR;
+ } else {
+ return mFormatter.FormatHexValue(value.Bank, 2);
}
}
}