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"/> + + + diff --git a/SourceGen/WpfGui/MainWindow.xaml.cs b/SourceGen/WpfGui/MainWindow.xaml.cs index 4f266d2..04365ef 100644 --- a/SourceGen/WpfGui/MainWindow.xaml.cs +++ b/SourceGen/WpfGui/MainWindow.xaml.cs @@ -1437,6 +1437,10 @@ namespace SourceGen.WpfGui { mMainCtrl.UndoChanges(); } + private void ViewAddressMapCmd_Executed(object sender, ExecutedRoutedEventArgs e) { + mMainCtrl.ViewAddressMap(); + } + private void Debug_ApplesoftToHtmlCmd_Executed(object sender, ExecutedRoutedEventArgs e) { mMainCtrl.Debug_ApplesoftToHtml(); }