diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs
index 1e295c1..caddc2a 100644
--- a/CommonUtil/AddressMap.cs
+++ b/CommonUtil/AddressMap.cs
@@ -308,6 +308,11 @@ namespace CommonUtil {
///
public int EntryCount { get { return mMapEntries.Count; } }
+ ///
+ /// Number of bytes spanned by the address map.
+ ///
+ public int Length { get { return mSpanLength; } }
+
///
/// Error codes for AddEntry().
///
@@ -1086,27 +1091,37 @@ namespace CommonUtil {
///
/// Instances are immutable.
///
+ ///
+ /// We use inclusive Offset values for both start and end. If we don't do this, the
+ /// offset for end records will be outside the file bounds. It also gets a bit painful
+ /// when the display list tries to update [M,N] if put the end at N+1.
+ ///
public class AddressChange {
// True if this is a region start, false if a region end.
public bool IsStart { get; private set; }
- // Offset at which change occurs. For end points, this at the offset AFTER
- // the last offset in a region.
+ // Offset at which change occurs. For end points, this is the last offset in
+ // the region (i.e. an inclusive end point).
public int Offset { get; private set; }
- // Address at Offset after change. For a region-end change, this is an address
- // in the parent's range.
+ // Address at Offset after change. For a region-end change, this is the address
+ // in the parent's range for the following offset.
public int Address { get; private set; }
// Reference to the AddressRegion that generated this entry. The reference
// will be the same for the "start" and "end" entries.
public AddressRegion Region { get; private set; }
- public AddressChange(bool isStart, int offset, int addr, AddressRegion region) {
+ // True if this region was synthesized to plug a hole.
+ public bool IsSynthetic { get; private set; }
+
+ public AddressChange(bool isStart, int offset, int addr, AddressRegion region,
+ bool isSynth) {
IsStart = isStart;
Offset = offset;
Address = addr;
Region = region;
+ IsSynthetic = isSynth;
}
}
@@ -1129,8 +1144,10 @@ namespace CommonUtil {
Debug.Assert(node.Region.Offset > startOffset);
AddressRegion tmpReg = new AddressRegion(startOffset,
node.Region.Offset - startOffset, NON_ADDR);
- changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpReg));
- changeList.Add(new AddressChange(false, node.Region.Offset, NON_ADDR, tmpReg));
+ changeList.Add(new AddressChange(true, startOffset, NON_ADDR,
+ tmpReg, true));
+ changeList.Add(new AddressChange(false, node.Region.Offset - 1, NON_ADDR,
+ tmpReg, true));
extraNodes++;
}
@@ -1145,8 +1162,8 @@ namespace CommonUtil {
Debug.Assert(startOffset < mSpanLength);
AddressRegion tmpReg = new AddressRegion(startOffset,
mSpanLength - startOffset, NON_ADDR);
- changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpReg));
- changeList.Add(new AddressChange(false, mSpanLength, NON_ADDR, tmpReg));
+ changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpReg, true));
+ changeList.Add(new AddressChange(false, mSpanLength - 1, NON_ADDR, tmpReg, true));
extraNodes++;
}
@@ -1177,9 +1194,9 @@ namespace CommonUtil {
nextAddr = parentStartAddr + node.Region.ActualLength;
}
AddressChange startChange = new AddressChange(true,
- node.Region.Offset, node.Region.Address, node.Region);
+ node.Region.Offset, node.Region.Address, node.Region, false);
AddressChange endChange = new AddressChange(false,
- node.Region.Offset + node.Region.ActualLength, nextAddr, node.Region);
+ node.Region.Offset + node.Region.ActualLength - 1, nextAddr, node.Region, false);
changeList.Add(startChange);
int curAddr = node.Region.Address;
@@ -1206,69 +1223,71 @@ namespace CommonUtil {
public string FormatAddressMap() {
StringBuilder sb = new StringBuilder();
int depth = 0;
- AddressChange prevChange = null;
+ int prevOffset = -1;
+ int prevAddr = 0;
sb.AppendLine("Address map, len=$" + mSpanLength.ToString("x4"));
IEnumerator iter = this.AddressChangeIterator;
while (iter.MoveNext()) {
AddressChange change = iter.Current;
if (change.IsStart) {
- if (prevChange != null && change.Offset != prevChange.Offset) {
+ if (prevOffset >= 0 && change.Offset != prevOffset) {
// Start of region at new offset. Output address info for space
// between previous start or end.
- sb.Append(" ");
- PrintAddressInfo(sb, depth, prevChange.Address,
- change.Offset - prevChange.Offset);
+ PrintAddressInfo(sb, depth, prevAddr, change.Offset - prevOffset);
}
// Start following end, or start following start after a gap.
if (!string.IsNullOrEmpty(change.Region.PreLabel)) {
- sb.Append(" ");
- PrintDepthLines(sb, depth);
+ PrintDepthLines(sb, depth, true);
sb.Append("| pre='" + change.Region.PreLabel + "' ");
- if (change.Region.PreLabelAddress != NON_ADDR) {
- sb.Append("$" + change.Region.PreLabelAddress.ToString("x4"));
- } else {
- sb.Append("(non-addr)");
- }
+ PrintAddress(sb, change.Region.PreLabelAddress);
sb.Append(CRLF);
}
sb.Append("+" + change.Offset.ToString("x6"));
- PrintDepthLines(sb, depth);
+ PrintDepthLines(sb, depth, false);
sb.Append("+- " + "START (");
PrintAddress(sb, change.Address);
sb.Append(")");
+ if (change.IsSynthetic) {
+ sb.Append(" (auto-generated)");
+ }
sb.Append(CRLF);
+ prevOffset = change.Offset;
+ prevAddr = change.Address;
depth++;
} else {
- Debug.Assert(prevChange != null);
+ Debug.Assert(prevOffset >= 0);
depth--;
- if (change.Offset != prevChange.Offset) {
+ if (change.Offset + 1 != prevOffset) {
// End of region at new offset. Output address info for space
// between previous start or end.
- sb.Append(" ");
- PrintAddressInfo(sb, depth + 1, prevChange.Address,
- change.Offset - prevChange.Offset);
+ PrintAddressInfo(sb, depth + 1, prevAddr, change.Offset + 1 - prevOffset);
}
sb.Append("+" + change.Offset.ToString("x6"));
- PrintDepthLines(sb, depth);
+ PrintDepthLines(sb, depth, false);
sb.Append("+- " + "END (now ");
PrintAddress(sb, change.Address);
sb.Append(")");
sb.Append(CRLF);
- }
- prevChange = change;
+ // Use offset+1 here so it lines up with start records.
+ prevOffset = change.Offset + 1;
+ prevAddr = change.Address;
+ }
}
Debug.Assert(depth == 0);
return sb.ToString();
}
- private static void PrintDepthLines(StringBuilder sb, int depth) {
+ private static void PrintDepthLines(StringBuilder sb, int depth, bool doIndent) {
+ if (doIndent) {
+ sb.Append(" ");
+ }
sb.Append(" ");
while (depth-- > 0) {
sb.Append("| ");
@@ -1277,7 +1296,7 @@ namespace CommonUtil {
private static void PrintAddressInfo(StringBuilder sb, int depth,
int startAddr, int length) {
- PrintDepthLines(sb, depth);
+ PrintDepthLines(sb, depth, true);
sb.Append(' ');
if (startAddr == NON_ADDR) {
sb.Append("-NA-");
diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs
index 390db74..c63a48c 100644
--- a/SourceGen/AsmGen/GenCommon.cs
+++ b/SourceGen/AsmGen/GenCommon.cs
@@ -135,12 +135,12 @@ namespace SourceGen.AsmGen {
}
// Check for address region ends. There may be more than one at a given offset.
- // The end-region offset is listed as the byte *following* the instruction
- // or data item, so it should match the updated offset.
+ // The end-region offset will be the last byte of the instruction or data item,
+ // so it should be one less than the updated offset.
//
// If we encounter a region start, we'll handle that at the top of the next
// loop iteration.
- while (change != null && change.Offset == offset) {
+ while (change != null && change.Offset + 1 == offset) {
if (!change.IsStart) {
gen.OutputArDirective(change.Region, change.IsStart);
addrIter.MoveNext();
diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs
index c1a1571..2714e4d 100644
--- a/SourceGen/CodeAnalysis.cs
+++ b/SourceGen/CodeAnalysis.cs
@@ -362,13 +362,19 @@ namespace SourceGen {
for (int offset = 0; offset < mAnattribs.Length; offset++) {
// Process all change events at this offset.
AddressMap.AddressChange change = addrIter.Current;
- while (change != null && change.Offset == offset) {
+ while (change != null && change.IsStart && change.Offset == offset) {
addr = change.Address;
addrIter.MoveNext();
change = addrIter.Current;
}
mAnattribs[offset].Address = addr++;
+
+ while (change != null && !change.IsStart && change.Offset == offset) {
+ addr = change.Address;
+ addrIter.MoveNext();
+ change = addrIter.Current;
+ }
}
}
diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs
index 2ad9aaa..29911a5 100644
--- a/SourceGen/LineListGen.cs
+++ b/SourceGen/LineListGen.cs
@@ -191,6 +191,15 @@ namespace SourceGen {
}
}
+ ///
+ /// True if this line is an address range start or end.
+ ///
+ public bool IsAddressRangeDirective {
+ get {
+ return LineType == Type.ArStartDirective || LineType == Type.ArEndDirective;
+ }
+ }
+
///
/// Returns true if the specified offset is represented by this line. There
/// will be only one code/data line for a given offset, but there may be
@@ -822,14 +831,23 @@ namespace SourceGen {
// Blank lines and comments can appear before or after code/data. They
// must have the offset of the associated line, and a span of zero.
if (line.FileOffset != expectedOffset && line.FileOffset != lastOffset) {
- Debug.WriteLine("ValidateLineList: bad offset " + line.FileOffset +
- " (last=" + lastOffset + ", expected next=" + expectedOffset + ")");
- return false;
+ // .arend directives are associated with the last byte of a region, so
+ // we need to make a special exemption for them.
+ if (line.LineType == Line.Type.ArEndDirective &&
+ line.FileOffset == expectedOffset - 1) {
+ // allow
+ } else {
+ Debug.WriteLine("ValidateLineList: bad offset " + line.FileOffset +
+ " (last=" + lastOffset + ", expected next=" + expectedOffset + ")");
+ return false;
+ }
}
if (line.SubLineIndex != 0) {
// In the middle of a multi-line thing, don't advance last/expected.
- Debug.Assert(line.FileOffset == lastOffset);
+ if (line.LineType != Line.Type.ArEndDirective) {
+ Debug.Assert(line.FileOffset == lastOffset);
+ }
} else {
lastOffset = expectedOffset;
expectedOffset += line.OffsetSpan;
@@ -984,7 +1002,7 @@ namespace SourceGen {
// Create an address map iterator and advance it to match gen.StartOffset.
IEnumerator addrIter = mProject.AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
- if (addrIter.Current.IsStart && addrIter.Current.Offset >= startOffset) {
+ if (/*addrIter.Current.IsStart &&*/ addrIter.Current.Offset >= startOffset) {
break;
}
}
@@ -1038,6 +1056,8 @@ namespace SourceGen {
}
// Handle address region starts.
+ bool multiStart = false;
+ arSubLine = 0;
while (addrIter.Current != null && addrIter.Current.Offset <= offset) {
AddressMap.AddressChange change = addrIter.Current;
// Range starts/ends shouldn't be embedded in something.
@@ -1054,10 +1074,11 @@ namespace SourceGen {
if (change.IsStart) {
// Blank line above ORG directive, except at top of file or when we've
// already added one for another reason.
- if (region.Offset != 0 && !spaceAdded) {
+ if (region.Offset != 0 && !spaceAdded && !multiStart) {
lines.Add(GenerateBlankLine(offset));
}
spaceAdded = false; // next one will need a blank line
+ multiStart = true; // unless it's another .arstart immediately
// TODO(org): pre-label (address / label only, logically part of ORG)
Line newLine = new Line(offset, 0, Line.Type.ArStartDirective, arSubLine++);
@@ -1074,6 +1095,9 @@ namespace SourceGen {
#else
string comment = string.Empty;
#endif
+ if (change.IsSynthetic) {
+ comment += " (auto-generated)";
+ }
newLine.Parts = FormattedParts.CreateFullDirective(string.Empty,
mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective),
addrStr, comment);
@@ -1236,29 +1260,45 @@ namespace SourceGen {
offset += attr.Length;
}
- // 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) {
+ // Check for address region ends, which will be positioned one byte before the
+ // updated offset (unless they somehow got embedded inside something else).
+ 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);
+ Debug.Assert(change.Offset == offset - 1);
if (change.Region.Offset == 0 && hasPrgHeader) {
- // Suppress the .arend at offset +000002.
+ // Suppress the .arend at offset +000001.
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, arSubLine++);
+ // Show the start address to make it easier to pair them visually.
+ string addrStr;
+ if (change.Region.Address == AddressMap.NON_ADDR) {
+ addrStr = "(NA)";
+ } else {
+ addrStr = "(" + mFormatter.FormatHexValue(change.Region.Address, 4) +
+ ")";
+ }
+
+ // Associate with offset of previous byte.
+ Line newLine = new Line(offset - 1, 0, Line.Type.ArEndDirective,
+ arSubLine++);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.ArEndDirective),
- string.Empty);
+ addrStr);
lines.Add(newLine);
addrIter.MoveNext();
+
+ // Put a blank line before the next thing.
+ // TODO(maybe): this gets lost in a partial update, e.g. you add a
+ // long comment right after a .arend with no following .arstart and
+ // then hit "undo", because the blank is logically part of the
+ // following offset.
+ addBlank = true;
} else {
break;
}
@@ -1564,12 +1604,10 @@ 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];
+ public AddressMap.AddressRegion GetAddrRegionFromLine(Line line) {
+ // A given offset can have one or more .arstart lines and one or more .arend lines.
+ // You can't have an end followed by a start, because that would mean the regions
+ // overlap. If there's both start and end present, we have a 1-byte region.
int offset = line.FileOffset;
List entries = mProject.AddrMap.GetEntries(offset);
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 93695d7..11ddbd7 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -1658,6 +1658,28 @@ namespace SourceGen {
return false;
}
+ if (line.IsAddressRangeDirective) {
+ // TODO(someday): make this jump to the actual directive rather than nearby code
+ AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line);
+ if (region == null) {
+ Debug.Assert(false);
+ return false;
+ }
+ if (!testOnly) {
+ if (line.LineType == LineListGen.Line.Type.ArStartDirective) {
+ // jump to end
+ GoToLocation(
+ new NavStack.Location(region.Offset + region.ActualLength - 1, 0, true),
+ GoToMode.JumpToCodeData, true);
+ } else {
+ // jump to start
+ GoToLocation(new NavStack.Location(region.Offset, 0, true),
+ GoToMode.JumpToCodeData, true);
+ }
+ }
+ return true;
+ }
+
Anattrib attr = mProject.GetAnattrib(line.FileOffset);
FormatDescriptor dfd = attr.DataDescriptor;
@@ -1828,7 +1850,7 @@ namespace SourceGen {
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);
+ curRegion = CodeLineList.GetAddrRegionFromLine(CodeLineList[selIndex]);
Debug.Assert(curRegion != null);
Debug.WriteLine("Using region from " + CodeLineList[selIndex].LineType +
": " + curRegion);
@@ -2927,7 +2949,9 @@ namespace SourceGen {
}
LineListGen.Line.Type lineType = SelectionAnalysis.mLineType;
if (lineType != LineListGen.Line.Type.Code &&
- lineType != LineListGen.Line.Type.Data) {
+ lineType != LineListGen.Line.Type.Data &&
+ lineType != LineListGen.Line.Type.ArStartDirective &&
+ lineType != LineListGen.Line.Type.ArEndDirective) {
return false;
}
@@ -3884,11 +3908,11 @@ namespace SourceGen {
case LineListGen.Line.Type.ArStartDirective:
isSimple = false;
- lineTypeStr = "address range start directive";
+ lineTypeStr = "address region start directive";
break;
case LineListGen.Line.Type.ArEndDirective:
isSimple = false;
- lineTypeStr = "address range end directive";
+ lineTypeStr = "address region end directive";
break;
case LineListGen.Line.Type.LocalVariableTable:
isSimple = false;
@@ -3952,7 +3976,7 @@ namespace SourceGen {
if (line.LineType == LineListGen.Line.Type.ArStartDirective ||
line.LineType == LineListGen.Line.Type.ArEndDirective) {
- AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(lineIndex);
+ AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line);
StringBuilder esb = new StringBuilder();
esb.Append("Address: ");
if (region.Address == AddressMap.NON_ADDR) {
@@ -4388,6 +4412,21 @@ namespace SourceGen {
return true;
}
+ ///
+ /// Displays a representation of the address map.
+ ///
+ ///
+ /// This is in the "tools" section, but it's not a tool. It's in the "navigation" menu
+ /// but has nothing to do with navigation. Bit of an oddball.
+ ///
+ public void ViewAddressMap() {
+ string mapStr = RenderAddressMap.GenerateString(mProject, mFormatter);
+
+ Tools.WpfGui.ShowText dlg = new Tools.WpfGui.ShowText(mMainWin, mapStr);
+ dlg.Title = "Address Map";
+ dlg.ShowDialog();
+ }
+
#endregion Tools
#region Debug features
diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs
index 42a3d7c..98b58c1 100644
--- a/SourceGen/ProjectFile.cs
+++ b/SourceGen/ProjectFile.cs
@@ -20,7 +20,6 @@ using System.Diagnostics;
using System.IO;
using System.Text;
using System.Web.Script.Serialization;
-using System.Windows.Media;
using CommonUtil;
@@ -53,7 +52,7 @@ namespace SourceGen {
// ignore stuff that's in one side but not the other. However, if we're opening a
// newer file in an older program, it's worth letting the user know that some stuff
// may get lost as soon as they save the file.
- public const int CONTENT_VERSION = 4;
+ public const int CONTENT_VERSION = 5;
private static readonly bool ADD_CRLF = true;
@@ -238,15 +237,24 @@ namespace SourceGen {
SmartPlbHandling = src.SmartPlbHandling;
}
}
- public class SerAddressMap {
+ public class SerAddressMapEntry {
public int Offset { get; set; }
public int Addr { get; set; }
- // Length is computed field, no need to serialize
+ public int Length { get; set; }
+ public string PreLabel { get; set; }
+ public bool IsRelative { get; set; }
- public SerAddressMap() { }
- public SerAddressMap(AddressMap.AddressMapEntry ent) {
+ public SerAddressMapEntry() {
+ // Before v1.8, Length was always floating.
+ Length = CommonUtil.AddressMap.FLOATING_LEN;
+ PreLabel = string.Empty;
+ }
+ public SerAddressMapEntry(AddressMap.AddressMapEntry ent) {
Offset = ent.Offset;
Addr = ent.Address;
+ Length = ent.Length;
+ PreLabel = ent.PreLabel;
+ IsRelative = ent.IsRelative;
}
}
public class SerTypeHintRange {
@@ -421,7 +429,7 @@ namespace SourceGen {
public int FileDataLength { get; set; }
public int FileDataCrc32 { get; set; }
public SerProjectProperties ProjectProps { get; set; }
- public List AddressMap { get; set; }
+ public List AddressMap { get; set; }
public List TypeHints { get; set; }
public Dictionary StatusFlagOverrides { get; set; }
public Dictionary Comments { get; set; }
@@ -453,10 +461,12 @@ namespace SourceGen {
spf.FileDataLength = proj.FileDataLength;
spf.FileDataCrc32 = (int)proj.FileDataCrc32;
- // Convert AddressMap to serializable form.
- spf.AddressMap = new List();
+ // Convert AddressMap to serializable form. (AddressMapEntry objects are now
+ // serializable, so this is a bit redundant, but isolating project I/O from
+ // internal data structures is still useful.)
+ spf.AddressMap = new List();
foreach (AddressMap.AddressMapEntry ent in proj.AddrMap) {
- spf.AddressMap.Add(new SerAddressMap(ent));
+ spf.AddressMap.Add(new SerAddressMapEntry(ent));
}
// Reduce analyzer tags (formerly known as "type hints") to a collection of ranges.
@@ -669,16 +679,14 @@ namespace SourceGen {
// Deserialize address map.
proj.AddrMap.Clear();
- foreach (SerAddressMap addr in spf.AddressMap) {
- // TODO(org): serialize length, isRelative, and preLabel
- int length = CommonUtil.AddressMap.FLOATING_LEN;
-
+ foreach (SerAddressMapEntry addr in spf.AddressMap) {
AddressMap.AddResult addResult = proj.AddrMap.AddEntry(addr.Offset,
- length, addr.Addr);
+ addr.Length, addr.Addr, addr.PreLabel, addr.IsRelative);
if (addResult != CommonUtil.AddressMap.AddResult.Okay) {
- string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" +
- addr.Addr.ToString("x4") + " len=" +
- (length == CommonUtil.AddressMap.FLOATING_LEN ? "(floating)" : length.ToString());
+ string msg = "off=+" + addr.Offset.ToString("x6") + " len=" +
+ (addr.Length == CommonUtil.AddressMap.FLOATING_LEN ?
+ "(floating)" : addr.Length.ToString() +
+ " addr=$" + addr.Addr.ToString("x4"));
string errMsg = string.Format(Res.Strings.ERR_BAD_ADDRESS_REGION_FMT, msg);
report.Add(FileLoadItem.Type.Warning, errMsg);
}
diff --git a/SourceGen/RenderAddressMap.cs b/SourceGen/RenderAddressMap.cs
new file mode 100644
index 0000000..9f6d6b1
--- /dev/null
+++ b/SourceGen/RenderAddressMap.cs
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2021 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.Diagnostics;
+using System.Text;
+
+using Asm65;
+using CommonUtil;
+using AddressChange = CommonUtil.AddressMap.AddressChange;
+
+namespace SourceGen {
+ ///
+ /// Functions for generating a human-readable form of the address map.
+ ///
+ /// A graphical / interactive visualization would be nicer, but this can be pasted
+ /// into bug reports.
+ ///
+ public static class RenderAddressMap {
+ private const string CRLF = "\r\n";
+
+ ///
+ /// Formats the address map for viewing.
+ ///
+ public static string GenerateString(DisasmProject project, Formatter formatter) {
+ AddressMap addrMap = project.AddrMap;
+ bool showBank = !project.CpuDef.HasAddr16;
+
+ StringBuilder sb = new StringBuilder();
+ int depth = 0;
+ int prevOffset = -1;
+ int prevAddr = 0;
+ int lastEndOffset = -1;
+
+ sb.AppendLine("Map of address regions");
+ IEnumerator iter = addrMap.AddressChangeIterator;
+ while (iter.MoveNext()) {
+ AddressChange change = iter.Current;
+ if (change.IsStart) {
+ //if (change.Offset == lastEndOffset) {
+ // // Extra vertical space for a START following an END at the same offset.
+ // PrintDepthLines(sb, depth, true);
+ // sb.Append(CRLF);
+ // lastEndOffset = -1;
+ //}
+
+ if (prevOffset >= 0 && change.Offset != prevOffset) {
+ // Start of region at new offset. Output address info for space
+ // between previous start or end.
+ PrintAddressInfo(sb, formatter, depth, prevAddr,
+ change.Offset - prevOffset, showBank);
+ }
+
+ // Start following end, or start following start after a gap.
+ if (!string.IsNullOrEmpty(change.Region.PreLabel)) {
+ PrintDepthLines(sb, depth, true);
+ sb.Append("| pre='" + change.Region.PreLabel + "' ");
+ PrintAddress(sb, formatter, change.Region.PreLabelAddress, showBank);
+ sb.Append(CRLF);
+ }
+ sb.Append(formatter.FormatOffset24(change.Offset));
+ PrintDepthLines(sb, depth, false);
+ sb.Append("+- " + "start");
+
+ if (change.IsSynthetic) {
+ sb.Append(" (auto-generated)");
+ } else {
+ // If there's a label here, show it.
+ Anattrib attr = project.GetAnattrib(change.Offset);
+ if (attr.Symbol != null && !string.IsNullOrEmpty(attr.Symbol.Label)) {
+ sb.Append(" : ");
+ sb.Append(attr.Symbol.Label);
+ }
+ }
+
+ sb.Append(CRLF);
+
+ prevOffset = change.Offset;
+ prevAddr = change.Address;
+ depth++;
+ } else {
+ Debug.Assert(prevOffset >= 0);
+ depth--;
+
+ if (change.Offset + 1 != prevOffset) {
+ // End of region at new offset. Output address info for space
+ // between previous start or end.
+ PrintAddressInfo(sb, formatter, depth + 1, prevAddr,
+ change.Offset + 1 - prevOffset, showBank);
+ }
+
+ sb.Append(formatter.FormatOffset24(change.Offset));
+ PrintDepthLines(sb, depth, false);
+ sb.Append("+- " + "end");
+ //PrintAddress(sb, formatter, change.Address, showBank);
+ //sb.Append(")");
+ sb.Append(CRLF);
+
+ PrintDepthLines(sb, depth, true);
+ sb.Append(CRLF);
+
+ // Use offset+1 here so it lines up with start records.
+ prevOffset = lastEndOffset = change.Offset + 1;
+ prevAddr = change.Address;
+ }
+ }
+ Debug.Assert(depth == 0);
+
+ return sb.ToString();
+ }
+
+ private static void PrintDepthLines(StringBuilder sb, int depth, bool doIndent) {
+ if (doIndent) {
+ sb.Append(" ");
+ }
+ sb.Append(" ");
+ while (depth-- > 0) {
+ sb.Append("| ");
+ }
+ }
+
+ private static void PrintAddressInfo(StringBuilder sb, Formatter formatter, int depth,
+ int startAddr, int length, bool showBank) {
+ //PrintDepthLines(sb, depth);
+ //sb.Append(CRLF);
+
+ PrintDepthLines(sb, depth, true);
+ sb.Append(' ');
+ if (startAddr == AddressMap.NON_ADDR) {
+ sb.Append("-NA-");
+ } else {
+ PrintAddress(sb, formatter, startAddr, showBank);
+ sb.Append(" - ");
+ PrintAddress(sb, formatter, startAddr + length - 1, showBank);
+ }
+ sb.Append(" length=" + length + " ($" + length.ToString("x4") + ")");
+ sb.Append(CRLF);
+
+ //PrintDepthLines(sb, depth, true);
+ //sb.Append(CRLF);
+ }
+
+ private static void PrintAddress(StringBuilder sb, Formatter formatter, int addr,
+ bool showBank) {
+ if (addr == AddressMap.NON_ADDR) {
+ sb.Append("-NA-");
+ } else {
+ sb.Append("$");
+ sb.Append(formatter.FormatAddress(addr, showBank));
+ }
+ }
+ }
+}
diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index 10094c7..5ef41c2 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -117,6 +117,7 @@
ShowText.xaml
+
diff --git a/SourceGen/WpfGui/EditAddress.xaml b/SourceGen/WpfGui/EditAddress.xaml
index c903f7f..cf06716 100644
--- a/SourceGen/WpfGui/EditAddress.xaml
+++ b/SourceGen/WpfGui/EditAddress.xaml
@@ -22,7 +22,7 @@ limitations under the License.
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
- Title="Set Address Range Properties"
+ Title="Edit Address Region Properties"
Width="350" SizeToContent="Height" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
ContentRendered="Window_ContentRendered">
diff --git a/SourceGen/WpfGui/EditAddress.xaml.cs b/SourceGen/WpfGui/EditAddress.xaml.cs
index 6d8e1e8..06e3b2e 100644
--- a/SourceGen/WpfGui/EditAddress.xaml.cs
+++ b/SourceGen/WpfGui/EditAddress.xaml.cs
@@ -379,6 +379,7 @@ namespace SourceGen.WpfGui {
Option1Str = string.Format(fmt,
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion1.ActualLength));
+ mPreLabelAddress = newRegion1.PreLabelAddress;
} else {
Option1Str = (string)FindResource("str_CreateFixedFail");
CheckOption2 = true;
@@ -392,6 +393,7 @@ namespace SourceGen.WpfGui {
Option2Str = string.Format(fmt,
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion2.ActualLength));
+ mPreLabelAddress = newRegion2.PreLabelAddress;
} else {
Option2Str = (string)FindResource("str_CreateFloatingFail");
CheckOption1 = true;
@@ -402,6 +404,7 @@ namespace SourceGen.WpfGui {
// Unable to create region here. Explain why not.
EnableAttributeControls = false;
CheckOption1 = CheckOption2 = false;
+ mPreLabelAddress = AddressMap.NON_ADDR;
SetErrorString(ares1);
}
diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml
index 621be80..1c45e54 100644
--- a/SourceGen/WpfGui/MainWindow.xaml
+++ b/SourceGen/WpfGui/MainWindow.xaml
@@ -71,7 +71,7 @@ limitations under the License.
Del
-
+
@@ -203,6 +203,7 @@ limitations under the License.
Ctrl+Z
+
@@ -339,6 +340,8 @@ limitations under the License.
CanExecute="CanToggleSingleByteFormat" Executed="ToggleSingleByteFormatCmd_Executed"/>
+
+
+