ORG rework, part 9

Modified "jump to" code to understand address range start/end lines.
If there are multiple starts or ends at the same offset, we jump to
the first one in the set, which is suboptimal but simpler to do.
Simplified the API, embedding GoToMode in the Location object (which
is where it really needs to be, to make fwd/back work right).

Updated HTML export to grey out addresses in NON_ADDR sections.

Changed default pseudo-op strings for address regions to ".addrs" and
".adrend", after trying a bunch of things that were worse.  Added
definitions for region-end pseudo-ops to Merlin32 and cc65 for display
on screen.

Added regression test 20260 for address region pre-labels.

Fixed handling of leading underscores in platform/project symbols.
These need to be escaped in 64tass output.  Updated regression test
20170-external-symbols to check it.
This commit is contained in:
Andy McFadden 2021-10-07 12:39:08 -07:00
parent d2326c389f
commit 0ac0686c7a
26 changed files with 821 additions and 121 deletions

View File

@ -977,7 +977,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 .arstart/.arend statement in the middle.
/// is to identify multi-byte elements that have an 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

@ -112,8 +112,8 @@ namespace SourceGen.AsmGen {
{ "EquDirective", "=" },
{ "VarDirective", ".set" },
{ "ArStartDirective", ".org" },
//ArEndDirective
//RegWidthDirective // .a8, .a16, .i8, .i16
{ "ArEndDirective", ".adrend" }, // on-screen display only
//RegWidthDirective // .a8, .a16, .i8, .i16
//DataBankDirective
{ "DefineData1", ".byte" },
{ "DefineData2", ".word" },
@ -123,12 +123,12 @@ namespace SourceGen.AsmGen {
//DefineBigData3
//DefineBigData4
{ "Fill", ".res" },
{ "Dense", ".byte" }, // not really dense, just comma-separated bytes
{ "Dense", ".byte" }, // really just just comma-separated bytes
//Junk
{ "StrGeneric", ".byte" },
//StrReverse
{ "StrNullTerm", ".asciiz" },
//StrLen8 // macro with .strlen?
//StrLen8 // TODO(maybe): macro with .strlen?
//StrLen16
//StrDci
});

View File

