1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-05-31 22:41:37 +00:00

ORG rework, part 4

Reimplemented "set address" dialog as the Address Region Editor.  The
new dialog configures itself differently depending on whether the user
appears to be trying to create, edit, or resize a region.  Each mode
has two options, to allow the user to choose between floating and fixed
end points.

The old dialog would allow you to delete an address override by erasing
the address field.  Now there's an explicit "delete region" button.

Changed the SetAddress undoable change function to use AddressMapEntry
objects.

We now show detailed information on .arstart/.arend in the Info window
when the lines are selected.

PRG files are now created without specifying a region for the first
two bytes, so the load address exists in a NON_ADDR hole.  Fixed a
couple of issues to make that look right.
This commit is contained in:
Andy McFadden 2021-09-26 17:06:43 -07:00
parent 5f472b60cf
commit 3a2c4fa6d2
9 changed files with 694 additions and 298 deletions

View File

@ -83,11 +83,6 @@ namespace CommonUtil {
/// </summary>
public const int NON_ADDR = -1025;
/// <summary>
/// Address value to use for an invalid address.
/// </summary>
public const int INVALID_ADDR = -2048;
#region Structural
/// <summary>
@ -287,6 +282,17 @@ namespace CommonUtil {
return newList;
}
/// <summary>
/// Creates a clone of the address map.
/// </summary>
/// <returns>Cloned object.</returns>
public AddressMap Clone() {
List<AddressMap.AddressMapEntry> entries;
int spanLength;
entries = GetEntryList(out spanLength);
return new AddressMap(spanLength, entries);
}
// IEnumerable
public IEnumerator<AddressMapEntry> GetEnumerator() {
return ((IEnumerable<AddressMapEntry>)mMapEntries).GetEnumerator();
@ -308,6 +314,7 @@ namespace CommonUtil {
public enum AddResult {
Unknown = 0,
Okay, // success!
InternalError, // something weird happened
InvalidValue, // offset, length, or address parameter is invalid
OverlapExisting, // new region overlaps existing region exactly
OverlapFloating, // new start matches existing; one or both are floating
@ -498,30 +505,6 @@ namespace CommonUtil {
return AddResult.Okay;
}
/// <summary>
/// Edits the region with the specified offset/len, changing the values of addr and isRel.
/// </summary>
/// <param name="offset">Offset of region to edit.</param>
/// <param name="length">Length of region to edit.</param>
/// <param name="addr">New value for address.</param>
/// <param name="preLabel">Pre-block label.</param>
/// <param name="isRelative">New value for IsRelative.</param>
/// <returns>True if a region was edited, false otherwise.</returns>
public bool EditEntry(int offset, int length, int addr, string preLabel, bool isRelative) {
if (!ValidateArgs(offset, length, addr, preLabel)) {
throw new Exception("Bad EditRegion args +" + offset.ToString("x6") +
" " + length + " $" + addr);
}
int idx = FindEntry(offset, length);
if (idx < 0) {
return false;
}
mMapEntries[idx] = new AddressMapEntry(offset, length, addr, preLabel, isRelative);
Regenerate();
return true;
}
/// <summary>
/// Removes the region with the specified offset/len.
/// </summary>
@ -998,7 +981,7 @@ namespace CommonUtil {
/// </summary>
/// <remarks>
/// This is NOT intended to say whether the sequence of addresses has a hiccup. The goal
/// is to identify multi-byte elements that have a .ORG statement in the middle.
/// is to identify multi-byte elements that have a .arstart/.arend statement in the middle.
///
/// We can do this in a couple of different ways:
/// 1. Find the node that holds the offset, confirm that it spans offset+length, and

View File

@ -391,18 +391,13 @@ namespace SourceGen {
AddrMap.Clear();
if (SystemDefaults.GetFirstWordIsLoadAddr(sysDef) && mFileData.Length > 2) {
// First two bytes are the load address, with the actual file data starting
// at +000002. We need to assign an address to the bytes, but don't want them
// to be referenced as an address by something else. One approach is to use
// the load address, and rely on the multi-address code to keep them distinct,
// but that throws off the "load address" display in the set-address dialog.
// So we just give it an offset of (start - 2) and leave it to the user to
// update if necessary.
// at +000002. The first two bytes are non-addressable, so we leave them
// out of the map.
int loadAddr = RawData.GetWord(mFileData, 0, 2, false);
// TODO(org): use NON_ADDR for first two bytes
AddressMap.AddResult addRes =
AddrMap.AddEntry(0, 2, loadAddr < 2 ? 0 : loadAddr - 2);
Debug.Assert(addRes == AddressMap.AddResult.Okay);
addRes = AddrMap.AddEntry(2, mFileData.Length - 2, loadAddr);
//AddressMap.AddResult addRes =
// AddrMap.AddEntry(0, 2, loadAddr < 2 ? 0 : loadAddr - 2);
//Debug.Assert(addRes == AddressMap.AddResult.Okay);
AddressMap.AddResult addRes = AddrMap.AddEntry(2, mFileData.Length - 2, loadAddr);
Debug.Assert(addRes == AddressMap.AddResult.Okay);
OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE,
@ -2147,31 +2142,24 @@ namespace SourceGen {
//}
break;
case UndoableChange.ChangeType.SetAddress: {
// TODO(org): rewrite this
AddressMap.AddressMapEntry oldEnt =
(AddressMap.AddressMapEntry)oldValue;
AddressMap.AddressMapEntry newEnt =
(AddressMap.AddressMapEntry)newValue;
AddressMap addrMap = AddrMap;
if ((int)oldValue == AddressMap.NON_ADDR) {
// adding new entry
if (addrMap.AddEntry(offset, AddressMap.FLOATING_LEN,
(int)newValue) != AddressMap.AddResult.Okay) {
Debug.Assert(false, "failed adding region");
}
} else if ((int)newValue == AddressMap.NON_ADDR) {
// removing existing entry
if (!addrMap.RemoveEntry(offset, AddressMap.FLOATING_LEN)) {
if (oldEnt != null) {
// remove existing entry, possibly replacing it in the next step
if (!addrMap.RemoveEntry(oldEnt.Offset, oldEnt.Length)) {
Debug.Assert(false, "failed removing region");
}
} else {
// updating existing entry
if (!addrMap.EditEntry(offset, AddressMap.FLOATING_LEN,
(int) newValue, string.Empty, false)) {
Debug.Assert(false, "failed editing region");
}
if (newValue != null) {
// adding new or replacement entry
if (addrMap.AddEntry(newEnt) != AddressMap.AddResult.Okay) {
Debug.Assert(false, "failed adding region");
}
}
Debug.WriteLine("Map offset +" + offset.ToString("x6") + " to $" +
((int)newValue).ToString("x6"));
// Visualization generators in extension scripts could be chasing
// addresses. Give them a chance to update.
ClearVisualizationCache();

View File

@ -447,7 +447,7 @@ namespace SourceGen {
return parts;
}
public static FormattedParts CreateEquDirective(string label, string opstr,
public static FormattedParts CreateFullDirective(string label, string opstr,
string addrStr, string comment) {
FormattedParts parts = new FormattedParts();
parts.Label = label;

View File

@ -902,7 +902,7 @@ namespace SourceGen {
PseudoOp.FormatNumericOpFlags.None);
valueStr = PseudoOp.AnnotateEquDirective(formatter, valueStr, defSym);
string comment = formatter.FormatEolComment(defSym.Comment);
FormattedParts parts = FormattedParts.CreateEquDirective(
FormattedParts parts = FormattedParts.CreateFullDirective(
defSym.GenerateDisplayLabel(formatter),
formatter.FormatPseudoOp(opNames.EquDirective),
valueStr, comment);
@ -989,9 +989,13 @@ namespace SourceGen {
}
}
bool hasPrgHeader = AsmGen.GenCommon.HasPrgHeader(mProject);
int offset = startOffset;
int arSubLine = 0;
while (offset <= endOffset) {
bool spaceAdded = false;
Anattrib attr = mProject.GetAnattrib(offset);
if (attr.IsInstructionStart && offset > 0 &&
mProject.GetAnattrib(offset - 1).IsData) {
@ -1040,8 +1044,8 @@ namespace SourceGen {
Debug.Assert(change.Offset == offset);
AddressMap.AddressRegion region = change.Region;
if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) {
// Suppress the ORG at offset zero. We know there's another one
if (region.Offset == 0 && hasPrgHeader) {
// Suppress the .arstart at offset zero. We know there's another one
// at offset +000002, and that it matches the value at +0/1.
addrIter.MoveNext();
continue;
@ -1056,10 +1060,23 @@ namespace SourceGen {
spaceAdded = false; // next one will need a blank line
// TODO(org): pre-label (address / label only, logically part of ORG)
Line newLine = new Line(offset, 0, Line.Type.ArStartDirective);
string addrStr = mFormatter.FormatHexValue(region.Address, 4);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective), addrStr);
Line newLine = new Line(offset, 0, Line.Type.ArStartDirective, arSubLine++);
string addrStr;
if (region.Address == AddressMap.NON_ADDR) {
addrStr = "NA";
} else {
addrStr = mFormatter.FormatHexValue(region.Address, 4);
}
#if DEBUG
string comment = mFormatter.FormatEolComment("ends at " +
mFormatter.FormatOffset24(region.Offset + region.ActualLength - 1) +
(region.IsFloating ? " (floating)" : string.Empty));
#else
string comment = string.Empty;
#endif
newLine.Parts = FormattedParts.CreateFullDirective(string.Empty,
mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective),
addrStr, comment);
lines.Add(newLine);
addrIter.MoveNext();
} else {
@ -1221,14 +1238,22 @@ namespace SourceGen {
// Check for address region ends, which will be positioned at the updated offset
// (unless they somehow got embedded inside something else).
arSubLine = 0;
while (addrIter.Current != null && addrIter.Current.Offset <= offset) {
AddressMap.AddressChange change = addrIter.Current;
// Range starts/ends shouldn't be embedded in something.
Debug.Assert(change.Offset == offset);
if (change.Region.Offset == 0 && hasPrgHeader) {
// Suppress the .arend at offset +000002.
addrIter.MoveNext();
arSubLine++; // need to track address map
continue;
}
if (!change.IsStart) {
// NOTE: last end(s) are at an offset outside file bounds.
Line newLine = new Line(offset, 0, Line.Type.ArEndDirective);
Line newLine = new Line(offset, 0, Line.Type.ArEndDirective, arSubLine++);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.ArEndDirective),
string.Empty);
@ -1509,7 +1534,7 @@ namespace SourceGen {
PseudoOp.FormatNumericOpFlags.None);
addrStr = PseudoOp.AnnotateEquDirective(mFormatter, addrStr, defSym);
string comment = mFormatter.FormatEolComment(defSym.Comment);
return FormattedParts.CreateEquDirective(
return FormattedParts.CreateFullDirective(
mFormatter.FormatVariableLabel(defSym.GenerateDisplayLabel(mFormatter)),
mFormatter.FormatPseudoOp(mPseudoOpNames.VarDirective),
addrStr, comment);
@ -1539,6 +1564,42 @@ namespace SourceGen {
return lvars[tableIndex];
}
public AddressMap.AddressRegion GetAddrRegionFromLine(int lineIndex) {
// A given offset can have one or more .arend lines followed by one or more
// .arstart lines. You can't have start followed by end because that would
// be a zero-length block. We do need to handle both, though, and we need to
// handle any synthetic non-addressable regions, so we walk the change list.
Line line = this[lineIndex];
int offset = line.FileOffset;
List<AddressMap.AddressMapEntry> entries = mProject.AddrMap.GetEntries(offset);
IEnumerator<AddressMap.AddressChange> addrIter = mProject.AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
if (addrIter.Current.Offset >= offset) {
break;
}
}
int count = line.SubLineIndex;
while (addrIter.Current != null && addrIter.Current.Offset == offset) {
AddressMap.AddressChange change = addrIter.Current;
if (count == 0) {
return change.Region;
}
if (change.IsStart && !string.IsNullOrEmpty(change.Region.PreLabel)) {
count--;
if (count == 0) {
return change.Region;
}
}
count--;
addrIter.MoveNext();
}
return null;
}
private FormattedParts[] GenerateStringLines(int offset, string popcode,
List<string> operands) {
FormattedParts[] partsArray = new FormattedParts[operands.Count];

View File

@ -1531,10 +1531,10 @@ namespace SourceGen {
}
break;
case LineListGen.Line.Type.ArStartDirective:
case LineListGen.Line.Type.ArEndDirective:
if (CanEditAddress()) {
EditAddress();
}
// TODO(org): handle ArEndDirective
break;
case LineListGen.Line.Type.RegWidthDirective:
if (CanEditStatusFlags()) {
@ -1764,7 +1764,7 @@ namespace SourceGen {
}
public bool CanEditAddress() {
// First line must be code, data, or an ORG directive.
// First line must be code, data, or an AR directive.
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
if (selIndex < 0) {
return false;
@ -1772,13 +1772,25 @@ namespace SourceGen {
LineListGen.Line selLine = CodeLineList[selIndex];
if (selLine.LineType != LineListGen.Line.Type.Code &&
selLine.LineType != LineListGen.Line.Type.Data &&
selLine.LineType != LineListGen.Line.Type.ArStartDirective) {
// TODO(org): handle ArEnd
selLine.LineType != LineListGen.Line.Type.ArStartDirective &&
selLine.LineType != LineListGen.Line.Type.ArEndDirective) {
return false;
}
// If multiple lines are selected, there must not be an address change between them.
int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex();
// Can only start with .arend if it's single-selection.
if (selIndex != lastIndex && selLine.LineType == LineListGen.Line.Type.ArEndDirective) {
return false;
}
// If multiple lines with code/data are selected, there must not be an address change
// between them unless we're resizing a region. Determining whether or not a resize
// is valid is left to the edit dialog.
if (selLine.LineType == LineListGen.Line.Type.ArStartDirective) {
// Skip overlapping region check.
return true;
}
int firstOffset = CodeLineList[selIndex].FileOffset;
int lastOffset = CodeLineList[lastIndex].FileOffset;
if (firstOffset == lastOffset) {
@ -1786,17 +1798,18 @@ namespace SourceGen {
return true;
}
// Compute exclusive end point of selected range.
int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
foreach (AddressMap.AddressMapEntry ent in mProject.AddrMap) {
// It's okay to have an existing entry at firstOffset or nextOffset.
if (ent.Offset > firstOffset && ent.Offset < nextOffset) {
Debug.WriteLine("Found mid-selection AddressMap entry at +" +
ent.Offset.ToString("x6"));
return false;
}
if (!mProject.AddrMap.IsRangeUnbroken(firstOffset, nextOffset - firstOffset)) {
Debug.WriteLine("Found mid-selection AddressMap entry (len=" +
(nextOffset - firstOffset) + ")");
return false;
}
//Debug.WriteLine("First +" + firstOffset.ToString("x6") +
// ", last +" + lastOffset.ToString("x6") + ",next +" + nextOffset.ToString("x6"));
return true;
}
@ -1805,69 +1818,52 @@ namespace SourceGen {
int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex();
int firstOffset = CodeLineList[selIndex].FileOffset;
int lastOffset = CodeLineList[lastIndex].FileOffset;
//int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
AddressMap addrMap = mProject.AddrMap;
// TODO(org): rewrite this - need to identify the specific .ORG statement since
// there can now be several at a single offset
// Compute length of selection. May be zero if it's entirely .arstart/.arend.
int selectedLen = nextOffset - firstOffset;
int addr = addrMap.OffsetToAddress(firstOffset);
int newLen = lastOffset - firstOffset;
if (newLen == 0) {
newLen = 123;
AddressMap.AddressRegion curRegion;
if (CodeLineList[selIndex].LineType == LineListGen.Line.Type.ArStartDirective ||
CodeLineList[selIndex].LineType == LineListGen.Line.Type.ArEndDirective) {
// First selected line was .arstart/.arend, find the address map entry.
curRegion = CodeLineList.GetAddrRegionFromLine(selIndex);
Debug.Assert(curRegion != null);
Debug.WriteLine("Using region from " + CodeLineList[selIndex].LineType +
": " + curRegion);
} else {
if (selectedLen == 0) {
// A length of zero is only possible if nothing but directives were selected,
// but since the first entry wasn't .arstart/.arend this can't happen.
Debug.Assert(false);
return;
}
curRegion = null;
}
AddressMap.AddressMapEntry newEntry = new AddressMap.AddressMapEntry(firstOffset,
newLen /*DEBUG - replace*/, addr, string.Empty, false);
EditAddress dlg = new EditAddress(mMainWin, newEntry, true, newLen,
mProject, mFormatter);
AddressMap.AddressMapEntry newEntry = null;
if (curRegion == null) {
// No entry, create a new one.
int addr = addrMap.OffsetToAddress(firstOffset);
// Create a prototype entry with the various values.
newEntry = new AddressMap.AddressMapEntry(firstOffset,
selectedLen, addr, string.Empty, false);
Debug.WriteLine("New entry prototype: " + newEntry);
}
EditAddress dlg = new EditAddress(mMainWin, curRegion, newEntry,
selectedLen, firstOffset == lastOffset, mProject, mFormatter);
if (dlg.ShowDialog() != true) {
return;
}
//if (firstOffset == 0 && dlg.NewAddress < 0) {
// // Not allowed. The AddressMap will just put it back, which confuses
// // the undo operation.
// Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000");
// return;
//}
ChangeSet cs = new ChangeSet(1);
// TODO(org): I'm just commenting this out for now; needs to be totally redone
//if (addrMap.Get(firstOffset) != dlg.NewAddress) {
// // Added / removed / changed existing entry.
// //
// // We allow creation of an apparently redundant address override, because
// // sometimes it's helpful to add one to "anchor" an area before relocating
// // something that appears earlier in the file.
// int prevAddress = addrMap.Get(firstOffset);
// UndoableChange uc = UndoableChange.CreateAddressChange(firstOffset,
// prevAddress, dlg.NewAddress);
// cs.Add(uc);
// Debug.WriteLine("EditAddress: changing addr at offset +" +
// firstOffset.ToString("x6") + " to $" + dlg.NewAddress.ToString("x4"));
//}
// We want to create an entry for the chunk that follows the selected area.
// We don't modify the trailing address if an entry already exists.
// (Note the "can edit" code prevented us from being called if there's an
// address map entry in the middle of the selected area.)
//
//// If they're removing an existing entry, don't add a new entry at the end.
//if (nextAddr >= 0 && dlg.NewAddress != AddressMap.NO_ENTRY_ADDR &&
// addrMap.Get(nextOffset) == AddressMap.NO_ENTRY_ADDR) {
// // We don't screen for redundant entries here. That should only happen if
// // they select a range and then don't change the address. Maybe it's useful?
// int prevAddress = addrMap.Get(nextOffset);
// UndoableChange uc = UndoableChange.CreateAddressChange(nextOffset,
// prevAddress, nextAddr);
// cs.Add(uc);
// Debug.WriteLine("EditAddress: setting trailing addr at offset +" +
// nextOffset.ToString("x6") + " to $" + nextAddr.ToString("x4"));
//}
if (curRegion != dlg.ResultEntry) {
UndoableChange uc = UndoableChange.CreateAddressChange(curRegion, dlg.ResultEntry);
cs.Add(uc);
}
if (cs.Count > 0) {
ApplyUndoableChanges(cs);
} else {
@ -3855,6 +3851,8 @@ namespace SourceGen {
#region Info panel
private void UpdateInfoPanel() {
const string CRLF = "\r\n";
mMainWin.ClearInfoPanel();
if (mMainWin.CodeListView_GetSelectionCount() != 1) {
// Nothing selected, or multiple lines selected.
@ -3877,12 +3875,6 @@ namespace SourceGen {
case LineListGen.Line.Type.Blank:
lineTypeStr = "blank line";
break;
case LineListGen.Line.Type.ArStartDirective:
lineTypeStr = "address range start directive";
break;
case LineListGen.Line.Type.ArEndDirective:
lineTypeStr = "address range end directive";
break;
case LineListGen.Line.Type.RegWidthDirective:
lineTypeStr = "register width directive";
break;
@ -3890,6 +3882,14 @@ namespace SourceGen {
lineTypeStr = "data bank directive";
break;
case LineListGen.Line.Type.ArStartDirective:
isSimple = false;
lineTypeStr = "address range start directive";
break;
case LineListGen.Line.Type.ArEndDirective:
isSimple = false;
lineTypeStr = "address range end directive";
break;
case LineListGen.Line.Type.LocalVariableTable:
isSimple = false;
lineTypeStr = "variable table";
@ -3943,12 +3943,48 @@ namespace SourceGen {
}
#if DEBUG
mMainWin.InfoOffsetText = ("[offset=+" + line.FileOffset.ToString("x6") + "]");
mMainWin.InfoOffsetText = "[offset=+" + line.FileOffset.ToString("x6") +
" sub=" + line.SubLineIndex + "]";
#endif
if (isSimple) {
return;
}
if (line.LineType == LineListGen.Line.Type.ArStartDirective ||
line.LineType == LineListGen.Line.Type.ArEndDirective) {
AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(lineIndex);
StringBuilder esb = new StringBuilder();
esb.Append("Address: ");
if (region.Address == AddressMap.NON_ADDR) {
esb.Append("non-addressable");
} else {
esb.Append("$" +
mFormatter.FormatAddress(region.Address, !mProject.CpuDef.HasAddr16));
}
esb.Append(CRLF);
esb.Append("Start: " + mFormatter.FormatOffset24(region.Offset));
esb.Append(CRLF);
esb.Append("End: ");
esb.Append(mFormatter.FormatOffset24(region.Offset + region.ActualLength - 1));
esb.Append(CRLF);
esb.Append("Length: " + region.ActualLength + " / " +
mFormatter.FormatHexValue(region.ActualLength, 2));
if (region.Length == AddressMap.FLOATING_LEN) {
esb.Append(" (floating)");
}
esb.Append(CRLF);
if (!string.IsNullOrEmpty(region.PreLabel)) {
esb.Append("Pre-label: '" + region.PreLabel + "' addr=$");
esb.Append(mFormatter.FormatAddress(region.PreLabelAddress,
!mProject.CpuDef.HasAddr16));
esb.Append(CRLF);
}
esb.Append("Relative: " + region.IsRelative);
esb.Append(CRLF);
mMainWin.InfoPanelDetail1 = esb.ToString();
return;
}
if (line.LineType == LineListGen.Line.Type.LocalVariableTable) {
string str = string.Empty;
if (mProject.LvTables.TryGetValue(line.FileOffset,

View File

@ -607,9 +607,10 @@ namespace SourceGen.Tools.Omf {
while (true) {
// Generate an ORG directive.
int origAddr = proj.AddrMap.OffsetToAddress(bufOffset);
UndoableChange uc = UndoableChange.CreateAddressChange(bufOffset,
origAddr, addr);
//int origAddr = proj.AddrMap.OffsetToAddress(bufOffset);
AddressMap.AddressMapEntry addrEnt = new AddressMap.AddressMapEntry(bufOffset,
AddressMap.FLOATING_LEN, addr, string.Empty, false);
UndoableChange uc = UndoableChange.CreateAddressChange(null, addrEnt);
cs.Add(uc);
// Compare amount of space in this bank to amount left in segment.

View File

@ -188,20 +188,29 @@ namespace SourceGen {
/// Creates an UndoableChange for an address map update.
/// </summary>
/// <param name="offset">Affected offset.</param>
/// <param name="oldAddress">Previous address map entry, or -1 if none.</param>
/// <param name="newAddress">New address map entry, or -1 if none.</param>
/// <param name="oldEntry">Previous address map entry, or null if none.</param>
/// <param name="newEntry">New address map entry, or null for deletion.</param>
/// <returns>Change record.</returns>
public static UndoableChange CreateAddressChange(int offset, int oldAddress,
int newAddress) {
if (oldAddress == newAddress) {
Debug.WriteLine("No-op address change at +" + offset.ToString("x6") +
": " + oldAddress);
public static UndoableChange CreateAddressChange(AddressMap.AddressMapEntry oldEntry,
AddressMap.AddressMapEntry newEntry) {
int offset;
if (oldEntry != null) {
offset = oldEntry.Offset;
} else if (newEntry != null) {
offset = newEntry.Offset;
} else {
// Shouldn't happen.
Debug.Assert(false);
offset = -1;
}
if (oldEntry == newEntry) {
Debug.WriteLine("No-op address change at +" + offset.ToString("x6"));
}
UndoableChange uc = new UndoableChange();
uc.Type = ChangeType.SetAddress;
uc.Offset = offset;
uc.OldValue = oldAddress;
uc.NewValue = newAddress;
uc.OldValue = oldEntry;
uc.NewValue = newEntry;
uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
return uc;
}

View File

@ -22,20 +22,60 @@ limitations under the License.
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Edit Address Range"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
Title="Set Address Range Properties"
Width="350" SizeToContent="Height" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
ContentRendered="Window_ContentRendered">
<Window.Resources>
<system:String x:Key="str_HdrCreate">Creating new address range, with properties:</system:String>
<system:String x:Key="str_HdrEdit">Editing existing address range, with properties:</system:String>
<system:String x:Key="str_HdrResize">Resizing existing address range, with properties:</system:String>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<system:String x:Key="str_HdrCreate">Creating new address region</system:String>
<system:String x:Key="str_HdrEdit">Editing existing address region:</system:String>
<system:String x:Key="str_OptEditAsIs">
Edit the region's attributes. Region length will not be changed.
</system:String>
<system:String x:Key="str_OptEditAndFix">
Edit the region's attributes, and convert the floating end to a fixed end.
</system:String>
<system:String x:Key="str_OptResize">
Edit the region's attributes, and resize it to the selection. The new
end offset will be {0}, for a length of {1}.
</system:String>
<system:String x:Key="str_CreateFixed">
Create a new region, with a fixed end point. The region will start at {0}
and have a length of {1}.
</system:String>
<system:String x:Key="str_CreateFixedFail">
Unable to create a new region with a fixed end point here.
</system:String>
<system:String x:Key="str_CreateFloating">
Create a new region, with a floating end point. The region will start at {0}
and end at the next region boundary. Currently, the length is {1}, but that
may change as other regions are added and removed.
</system:String>
<system:String x:Key="str_CreateFloatingFail">
Unable to create a new region with a floating end point here.
</system:String>
<system:String x:Key="str_ErrInternal">Internal error.</system:String>
<system:String x:Key="str_ErrInvalidValue">Internal error (invalid value).</system:String>
<system:String x:Key="str_ErrOverlapExisting">
The new region has the same start offset and length as an existing region.
</system:String>
<system:String x:Key="str_ErrOverlapFloating">
The start offset of the new region is the same as the start offset of an existing
region, and one or both have a floating end point.
</system:String>
<system:String x:Key="str_ErrStraddleExisting">
The new region straddles the start or end of an existing region.
</system:String>
</Window.Resources>
<StackPanel Margin="8">
<TextBlock Name="headerText" Text="{StaticResource str_HdrCreate}"/>
<Grid>
<TextBlock Text="{Binding OperationStr, FallbackValue=Editing existing region}"/>
<Grid Visibility="{Binding ShowExistingRegion, Converter={StaticResource BoolToVis}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
@ -44,6 +84,7 @@ limitations under the License.
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="8,0,0,0" Text="Start:"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="4,2,0,0"
@ -51,23 +92,47 @@ limitations under the License.
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="0" Grid.Row="1" Margin="8,0,0,0" Text="End:"/>
<TextBlock Grid.Column="1" Grid.Row="1" Margin="4,2,0,0"
Text="{Binding RegionEndOffsetStr, FallbackValue=+123456}"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="0" Grid.Row="2" Margin="8,0,0,0" Text="Length:"/>
<StackPanel Grid.Column="1" Grid.Row="2" Margin="4,0,0,0" Orientation="Horizontal">
<StackPanel Grid.Column="1" Grid.Row="1" Margin="4,0,0,0" Orientation="Horizontal">
<TextBlock Margin="0,2,0,0"
Text="{Binding RegionLengthStr, FallbackValue=23456 / $1234}"
Text="{Binding RegionEndOffsetStr, FallbackValue=+123456}"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text="(floating)" Margin="4,0,0,0" FontWeight="Bold"
Visibility="{Binding FloatTextVis}"/>
Visibility="{Binding IsFloating, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
<TextBlock Grid.Column="0" Grid.Row="2" Margin="8,0,0,0" Text="Length:"/>
<TextBlock Grid.Column="1" Grid.Row="2" Margin="4,2,0,0"
Text="{Binding RegionLengthStr, FallbackValue=23456 ($1234)}"
FontFamily="{StaticResource GeneralMonoFont}"/>
</Grid>
<!-- failure message goes here -->
<RadioButton Name="radioOption1" Margin="0,12,0,0"
Visibility="{Binding ShowOption1, Converter={StaticResource BoolToVis}}"
IsEnabled="{Binding EnableOption1}"
IsChecked="{Binding CheckOption1}">
<RadioButton.Content>
<TextBlock TextWrapping="Wrap" Text="{Binding Option1Str, FallbackValue=Option 1}"/>
</RadioButton.Content>
</RadioButton>
<RadioButton Name="radioOption2" Margin="0,4,0,0"
Visibility="{Binding ShowOption2, Converter={StaticResource BoolToVis}}"
IsEnabled="{Binding EnableOption2}"
IsChecked="{Binding CheckOption2}">
<RadioButton.Content>
<TextBlock TextWrapping="Wrap" Text="{Binding Option2Str, FallbackValue=Option 2}"/>
</RadioButton.Content>
</RadioButton>
<StackPanel IsEnabled="{Binding IsRegionValid}">
<Border Visibility="{Binding ShowErrorMessage, Converter={StaticResource BoolToVis}}"
BorderThickness="1" Padding="1" Margin="0,6"
BorderBrush="Red">
<TextBlock TextWrapping="Wrap" Width="300"
Text="{Binding ErrorMessageStr, FallbackValue=Error Message}"/>
</Border>
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2" Margin="0,8,0,0"/>
<StackPanel IsEnabled="{Binding EnableAttributeControls}">
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Text="Address (hex):"/>
@ -84,38 +149,41 @@ limitations under the License.
</StackPanel>
<TextBlock Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00." Margin="0,4,0,0"/>
<TextBlock Text="• Leave the field blank to remove the address override."/>
<TextBlock Text="• Enter 'NA' if the region is non-addressable."/>
</StackPanel>
<GroupBox Header="Advanced" Padding="2,4" Margin="0,12,0,0">
<StackPanel>
<CheckBox Content="Use relative addressing"
IsChecked="{Binding UseRelativeAddressing}"/>
<StackPanel IsEnabled="{Binding EnableAttributeControls}">
<GroupBox Header="Advanced" Padding="2,4" Margin="0,12,0,0">
<StackPanel>
<CheckBox Content="Use relative addressing"
IsChecked="{Binding UseRelativeAddressing}"/>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0">
<TextBlock Text="Pre-label:"/>
<TextBox Name="preLabelTextBox" Width="100" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding Path=PreLabelText, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="(address: " Margin="8,0,0,0"/>
<TextBlock Text="{Binding Path=PreLabelAddressStr, FallbackValue=$1234}"
Margin="0,2,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=")"/>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0">
<TextBlock Text="Pre-label:"/>
<TextBox Name="preLabelTextBox" Width="100" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding Path=PreLabelText, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="(address: " Margin="8,0,0,0"/>
<TextBlock Text="{Binding Path=PreLabelAddressStr, FallbackValue=$1234}"
Margin="0,2,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=")"/>
</StackPanel>
<TextBlock Text="• Must be valid label syntax; may not be a local label." Margin="0,4,0,0"/>
<TextBlock Text="• Must not be a duplicate of an existing label."/>
<TextBlock Text="• Will not appear if parent region is non-addressable."/>
</StackPanel>
<TextBlock Text="• Must be valid label syntax; may not be a local label." Margin="0,4,0,0"/>
<TextBlock Text="• Must not be a duplicate of an existing label."/>
<TextBlock Text="• Will be ignored if preceding region is non-addressable."/>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="OK" IsDefault="True" Width="70"
IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>
<Button Content="Cancel" IsCancel="True" Width="70" Margin="4,0,0,0"/>
</GroupBox>
</StackPanel>
<DockPanel Margin="0,10,0,0" LastChildFill="False">
<Button DockPanel.Dock="Left" Content="Delete Region" Width="120"
IsEnabled="{Binding CanDeleteRegion}" Click="DeleteRegion_Click"/>
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="4,0,0,0" IsCancel="True"/>
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
IsDefault="True" IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>
</DockPanel>
</StackPanel>
</Window>

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@ -28,43 +27,129 @@ namespace SourceGen.WpfGui {
/// Edit Address Region dialog.
/// </summary>
public partial class EditAddress : Window, INotifyPropertyChanged {
/// <summary>
/// Updated address map entry. Will be null if we want to delete the entry.
/// </summary>
public AddressMap.AddressMapEntry NewEntry { get; private set; }
private const string NON_ADDR_STR = "NA";
/// <summary>
/// Offset being edited.
/// Updated address map entry. Will be null if we want to delete the existing entry.
/// </summary>
public AddressMap.AddressMapEntry ResultEntry { get; private set; }
/// <summary>
/// Dialog header.
/// </summary>
public string OperationStr {
get { return mOperationStr; }
set { mOperationStr = value; OnPropertyChanged(); }
}
private string mOperationStr;
/// <summary>
/// Offset of first selected byte. (Does not change.)
/// </summary>
private int mRegionStartOffset;
public string RegionStartOffsetStr {
get { return mFormatter.FormatOffset24(mRegionStartOffset); }
}
private int mRegionStartOffset;
/// <summary>
/// Offset after the end of the selection, or -1 if only one line is selected.
/// Offset of last selected byte. (Does not change.)
/// </summary>
private int mRegionEndOffset;
public string RegionEndOffsetStr {
get { return mFormatter.FormatOffset24(mRegionEndOffset); }
}
private int mRegionEndOffset;
public string RegionLengthStr {
get {
int count = mRegionEndOffset - mRegionStartOffset;
return count.ToString() + " (" + mFormatter.FormatHexValue(count, 2) + ")";
int count = mRegionEndOffset - mRegionStartOffset + 1;
return FormatLength(count);
}
}
/// <summary>
/// Set to true to show the offset/length stats for a current region.
/// </summary>
public bool ShowExistingRegion {
get { return mShowExistingRegion; }
set { mShowExistingRegion = value; OnPropertyChanged(); }
}
private bool mShowExistingRegion;
public bool ShowOption1 {
get { return mShowOption1; }
set { mShowOption1 = value; OnPropertyChanged(); }
}
private bool mShowOption1;
public bool ShowOption2 {
get { return mShowOption2; }
set { mShowOption2 = value; OnPropertyChanged(); }
}
private bool mShowOption2;
public bool EnableOption1 {
get { return mEnableOption1; }
set { mEnableOption1 = value; OnPropertyChanged(); }
}
private bool mEnableOption1;
public bool EnableOption2 {
get { return mEnableOption2; }
set { mEnableOption2 = value; OnPropertyChanged(); }
}
private bool mEnableOption2;
public bool CheckOption1 {
get { return mCheckOption1; }
set { mCheckOption1 = value; OnPropertyChanged(); }
}
private bool mCheckOption1;
public bool CheckOption2 {
get { return mCheckOption2; }
set { mCheckOption2 = value; OnPropertyChanged(); }
}
private bool mCheckOption2;
public string Option1Str {
get { return mOption1Str; }
set { mOption1Str = value; OnPropertyChanged(); }
}
private string mOption1Str;
public string Option2Str {
get { return mOption2Str; }
set { mOption2Str = value; OnPropertyChanged(); }
}
private string mOption2Str;
/// <summary>
/// Address at which a pre-label would be placed. This is determined by the parent
/// region, so its value is fixed.
/// </summary>
private int mPreLabelAddress;
public string PreLabelAddressStr {
get { return mFormatter.FormatOffset24(mRegionEndOffset); }
get {
if (mPreLabelAddress == AddressMap.NON_ADDR) {
return "NA";
} else {
return "$" + mFormatter.FormatAddress(mPreLabelAddress, mShowBank);
}
}
}
private bool mShowBank;
public bool ShowErrorMessage {
get { return mShowErrorMessage; }
set { mShowErrorMessage = value; OnPropertyChanged(); }
}
private bool mShowErrorMessage;
public string ErrorMessageStr {
get { return mErrorMessageStr; }
set { mErrorMessageStr = value; OnPropertyChanged(); }
}
private string mErrorMessageStr;
/// <summary>
/// Address input TextBox.
@ -100,22 +185,31 @@ namespace SourceGen.WpfGui {
private bool mIsValid;
/// <summary>
/// Set to true when requested region is valid. Everything but the cancel button is
/// disabled if not.
/// Set to true unless there are no valid options (e.g. invalid new region).
/// </summary>
public bool IsRegionValid {
get { return mIsRegionValid; }
set { mIsRegionValid = value; OnPropertyChanged(); }
public bool EnableAttributeControls {
get { return mEnableAttributeControls; }
set { mEnableAttributeControls = value; OnPropertyChanged(); }
}
private bool mIsRegionValid;
private bool mEnableAttributeControls;
/// <summary>
/// Determines whether the "(floating)" message appears next to the length.
/// Set to true if the region has a floating end point.
/// </summary>
public Visibility FloatTextVis {
get { return mFloatTextVis; }
public bool IsFloating {
get { return mIsFloating; }
set { mIsFloating = value; OnPropertyChanged(); }
}
private Visibility mFloatTextVis;
private bool mIsFloating;
/// <summary>
/// Set to true if the region is not new, and thus can be deleted.
/// </summary>
public bool CanDeleteRegion {
get { return mCanDeleteRegion; }
set { mCanDeleteRegion = value; OnPropertyChanged(); }
}
private bool mCanDeleteRegion;
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
@ -123,7 +217,15 @@ namespace SourceGen.WpfGui {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private AddressMap.AddressRegion mNewRegion;
/// <summary>
/// Result for option #1.
/// </summary>
private AddressMap.AddressMapEntry mResultEntry1;
/// <summary>
/// Result for option #2.
/// </summary>
private AddressMap.AddressMapEntry mResultEntry2;
/// <summary>
/// Maximum allowed address value, based on CPU type.
@ -145,95 +247,228 @@ namespace SourceGen.WpfGui {
/// Constructor.
/// </summary>
/// <param name="owner">Parent window.</param>
/// <param name="entry">Map entry definition. This may be an existing entry, or values
/// representing the selection.</param>
/// <param name="newLength">Length of region. Only used if we're resizing an
/// existing region.</param>
/// <param name="curRegion">Current region; will be null for new entries.</param>
/// <param name="newEntry">Prototype entry to create.</param>
/// <param name="selectionLen">Length, in bytes, of the selection.</param>
/// <param name="isSingleLine">True if the selection is a single line.</param>
/// <param name="project">Project reference.</param>
/// <param name="formatter">Text formatter object.</param>
public EditAddress(Window owner, AddressMap.AddressMapEntry entry, bool isNew,
int newLength, DisasmProject project, Formatter formatter) {
public EditAddress(Window owner, AddressMap.AddressRegion curRegion,
AddressMap.AddressMapEntry newEntry, int selectionLen, bool isSingleLine,
DisasmProject project, Formatter formatter) {
InitializeComponent();
Owner = owner;
DataContext = this;
Debug.Assert((curRegion == null) ^ (newEntry == null)); // exactly one must be true
mProject = project;
mMaxAddressValue = project.CpuDef.MaxAddressValue;
mShowBank = !project.CpuDef.HasAddr16;
mFormatter = formatter;
Configure(entry, isNew, newLength);
Configure(curRegion, newEntry, selectionLen, isSingleLine);
UpdateControls();
}
private void Configure(AddressMap.AddressMapEntry entry, bool isNew, int newLength) {
mRegionStartOffset = mRegionEndOffset = entry.Offset;
mPreLabelAddress = 0;
IsRegionValid = false;
private void Configure(AddressMap.AddressRegion curRegion,
AddressMap.AddressMapEntry newEntry, int selectionLen, bool isSingleLine) {
Debug.WriteLine("Configuring AR: reg=" + curRegion + " newEnt=" + newEntry +
" selLen=" + selectionLen + " isSingle=" + isSingleLine);
// 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<AddressMap.AddressMapEntry> 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;
}
}
ShowOption1 = ShowOption2 = true;
EnableOption1 = EnableOption2 = true;
CheckOption1 = true;
EnableAttributeControls = true;
if (curRegion != null) {
// Editing an existing region.
CanDeleteRegion = true;
ShowExistingRegion = true;
AddressText = Asm65.Address.AddressToString(curRegion.Address, false);
PreLabelText = curRegion.PreLabel;
UseRelativeAddressing = curRegion.IsRelative;
OperationStr = (string)FindResource("str_HdrEdit");
mRegionStartOffset = curRegion.Offset;
mRegionEndOffset = curRegion.Offset + curRegion.ActualLength - 1;
mPreLabelAddress = curRegion.PreLabelAddress;
if (isSingleLine) {
// Only thing selected was .arstart/.arend. First action is to edit
// the region properties, second action is to convert floating end
// to fixed.
mResultEntry1 = new AddressMap.AddressMapEntry(curRegion.Offset,
curRegion.Length, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
Option1Str = (string)FindResource("str_OptEditAsIs");
Option2Str = (string)FindResource("str_OptEditAndFix");
if (curRegion.IsFloating) {
mResultEntry2 = new AddressMap.AddressMapEntry(curRegion.Offset,
curRegion.ActualLength, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
} else {
mResultEntry2 = null;
EnableOption2 = false; // show it, but disabled
}
// 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;
// Selection started with .arstart and included multiple lines. First
// action is to resize region. Second action is edit without resize.
// If resize is illegal (e.g. new region exactly overlaps another),
// first action is disabled.
mResultEntry1 = new AddressMap.AddressMapEntry(curRegion.Offset,
selectionLen, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
mResultEntry2 = new AddressMap.AddressMapEntry(curRegion.Offset,
curRegion.Length, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
string fmt = (string)FindResource("str_OptResize");
Option1Str = string.Format(fmt,
mFormatter.FormatOffset24(curRegion.Offset + selectionLen - 1),
FormatLength(selectionLen));
Option2Str = (string)FindResource("str_OptEditAsIs");
Debug.Assert(selectionLen > 0);
AddressMap.AddResult ares;
TryCreateRegion(curRegion, curRegion.Offset, selectionLen,
curRegion.Address, out ares);
if (ares != AddressMap.AddResult.Okay) {
// Can't create the new region, so disable that option (still visible).
EnableOption1 = false;
CheckOption2 = true;
}
}
} else {
// Creating a new region. Prototype entry specifies offset, length, and address.
// First action is to create a fixed-length region, second action is to create
// a floating region. Default changes for single-item selections.
CanDeleteRegion = false;
ShowExistingRegion = false;
AddressText = Asm65.Address.AddressToString(newEntry.Address, false);
PreLabelText = string.Empty;
UseRelativeAddressing = false;
OperationStr = (string)FindResource("str_HdrCreate");
AddressMap.AddResult ares1;
AddressMap.AddressRegion newRegion1 = TryCreateRegion(null, newEntry.Offset,
newEntry.Length, newEntry.Address, out ares1);
AddressMap.AddResult ares2;
AddressMap.AddressRegion newRegion2 = TryCreateRegion(null, newEntry.Offset,
AddressMap.FLOATING_LEN, newEntry.Address, out ares2);
if (isSingleLine) {
// For single-line selection, create a floating region by default.
CheckOption2 = true;
}
// If it failed, report the error. Most common reason will be a start offset
// that overlaps an existing region. You can create a fixed region inside
// a fixed region with the same start offset, but can't create a float there.
if (ares1 == AddressMap.AddResult.Okay) {
mResultEntry1 = new AddressMap.AddressMapEntry(newEntry.Offset,
newRegion1.ActualLength, newEntry.Address, string.Empty, false);
string fmt = (string)FindResource("str_CreateFixed");
Option1Str = string.Format(fmt,
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion1.ActualLength));
} else {
Option1Str = (string)FindResource("str_CreateFixedFail");
CheckOption2 = true;
EnableOption1 = false;
}
if (ares2 == AddressMap.AddResult.Okay) {
mResultEntry2 = new AddressMap.AddressMapEntry(newEntry.Offset,
AddressMap.FLOATING_LEN, newEntry.Address, string.Empty, false);
string fmt = (string)FindResource("str_CreateFloating");
Option2Str = string.Format(fmt,
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion2.ActualLength));
} else {
Option2Str = (string)FindResource("str_CreateFloatingFail");
CheckOption1 = true;
CheckOption2 = false; // required for some reason
EnableOption2 = false;
}
if (ares1 != AddressMap.AddResult.Okay && ares2 != AddressMap.AddResult.Okay) {
// Unable to create region here. Explain why not.
EnableAttributeControls = false;
CheckOption1 = CheckOption2 = false;
SetErrorString(ares1);
}
}
}
private string FormatLength(int len) {
return len + " (" + mFormatter.FormatHexValue(len, 2) + ")";
}
private AddressMap.AddressRegion TryCreateRegion(AddressMap.AddressRegion delRegion,
int offset, int length, int addr, out AddressMap.AddResult result) {
AddressMap tmpMap = mProject.AddrMap.Clone();
if (delRegion != null && !tmpMap.RemoveEntry(delRegion.Offset, delRegion.Length)) {
Debug.Assert(false, "Failed to remove existing region");
result = AddressMap.AddResult.InternalError;
return null;
}
result = tmpMap.AddEntry(offset, length, addr);
if (result != AddressMap.AddResult.Okay) {
return null;
}
AddressMap.AddressRegion newRegion = tmpMap.FindRegion(offset, length);
if (newRegion == null) {
// Shouldn't happen.
Debug.Assert(false, "Failed to find region we just created");
result = AddressMap.AddResult.InternalError;
return null;
}
return newRegion;
}
private void SetErrorString(AddressMap.AddResult result) {
string rsrc;
switch (result) {
case AddressMap.AddResult.InternalError:
rsrc = "str_ErrInternal";
break;
case AddressMap.AddResult.InvalidValue:
rsrc = "str_ErrInvalidValue";
break;
case AddressMap.AddResult.OverlapExisting:
rsrc = "str_ErrOverlapExisting";
break;
case AddressMap.AddResult.OverlapFloating:
rsrc = "str_ErrOverlapFloating";
break;
case AddressMap.AddResult.StraddleExisting:
rsrc = "str_ErrStraddelExisting";
break;
default:
Debug.Assert(false);
rsrc = "str_ErrInternal";
break;
}
ErrorMessageStr = (string)FindResource(rsrc); // throws exception on failure
ShowErrorMessage = true;
}
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;
}
/// <summary>
/// Handles a TextChanged event on the address text box.
/// </summary>
@ -242,24 +477,34 @@ namespace SourceGen.WpfGui {
/// for TextBox is LostFocus.
/// </remarks>
private void UpdateControls() {
IsValid = IsRegionValid && ParseAddress(out int unused);
IsValid = EnableAttributeControls && ParseAddress(out int unused);
// TODO(org): check pre-label syntax
}
private const string NON_ADDR_STR = "NA";
private void OkButton_Click(object sender, RoutedEventArgs e) {
bool ok = ParseAddress(out int addr);
Debug.Assert(ok);
AddressMap.AddressMapEntry baseEntry;
if (CheckOption1) {
baseEntry = mResultEntry1;
} else {
baseEntry = mResultEntry2;
}
// Combine base entry with pre-label string and relative addressing checkbox.
ResultEntry = new AddressMap.AddressMapEntry(baseEntry.Offset,
baseEntry.Length, addr, PreLabelText, UseRelativeAddressing);
Debug.WriteLine("Dialog result: " + ResultEntry);
DialogResult = true;
}
/// <summary>
/// Parses the address out of the AddressText text box.
/// </summary>
/// <param name="addr">Receives the parsed address. Will be NON_ADDR for "NA", and
/// INVALID_ADDR if blank.</param>
/// <param name="addr">Receives the parsed address. Will be NON_ADDR for "NA".</param>
/// <returns>True if the string parsed successfully.</returns>
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) {
@ -269,5 +514,10 @@ namespace SourceGen.WpfGui {
// Parse numerically.
return Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out addr);
}
private void DeleteRegion_Click(object sender, RoutedEventArgs e) {
ResultEntry = null;
DialogResult = true;
}
}
}
}