mirror of
https://github.com/fadden/6502bench.git
synced 2025-01-20 14:31:17 +00:00
Layout and basic integration of Format Split Address Table
This commit is contained in:
parent
0d75282756
commit
c6b63afb3e
@ -1851,6 +1851,72 @@ namespace SourceGenWPF {
|
||||
mMainWin.CodeListView_Focus();
|
||||
}
|
||||
|
||||
public bool CanFormatSplitAddress() {
|
||||
EntityCounts counts = SelectionAnalysis.mEntityCounts;
|
||||
// Must be at least one byte of data, and no code.
|
||||
return (counts.mDataLines > 0 && counts.mCodeLines == 0);
|
||||
|
||||
}
|
||||
|
||||
public void FormatSplitAddress() {
|
||||
TypedRangeSet trs = GroupedOffsetSetFromSelected();
|
||||
if (trs.Count == 0) {
|
||||
// shouldn't happen
|
||||
Debug.Assert(false, "FormatSplitAddressTable found nothing to edit");
|
||||
return;
|
||||
}
|
||||
|
||||
FormatSplitAddress dlg = new FormatSplitAddress(mMainWin, mProject, trs,
|
||||
mOutputFormatter);
|
||||
|
||||
dlg.ShowDialog();
|
||||
if (dlg.DialogResult != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start with the format descriptors.
|
||||
ChangeSet cs = mProject.GenerateFormatMergeSet(dlg.NewFormatDescriptors);
|
||||
|
||||
// Add in the user labels.
|
||||
foreach (KeyValuePair<int, Symbol> kvp in dlg.NewUserLabels) {
|
||||
Symbol oldUserValue = null;
|
||||
if (mProject.UserLabels.ContainsKey(kvp.Key)) {
|
||||
Debug.Assert(false, "should not be replacing label");
|
||||
oldUserValue = mProject.UserLabels[kvp.Key];
|
||||
}
|
||||
UndoableChange uc = UndoableChange.CreateLabelChange(kvp.Key,
|
||||
oldUserValue, kvp.Value);
|
||||
cs.Add(uc);
|
||||
}
|
||||
|
||||
// Apply code hints.
|
||||
if (dlg.WantCodeHints) {
|
||||
TypedRangeSet newSet = new TypedRangeSet();
|
||||
TypedRangeSet undoSet = new TypedRangeSet();
|
||||
|
||||
foreach (int offset in dlg.AllTargetOffsets) {
|
||||
if (!mProject.GetAnattrib(offset).IsInstruction) {
|
||||
CodeAnalysis.TypeHint oldType = mProject.TypeHints[offset];
|
||||
if (oldType == CodeAnalysis.TypeHint.Code) {
|
||||
continue; // already set
|
||||
}
|
||||
undoSet.Add(offset, (int)oldType);
|
||||
newSet.Add(offset, (int)CodeAnalysis.TypeHint.Code);
|
||||
}
|
||||
}
|
||||
if (newSet.Count != 0) {
|
||||
cs.Add(UndoableChange.CreateTypeHintChange(undoSet, newSet));
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, apply the change.
|
||||
if (cs.Count != 0) {
|
||||
ApplyUndoableChanges(cs);
|
||||
} else {
|
||||
Debug.WriteLine("No changes found");
|
||||
}
|
||||
}
|
||||
|
||||
public void Goto() {
|
||||
GotoBox dlg = new GotoBox(mMainWin, mProject, mOutputFormatter);
|
||||
if (dlg.ShowDialog() == true) {
|
||||
|
@ -112,6 +112,9 @@
|
||||
<Compile Include="WpfGui\FontPicker.xaml.cs">
|
||||
<DependentUpon>FontPicker.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="WpfGui\FormatSplitAddress.xaml.cs">
|
||||
<DependentUpon>FormatSplitAddress.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="WpfGui\GotoBox.xaml.cs">
|
||||
<DependentUpon>GotoBox.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@ -257,6 +260,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="WpfGui\FormatSplitAddress.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="WpfGui\GotoBox.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
139
SourceGenWPF/WpfGui/FormatSplitAddress.xaml
Normal file
139
SourceGenWPF/WpfGui/FormatSplitAddress.xaml
Normal file
@ -0,0 +1,139 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<Window x:Class="SourceGenWPF.WpfGui.FormatSplitAddress"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:local="clr-namespace:SourceGenWPF.WpfGui"
|
||||
mc:Ignorable="d"
|
||||
Title="Format Split-Address Table"
|
||||
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
|
||||
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
||||
Loaded="Window_Loaded">
|
||||
|
||||
<Window.Resources>
|
||||
<system:String x:Key="str_SingleByte">One byte is selected</system:String>
|
||||
<system:String x:Key="str_SingleGroup">There are {0} bytes selected</system:String>
|
||||
<system:String x:Key="str_MultiGroup">There are {0} bytes selected, across {1} groups</system:String>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2"
|
||||
Text="{StaticResource str_MultiGroup}"/>
|
||||
|
||||
<StackPanel Grid.Column="0" Grid.Row="1" Margin="0,8,8,0">
|
||||
<GroupBox Header="Address Characteristics">
|
||||
<StackPanel>
|
||||
<RadioButton Name="width16Radio" GroupName="Addr" Margin="0,4,0,0"
|
||||
Content="16-bit"/>
|
||||
<RadioButton Name="width24Radio" GroupName="Addr" Margin="0,4,0,0"
|
||||
Content="24-bit"/>
|
||||
<CheckBox Margin="0,4,0,0" Content="Adjusted for RTS/RTL (target - 1)"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Low Bytes">
|
||||
<StackPanel>
|
||||
<RadioButton Name="lowFirstPartRadio" GroupName="Low" Margin="0,4,0,0"
|
||||
Content="First part of selection"/>
|
||||
<RadioButton Name="lowSecondPartRadio" GroupName="Low" Margin="0,4,0,0"
|
||||
Content="Second part of selection"/>
|
||||
<RadioButton Name="lowThirdPartRadio" GroupName="Low" Margin="0,4,0,0"
|
||||
Content="Third part of selection"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="High Bytes">
|
||||
<StackPanel>
|
||||
<RadioButton Name="highFirstPartRadio" GroupName="High" Margin="0,4,0,0"
|
||||
Content="First part of selection"/>
|
||||
<RadioButton Name="highSecondPartRadio" GroupName="High" Margin="0,4,0,0"
|
||||
Content="Second part of selection"/>
|
||||
<RadioButton Name="highThirdPartRadio" GroupName="High" Margin="0,4,0,0"
|
||||
Content="Third part of selection"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton Name="highConstantRadio" GroupName="High" Margin="0,4,0,0"
|
||||
Content="Constant:"/>
|
||||
<TextBox Name="highConstantTextBox" Width="100" Margin="8,1,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Bank Bytes">
|
||||
<StackPanel>
|
||||
<RadioButton Name="bankNthPartRadio" GroupName="Bank" Margin="0,4,0,0"
|
||||
Content="Nth part of selection"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton Name="bankConstantRadio" GroupName="Bank" Margin="0,4,0,0"
|
||||
Content="Constant:"/>
|
||||
<TextBox Name="bankConstantTextBox" Width="100" Margin="8,1,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Options">
|
||||
<StackPanel>
|
||||
<CheckBox Content="Add code entry hint if needed" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
|
||||
<DockPanel Grid.Column="1" Grid.Row="1" Margin="0,8,0,0">
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<Button Name="okButton" Content="OK" IsDefault="True" Width="70"
|
||||
IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>
|
||||
<Button Name="cancelButton" Content="Cancel" IsCancel="True"
|
||||
Width="70" Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<GroupBox DockPanel.Dock="Top" Header="Generated Addresses">
|
||||
<!-- We're currently auto-sizing everything outside this, so the width of
|
||||
the columns determines the width of the window. Resizing the columns resizes
|
||||
the window, even though the window isn't resizeable. "It's a feature." -->
|
||||
<DataGrid Name="outputPreviewList"
|
||||
Margin="0,2,0,0"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding OutputPreviewList}"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
SnapsToDevicePixels="True"
|
||||
GridLinesVisibility="Vertical"
|
||||
VerticalGridLinesBrush="#FF7F7F7F"
|
||||
AutoGenerateColumns="False"
|
||||
HeadersVisibility="Column"
|
||||
CanUserReorderColumns="False"
|
||||
SelectionMode="Single">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Addr" Width="60" Binding="{Binding Addr}"/>
|
||||
<DataGridTextColumn Header="Offset" Width="60" Binding="{Binding Offset}"/>
|
||||
<DataGridTextColumn Header="Symbol" Width="180" Binding="{Binding Type}"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Window>
|
463
SourceGenWPF/WpfGui/FormatSplitAddress.xaml.cs
Normal file
463
SourceGenWPF/WpfGui/FormatSplitAddress.xaml.cs
Normal file
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* 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 System.Windows.Controls;
|
||||
|
||||
using Asm65;
|
||||
using CommonUtil;
|
||||
|
||||
namespace SourceGenWPF.WpfGui {
|
||||
/// <summary>
|
||||
/// Split-address table generator.
|
||||
/// </summary>
|
||||
public partial class FormatSplitAddress : Window, INotifyPropertyChanged {
|
||||
/// Format descriptors to apply.
|
||||
/// </summary>
|
||||
public SortedList<int, FormatDescriptor> NewFormatDescriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// User labels to apply.
|
||||
/// </summary>
|
||||
public Dictionary<int, Symbol> NewUserLabels { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// All target offsets found. The list may contain redundant entries.
|
||||
/// </summary>
|
||||
public List<int> AllTargetOffsets { get; private set; }
|
||||
|
||||
public bool WantCodeHints = true; // TODO
|
||||
#if false
|
||||
{
|
||||
get {
|
||||
return addCodeHintCheckBox.Checked;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set to true when valid output is available.
|
||||
/// </summary>
|
||||
private bool mOutputReady;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Set to true when input is valid. Controls whether the OK button is enabled.
|
||||
/// </summary>
|
||||
public bool IsValid {
|
||||
get { return mIsValid; }
|
||||
set {
|
||||
mIsValid = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
private bool mIsValid;
|
||||
|
||||
/// <summary>
|
||||
/// Selected offsets. An otherwise contiguous range of offsets can be broken up
|
||||
/// by user-specified labels and address discontinuities, so this needs to be
|
||||
/// processed by range.
|
||||
/// </summary>
|
||||
private TypedRangeSet mSelection;
|
||||
|
||||
/// <summary>
|
||||
/// Project reference.
|
||||
/// </summary>
|
||||
private DisasmProject mProject;
|
||||
|
||||
/// <summary>
|
||||
/// Formatter to use when displaying addresses and hex values.
|
||||
/// </summary>
|
||||
private Formatter mFormatter;
|
||||
|
||||
// INotifyPropertyChanged implementation
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
public FormatSplitAddress(Window owner, DisasmProject project, TypedRangeSet selection,
|
||||
Formatter formatter) {
|
||||
InitializeComponent();
|
||||
Owner = owner;
|
||||
DataContext = this;
|
||||
|
||||
mProject = project;
|
||||
mFormatter = formatter;
|
||||
mSelection = selection;
|
||||
IsValid = false;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
||||
// TDOO
|
||||
}
|
||||
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
#if false
|
||||
private void FormatSplitAddress_Load(object sender, EventArgs e) {
|
||||
mInitializing = true;
|
||||
|
||||
string fmt = selectionInfoLabel.Text;
|
||||
selectionInfoLabel.Text = string.Format(fmt, mSelection.Count, mSelection.RangeCount);
|
||||
|
||||
width16Radio.Checked = true;
|
||||
lowFirstPartRadio.Checked = true;
|
||||
highSecondPartRadio.Checked = true;
|
||||
bankNthPartRadio.Checked = true;
|
||||
|
||||
incompatibleSelectionLabel.Visible = invalidConstantLabel.Visible = false;
|
||||
|
||||
if (mProject.CpuDef.HasAddr16) {
|
||||
// Disable the 24-bit option. Having 16-bit selected will disable the rest.
|
||||
width24Radio.Enabled = false;
|
||||
}
|
||||
|
||||
outputPreviewListView.SetDoubleBuffered(true);
|
||||
|
||||
mInitializing = false;
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void UpdateControls() {
|
||||
if (mInitializing) {
|
||||
return;
|
||||
}
|
||||
mInitializing = true; // no re-entry
|
||||
|
||||
lowThirdPartRadio.Enabled = width24Radio.Checked;
|
||||
highThirdPartRadio.Enabled = width24Radio.Checked;
|
||||
bankByteGroupBox.Enabled = width24Radio.Checked;
|
||||
|
||||
lowSecondPartRadio.Enabled = true;
|
||||
|
||||
// If the user selects "constant" for high byte or bank byte, then there is no
|
||||
// 3rd part available for low/high, so we need to turn those back off.
|
||||
if (width24Radio.Checked) {
|
||||
bool haveThree = !(highConstantRadio.Checked || bankConstantRadio.Checked);
|
||||
lowThirdPartRadio.Enabled = haveThree;
|
||||
highThirdPartRadio.Enabled = haveThree;
|
||||
|
||||
// If "constant" is selected for high byte *and* bank byte, then there's no
|
||||
// 2nd part available for low.
|
||||
if (highConstantRadio.Checked && bankConstantRadio.Checked) {
|
||||
lowSecondPartRadio.Enabled = false;
|
||||
}
|
||||
} else {
|
||||
// For 16-bit address, if high byte is constant, then there's no second
|
||||
// part for the low byte.
|
||||
if (highConstantRadio.Checked) {
|
||||
lowSecondPartRadio.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Was a now-invalidated radio button selected before?
|
||||
if (!lowThirdPartRadio.Enabled && lowThirdPartRadio.Checked) {
|
||||
// low now invalid, switch to whatever high isn't using
|
||||
if (highFirstPartRadio.Checked) {
|
||||
lowSecondPartRadio.Checked = true;
|
||||
} else {
|
||||
lowFirstPartRadio.Checked = true;
|
||||
}
|
||||
}
|
||||
if (!highThirdPartRadio.Enabled && highThirdPartRadio.Checked) {
|
||||
// high now invalid, switch to whatever low isn't using
|
||||
if (lowFirstPartRadio.Checked) {
|
||||
highSecondPartRadio.Checked = true;
|
||||
} else {
|
||||
highFirstPartRadio.Checked = true;
|
||||
}
|
||||
}
|
||||
if (!lowSecondPartRadio.Enabled && lowSecondPartRadio.Checked) {
|
||||
// Should only happen when high part is constant.
|
||||
Debug.Assert(highFirstPartRadio.Checked == false);
|
||||
lowFirstPartRadio.Checked = true;
|
||||
}
|
||||
|
||||
mInitializing = false;
|
||||
UpdatePreview();
|
||||
|
||||
okButton.Enabled = mOutputReady;
|
||||
}
|
||||
|
||||
private void widthRadio_CheckedChanged(object sender, EventArgs e) {
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void pushRtsCheckBox_CheckedChanged(object sender, EventArgs e) {
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void lowByte_CheckedChanged(object sender, EventArgs e) {
|
||||
// If we conflict with the high byte, change the high byte.
|
||||
if (lowFirstPartRadio.Checked && highFirstPartRadio.Checked) {
|
||||
highSecondPartRadio.Checked = true;
|
||||
} else if (lowSecondPartRadio.Checked && highSecondPartRadio.Checked) {
|
||||
highFirstPartRadio.Checked = true;
|
||||
} else if (lowThirdPartRadio.Checked && highThirdPartRadio.Checked) {
|
||||
highFirstPartRadio.Checked = true;
|
||||
}
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void highByte_CheckedChanged(object sender, EventArgs e) {
|
||||
// If we conflict with the low byte, change the low byte.
|
||||
if (lowFirstPartRadio.Checked && highFirstPartRadio.Checked) {
|
||||
lowSecondPartRadio.Checked = true;
|
||||
} else if (lowSecondPartRadio.Checked && highSecondPartRadio.Checked) {
|
||||
lowFirstPartRadio.Checked = true;
|
||||
} else if (lowThirdPartRadio.Checked && highThirdPartRadio.Checked) {
|
||||
lowFirstPartRadio.Checked = true;
|
||||
}
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void bankByte_CheckedChanged(object sender, EventArgs e) {
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void highConstantTextBox_TextChanged(object sender, EventArgs e) {
|
||||
highConstantRadio.Checked = true;
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void bankConstantTextBox_TextChanged(object sender, EventArgs e) {
|
||||
bankConstantRadio.Checked = true;
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void UpdatePreview() {
|
||||
mOutputReady = false;
|
||||
|
||||
int minDiv;
|
||||
|
||||
if (width16Radio.Checked) {
|
||||
if (highConstantRadio.Checked) {
|
||||
minDiv = 1;
|
||||
} else {
|
||||
minDiv = 2;
|
||||
}
|
||||
} else {
|
||||
if (highConstantRadio.Checked) {
|
||||
if (bankConstantRadio.Checked) {
|
||||
minDiv = 1;
|
||||
} else {
|
||||
minDiv = 2;
|
||||
}
|
||||
} else {
|
||||
if (bankConstantRadio.Checked) {
|
||||
minDiv = 2;
|
||||
} else {
|
||||
minDiv = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
incompatibleSelectionLabel.Visible = invalidConstantLabel.Visible = false;
|
||||
|
||||
try {
|
||||
// Start by clearing the previous contents of the list. If something goes
|
||||
// wrong, we want to show the error messages on an empty list.
|
||||
outputPreviewListView.BeginUpdate();
|
||||
outputPreviewListView.Items.Clear();
|
||||
|
||||
if ((mSelection.Count % minDiv) != 0) {
|
||||
incompatibleSelectionLabel.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int highConstant = -1;
|
||||
if (highConstantRadio.Checked) {
|
||||
if (!Number.TryParseInt(highConstantTextBox.Text, out highConstant,
|
||||
out int unused) || (highConstant != (byte) highConstant)) {
|
||||
invalidConstantLabel.Visible = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int bankConstant = -1;
|
||||
if (bankConstantRadio.Enabled && bankConstantRadio.Checked) {
|
||||
if (!Number.TryParseInt(bankConstantTextBox.Text, out bankConstant,
|
||||
out int unused) || (bankConstant != (byte) bankConstant)) {
|
||||
invalidConstantLabel.Visible = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Looks valid, generate format list.
|
||||
GenerateFormats(minDiv, highConstant, bankConstant);
|
||||
} finally {
|
||||
outputPreviewListView.EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateFormats(int div, int highConst, int bankConst) {
|
||||
SortedList<int, FormatDescriptor> newDfds = new SortedList<int, FormatDescriptor>();
|
||||
Dictionary<int, Symbol> newLabels = new Dictionary<int, Symbol>();
|
||||
List<int> targetOffsets = new List<int>();
|
||||
|
||||
// Identify the offset where each set of data starts.
|
||||
int span = mSelection.Count / div;
|
||||
int lowOff, highOff, bankOff;
|
||||
|
||||
if (lowFirstPartRadio.Checked) {
|
||||
lowOff = 0;
|
||||
} else if (lowSecondPartRadio.Checked) {
|
||||
lowOff = span;
|
||||
} else if (lowThirdPartRadio.Checked) {
|
||||
lowOff = span * 2;
|
||||
} else {
|
||||
Debug.Assert(false);
|
||||
lowOff = -1;
|
||||
}
|
||||
if (highFirstPartRadio.Checked) {
|
||||
highOff = 0;
|
||||
} else if (highSecondPartRadio.Checked) {
|
||||
highOff = span;
|
||||
} else if (highThirdPartRadio.Checked) {
|
||||
highOff = span * 2;
|
||||
} else {
|
||||
highOff = -1; // use constant
|
||||
}
|
||||
if (width24Radio.Checked) {
|
||||
if (bankNthPartRadio.Checked) {
|
||||
// Use whichever part isn't being used by the other two.
|
||||
if (lowOff != 0 && highOff != 0) {
|
||||
bankOff = 0;
|
||||
} else if (lowOff != span && highOff != span) {
|
||||
bankOff = span;
|
||||
} else {
|
||||
Debug.Assert(lowOff != span * 2 && highOff != span * 2);
|
||||
bankOff = span * 2;
|
||||
}
|
||||
} else {
|
||||
bankOff = -1; // use constant
|
||||
}
|
||||
} else {
|
||||
bankOff = -1; // use constant
|
||||
bankConst = 0; // always bank 0
|
||||
}
|
||||
|
||||
Debug.WriteLine("Extract from low=" + lowOff + " high=" + highOff +
|
||||
" bank=" + bankOff);
|
||||
|
||||
// The TypedRangeSet doesn't have an index operation, so copy the values into
|
||||
// an array.
|
||||
int[] offsets = new int[mSelection.Count];
|
||||
int index = 0;
|
||||
foreach (TypedRangeSet.Tuple tup in mSelection) {
|
||||
offsets[index++] = tup.Value;
|
||||
}
|
||||
|
||||
int adj = 0;
|
||||
if (pushRtsCheckBox.Checked) {
|
||||
adj = 1;
|
||||
}
|
||||
|
||||
// Walk through the file data, generating addresses as we go.
|
||||
byte[] fileData = mProject.FileData;
|
||||
for (int i = 0; i < span; i++) {
|
||||
byte low, high, bank;
|
||||
|
||||
low = fileData[offsets[lowOff + i]];
|
||||
if (highOff >= 0) {
|
||||
high = fileData[offsets[highOff + i]];
|
||||
} else {
|
||||
high = (byte) highConst;
|
||||
}
|
||||
if (bankOff >= 0) {
|
||||
bank = fileData[offsets[bankOff + i]];
|
||||
} else {
|
||||
bank = (byte) bankConst;
|
||||
}
|
||||
|
||||
int addr = ((bank << 16) | (high << 8) | low) + adj;
|
||||
|
||||
int targetOffset = mProject.AddrMap.AddressToOffset(offsets[0], addr);
|
||||
if (targetOffset < 0) {
|
||||
// Address not within file bounds.
|
||||
// TODO(maybe): look for matching platform/project symbols
|
||||
AddPreviewItem(addr, -1, Properties.Resources.INVALID_ADDRESS);
|
||||
} else {
|
||||
// Note the same target offset may appear more than once.
|
||||
targetOffsets.Add(targetOffset);
|
||||
|
||||
// If there's a user-defined label there already, use it. Otherwise, we'll
|
||||
// need to generate one.
|
||||
string targetLabel;
|
||||
if (mProject.UserLabels.TryGetValue(targetOffset, out Symbol sym)) {
|
||||
targetLabel = sym.Label;
|
||||
AddPreviewItem(addr, targetOffset, targetLabel);
|
||||
} else {
|
||||
// Generate a symbol that's unique vs. the symbol table. We don't need
|
||||
// it to be unique vs. the labels we're generating here, because we
|
||||
// won't generate identical labels for different addresses, and we do
|
||||
// want to generate a single label if more than one table entry refers
|
||||
// to the same target.
|
||||
Symbol tmpSym = AutoLabel.GenerateUniqueForAddress(addr,
|
||||
mProject.SymbolTable, "T");
|
||||
// tmpSym was returned as an auto-label, make it a user label instead
|
||||
tmpSym = new Symbol(tmpSym.Label, tmpSym.Value, Symbol.Source.User,
|
||||
Symbol.Type.LocalOrGlobalAddr);
|
||||
newLabels[targetOffset] = tmpSym; // overwrites previous
|
||||
targetLabel = tmpSym.Label;
|
||||
AddPreviewItem(addr, targetOffset, "(+) " + targetLabel);
|
||||
}
|
||||
|
||||
// Now we need to create format descriptors for the addresses where we
|
||||
// extracted the low, high, and bank values.
|
||||
newDfds.Add(offsets[lowOff + i], FormatDescriptor.Create(1,
|
||||
new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Low), false));
|
||||
if (highOff >= 0) {
|
||||
newDfds.Add(offsets[highOff + i], FormatDescriptor.Create(1,
|
||||
new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.High), false));
|
||||
}
|
||||
if (bankOff >= 0) {
|
||||
newDfds.Add(offsets[bankOff + i], FormatDescriptor.Create(1,
|
||||
new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Bank), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NewFormatDescriptors = newDfds;
|
||||
NewUserLabels = newLabels;
|
||||
AllTargetOffsets = targetOffsets;
|
||||
|
||||
// Don't show ready if all addresses are invalid.
|
||||
mOutputReady = (AllTargetOffsets.Count > 0);
|
||||
}
|
||||
|
||||
private void AddPreviewItem(int addr, int offset, string label) {
|
||||
ListViewItem lvi = new ListViewItem(mFormatter.FormatAddress(addr,
|
||||
!mProject.CpuDef.HasAddr16));
|
||||
if (offset >= 0) {
|
||||
lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi,
|
||||
mFormatter.FormatOffset24(offset)));
|
||||
} else {
|
||||
lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, "---"));
|
||||
}
|
||||
lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, label));
|
||||
outputPreviewListView.Items.Add(lvi);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -86,6 +86,7 @@ limitations under the License.
|
||||
<KeyGesture>F3</KeyGesture>
|
||||
</RoutedUICommand.InputGestures>
|
||||
</RoutedUICommand>
|
||||
<RoutedUICommand x:Key="FormatSplitAddressCmd" Text="Format Split-Address Table..."/>
|
||||
<RoutedUICommand x:Key="GotoCmd" Text="Go To...">
|
||||
<RoutedUICommand.InputGestures>
|
||||
<KeyGesture>Ctrl+G</KeyGesture>
|
||||
@ -172,6 +173,8 @@ limitations under the License.
|
||||
CanExecute="IsProjectOpen" Executed="FindCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource FindNextCmd}"
|
||||
CanExecute="IsProjectOpen" Executed="FindNextCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource FormatSplitAddressCmd}"
|
||||
CanExecute="CanFormatSplitAddress" Executed="FormatSplitAddressCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource GotoCmd}"
|
||||
CanExecute="IsProjectOpen" Executed="GotoCmd_Executed"/>
|
||||
<CommandBinding Command="Help"
|
||||
@ -267,7 +270,7 @@ limitations under the License.
|
||||
<MenuItem Command="{StaticResource HintAsInlineDataCmd}" InputGestureText="Ctrl+H, Ctrl+I"/>
|
||||
<MenuItem Command="{StaticResource RemoveHintsCmd}" InputGestureText="Ctrl+H, Ctrl+R"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="Format Split-Address Table..."/>
|
||||
<MenuItem Command="{StaticResource FormatSplitAddressCmd}"/>
|
||||
<MenuItem Header="Toggle Single-Byte Format"/> <!-- Ctrl+B -->
|
||||
<MenuItem Header="Format As Word"/> <!-- Ctrl+W -->
|
||||
<MenuItem Header="Delete Note/Long Comment"/> <!-- Del -->
|
||||
|
@ -769,6 +769,10 @@ namespace SourceGenWPF.WpfGui {
|
||||
e.CanExecute = IsProjectOpen() && mMainCtrl.CanEditStatusFlags();
|
||||
}
|
||||
|
||||
private void CanFormatSplitAddress(object sender, CanExecuteRoutedEventArgs e) {
|
||||
e.CanExecute = IsProjectOpen() && mMainCtrl.CanFormatSplitAddress();
|
||||
}
|
||||
|
||||
private void CanHintAsCodeEntryPoint(object sender, CanExecuteRoutedEventArgs e) {
|
||||
if (!IsProjectOpen()) {
|
||||
e.CanExecute = false;
|
||||
@ -892,6 +896,10 @@ namespace SourceGenWPF.WpfGui {
|
||||
mMainCtrl.FindNext();
|
||||
}
|
||||
|
||||
private void FormatSplitAddressCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
mMainCtrl.FormatSplitAddress();
|
||||
}
|
||||
|
||||
private void GotoCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
mMainCtrl.Goto();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user