/* * 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.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Windows; using Asm65; using CommonUtil; namespace SourceGen.WpfGui { /// /// Edit Address Region dialog. /// public partial class EditAddress : Window, INotifyPropertyChanged { /// /// Updated address map entry. Will be null if we want to delete the entry. /// public AddressMap.AddressMapEntry NewEntry { get; private set; } /// /// Offset being edited. /// private int mRegionStartOffset; public string RegionStartOffsetStr { get { return mFormatter.FormatOffset24(mRegionStartOffset); } } /// /// Offset after the end of the selection, or -1 if only one line is selected. /// private int mRegionEndOffset; public string RegionEndOffsetStr { get { return mFormatter.FormatOffset24(mRegionEndOffset); } } public string RegionLengthStr { get { int count = mRegionEndOffset - mRegionStartOffset; return count.ToString() + " (" + mFormatter.FormatHexValue(count, 2) + ")"; } } /// /// Address at which a pre-label would be placed. This is determined by the parent /// region, so its value is fixed. /// private int mPreLabelAddress; public string PreLabelAddressStr { get { return mFormatter.FormatOffset24(mRegionEndOffset); } } /// /// Address input TextBox. /// public string AddressText { get { return mAddressText; } set { mAddressText = value; OnPropertyChanged(); UpdateControls(); } } private string mAddressText; public bool UseRelativeAddressing { get { return mUseRelativeAddressing; } set { mUseRelativeAddressing = value; OnPropertyChanged(); UpdateControls(); } } private bool mUseRelativeAddressing; /// /// Pre-label input TextBox. /// public string PreLabelText { get { return mPreLabelText; } set { mPreLabelText = value; OnPropertyChanged(); UpdateControls(); } } private string mPreLabelText; /// /// Set to true when input is valid. Controls whether the OK button is enabled. /// public bool IsValid { get { return mIsValid; } set { mIsValid = value; OnPropertyChanged(); } } private bool mIsValid; /// /// Set to true when requested region is valid. Everything but the cancel button is /// disabled if not. /// public bool IsRegionValid { get { return mIsRegionValid; } set { mIsRegionValid = value; OnPropertyChanged(); } } private bool mIsRegionValid; /// /// Determines whether the "(floating)" message appears next to the length. /// public Visibility FloatTextVis { get { return mFloatTextVis; } } private Visibility mFloatTextVis; // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private AddressMap.AddressRegion mNewRegion; /// /// Maximum allowed address value, based on CPU type. /// private int mMaxAddressValue; /// /// Reference to project. We need the address map and symbol table. /// private DisasmProject mProject; /// /// Reference to text formatter. /// private Formatter mFormatter; /// /// Constructor. /// /// Parent window. /// Map entry definition. This may be an existing entry, or values /// representing the selection. /// Length of region. Only used if we're resizing an /// existing region. /// Project reference. /// Text formatter object. public EditAddress(Window owner, AddressMap.AddressMapEntry entry, bool isNew, int newLength, DisasmProject project, Formatter formatter) { InitializeComponent(); Owner = owner; DataContext = this; mProject = project; mMaxAddressValue = project.CpuDef.MaxAddressValue; mFormatter = formatter; Configure(entry, isNew, newLength); } private void Configure(AddressMap.AddressMapEntry entry, bool isNew, int newLength) { mRegionStartOffset = mRegionEndOffset = entry.Offset; mPreLabelAddress = 0; IsRegionValid = false; // The passed-in region could have Length=FLOATING_LEN, so we need to resolve // that now. We also need to figure out if it's valid. The easiest way to do // that is to clone the address map, add the region to it, and see how the values // resolve. This also gets us an address for the pre-label. List entries; int spanLength; entries = mProject.AddrMap.GetEntryList(out spanLength); AddressMap tmpMap = new AddressMap(spanLength, entries); if (!isNew) { // Remove the old entry. if (!tmpMap.RemoveEntry(entry.Offset, entry.Length)) { // Shouldn't happen. Debug.Assert(false); // TODO(org): some sort of failure indicator return; } } // Add the new / replacement entry. AddressMap.AddResult result = tmpMap.AddEntry(entry); if (result != AddressMap.AddResult.Okay) { // TODO(org): various things with failures Debug.Assert(false); // remove } else { // Find it in the region tree. mNewRegion = tmpMap.FindRegion(entry.Offset, entry.Length); if (mNewRegion == null) { // Shouldn't happen. Debug.Assert(false); // TODO(org): some sort of failure indicator return; } else { // Set offset / length values based on what we got. IsRegionValid = true; mRegionStartOffset = mNewRegion.Offset; mRegionEndOffset = mNewRegion.Offset + mNewRegion.ActualLength; mPreLabelAddress = mNewRegion.PreLabelAddress; mFloatTextVis = mNewRegion.IsFloating ? Visibility.Visible : Visibility.Hidden; // Init editable stuff. AddressText = Asm65.Address.AddressToString(mNewRegion.Address, false); PreLabelText = mNewRegion.PreLabel; UseRelativeAddressing = mNewRegion.IsRelative; } } } private void Window_ContentRendered(object sender, EventArgs e) { addrTextBox.SelectAll(); addrTextBox.Focus(); } private void OkButton_Click(object sender, RoutedEventArgs e) { bool ok = ParseAddress(out int addr); Debug.Assert(ok); if (addr == AddressMap.INVALID_ADDR) { // field was blank, want to delete the entry NewEntry = null; } else { NewEntry = new AddressMap.AddressMapEntry(mNewRegion.Offset, mNewRegion.Length, addr, PreLabelText, UseRelativeAddressing); } DialogResult = true; } /// /// Handles a TextChanged event on the address text box. /// /// /// Must have UpdateSourceTrigger=PropertyChanged set for this to work. The default /// for TextBox is LostFocus. /// private void UpdateControls() { IsValid = IsRegionValid && ParseAddress(out int unused); // TODO(org): check pre-label syntax } private const string NON_ADDR_STR = "NA"; /// /// Parses the address out of the AddressText text box. /// /// Receives the parsed address. Will be NON_ADDR for "NA", and /// INVALID_ADDR if blank. /// True if the string parsed successfully. private bool ParseAddress(out int addr) { // Left blank? if (AddressText.Length == 0) { addr = AddressMap.INVALID_ADDR; return true; } // "NA" for non-addressable? string upper = AddressText.ToUpper(); if (upper == NON_ADDR_STR) { addr = AddressMap.NON_ADDR; return true; } // Parse numerically. return Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out addr); } } }