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

ORG rework, part 5

Updated project file format to save the new map entries.

Tweaked appearance of .arend directives to show the .arstart address
in the operand field.  This makes it easier to match them up on screen.
Also, add a synthetic comment on auto-generated .arstart entries.

Added .arstart/.arend to the things that respond to Jump to Operand
(Ctrl+J).  Selecting one jumps to the other end.  (Well, it jumps
to the code nearest the other, which will do for now.)

Added a menu item to display a text rendering of the address map.
Helpful when things get complicated.

Modified the linear map iterator to return .arend items with the offset
of the last byte in the region, rather than the first byte of the
following region.  While the "exclusive end" approach is pretty
common, it caused problems when updating the line list, because it
meant that the .arend directives were outside the range of offsets
being updated (and, for directives at the end of the file, outside
the file itself).  This was painful to deal with for partial updates.
Changing this required some relatively subtle changes and annoyed some
of the debug assertions, such as the one where all Line items have
offsets that match the start of a line, but it's the cleaner approach.
This commit is contained in:
Andy McFadden 2021-09-27 17:02:18 -07:00
parent 3a2c4fa6d2
commit 2fed19ac47
12 changed files with 373 additions and 84 deletions

View File

@ -308,6 +308,11 @@ namespace CommonUtil {
/// </summary>
public int EntryCount { get { return mMapEntries.Count; } }
/// <summary>
/// Number of bytes spanned by the address map.
/// </summary>
public int Length { get { return mSpanLength; } }
/// <summary>
/// Error codes for AddEntry().
/// </summary>
@ -1086,27 +1091,37 @@ namespace CommonUtil {
///
/// Instances are immutable.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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<AddressChange> 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-");

View File

@ -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();

View File

@ -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;
}
}
}

View File

@ -191,6 +191,15 @@ namespace SourceGen {
}
}
/// <summary>
/// True if this line is an address range start or end.
/// </summary>
public bool IsAddressRangeDirective {
get {
return LineType == Type.ArStartDirective || LineType == Type.ArEndDirective;
}
}
/// <summary>
/// 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<AddressMap.AddressChange> 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<AddressMap.AddressMapEntry> entries = mProject.AddrMap.GetEntries(offset);

View File

@ -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;
}
/// <summary>
/// Displays a representation of the address map.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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

View File

@ -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<SerAddressMap> AddressMap { get; set; }
public List<SerAddressMapEntry> AddressMap { get; set; }
public List<SerTypeHintRange> TypeHints { get; set; }
public Dictionary<string, int> StatusFlagOverrides { get; set; }
public Dictionary<string, string> 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<SerAddressMap>();
// 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<SerAddressMapEntry>();
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);
}

View File

@ -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 {
/// <summary>
/// Functions for generating a human-readable form of the address map.
/// </summary>
/// A graphical / interactive visualization would be nicer, but this can be pasted
/// into bug reports.
/// </remarks>
public static class RenderAddressMap {
private const string CRLF = "\r\n";
/// <summary>
/// Formats the address map for viewing.
/// </summary>
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<AddressChange> 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));
}
}
}
}

View File

@ -117,6 +117,7 @@
<DependentUpon>ShowText.xaml</DependentUpon>
</Compile>
<Compile Include="LocalVariableTable.cs" />
<Compile Include="RenderAddressMap.cs" />
<Compile Include="Visualization.cs" />
<Compile Include="VisBitmapAnimation.cs" />
<Compile Include="VisualizationSet.cs" />

View File

@ -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">

View File

@ -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);
}

View File

@ -71,7 +71,7 @@ limitations under the License.
<KeyGesture>Del</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="EditAddressCmd" Text="Set Address..."/>
<RoutedUICommand x:Key="EditAddressCmd" Text="Define Address Region..."/>
<RoutedUICommand x:Key="EditAppSettingsCmd" Text="Settings..."/>
<RoutedUICommand x:Key="EditCommentCmd" Text="Edit Comment...">
<RoutedUICommand.InputGestures>
@ -203,6 +203,7 @@ limitations under the License.
<KeyGesture>Ctrl+Z</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="ViewAddressMapCmd" Text="View Address Map"/>
<RoutedUICommand x:Key="Debug_ApplesoftToHtmlCmd" Text="Applesoft to HTML..."/>
<RoutedUICommand x:Key="Debug_ApplyEditCommandsCmd" Text="Apply Edit Commands..."/>
@ -339,6 +340,8 @@ limitations under the License.
CanExecute="CanToggleSingleByteFormat" Executed="ToggleSingleByteFormatCmd_Executed"/>
<CommandBinding Command="{StaticResource UndoCmd}"
CanExecute="CanUndo" Executed="UndoCmd_Executed"/>
<CommandBinding Command="{StaticResource ViewAddressMapCmd}"
CanExecute="IsProjectOpen" Executed="ViewAddressMapCmd_Executed"/>
<!-- must come after analyzer tag Ctrl+H Ctrl+C def -->
<CommandBinding Command="Copy"
@ -418,6 +421,8 @@ limitations under the License.
<MenuItem Command="Find"/>
<MenuItem Command="{StaticResource FindNextCmd}"/>
<MenuItem Command="{StaticResource FindPreviousCmd}"/>
<Separator/>
<MenuItem Command="{StaticResource ViewAddressMapCmd}"/>
</MenuItem>
<MenuItem Name="actionsMenu" Header="_Actions">
<MenuItem Command="{StaticResource EditAddressCmd}"/>

View File

@ -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();
}