@ -106,7 +106,7 @@ namespace SourceGen.AsmGen {
{ "EquDirective", "equ" },
{ "VarDirective", "equ" },
{ "ArStartDirective", "org" },
{ "ArEndDirective", "org_end" }, // not actually used
{ "ArEndDirective", "adrend" }, // on-screen display only
//RegWidthDirective
//DataBankDirective
{ "DefineData1", "dfb" },

View File

@ -735,9 +735,15 @@ namespace SourceGen {
}
if ((mLeftFlags & ActiveColumnFlags.Address) != 0) {
if (!string.IsNullOrEmpty(parts.Addr)) {
string str = parts.Addr + ":";
string str;
if (parts.IsNonAddressable) {
str = "<span class=\"greytext\">" + parts.Addr + "</span>";
} else {
str = parts.Addr;
}
str += ":";
colPos = AddSpacedString(sb, colPos, mColStart[(int)Col.Address],
str, str.Length);
str, parts.Addr.Length + 1);
}
}
if ((mLeftFlags & ActiveColumnFlags.Bytes) != 0) {

View File

@ -282,8 +282,8 @@ namespace SourceGen {
int topOffset = dl[topIndex].FileOffset;
int firstIndex = dl.FindLineIndexByOffset(topOffset);
Debug.Assert(topIndex >= firstIndex);
savedSel.mTopPosition =
new NavStack.Location(topOffset, topIndex - firstIndex, false);
savedSel.mTopPosition = new NavStack.Location(topOffset, topIndex - firstIndex,
NavStack.GoToMode.JumpToAdjIndex);
List<Line> lineList = dl.mLineList;
Debug.Assert(lineList.Count == sel.Length);
@ -832,7 +832,7 @@ 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) {
// .arend directives are associated with the last byte of a region, so
// 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) {
@ -1035,7 +1035,7 @@ namespace SourceGen {
// Insert long comments and notes. These may span multiple display lines,
// and require word-wrap, so it's easiest just to render them fully here.
// Set "spaceAdded" to true so .arstart doesn't try to add one after the comment.
// Set "spaceAdded" to true so arstart doesn't try to add one after the comment.
//
// TODO: integrate into FormattedOperandCache so we don't have to
// regenerate them unless they change. Use the MLC as the dependency.
@ -1066,7 +1066,7 @@ namespace SourceGen {
AddressMap.AddressRegion region = change.Region;
if (region.Offset == 0 && hasPrgHeader) {
// Suppress the .arstart at offset zero. We know there's another one
// 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;
@ -1079,7 +1079,7 @@ namespace SourceGen {
lines.Add(GenerateBlankLine(offset));
}
spaceAdded = false; // next one will need a blank line
multiStart = true; // unless it's another .arstart immediately
multiStart = true; // unless it's another arstart immediately
if (region.HasValidPreLabel) {
Line preLine =
@ -1109,16 +1109,19 @@ namespace SourceGen {
} else {
addrStr = mFormatter.FormatHexValue(region.Address, 4);
}
#if DEBUG
#if DEBUG1
string comment = mFormatter.FormatEolComment("ends at " +
mFormatter.FormatOffset24(region.Offset + region.ActualLength - 1) +
(region.IsFloating ? " (floating)" : string.Empty));
#else
string comment = string.Empty;
#endif
if (change.IsSynthetic) {
comment += " (auto-generated)";
}
#else
string comment = string.Empty;
if (change.IsSynthetic) {
comment = mFormatter.FormatEolComment("(auto-generated)");
}
#endif
newLine.Parts = FormattedParts.CreateFullDirective(string.Empty,
mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective),
addrStr, comment);
@ -1284,10 +1287,10 @@ namespace SourceGen {
// Check for address region ends, which will be positioned one byte before the
// updated offset (unless they somehow got embedded inside something else).
//
// The .arend can only appear on the same offset as .arstart in a single-byte
// The arend can only appear on the same offset as arstart in a single-byte
// region, and we can't have more than one of those at the same offset.
// If this is not a single-byte region, we need to reset the sub-line count.
// (Alternatively: track the offset of the last .arstart, and reset subline
// (Alternatively: track the offset of the last arstart, and reset subline
// if they differ.)
if (addrIter.Current != null && addrIter.Current.Region.Length != 1) {
arSubLine = 0;
@ -1302,7 +1305,7 @@ namespace SourceGen {
}
if (change.Region.Offset == 0 && hasPrgHeader) {
// Suppress the .arend at offset +000001.
// Suppress the arend at offset +000001.
addrIter.MoveNext();
arSubLine++; // need to track address map
continue;
@ -1330,7 +1333,7 @@ namespace SourceGen {
// 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
// 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;
@ -1649,7 +1652,7 @@ namespace SourceGen {
}
public AddressMap.AddressRegion GetAddrRegionFromLine(Line line, out bool isSynth) {
// A given offset can have one or more .arstart lines and one or more .arend lines.
// 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;
@ -1666,14 +1669,14 @@ namespace SourceGen {
while (addrIter.Current != null && addrIter.Current.Offset == offset) {
AddressMap.AddressChange change = addrIter.Current;
if (count == 0) {
// Could be pre-label or .arstart that follows it. Both go to the same place.
// Could be pre-label or arstart that follows it. Both go to the same place.
isSynth = change.IsSynthetic;
return change.Region;
}
if (change.IsStart && change.Region.HasValidPreLabel) {
count--;
if (count == 0) {
// This is the .arstart following the pre-label.
// This is the arstart following the pre-label.
isSynth = change.IsSynthetic;
return change.Region;
}

View File

@ -1664,7 +1664,8 @@ namespace SourceGen {
}
if (line.IsAddressRangeDirective) {
// TODO(someday): make this jump to the actual directive rather than nearby code
// TODO(someday): make this jump to the specific directive rather than the first
// (should be able to do it with LineDelta)
AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line,
out bool unused);
if (region == null) {
@ -1674,13 +1675,12 @@ namespace SourceGen {
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);
GoToLocation(new NavStack.Location(region.Offset + region.ActualLength - 1,
0, NavStack.GoToMode.JumpToArEnd), true);
} else {
// jump to start
GoToLocation(new NavStack.Location(region.Offset, 0, true),
GoToMode.JumpToCodeData, true);
GoToLocation(new NavStack.Location(region.Offset,
0, NavStack.GoToMode.JumpToArStart), true);
}
}
return true;
@ -1706,9 +1706,11 @@ namespace SourceGen {
int labelOffset = mProject.FindLabelOffsetByName(dfd.SymbolRef.Label);
if (labelOffset >= 0) {
if (!testOnly) {
// TODO(org): jump to .arstart
GoToLocation(new NavStack.Location(labelOffset, 0, false),
GoToMode.JumpToCodeData, true);
NavStack.GoToMode mode = NavStack.GoToMode.JumpToCodeData;
if (sym.SymbolSource == Symbol.Source.AddrPreLabel) {
mode = NavStack.GoToMode.JumpToArStart;
}
GoToLocation(new NavStack.Location(labelOffset, 0, mode), true);
}
return true;
}
@ -1719,8 +1721,8 @@ namespace SourceGen {
if (mProject.ActiveDefSymbolList[i] == sym) {
int offset = LineListGen.DefSymOffsetFromIndex(i);
if (!testOnly) {
GoToLocation(new NavStack.Location(offset, 0, false),
GoToMode.JumpToCodeData, true);
GoToLocation(new NavStack.Location(offset, 0,
NavStack.GoToMode.JumpToCodeData), true);
}
return true;
}
@ -1737,8 +1739,8 @@ namespace SourceGen {
// Operand has an in-file target offset. We can resolve it as a numeric reference.
// Find the line for that offset and jump to it.
if (!testOnly) {
GoToLocation(new NavStack.Location(attr.OperandOffset, 0, false),
GoToMode.JumpToCodeData, true);
GoToLocation(new NavStack.Location(attr.OperandOffset, 0,
NavStack.GoToMode.JumpToCodeData), true);
}
return true;
} else if (attr.IsDataStart || attr.IsInlineDataStart) {
@ -1748,8 +1750,8 @@ namespace SourceGen {
int operandOffset = DataAnalysis.GetDataOperandOffset(mProject, line.FileOffset);
if (operandOffset >= 0) {
if (!testOnly) {
GoToLocation(new NavStack.Location(operandOffset, 0, false),
GoToMode.JumpToCodeData, true);
GoToLocation(new NavStack.Location(operandOffset, 0,
NavStack.GoToMode.JumpToCodeData), true);
}
return true;
}
@ -1809,15 +1811,15 @@ namespace SourceGen {
int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex();
// Can only start with .arend if it's single-selection.
// 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 a .arstart
// If multiple lines with code/data are selected, there must not be an arstart
// between them unless we're resizing a region. Determining whether or not a resize
// is valid is left to the edit dialog. It's okay for a .arend to be in the middle
// so long as the corresponding .arstart is at the current offset.
// is valid is left to the edit dialog. It's okay for an arend to be in the middle
// so long as the corresponding arstart is at the current offset.
if (selLine.LineType == LineListGen.Line.Type.ArStartDirective) {
// Skip overlapping region check.
return true;
@ -1856,7 +1858,7 @@ namespace SourceGen {
int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
AddressMap addrMap = mProject.AddrMap;
// The offset of a .arend directive is the last byte in the address region. It
// The offset of an arend directive is the last byte in the address region. It
// has a span length of zero because it's a directive, so if it's selected as
// the last offset then our nextOffset calculation will be off by one. (This would
// be avoided by using an exclusive end offset, but that causes other problems.)
@ -1865,13 +1867,13 @@ namespace SourceGen {
nextOffset++;
}
// Compute length of selection. May be zero if it's entirely .arstart/.arend.
// Compute length of selection. May be zero if it's entirely arstart/arend.
int selectedLen = nextOffset - firstOffset;
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.
// First selected line was arstart/arend, find the address map entry.
curRegion = CodeLineList.GetAddrRegionFromLine(CodeLineList[selIndex],
out bool isSynth);
Debug.Assert(curRegion != null);
@ -1888,7 +1890,7 @@ namespace SourceGen {
} 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.
// but since the first entry wasn't arstart/arend this can't happen.
Debug.Assert(false);
return;
}
@ -2820,20 +2822,20 @@ namespace SourceGen {
GotoBox dlg = new GotoBox(mMainWin, mProject, offset, mFormatter);
if (dlg.ShowDialog() == true) {
GoToLocation(new NavStack.Location(dlg.TargetOffset, 0, false),
GoToMode.JumpToCodeData, true);
GoToLocation(new NavStack.Location(dlg.TargetOffset, 0,
NavStack.GoToMode.JumpToCodeData), true);
//mMainWin.CodeListView_Focus();
}
}
public enum GoToMode { Unknown = 0, JumpToCodeData, JumpToNote, JumpToAdjIndex };
/// <summary>
/// Moves the view and selection to the specified offset. We want to select stuff
/// differently if we're jumping to a note vs. jumping to an instruction.
/// </summary>
/// <param name="gotoOffset">Offset to jump to.</param>
/// <param name="newLoc">Location to jump to.</param>
/// <param name="mode">Interesting set of lines within that offset.</param>
/// <param name="doPush">If set, push new offset onto navigation stack.</param>
public void GoToLocation(NavStack.Location loc, GoToMode mode, bool doPush) {
public void GoToLocation(NavStack.Location newLoc, bool doPush) {
NavStack.Location prevLoc = GetCurrentlySelectedLocation();
//Debug.WriteLine("GoToLocation: " + loc + " mode=" + mode + " doPush=" + doPush +
// " (curLoc=" + prevLoc + ")");
@ -2844,8 +2846,7 @@ namespace SourceGen {
// entry in the symbol table for the current offset, we want to move the selection,
// so we don't want to bail out if the offset matches. Easiest thing to do is to
// do the move but not push it.
bool jumpToNote = (mode == GoToMode.JumpToNote);
if (loc.Offset == prevLoc.Offset && jumpToNote == prevLoc.IsNote) {
if (newLoc.Offset == prevLoc.Offset && newLoc.Mode == prevLoc.Mode) {
// we're jumping to ourselves?
if (doPush) {
Debug.WriteLine("Ignoring push for goto to current offset");
@ -2853,27 +2854,30 @@ namespace SourceGen {
}
}
int topLineIndex = CodeLineList.FindLineIndexByOffset(loc.Offset);
int topLineIndex = CodeLineList.FindLineIndexByOffset(newLoc.Offset);
if (topLineIndex < 0) {
Debug.Assert(false, "failed goto offset +" + loc.Offset.ToString("x6"));
Debug.Assert(false, "failed goto offset +" + newLoc.Offset.ToString("x6"));
return;
}
int lastLineIndex;
if (mode == GoToMode.JumpToNote) {
if (newLoc.Mode == NavStack.GoToMode.JumpToNote) {
// Select all note lines, disregard the rest.
while (CodeLineList[topLineIndex].LineType != LineListGen.Line.Type.Note) {
if (CodeLineList[topLineIndex].FileOffset != newLoc.Offset) {
// This can happen if the note got deleted.
break;
}
topLineIndex++;
Debug.Assert(CodeLineList[topLineIndex].FileOffset == loc.Offset);
}
lastLineIndex = topLineIndex + 1;
while (lastLineIndex < CodeLineList.Count &&
CodeLineList[lastLineIndex].LineType == LineListGen.Line.Type.Note) {
lastLineIndex++;
}
} else if (loc.Offset < 0) {
} else if (newLoc.Offset < 0) {
// This is the offset of the header comment or a .EQ directive. Don't mess with it.
lastLineIndex = topLineIndex + 1;
} else if (mode == GoToMode.JumpToCodeData) {
} else if (newLoc.Mode == NavStack.GoToMode.JumpToCodeData) {
// Advance to the code or data line.
while (CodeLineList[topLineIndex].LineType != LineListGen.Line.Type.Code &&
CodeLineList[topLineIndex].LineType != LineListGen.Line.Type.Data) {
@ -2881,14 +2885,35 @@ namespace SourceGen {
}
lastLineIndex = topLineIndex + 1;
} else if (mode == GoToMode.JumpToAdjIndex) {
} else if (newLoc.Mode == NavStack.GoToMode.JumpToAdjIndex) {
// Adjust the line position by the line delta. If the adjustment moves us to
// a different element, ignore the adjustment.
if (CodeLineList[topLineIndex].FileOffset ==
CodeLineList[topLineIndex + loc.LineDelta].FileOffset) {
topLineIndex += loc.LineDelta;
CodeLineList[topLineIndex + newLoc.LineDelta].FileOffset) {
topLineIndex += newLoc.LineDelta;
}
lastLineIndex = topLineIndex + 1;
} else if (newLoc.Mode == NavStack.GoToMode.JumpToArStart ||
newLoc.Mode == NavStack.GoToMode.JumpToArEnd) {
LineListGen.Line.Type matchType = LineListGen.Line.Type.ArStartDirective;
if (newLoc.Mode != NavStack.GoToMode.JumpToArStart) {
matchType = LineListGen.Line.Type.ArEndDirective;
}
// Find first instance of specified type.
LineListGen.Line tmpLine = CodeLineList[topLineIndex];
while (CodeLineList[topLineIndex].LineType != matchType) {
if (CodeLineList[topLineIndex].FileOffset > newLoc.Offset) {
// This can happen if the region got deleted.
break;
}
topLineIndex++;
}
lastLineIndex = topLineIndex + 1;
// If there's multiple lines, make sure they're all on screen.
while (lastLineIndex < CodeLineList.Count &&
CodeLineList[lastLineIndex].LineType == matchType) {
lastLineIndex++;
}
} else {
Debug.Assert(false);
lastLineIndex = topLineIndex + 1;
@ -3029,7 +3054,8 @@ namespace SourceGen {
int offset = CodeLineList[index].FileOffset;
int lineDelta = index - CodeLineList.FindLineIndexByOffset(offset);
bool isNote = (CodeLineList[index].LineType == LineListGen.Line.Type.Note);
return new NavStack.Location(offset, lineDelta, isNote);
return new NavStack.Location(offset, lineDelta,
isNote ? NavStack.GoToMode.JumpToNote : NavStack.GoToMode.JumpToAdjIndex);
}
public void GotoLastChange() {
@ -3076,11 +3102,11 @@ namespace SourceGen {
}
if (isNote) {
GoToLocation(new NavStack.Location(offset, 0, true),
GoToMode.JumpToNote, true);
GoToLocation(new NavStack.Location(offset, 0, NavStack.GoToMode.JumpToNote),
true);
} else {
GoToLocation(new NavStack.Location(offset, 0, false),
GoToMode.JumpToCodeData, true);
GoToLocation(new NavStack.Location(offset, 0, NavStack.GoToMode.JumpToCodeData),
true);
}
}
@ -3090,8 +3116,7 @@ namespace SourceGen {
public void NavigateBackward() {
Debug.Assert(mNavStack.HasBackward);
NavStack.Location backLoc = mNavStack.MoveBackward(GetCurrentlySelectedLocation());
GoToLocation(backLoc,
backLoc.IsNote ? GoToMode.JumpToNote : GoToMode.JumpToAdjIndex, false);
GoToLocation(backLoc, false);
}
public bool CanNavigateForward() {
@ -3100,8 +3125,7 @@ namespace SourceGen {
public void NavigateForward() {
Debug.Assert(mNavStack.HasForward);
NavStack.Location fwdLoc = mNavStack.MoveForward(GetCurrentlySelectedLocation());
GoToLocation(fwdLoc,
fwdLoc.IsNote ? GoToMode.JumpToNote : GoToMode.JumpToAdjIndex, false);
GoToLocation(fwdLoc, false);
}
/// <summary>
@ -3111,8 +3135,9 @@ namespace SourceGen {
public void GoToLabel(Symbol sym) {
int offset = mProject.FindLabelOffsetByName(sym.Label);
if (offset >= 0) {
GoToLocation(new NavStack.Location(offset, 0, false),
GoToMode.JumpToCodeData, true);
// TODO(someday): jump to correct line for address region pre-labels
GoToLocation(new NavStack.Location(offset, 0, NavStack.GoToMode.JumpToCodeData),
true);
} else {
Debug.WriteLine("DClick symbol: " + sym + ": label not found");
}
@ -4045,14 +4070,23 @@ namespace SourceGen {
esb.Append(" (floating)");
}
esb.Append(CRLF);
esb.Append("Pre-label: ");
if (!string.IsNullOrEmpty(region.PreLabel)) {
esb.Append("'");
esb.Append(region.PreLabel);
if (region.PreLabelAddress == Address.NON_ADDR) {
esb.Append("' (non-addressable)");
} else {
esb.Append("' addr=$");
esb.Append(mFormatter.FormatAddress(region.PreLabelAddress,
!mProject.CpuDef.HasAddr16));
}
} else {
esb.Append("none");
}
esb.Append(CRLF);
esb.Append("Synthetic: " + isSynth);
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();

View File

@ -38,6 +38,15 @@ namespace SourceGen {
// TODO(someday): change the back button to a pop-up list of locations (like the way
// VS 2017 does it).
public enum GoToMode {
Unknown = 0,
JumpToCodeData, // destination is first byte of code or data at target offset
JumpToNote, // destination is Note at target offset
JumpToAdjIndex, // destination is first line at target offset plus LineDelta
JumpToArStart, // destination is arstart at target offset.
JumpToArEnd, // destination is arend at target offset
};
/// <summary>
/// Holds enough information to get us back where we were, in style.
/// </summary>
@ -49,7 +58,7 @@ namespace SourceGen {
/// <summary>
/// Number of lines between the first line at the specified offset, and the
/// line we actually want to land on.
/// line we actually want to land on. Used when Mode=JumpToAdjIndex.
/// </summary>
/// <remarks>
/// It's possible this line no longer exists. Easiest test is to compare the
@ -59,21 +68,22 @@ namespace SourceGen {
public int LineDelta { get; set; }
/// <summary>
/// True if the target is the note at the given offset, rather than the code/data.
/// Specifies interesting things to find at the target offset.
/// </summary>
/// <remarks>
/// This is an alternative to LineDelta.
/// </remarks>
public bool IsNote { get; set; }
public GoToMode Mode { get; set; }
public Location(int offset, int lineDelta, bool isNote) {
public Location(int offset, int lineDelta, GoToMode mode) {
Offset = offset;
LineDelta = lineDelta;
IsNote = isNote;
Mode = mode;
//if (lineDelta != 0 && mode != GoToMode.JumpToAdjIndex) {
// Debug.WriteLine("HEY: lineDelta=" + lineDelta + " mode=" + mode);
//}
}
public override string ToString() {
return string.Format("[+{0:x6},{1},{2}]", Offset, LineDelta, IsNote);
return string.Format("[+{0:x6},{1},{2}]", Offset, LineDelta, Mode);
}
public static bool operator ==(Location a, Location b) {
@ -83,7 +93,7 @@ namespace SourceGen {
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
return false; // one is null
}
return a.Offset == b.Offset && a.LineDelta == b.LineDelta && a.IsNote == b.IsNote;
return a.Offset == b.Offset && a.LineDelta == b.LineDelta && a.Mode == b.Mode;
}
public static bool operator !=(Location a, Location b) {
return !(a == b);
@ -92,7 +102,7 @@ namespace SourceGen {
return obj is Location && this == (Location)obj;
}
public override int GetHashCode() {
return Offset + (LineDelta * 1000000) + (IsNote ? (1<<24) : 0);
return Offset + (LineDelta * 1000000) + ((int)Mode << 24);
}
}

View File

@ -228,8 +228,8 @@ namespace SourceGen {
new PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", ".eq" },
{ "VarDirective", ".var" },
{ "ArStartDirective", ".arstart" },
{ "ArEndDirective", ".arend" },
{ "ArStartDirective", ".addrs" },
{ "ArEndDirective", ".adrend" },
{ "RegWidthDirective", ".rwid" },
{ "DataBankDirective", ".dbank" },

View File

@ -45,7 +45,7 @@ namespace SourceGen {
int prevAddr = 0;
int lastEndOffset = -1;
sb.AppendLine("Address region map for " + project.DataFileName);
sb.AppendLine("Address region map for \"" + project.DataFileName + "\"");
sb.Append(CRLF);
IEnumerator<AddressChange> iter = addrMap.AddressChangeIterator;
@ -67,12 +67,6 @@ namespace SourceGen {
}
// 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");
@ -83,10 +77,16 @@ namespace SourceGen {
// 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(" '");
sb.Append(attr.Symbol.GenerateDisplayLabel(formatter));
sb.Append("'");
}
}
if (change.Region.HasValidPreLabel) {
sb.Append(" pre='");
sb.Append(change.Region.PreLabel);
sb.Append("'");
}
sb.Append(CRLF);

View File

@ -1,6 +1,10 @@
/*
* 6502bench SourceGen disassembly output style.
*/
/*
* General formatting.
*/
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px; /* 16 recommended for mobile */
@ -11,6 +15,9 @@ table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
.greytext {
color: grey;
}
/*
* Blue underlined text is distracting. Change internal links to be

View File

@ -52,7 +52,8 @@ OverVar @ $40 4
BankWrap @ $fff0 $20
; Width specifiers on constants should be ignored.
FatConst = $4000 8
; Uses a leading '_' to test special 64tass handling.
_FatConst = $4000 8
; Overlapping multi-byte items with exact and inexact matches.
OverA_0 @ $6000 8 ;should win, alphabetically

View File

@ -27,7 +27,7 @@
"OperandFormats":{
"207":{
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
"Label":"FatConst","Part":"Low"}}},
"Label":"_FatConst","Part":"Low"}}},
"LvTables":{
"192":{
"Variables":[{

Binary file not shown.

View File

@ -0,0 +1,219 @@
### 6502bench SourceGen dis65 v1.0 ###
{
"_ContentVersion":5,
"FileDataLength":95,
"FileDataCrc32":482033261,
"ProjectProps":{
"CpuName":"6502",
"IncludeUndocumentedInstr":false,
"TwoByteBrk":false,
"EntryFlags":32702671,
"AutoLabelStyle":"Simple",
"AnalysisParams":{
"AnalyzeUncategorizedData":true,
"DefaultTextScanMode":"LowHighAscii",
"MinCharsForString":4,
"SeekNearbyTargets":false,
"UseRelocData":false,
"SmartPlpHandling":false,
"SmartPlbHandling":true},
"PlatformSymbolFileIdentifiers":[],
"ExtensionScriptFileIdentifiers":[],
"ProjectSyms":{
"part2":{
"DataDescriptor":{
"Length":1,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"precedence test",
"HasWidth":false,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"part2",
"Value":4155,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"},
"zzz":{
"DataDescriptor":{
"Length":1,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"",
"HasWidth":false,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"zzz",
"Value":4155,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"}}},
"AddressMap":[{
"Offset":0,
"Addr":-1025,
"Length":2,
"PreLabel":"",
"IsRelative":false},
{
"Offset":2,
"Addr":4096,
"Length":-1024,
"PreLabel":"b4_begin",
"IsRelative":false},
{
"Offset":11,
"Addr":8192,
"Length":48,
"PreLabel":"b4_part2",
"IsRelative":false},
{
"Offset":35,
"Addr":12288,
"Length":-1024,
"PreLabel":"b4_part3",
"IsRelative":false},
{
"Offset":59,
"Addr":16384,
"Length":31,
"PreLabel":"b4_part4b",
"IsRelative":false},
{
"Offset":59,
"Addr":20480,
"Length":21,
"PreLabel":"b4_part4a",
"IsRelative":false},
{
"Offset":59,
"Addr":24576,
"Length":12,
"PreLabel":"_b4_part4",
"IsRelative":false},
{
"Offset":90,
"Addr":61440,
"Length":3,
"PreLabel":"",
"IsRelative":false}],
"TypeHints":[{
"Low":2,
"High":2,
"Hint":"Code"}],
"StatusFlagOverrides":{
},
"Comments":{
},
"LongComments":{
},
"Notes":{
},
"UserLabels":{
"2":{
"Label":"begin",
"Value":4096,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"},
"11":{
"Label":"part2",
"Value":8192,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"},
"33":{
"Label":"local1",
"Value":8214,
"Source":"User",
"Type":"NonUniqueLocalAddr",
"LabelAnno":"None"},
"34":{
"Label":"local2",
"Value":8215,
"Source":"User",
"Type":"NonUniqueLocalAddr",
"LabelAnno":"None"},
"57":{
"Label":"local3",
"Value":12310,
"Source":"User",
"Type":"NonUniqueLocalAddr",
"LabelAnno":"None"},
"58":{
"Label":"local4",
"Value":12311,
"Source":"User",
"Type":"NonUniqueLocalAddr",
"LabelAnno":"None"},
"35":{
"Label":"part3",
"Value":12288,
"Source":"User",
"Type":"NonUniqueLocalAddr",
"LabelAnno":"None"},
"81":{
"Label":"part4b",
"Value":16406,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"},
"71":{
"Label":"part4a",
"Value":20492,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"},
"59":{
"Label":"part4",
"Value":24576,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"}},
"OperandFormats":{
"0":{
"Length":2,
"Format":"NumericLE",
"SubFormat":"None",
"SymbolRef":null}},
"LvTables":{
},
"Visualizations":[],
"VisualizationAnimations":[],
"VisualizationSets":{
},
"RelocList":{
},
"DbrValues":{
}}

View File

@ -1,5 +1,5 @@
.cpu "6502"
FatConst = $4000
X_FatConst = $4000
OverVar = $40
CodeWrap = $0f00 ;encases program
@ -116,7 +116,7 @@ LocalVar .var $41
ldx OverVar+3
ldx $44
nop
lda FatConst-1
lda X_FatConst-1
lda $4000
lda $4001
lda BankWrap+8

View File

@ -1,5 +1,5 @@
!cpu 6502
FatConst = $4000
_FatConst = $4000
OverVar = $40
CodeWrap = $0f00 ;encases program
@ -117,7 +117,7 @@ L1000 lda CodeWrap+255
ldx OverVar+3
ldx $44
nop
lda FatConst-1
lda _FatConst-1
lda $4000
lda $4001
lda BankWrap+8

View File

@ -1,5 +1,5 @@
.setcpu "6502"
FatConst = $4000
_FatConst = $4000
OverVar = $40
CodeWrap = $0f00 ;encases program
@ -116,7 +116,7 @@ LocalVar .set $41
ldx OverVar+3
ldx $44
nop
lda FatConst-1
lda _FatConst-1
lda $4000
lda $4001
lda BankWrap+8

View File

@ -1,4 +1,4 @@
FatConst equ $4000
_FatConst equ $4000
OverVar equ $40
CodeWrap equ $0f00 ;encases program
@ -115,7 +115,7 @@ L1000 lda CodeWrap+255
ldx OverVar+3
ldx $44
nop
lda FatConst-1
lda _FatConst-1
lda $4000
lda $4001
lda BankWrap+8

View File

@ -0,0 +1,76 @@
.cpu "6502"
zzz = $103b
* = $1000
begin bit begin
nop
nop
nop
jmp part2
.logical $2000
part2 bit part2
nop
lda _local1
lda local2
lda local4
nop
nop
nop
bit b4_part3
jmp part3
_local1 .byte $81
local2 .byte $82
b4_part3
.logical $3000
part3 bit part3
nop
lda local2
lda _local3
lda local4
nop
nop
nop
bit X_b4_part4
jmp part4
_local3 .byte $83
local4 .byte $84
.here
.here
.logical $4000
b4_part4a
.logical $5000
X_b4_part4
.logical $6000
part4 bit part4
bit X_b4_part4
nop
nop
nop
jmp part4a
.here
part4a bit part4a
bit b4_part4a
jsr part4b
.here
.byte $00
part4b bit part4b
bit zzz
jmp _LF000
.here
.logical $f000
_LF000 nop
bne $f003
.here
.logical $0000
.byte $ea
.byte $00
.here

View File

@ -0,0 +1,79 @@
!cpu 6502
zzz = $103b
* = $0000
!word $1000
!pseudopc $1000 {
begin bit begin
nop
nop
nop
jmp part2
}
!pseudopc $2000 {
part2 bit part2
nop
lda @local1
lda local2
lda local4
nop
nop
nop
bit b4_part3
jmp part3
@local1 !byte $81
local2 !byte $82
b4_part3
!pseudopc $3000 {
part3 bit part3
nop
lda local2
lda @local3
lda local4
nop
nop
nop
bit _b4_part4
jmp part4
@local3 !byte $83
local4 !byte $84
}
}
!pseudopc $4000 {
b4_part4a
!pseudopc $5000 {
_b4_part4
!pseudopc $6000 {
part4 bit part4
bit _b4_part4
nop
nop
nop
jmp part4a
}
part4a bit part4a
bit b4_part4a
jsr part4b
}
!byte $00
part4b bit part4b
bit zzz
jmp @LF000
}
!pseudopc $f000 {
@LF000 nop
bne $f003
}
!pseudopc $0000 {
!byte $ea
!byte $00
}

View File

@ -0,0 +1,73 @@
.setcpu "6502"
zzz = $103b
.org $0000
.word $1000
.org $1000
begin: bit begin
nop
nop
nop
jmp part2
.org $2000
part2: bit part2
nop
lda @local1
lda local2
lda local4
nop
nop
nop
bit b4_part3
jmp part3
@local1: .byte $81
local2: .byte $82
b4_part3:
.org $3000
part3: bit part3
nop
lda local2
lda @local3
lda local4
nop
nop
nop
bit _b4_part4
jmp part4
@local3: .byte $83
local4: .byte $84
.org $4000
b4_part4a:
.org $5000
_b4_part4:
.org $6000
part4: bit part4
bit _b4_part4
nop
nop
nop
jmp part4a
.org $500c
part4a: bit part4a
bit b4_part4a
jsr part4b
.org $4015
.byte $00
part4b: bit part4b
bit zzz
jmp @LF000
.org $f000
@LF000: nop
bne $f003
.org $0000
.byte $ea
.byte $00

View File

@ -0,0 +1,9 @@
# 6502bench SourceGen generated linker script for 20260-pre-labels
MEMORY {
MAIN: file=%O, start=%S, size=65536;
}
SEGMENTS {
CODE: load=MAIN, type=rw;
}
FEATURES {}
SYMBOLS {}

View File

@ -0,0 +1,72 @@
zzz equ $103b
org $0000
dw $1000
org $1000
begin bit begin
nop
nop
nop
jmp part2
org $2000
part2 bit part2
nop
lda :local1
lda local2
lda local4
nop
nop
nop
bit b4_part3
jmp part3
:local1 dfb $81
local2 dfb $82
b4_part3
org $3000
part3 bit part3
nop
lda local2
lda :local3
lda local4
nop
nop
nop
bit _b4_part4
jmp part4
:local3 dfb $83
local4 dfb $84
org $4000
b4_part4a
org $5000
_b4_part4
org $6000
part4 bit part4
bit _b4_part4
nop
nop
nop
jmp part4a
org $500c
part4a bit part4a
bit b4_part4a
jsr part4b
org $4015
dfb $00
part4b bit part4b
bit zzz
jmp :LF000
org $f000
:LF000 nop
bne $f003
org $0000
dfb $ea
dfb $00

View File

@ -0,0 +1,111 @@
; Copyright 2021 faddenSoft. All Rights Reserved.
; See the LICENSE.txt file for distribution terms (Apache 2.0).
;
; Test address region pre-labels.
;
; Assembler: 64tass
; % tass64 --ascii --case-sensitive --nostart 20260-nested-regions.S
.cpu "6502"
* = $1000
; EDIT: create project symbol with same name as pre-label (pre-label should win)
; FILE-EDIT: change pre-label to match user label
; EDIT: create explicit 2-byte NON_ADDR region here.
START
.word $1000
.logical $1000
; EDIT: create floating address region with pre-label here ($1000)
; (label should not appear because parent is non-addr)
begin bit begin
nop
nop
nop
jmp part2
.here
; EDIT: create fixed address region with pre-label here ($2000)
; (label should not appear because parent is non-addr)
.logical $2000
part2 bit part2
nop
lda _local1
lda _local2
lda _local4 ;this should force global conv
; (don't do _local3 here, or it gets promoted to global and interferes
; with local access to both _local3 and _local 4 later on)
nop
nop
nop
bit _b4_part3
jmp _part3
_local1 .byte $81 ;data item with local label
_local2 .byte $82 ;data item with local label
; EDIT: create floating address region with pre-label here ($3000)
_b4_part3
.logical $3000
_part3 bit _part3 ;NOTE: label must be declared local
nop
lda _local2 ;this should force global conv
lda _local3
lda _local4
nop
nop
nop
bit b4_part4
jmp part4
_local3 .byte $83 ;data item with local label
_local4 .byte $84 ;data item with local label
; the $2000 range ends here; the $3000 floater also ends here
.here
.here
; Stack up multiple pre-labels at same offset.
; EDIT: create fixed address region with pre-label here ($4000)
; (label should not appear)
b4_part4b ;(not accessible)
.logical $4000
; EDIT: create fixed address region with pre-label here ($5000)
b4_part4a
.logical $5000
; EDIT: create fixed address region with pre-label here ($6000)
b4_part4
.logical $6000
part4 bit part4
bit b4_part4
nop
nop
nop
jmp part4a
.here
part4a bit part4a
bit b4_part4a
jsr part4b ;JSR to test code analysis halt
.here
brk ;shouldn't reach here
part4b bit part4b
bit b4_part4b
jmp end
.here
; EDIT: created fixed 3-byte address region
.logical $f000
end
nop
bne past ;BNE operand should be shown as hex value
; EDIT: put actual end here, before the nop
past nop
.here
brk

View File

@ -309,7 +309,7 @@ namespace SourceGen.WpfGui {
mPreLabelAddress = curRegion.PreLabelAddress;
if (isSingleLine) {
// Only thing selected was .arstart/.arend. First action is to edit
// 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,
@ -332,7 +332,7 @@ namespace SourceGen.WpfGui {
}
} else {
// Selection started with .arstart and included multiple lines. First
// 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.

View File

@ -1672,8 +1672,8 @@ namespace SourceGen.WpfGui {
ReferencesListItem rli = (ReferencesListItem)item;
// Jump to the offset, then shift the focus back to the code list.
mMainCtrl.GoToLocation(new NavStack.Location(rli.OffsetValue, 0, false),
MainController.GoToMode.JumpToCodeData, true);
mMainCtrl.GoToLocation(new NavStack.Location(rli.OffsetValue, 0,
NavStack.GoToMode.JumpToCodeData), true);
codeListView.Focus();
}
@ -1716,8 +1716,8 @@ namespace SourceGen.WpfGui {
NotesListItem nli = (NotesListItem)item;
// Jump to the offset, then shift the focus back to the code list.
mMainCtrl.GoToLocation(new NavStack.Location(nli.OffsetValue, 0, true),
MainController.GoToMode.JumpToNote, true);
mMainCtrl.GoToLocation(new NavStack.Location(nli.OffsetValue, 0,
NavStack.GoToMode.JumpToNote), true);
codeListView.Focus();
}
@ -2098,8 +2098,8 @@ namespace SourceGen.WpfGui {
MessageListItem mli = (MessageListItem)item;
// Jump to the offset, then shift the focus back to the code list.
mMainCtrl.GoToLocation(new NavStack.Location(mli.OffsetValue, 0, false),
MainController.GoToMode.JumpToCodeData, true);
mMainCtrl.GoToLocation(new NavStack.Location(mli.OffsetValue, 0,
NavStack.GoToMode.JumpToCodeData), true);
codeListView.Focus();
}