2019-07-08 22:40:30 +00:00
|
|
|
|
/*
|
|
|
|
|
* Copyright 2019 faddenSoft
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
using System;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Controls;
|
2019-07-09 00:02:25 +00:00
|
|
|
|
|
2019-07-08 22:40:30 +00:00
|
|
|
|
using Asm65;
|
|
|
|
|
|
2019-07-20 20:28:10 +00:00
|
|
|
|
namespace SourceGen.WpfGui {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Instruction operand editor.
|
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public partial class EditInstructionOperand : Window, INotifyPropertyChanged {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Updated format descriptor. Will be null if the user selected "default".
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public FormatDescriptor FormatDescriptorResult { get; private set; }
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private readonly string SYMBOL_NOT_USED;
|
|
|
|
|
private readonly string SYMBOL_UNKNOWN;
|
|
|
|
|
private readonly string SYMBOL_INVALID;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Project reference.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private DisasmProject mProject;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Offset of instruction being edited.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private int mOffset;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Format object.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private Formatter mFormatter;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Operation definition, from file data.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private OpDef mOpDef;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Status flags at the point where the instruction is defined. This tells us whether
|
|
|
|
|
/// an operand is 8-bit or 16-bit.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private StatusFlags mOpStatusFlags;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Operand value, extracted from file data. For a relative branch, this will be
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// an address instead.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
private int mOperandValue;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// True when the input is valid. Controls whether the OK button is enabled.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public bool IsValid {
|
|
|
|
|
get { return mIsValid; }
|
|
|
|
|
set { mIsValid = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mIsValid;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Set when our load-time initialization is complete.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private bool mLoadDone;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
// INotifyPropertyChanged implementation
|
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
|
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
|
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
|
|
|
}
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Constructor.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// <param name="owner">Parent window.</param>
|
|
|
|
|
/// <param name="project">Project reference.</param>
|
|
|
|
|
/// <param name="offset">File offset of instruction start.</param>
|
|
|
|
|
/// <param name="formatter">Formatter object, for preview window.</param>
|
|
|
|
|
public EditInstructionOperand(Window owner, DisasmProject project, int offset,
|
|
|
|
|
Formatter formatter) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
InitializeComponent();
|
|
|
|
|
Owner = owner;
|
|
|
|
|
DataContext = this;
|
|
|
|
|
|
|
|
|
|
mProject = project;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
mOffset = offset;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
mFormatter = formatter;
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
SYMBOL_NOT_USED = (string)FindResource("str_SymbolNotUsed");
|
|
|
|
|
SYMBOL_INVALID = (string)FindResource("str_SymbolNotValid");
|
|
|
|
|
SYMBOL_UNKNOWN = (string)FindResource("str_SymbolUnknown");
|
|
|
|
|
|
|
|
|
|
Debug.Assert(offset >= 0 && offset < project.FileDataLength);
|
|
|
|
|
mOpDef = project.CpuDef.GetOpDef(project.FileData[offset]);
|
|
|
|
|
Anattrib attr = project.GetAnattrib(offset);
|
|
|
|
|
mOpStatusFlags = attr.StatusFlags;
|
|
|
|
|
Debug.Assert(offset + mOpDef.GetLength(mOpStatusFlags) <= project.FileDataLength);
|
|
|
|
|
|
|
|
|
|
if (attr.OperandAddress >= 0) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
// Use this as the operand value when available. This lets us present
|
|
|
|
|
// relative branch instructions in the expected form.
|
2019-09-07 20:39:22 +00:00
|
|
|
|
mOperandValue = attr.OperandAddress;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
} else {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
// For BlockMove this will have both parts.
|
|
|
|
|
mOperandValue = mOpDef.GetOperand(project.FileData, offset, attr.StatusFlags);
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
BasicFormat_Loaded();
|
|
|
|
|
mLoadDone = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Window_ContentRendered(object sender, EventArgs e) {
|
|
|
|
|
UpdateControls();
|
2019-09-07 20:39:22 +00:00
|
|
|
|
symbolTextBox.SelectAll();
|
|
|
|
|
symbolTextBox.Focus();
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatDescriptorResult = CreateDescriptorFromControls();
|
2019-07-08 22:40:30 +00:00
|
|
|
|
DialogResult = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Updates the state of the UI controls as the user interacts with the dialog.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
private void UpdateControls() {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (!mLoadDone) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
// Parts panel IsEnabled depends directly on formatSymbolButton.IsChecked.
|
|
|
|
|
IsValid = true;
|
|
|
|
|
IsSymbolAuto = false;
|
|
|
|
|
SymbolValueDecimal = string.Empty;
|
|
|
|
|
if (FormatSymbol) {
|
|
|
|
|
if (!Asm65.Label.ValidateLabel(SymbolLabel)) {
|
|
|
|
|
SymbolValueHex = SYMBOL_INVALID;
|
|
|
|
|
IsValid = false;
|
|
|
|
|
} else if (mProject.SymbolTable.TryGetValue(SymbolLabel, out Symbol sym)) {
|
|
|
|
|
if (sym.SymbolSource == Symbol.Source.Auto) {
|
|
|
|
|
// We try to block references to auto labels, but it's possible to get
|
|
|
|
|
// around it because FormatDescriptors are weak references (replace auto
|
|
|
|
|
// label with user label, reference non-existent auto label, remove user
|
|
|
|
|
// label). We could try harder, but currently not necessary.
|
|
|
|
|
IsValid = false;
|
|
|
|
|
IsSymbolAuto = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SymbolValueHex = mFormatter.FormatHexValue(sym.Value, 4);
|
|
|
|
|
SymbolValueDecimal = mFormatter.FormatDecimalValue(sym.Value);
|
|
|
|
|
} else {
|
|
|
|
|
// Valid but unknown symbol. This is fine -- symbols don't have to exist.
|
|
|
|
|
SymbolValueHex = SYMBOL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
SymbolValueHex = SYMBOL_NOT_USED;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
|
|
|
|
|
UpdatePreview();
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Updates the contents of the preview text box.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private void UpdatePreview() {
|
|
|
|
|
// Generate a descriptor from the controls. This isn't strictly necessary, but it
|
|
|
|
|
// gets all of the data in one small package.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
FormatDescriptor dfd = CreateDescriptorFromControls();
|
2019-09-07 20:39:22 +00:00
|
|
|
|
|
2019-07-08 22:40:30 +00:00
|
|
|
|
if (dfd == null) {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
// Showing the right thing for the default format is surprisingly hard. There
|
|
|
|
|
// are a bunch of complicated steps that are performed in sequence, including
|
|
|
|
|
// the "nearby label" lookups, the elision of hidden symbols, and other
|
|
|
|
|
// obscure bits that may get tweaked from time to time. These things are not
|
|
|
|
|
// easy to factor out because we're slicing the data at a different angle: the
|
|
|
|
|
// initial pass walks the entire file looking for one thing at a point before
|
|
|
|
|
// analysis has completed, while here we're trying to mimic all of the
|
|
|
|
|
// steps for a single offset, after analysis has finished. It's a lot of work
|
|
|
|
|
// to show text that they'll see as soon as they hit "OK".
|
|
|
|
|
PreviewText = string.Empty;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
StringBuilder sb = new StringBuilder(16);
|
|
|
|
|
|
|
|
|
|
// Show the opcode. Don't bother trying to figure out width disambiguation here.
|
|
|
|
|
sb.Append(mFormatter.FormatOpcode(mOpDef, OpDef.WidthDisambiguation.None));
|
|
|
|
|
sb.Append(' ');
|
|
|
|
|
|
|
|
|
|
bool showHashPrefix = mOpDef.IsImmediate ||
|
|
|
|
|
mOpDef.AddrMode == OpDef.AddressMode.BlockMove;
|
|
|
|
|
if (showHashPrefix) {
|
|
|
|
|
sb.Append('#');
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
Anattrib attr = mProject.GetAnattrib(mOffset);
|
|
|
|
|
int previewHexDigits = (attr.Length - 1) * 2;
|
|
|
|
|
int operandValue = mOperandValue;
|
|
|
|
|
bool isPcRelative = false;
|
|
|
|
|
bool isBlockMove = false;
|
|
|
|
|
if (attr.OperandAddress >= 0) {
|
|
|
|
|
if (mOpDef.AddrMode == OpDef.AddressMode.PCRel) {
|
|
|
|
|
previewHexDigits = 4; // show branches as $xxxx even when on zero page
|
|
|
|
|
isPcRelative = true;
|
|
|
|
|
} else if (mOpDef.AddrMode == OpDef.AddressMode.PCRelLong ||
|
|
|
|
|
mOpDef.AddrMode == OpDef.AddressMode.StackPCRelLong) {
|
|
|
|
|
isPcRelative = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (mOpDef.AddrMode == OpDef.AddressMode.BlockMove) {
|
|
|
|
|
// MVN and MVP screw things up by having two operands in one instruction.
|
|
|
|
|
// We deal with this by passing in the value from the second byte
|
|
|
|
|
// (source bank) as the value, and applying the chosen format to both bytes.
|
|
|
|
|
isBlockMove = true;
|
|
|
|
|
operandValue = mOperandValue >> 8;
|
|
|
|
|
previewHexDigits = 2;
|
|
|
|
|
}
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (dfd.FormatSubType) {
|
|
|
|
|
case FormatDescriptor.SubType.Hex:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
sb.Append(mFormatter.FormatHexValue(operandValue, previewHexDigits));
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.Decimal:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
sb.Append(mFormatter.FormatDecimalValue(operandValue));
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.Binary:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
sb.Append(mFormatter.FormatBinaryValue(operandValue, 8));
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
2019-08-12 00:59:20 +00:00
|
|
|
|
case FormatDescriptor.SubType.Ascii:
|
2019-08-10 21:24:19 +00:00
|
|
|
|
case FormatDescriptor.SubType.HighAscii:
|
2019-08-12 00:59:20 +00:00
|
|
|
|
case FormatDescriptor.SubType.C64Petscii:
|
|
|
|
|
case FormatDescriptor.SubType.C64Screen:
|
|
|
|
|
CharEncoding.Encoding enc = PseudoOp.SubTypeToEnc(dfd.FormatSubType);
|
2019-09-07 20:39:22 +00:00
|
|
|
|
sb.Append(mFormatter.FormatCharacterValue(operandValue, enc));
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.Symbol:
|
|
|
|
|
if (mProject.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
// Block move is a little weird. "MVN label1,label2" is supposed to use
|
|
|
|
|
// the bank byte, while "MVN #const1,#const2" uses the entire symbol.
|
|
|
|
|
// The easiest thing to do is require the user to specify the "bank"
|
|
|
|
|
// part for 24-bit symbols, and always generate this as an immediate.
|
|
|
|
|
//
|
|
|
|
|
// MVN/MVP are also the only instructions with two operands, something
|
|
|
|
|
// we don't really handle.
|
|
|
|
|
// TODO(someday): allow a different symbol for each part of the operand.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
// Hack to make relative branches look right in the preview window.
|
|
|
|
|
// Otherwise they show up like "<LABEL" because they appear to be
|
|
|
|
|
// only 8 bits.
|
|
|
|
|
int operandLen = dfd.Length - 1;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (operandLen == 1 && isPcRelative) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
operandLen = 2;
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
|
|
|
|
|
// Set the operand length to 1 for block move so we use the part
|
|
|
|
|
// operators (<, >, ^) rather than bit-shifting.
|
|
|
|
|
if (isBlockMove) {
|
|
|
|
|
operandLen = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 22:40:30 +00:00
|
|
|
|
PseudoOp.FormatNumericOpFlags flags;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (isPcRelative) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
flags = PseudoOp.FormatNumericOpFlags.IsPcRel;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (showHashPrefix) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
flags = PseudoOp.FormatNumericOpFlags.HasHashPrefix;
|
|
|
|
|
} else {
|
|
|
|
|
flags = PseudoOp.FormatNumericOpFlags.None;
|
|
|
|
|
}
|
|
|
|
|
string str = PseudoOp.FormatNumericOperand(mFormatter,
|
|
|
|
|
mProject.SymbolTable, null, dfd,
|
2019-09-07 20:39:22 +00:00
|
|
|
|
operandValue, operandLen, flags);
|
|
|
|
|
sb.Append(str);
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
if (sym.SymbolSource == Symbol.Source.Auto) {
|
|
|
|
|
mIsSymbolAuto = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
sb.Append(dfd.SymbolRef.Label + " (?)");
|
2019-07-08 22:40:30 +00:00
|
|
|
|
Debug.Assert(!string.IsNullOrEmpty(dfd.SymbolRef.Label));
|
|
|
|
|
//symbolValueLabel.Text = Properties.Resources.MSG_SYMBOL_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false);
|
2019-09-07 20:39:22 +00:00
|
|
|
|
sb.Append("BUG");
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
|
|
|
|
|
if (isBlockMove) {
|
|
|
|
|
sb.Append(",#<dest>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PreviewText = sb.ToString();
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
#region Basic Format
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public bool FormatDefault {
|
|
|
|
|
get { return mFormatDefault; }
|
|
|
|
|
set { mFormatDefault = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatDefault;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public bool FormatHex {
|
|
|
|
|
get { return mFormatHex; }
|
|
|
|
|
set { mFormatHex = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatHex;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public bool FormatDecimal {
|
|
|
|
|
get { return mFormatDecimal; }
|
|
|
|
|
set { mFormatDecimal = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatDecimal;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
public bool FormatBinary {
|
|
|
|
|
get { return mFormatBinary; }
|
|
|
|
|
set { mFormatBinary = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatBinary;
|
|
|
|
|
|
|
|
|
|
public bool IsFormatAsciiAllowed {
|
|
|
|
|
get { return mIsFormatAsciiAllowed; }
|
|
|
|
|
set { mIsFormatAsciiAllowed = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mIsFormatAsciiAllowed;
|
|
|
|
|
|
|
|
|
|
public bool FormatAscii {
|
|
|
|
|
get { return mFormatAscii; }
|
|
|
|
|
set { mFormatAscii = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatAscii;
|
|
|
|
|
|
|
|
|
|
public bool IsFormatPetsciiAllowed {
|
|
|
|
|
get { return mIsFormatPetsciiAllowed; }
|
|
|
|
|
set { mIsFormatPetsciiAllowed = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mIsFormatPetsciiAllowed;
|
|
|
|
|
|
|
|
|
|
public bool FormatPetscii {
|
|
|
|
|
get { return mFormatPetscii; }
|
|
|
|
|
set { mFormatPetscii = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatPetscii;
|
|
|
|
|
|
|
|
|
|
public bool IsFormatScreenCodeAllowed {
|
|
|
|
|
get { return mIsFormatScreenCodeAllowed; }
|
|
|
|
|
set { mIsFormatScreenCodeAllowed = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mIsFormatScreenCodeAllowed;
|
|
|
|
|
|
|
|
|
|
public bool FormatScreenCode {
|
|
|
|
|
get { return mFormatScreenCode; }
|
|
|
|
|
set { mFormatScreenCode = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatScreenCode;
|
|
|
|
|
|
|
|
|
|
public bool FormatSymbol {
|
|
|
|
|
get { return mFormatSymbol; }
|
|
|
|
|
set { mFormatSymbol = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatSymbol;
|
|
|
|
|
|
|
|
|
|
public string SymbolLabel {
|
|
|
|
|
get { return mSymbolLabel; }
|
|
|
|
|
set {
|
|
|
|
|
mSymbolLabel = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
// Set the radio button when the user starts typing.
|
|
|
|
|
if (mLoadDone) {
|
|
|
|
|
FormatSymbol = true;
|
|
|
|
|
}
|
|
|
|
|
UpdateControls();
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private string mSymbolLabel;
|
|
|
|
|
|
|
|
|
|
public bool IsSymbolAuto {
|
|
|
|
|
get { return mIsSymbolAuto; }
|
|
|
|
|
set { mIsSymbolAuto = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mIsSymbolAuto;
|
|
|
|
|
|
|
|
|
|
public string SymbolValueHex {
|
|
|
|
|
get { return mSymbolValueHex; }
|
|
|
|
|
set { mSymbolValueHex = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private string mSymbolValueHex;
|
|
|
|
|
|
|
|
|
|
public string SymbolValueDecimal {
|
|
|
|
|
get { return mSymbolValueDecimal; }
|
|
|
|
|
set { mSymbolValueDecimal = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private string mSymbolValueDecimal;
|
|
|
|
|
|
|
|
|
|
public bool FormatPartLow {
|
|
|
|
|
get { return mFormatPartLow; }
|
|
|
|
|
set { mFormatPartLow = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatPartLow;
|
|
|
|
|
|
|
|
|
|
public bool FormatPartHigh {
|
|
|
|
|
get { return mFormatPartHigh; }
|
|
|
|
|
set { mFormatPartHigh = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatPartHigh;
|
|
|
|
|
|
|
|
|
|
public bool FormatPartBank {
|
|
|
|
|
get { return mFormatPartBank; }
|
|
|
|
|
set { mFormatPartBank = value; OnPropertyChanged(); UpdateControls(); }
|
|
|
|
|
}
|
|
|
|
|
private bool mFormatPartBank;
|
|
|
|
|
|
|
|
|
|
public string PreviewText {
|
|
|
|
|
get { return mPreviewText; }
|
|
|
|
|
set { mPreviewText = value; OnPropertyChanged(); }
|
|
|
|
|
}
|
|
|
|
|
private string mPreviewText;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// Configures the basic formatting options, based on the existing format descriptor.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
private void BasicFormat_Loaded() {
|
|
|
|
|
// 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) {
|
|
|
|
|
IsFormatAsciiAllowed =
|
|
|
|
|
CharEncoding.IsPrintableLowOrHighAscii((byte)mOperandValue);
|
|
|
|
|
IsFormatPetsciiAllowed =
|
|
|
|
|
CharEncoding.IsPrintableC64Petscii((byte)mOperandValue);
|
|
|
|
|
IsFormatScreenCodeAllowed =
|
|
|
|
|
CharEncoding.IsPrintableC64ScreenCode((byte)mOperandValue);
|
|
|
|
|
} else {
|
|
|
|
|
IsFormatAsciiAllowed = IsFormatPetsciiAllowed = IsFormatScreenCodeAllowed =
|
|
|
|
|
false;
|
|
|
|
|
}
|
2019-07-08 22:40:30 +00:00
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
SymbolLabel = string.Empty;
|
|
|
|
|
FormatPartLow = true; // could default to high for MVN/MVP
|
|
|
|
|
FormatDefault = true; // if nothing better comes along
|
|
|
|
|
|
|
|
|
|
// Is there an operand format at this location? If not, we're done.
|
|
|
|
|
if (!mProject.OperandFormats.TryGetValue(mOffset, out FormatDescriptor dfd)) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: it's entirely possible to have a weird format (e.g. string) if the
|
|
|
|
|
// instruction used to be hinted as data. Handle it gracefully.
|
|
|
|
|
switch (dfd.FormatType) {
|
|
|
|
|
case FormatDescriptor.Type.NumericLE:
|
|
|
|
|
switch (dfd.FormatSubType) {
|
|
|
|
|
case FormatDescriptor.SubType.Hex:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatHex = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.Decimal:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatDecimal = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.Binary:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatBinary = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
2019-08-12 00:59:20 +00:00
|
|
|
|
case FormatDescriptor.SubType.Ascii:
|
2019-08-10 21:24:19 +00:00
|
|
|
|
case FormatDescriptor.SubType.HighAscii:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (IsFormatAsciiAllowed) {
|
|
|
|
|
FormatAscii = true;
|
|
|
|
|
}
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
2019-08-16 00:53:12 +00:00
|
|
|
|
case FormatDescriptor.SubType.C64Petscii:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (IsFormatPetsciiAllowed) {
|
|
|
|
|
FormatPetscii = true;
|
|
|
|
|
}
|
2019-08-16 00:53:12 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.C64Screen:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (IsFormatScreenCodeAllowed) {
|
|
|
|
|
FormatScreenCode = true;
|
|
|
|
|
}
|
2019-08-16 00:53:12 +00:00
|
|
|
|
break;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
case FormatDescriptor.SubType.Symbol:
|
|
|
|
|
Debug.Assert(dfd.HasSymbol);
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatSymbol = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
switch (dfd.SymbolRef.ValuePart) {
|
|
|
|
|
case WeakSymbolRef.Part.Low:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatPartLow = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case WeakSymbolRef.Part.High:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatPartHigh = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case WeakSymbolRef.Part.Bank:
|
2019-09-07 20:39:22 +00:00
|
|
|
|
FormatPartBank = true;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
SymbolLabel = dfd.SymbolRef.Label;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.None:
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.Type.NumericBE:
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.StringGeneric:
|
|
|
|
|
case FormatDescriptor.Type.StringReverse:
|
|
|
|
|
case FormatDescriptor.Type.StringNullTerm:
|
|
|
|
|
case FormatDescriptor.Type.StringL8:
|
|
|
|
|
case FormatDescriptor.Type.StringL16:
|
|
|
|
|
case FormatDescriptor.Type.StringDci:
|
|
|
|
|
case FormatDescriptor.Type.Dense:
|
2019-07-08 22:40:30 +00:00
|
|
|
|
case FormatDescriptor.Type.Fill:
|
|
|
|
|
default:
|
|
|
|
|
// Unexpected; used to be data?
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
|
|
|
|
|
// In theory, if FormatDefault is still checked, we failed to find a useful match
|
|
|
|
|
// for the format descriptor. In practice, the radio button checkification stuff
|
|
|
|
|
// happens later. If we want to tell the user that there's a bad descriptor present,
|
|
|
|
|
// we'll need to track it locally, or test all known radio buttons for True.
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a FormatDescriptor from the current state of the dialog controls.
|
|
|
|
|
/// </summary>
|
2019-09-07 20:39:22 +00:00
|
|
|
|
/// <returns>New FormatDescriptor. Will return null if the default format is
|
|
|
|
|
/// selected, or symbol is selected with an empty label.</returns>
|
2019-07-08 22:40:30 +00:00
|
|
|
|
private FormatDescriptor CreateDescriptorFromControls() {
|
2019-09-07 20:39:22 +00:00
|
|
|
|
int instructionLength = mProject.GetAnattrib(mOffset).Length;
|
|
|
|
|
|
|
|
|
|
if (FormatSymbol) {
|
|
|
|
|
if (string.IsNullOrEmpty(SymbolLabel)) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
// empty symbol --> default format (intuitive way to delete label reference)
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
WeakSymbolRef.Part part;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (FormatPartLow) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
part = WeakSymbolRef.Part.Low;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatPartHigh) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
part = WeakSymbolRef.Part.High;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatPartBank) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
part = WeakSymbolRef.Part.Bank;
|
|
|
|
|
} else {
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
part = WeakSymbolRef.Part.Low;
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
return FormatDescriptor.Create(instructionLength,
|
|
|
|
|
new WeakSymbolRef(SymbolLabel, part), false);
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FormatDescriptor.SubType subType;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
if (FormatDefault) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
return null;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatHex) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
subType = FormatDescriptor.SubType.Hex;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatDecimal) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
subType = FormatDescriptor.SubType.Decimal;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatBinary) {
|
2019-07-08 22:40:30 +00:00
|
|
|
|
subType = FormatDescriptor.SubType.Binary;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatAscii) {
|
2019-08-10 21:24:19 +00:00
|
|
|
|
if (mOperandValue > 0x7f) {
|
|
|
|
|
subType = FormatDescriptor.SubType.HighAscii;
|
|
|
|
|
} else {
|
2019-08-12 00:59:20 +00:00
|
|
|
|
subType = FormatDescriptor.SubType.Ascii;
|
2019-08-10 21:24:19 +00:00
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatPetscii) {
|
2019-08-16 00:53:12 +00:00
|
|
|
|
subType = FormatDescriptor.SubType.C64Petscii;
|
2019-09-07 20:39:22 +00:00
|
|
|
|
} else if (FormatScreenCode) {
|
2019-08-16 00:53:12 +00:00
|
|
|
|
subType = FormatDescriptor.SubType.C64Screen;
|
2019-07-08 22:40:30 +00:00
|
|
|
|
} else {
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
subType = FormatDescriptor.SubType.None;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
return FormatDescriptor.Create(instructionLength,
|
2019-07-08 22:40:30 +00:00
|
|
|
|
FormatDescriptor.Type.NumericLE, subType);
|
|
|
|
|
}
|
2019-09-07 20:39:22 +00:00
|
|
|
|
|
|
|
|
|
#endregion Basic Format
|
|
|
|
|
|
|
|
|
|
#if false
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Configures the buttons in the "symbol shortcuts" group box. The entire box is
|
|
|
|
|
/// disabled unless "symbol" is selected. Other options are selectively enabled or
|
|
|
|
|
/// disabled as appropriate for the current input. If we disable the selection option,
|
|
|
|
|
/// the selection will be reset to default.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ConfigureSymbolShortcuts() {
|
|
|
|
|
// operandOnlyRadioButton: always enabled
|
|
|
|
|
// labelInsteadRadioButton: symbol is unknown and operand address has no label
|
|
|
|
|
// operandAndLabelRadioButton: same as labelInstead
|
|
|
|
|
// operandAndProjRadioButton: symbol is unknown and operand address is outside project
|
|
|
|
|
|
|
|
|
|
string labelStr = symbolTextBox.Text;
|
|
|
|
|
ShortcutArg = -1;
|
|
|
|
|
|
|
|
|
|
// Is this a known symbol? If so, disable most options and bail.
|
|
|
|
|
if (mProject.SymbolTable.TryGetValue(labelStr, out Symbol sym)) {
|
|
|
|
|
labelInsteadButton.IsEnabled = operandAndLabelButton.IsEnabled =
|
|
|
|
|
operandAndProjButton.IsEnabled = false;
|
|
|
|
|
operandOnlyButton.IsChecked = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mAttr.OperandOffset >= 0) {
|
|
|
|
|
// Operand target is inside the file. Does the target offset already have a label?
|
|
|
|
|
int targetOffset =
|
|
|
|
|
DataAnalysis.GetBaseOperandOffset(mProject, mAttr.OperandOffset);
|
|
|
|
|
bool hasLabel = mProject.UserLabels.ContainsKey(targetOffset);
|
|
|
|
|
labelInsteadButton.IsEnabled = operandAndLabelButton.IsEnabled =
|
|
|
|
|
!hasLabel;
|
|
|
|
|
operandAndProjButton.IsEnabled = false;
|
|
|
|
|
ShortcutArg = targetOffset;
|
|
|
|
|
} else if (mAttr.OperandAddress >= 0) {
|
|
|
|
|
// Operand target is outside the file.
|
|
|
|
|
labelInsteadButton.IsEnabled = operandAndLabelButton.IsEnabled = false;
|
|
|
|
|
operandAndProjButton.IsEnabled = true;
|
|
|
|
|
ShortcutArg = mAttr.OperandAddress;
|
|
|
|
|
} else {
|
|
|
|
|
// Probably an immediate operand.
|
|
|
|
|
// ?? Should operandAndProjButton be enabled for 8-bit constants? We'd want
|
|
|
|
|
// to add it as a constant rather than an address.
|
|
|
|
|
labelInsteadButton.IsEnabled = operandAndLabelButton.IsEnabled =
|
|
|
|
|
operandAndProjButton.IsEnabled = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Select the default option if the currently-selected option is no longer available.
|
|
|
|
|
if ((labelInsteadButton.IsChecked == true && labelInsteadButton.IsEnabled != true) ||
|
|
|
|
|
(operandAndLabelButton.IsChecked == true && !operandAndLabelButton.IsEnabled == true) ||
|
|
|
|
|
(operandAndProjButton.IsChecked == true && !operandAndProjButton.IsEnabled == true)) {
|
|
|
|
|
operandOnlyButton.IsChecked = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2019-07-08 22:40:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|