diff --git a/Asm65/Address.cs b/Asm65/Address.cs
index 955855d..bc3c614 100644
--- a/Asm65/Address.cs
+++ b/Asm65/Address.cs
@@ -21,6 +21,25 @@ namespace Asm65 {
/// Memory address primitives.
///
public static class Address {
+ ///
+ /// Address value to use for non-addressable regions of the file, such as file headers
+ /// stripped by the system loader or chunks loaded into non-addressable memory.
+ ///
+ ///
+ /// If you change this value, also update the copy in CommonUtil.AddressMap.
+ ///
+ public const int NON_ADDR = -1025;
+
+ ///
+ /// Human-readable string that represents a non-addressable location.
+ ///
+ ///
+ /// This is a bit deep to bury a human-readable string, but it's useful to have the
+ /// value in one place.
+ ///
+ public const string NON_ADDR_STR = "NA";
+
+
///
/// Converts a 16- or 24-bit address to a string.
///
@@ -41,6 +60,9 @@ namespace Asm65 {
///
/// The following all evaluate to the same thing: 1000, $1000, 0x1000, 00/1000.
///
+ ///
+ /// This doesn't handle "NA", because most of the time we want to parse an actual address.
+ ///
/// String to validate.
/// Maximum valid address value.
/// Integer form.
diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs
index caddc2a..ef50165 100644
--- a/CommonUtil/AddressMap.cs
+++ b/CommonUtil/AddressMap.cs
@@ -78,10 +78,13 @@ namespace CommonUtil {
public const int FLOATING_LEN = -1024;
///
- /// Address value to use for non-addressable regions of the file, such as file headers
- /// stripped by the system loader or chunks loaded into non-addressable memory.
+ /// Value for non-addressable locations.
///
- public const int NON_ADDR = -1025;
+ ///
+ /// MUST match Asm65.Address.NON_ADDR. We can't use the constant directly here because
+ /// the classes are in different packages that aren't dependent upon each other.
+ ///
+ private const int NON_ADDR = -1025;
#region Structural
diff --git a/SourceGen/Anattrib.cs b/SourceGen/Anattrib.cs
index 5ee423b..d678e77 100644
--- a/SourceGen/Anattrib.cs
+++ b/SourceGen/Anattrib.cs
@@ -49,6 +49,8 @@ namespace SourceGen {
Changed = 1 << 17, // set/cleared as the analyzer works
ATagged = 1 << 18, // was this byte affected by an analyzer tag?
+ AddrRegionChange = 1 << 19, // is this byte in a different address region from prev?
+ NonAddressable = 1 << 20, // is this byte in a non-addressable range?
}
// Flags indicating what type of data is here. Use the following Is* properties
@@ -231,6 +233,30 @@ namespace SourceGen {
}
}
}
+ public bool IsAddrRegionChange {
+ get {
+ return (mAttribFlags & AttribFlags.AddrRegionChange) != 0;
+ }
+ set {
+ if (value) {
+ mAttribFlags |= AttribFlags.AddrRegionChange;
+ } else {
+ mAttribFlags &= ~AttribFlags.AddrRegionChange;
+ }
+ }
+ }
+ public bool IsNonAddressable {
+ get {
+ return (mAttribFlags & AttribFlags.NonAddressable) != 0;
+ }
+ set {
+ if (value) {
+ mAttribFlags |= AttribFlags.NonAddressable;
+ } else {
+ mAttribFlags &= ~AttribFlags.NonAddressable;
+ }
+ }
+ }
public bool IsDataStart {
get {
diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs
index d75a0ac..6c7a81f 100644
--- a/SourceGen/AsmGen/AsmAcme.cs
+++ b/SourceGen/AsmGen/AsmAcme.cs
@@ -208,7 +208,7 @@ namespace SourceGen.AsmGen {
// boundary and cause a failure. In that case we want to set the initial address
// to zero and "stream" the rest.
int firstAddr = project.AddrMap.OffsetToAddress(0);
- if (firstAddr == AddressMap.NON_ADDR) {
+ if (firstAddr == Address.NON_ADDR) {
firstAddr = 0;
}
if (firstAddr + project.FileDataLength > 65536) {
@@ -289,9 +289,11 @@ namespace SourceGen.AsmGen {
int firstAddr = Project.AddrMap.OffsetToAddress(0);
AddressMap.AddressRegion fakeRegion = new AddressMap.AddressRegion(0,
Project.FileData.Length, firstAddr);
- OutputArDirective(fakeRegion, true);
+ OutputArDirective(new AddressMap.AddressChange(true,
+ 0, firstAddr, fakeRegion, true));
OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty);
- OutputArDirective(fakeRegion, false);
+ OutputArDirective(new AddressMap.AddressChange(false,
+ 0, firstAddr, fakeRegion, true));
} else {
GenCommon.Generate(this, sw, worker);
}
@@ -568,10 +570,15 @@ namespace SourceGen.AsmGen {
}
// IGenerator
- public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
+ public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) {
// This is similar in operation to the AsmTass64 implementation. See comments there.
Debug.Assert(mPcDepth >= 0);
- if (isStart) {
+ int nextAddress = change.Address;
+ if (nextAddress == Address.NON_ADDR) {
+ // Start non-addressable regions at zero to ensure they don't overflow bank.
+ nextAddress = 0;
+ }
+ if (change.IsStart) {
if (mPcDepth == 0 && mFirstIsOpen) {
mPcDepth++;
@@ -579,7 +586,7 @@ namespace SourceGen.AsmGen {
// mode, just set "*=". If we're in "streaming" mode, we set "*=" to zero
// and then use a pseudo-PC.
if (mOutputMode == OutputMode.Loadable) {
- OutputLine("*", "=", SourceFormatter.FormatHexValue(addrEntry.Address, 4),
+ OutputLine("*", "=", SourceFormatter.FormatHexValue(nextAddress, 4),
string.Empty);
return;
} else {
@@ -589,7 +596,7 @@ namespace SourceGen.AsmGen {
}
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
- SourceFormatter.FormatHexValue(addrEntry.Address, 4) + " {",
+ SourceFormatter.FormatHexValue(nextAddress, 4) + " {",
string.Empty);
mPcDepth++;
} else {
@@ -607,6 +614,9 @@ namespace SourceGen.AsmGen {
}
}
+ // IGenerator
+ public void FlushArDirectives() { }
+
// IGenerator
public void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX) {
if (prevM != newM) {
diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs
index 760d47c..e344690 100644
--- a/SourceGen/AsmGen/AsmCc65.cs
+++ b/SourceGen/AsmGen/AsmCc65.cs
@@ -91,6 +91,11 @@ namespace SourceGen.AsmGen {
///
private bool mHighAsciiMacroOutput;
+ ///
+ /// Address of next byte of output.
+ ///
+ private int mNextAddress = -1;
+
///
/// Holds detected version of configured assembler.
///
@@ -534,13 +539,21 @@ namespace SourceGen.AsmGen {
}
// IGenerator
- public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
- if (!isStart) {
- return;
+ public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) {
+ int nextAddress = change.Address;
+ if (nextAddress == Address.NON_ADDR) {
+ // Start non-addressable regions at zero to ensure they don't overflow bank.
+ nextAddress = 0;
}
+ mNextAddress = nextAddress;
+ }
- OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
- SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);
+ // IGenerator
+ public void FlushArDirectives() {
+ OutputLine(string.Empty,
+ SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
+ SourceFormatter.FormatHexValue(mNextAddress, 4),
+ string.Empty);
}
// IGenerator
diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs
index 437d1f5..aa7e12f 100644
--- a/SourceGen/AsmGen/AsmMerlin32.cs
+++ b/SourceGen/AsmGen/AsmMerlin32.cs
@@ -86,6 +86,11 @@ namespace SourceGen.AsmGen {
///
private StreamWriter mOutStream;
+ ///
+ /// Address of next byte of output.
+ ///
+ private int mNextAddress = -1;
+
///
/// Holds detected version of configured assembler.
///
@@ -485,13 +490,21 @@ namespace SourceGen.AsmGen {
}
// IGenerator
- public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
- if (isStart) {
- OutputLine(string.Empty,
- SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
- SourceFormatter.FormatHexValue(addrEntry.Address, 4),
- string.Empty);
+ public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) {
+ int nextAddress = change.Address;
+ if (nextAddress == Address.NON_ADDR) {
+ // Start non-addressable regions at zero to ensure they don't overflow bank.
+ nextAddress = 0;
}
+ mNextAddress = nextAddress;
+ }
+
+ // IGenerator
+ public void FlushArDirectives() {
+ OutputLine(string.Empty,
+ SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
+ SourceFormatter.FormatHexValue(mNextAddress, 4),
+ string.Empty);
}
// IGenerator
diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs
index 32d0063..a781286 100644
--- a/SourceGen/AsmGen/AsmTass64.cs
+++ b/SourceGen/AsmGen/AsmTass64.cs
@@ -653,7 +653,7 @@ namespace SourceGen.AsmGen {
}
// IGenerator
- public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
+ public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) {
// 64tass separates the "compile offset", which determines where the output fits
// into the generated binary, and "program counter", which determines the code
// the assembler generates. Since we need to explicitly specify every byte in
@@ -675,7 +675,12 @@ namespace SourceGen.AsmGen {
// inside that. So we treat the first region specially, whether or not it wraps
// the rest of the file.
Debug.Assert(mPcDepth >= 0);
- if (isStart) {
+ int nextAddress = change.Address;
+ if (nextAddress == Address.NON_ADDR) {
+ // Start non-addressable regions at zero to ensure they don't overflow bank.
+ nextAddress = 0;
+ }
+ if (change.IsStart) {
if (mPcDepth == 0 && mFirstIsOpen) {
mPcDepth++;
@@ -684,7 +689,7 @@ namespace SourceGen.AsmGen {
// and then use a pseudo-PC.
if (mOutputMode == OutputMode.Loadable) {
OutputLine("*", "=",
- SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);
+ SourceFormatter.FormatHexValue(nextAddress, 4), string.Empty);
return;
} else {
// Set the real PC to address zero to ensure we get a full 64KB. The
@@ -694,7 +699,7 @@ namespace SourceGen.AsmGen {
}
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
- SourceFormatter.FormatHexValue(addrEntry.Address, 4),
+ SourceFormatter.FormatHexValue(nextAddress, 4),
string.Empty);
mPcDepth++;
} else {
@@ -711,6 +716,9 @@ namespace SourceGen.AsmGen {
}
}
+ // IGenerator
+ public void FlushArDirectives() { }
+
// IGenerator
public void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX) {
if (prevM != newM) {
diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs
index c63a48c..16741ee 100644
--- a/SourceGen/AsmGen/GenCommon.cs
+++ b/SourceGen/AsmGen/GenCommon.cs
@@ -59,6 +59,7 @@ namespace SourceGen.AsmGen {
}
}
+ bool arDirectPending = false;
while (offset < proj.FileData.Length) {
Anattrib attr = proj.GetAnattrib(offset);
@@ -80,7 +81,8 @@ namespace SourceGen.AsmGen {
AddressMap.AddressChange change = addrIter.Current;
while (change != null && change.Offset == offset) {
if (change.IsStart) {
- gen.OutputArDirective(change.Region, change.IsStart);
+ gen.OutputArDirective(change);
+ arDirectPending = true;
addrIter.MoveNext();
change = addrIter.Current;
} else {
@@ -88,6 +90,11 @@ namespace SourceGen.AsmGen {
}
}
+ if (arDirectPending) {
+ gen.FlushArDirectives();
+ arDirectPending = false;
+ }
+
List lvars = lvLookup.GetVariablesDefinedAtOffset(offset);
if (lvars != null) {
// table defined here
@@ -142,7 +149,8 @@ namespace SourceGen.AsmGen {
// loop iteration.
while (change != null && change.Offset + 1 == offset) {
if (!change.IsStart) {
- gen.OutputArDirective(change.Region, change.IsStart);
+ gen.OutputArDirective(change);
+ arDirectPending = true;
addrIter.MoveNext();
change = addrIter.Current;
} else {
diff --git a/SourceGen/AsmGen/IGenerator.cs b/SourceGen/AsmGen/IGenerator.cs
index 4f1cdf5..62bc727 100644
--- a/SourceGen/AsmGen/IGenerator.cs
+++ b/SourceGen/AsmGen/IGenerator.cs
@@ -168,9 +168,15 @@ namespace SourceGen.AsmGen {
///
/// Outputs an address region directive.
///
- /// Address region object.
/// True if this is the start of a region.
- void OutputArDirective(CommonUtil.AddressMap.AddressRegion region, bool isStart);
+ /// Address map change record.
+ void OutputArDirective(CommonUtil.AddressMap.AddressChange change);
+
+ ///
+ /// Signals the code generator to flush any pending address region directives. Useful
+ /// for generation of non-hierarchical directives.
+ ///
+ void FlushArDirectives();
///
/// Notify the assembler of a change in register width.
diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs
index 2714e4d..3b584a2 100644
--- a/SourceGen/CodeAnalysis.cs
+++ b/SourceGen/CodeAnalysis.cs
@@ -358,20 +358,43 @@ namespace SourceGen {
IEnumerator addrIter = mAddrMap.AddressChangeIterator;
addrIter.MoveNext();
int addr = 0;
+ bool nonAddr = false;
+ bool addrChange = false;
for (int offset = 0; offset < mAnattribs.Length; offset++) {
- // Process all change events at this offset.
AddressMap.AddressChange change = addrIter.Current;
+
+ // Process all start events at this offset. The new address takes effect
+ // immediately.
while (change != null && change.IsStart && change.Offset == offset) {
addr = change.Address;
+ if (addr == Address.NON_ADDR) {
+ addr = 0;
+ nonAddr = true;
+ } else {
+ nonAddr = false;
+ }
+ addrChange = true;
addrIter.MoveNext();
change = addrIter.Current;
}
mAnattribs[offset].Address = addr++;
+ mAnattribs[offset].IsAddrRegionChange = addrChange;
+ mAnattribs[offset].IsNonAddressable = nonAddr;
+ addrChange = false;
+ // Process all end events at this offset. The new address and "address
+ // region change" flag take effect on the *following* offset.
while (change != null && !change.IsStart && change.Offset == offset) {
addr = change.Address;
+ if (addr == Address.NON_ADDR) {
+ addr = 0;
+ nonAddr = true;
+ } else {
+ nonAddr = false;
+ }
+ addrChange = true;
addrIter.MoveNext();
change = addrIter.Current;
}
@@ -522,6 +545,10 @@ namespace SourceGen {
// the inline data "wins", and we stop here.
LogW(offset, "Code ran into inline data section");
return;
+ } else if (mAnattribs[offset].IsNonAddressable) {
+ mAnattribs[offset].IsInstruction = false;
+ LogW(offset, "Code ran into non-addressable area");
+ return;
}
// Identify the instruction, and see if it runs off the end of the file.
@@ -541,14 +568,18 @@ namespace SourceGen {
mAnattribs[offset].IsData = true;
return;
}
- if (mAnattribs[offset + instrLen -1].Address !=
- mAnattribs[offset].Address + instrLen - 1) {
- // Address change happened mid-instruction. Mark it as data.
- LogW(offset, "Detected address change mid-instruction");
- mAnattribs[offset].IsInstructionStart = false;
- mAnattribs[offset].IsInstruction = false;
- mAnattribs[offset].IsData = true;
- return;
+
+ // Check for mid-instruction address region changes. An address change on the
+ // first byte is fine.
+ for (int i = offset + 1; i < offset + instrLen; i++) {
+ if (mAnattribs[i].IsAddrRegionChange) {
+ // Found a region start and/or end. Mark this offset as data and return.
+ LogW(offset, "Detected address change mid-instruction");
+ mAnattribs[offset].IsInstructionStart = false;
+ mAnattribs[offset].IsInstruction = false;
+ mAnattribs[offset].IsData = true;
+ return;
+ }
}
// Instruction not defined for this CPU. Treat as data.
@@ -782,10 +813,10 @@ namespace SourceGen {
break;
}
- // Make sure we don't "continue" across an ORG.
- // NOTE: it's possible to do some crazy things with multiple ORGs that will
- // cause us to misinterpret things, but I don't think that matters. What's
- // important is that the code analyzer doesn't drive into a data area.
+ // Make sure we don't "continue" across an address change. This is different
+ // from the earlier mid-instruction check in that we don't actually care if
+ // there's a region change between instructions so long as the next address
+ // has the expected value.
int expectedAddr = mAnattribs[offset].Address + mAnattribs[offset].Length +
inlineDataGapLen;
if (mAnattribs[nextOffset].Address != expectedAddr) {
@@ -1118,6 +1149,7 @@ namespace SourceGen {
" label='" + label + "'; file length is" + mFileData.Length);
}
+ // NOTE: might be faster to check Anattrib IsAddrRegionChange for short regions
if (!mAddrMap.IsRangeUnbroken(offset, length)) {
LogW(offset, "SIDF: format crosses address map boundary (len=" + length + ")");
return false;
@@ -1383,12 +1415,15 @@ namespace SourceGen {
}
// Run through file, updating instructions as needed.
- // TODO(org): this is wrong if the file starts with a non-addr region; can walk
- // through anattribs and set to mAnattribs[].Address of first instruction; maybe
- // just init to DbrValue.UNKNOWN and set it inside the loop on first relevant instr
- int firstAddr = mAddrMap.OffsetToAddress(0);
- short curVal = (byte)(firstAddr >> 16); // start with B=K
+ short curVal = DbrValue.UNKNOWN;
for (int offset = 0; offset < mAnattribs.Length; offset++) {
+ if (mAnattribs[offset].IsNonAddressable) {
+ continue;
+ }
+ if (curVal == DbrValue.UNKNOWN) {
+ // On first encounter with addressable memory, init curVal so B=K.
+ curVal = (byte)(mAddrMap.OffsetToAddress(offset) >> 16);
+ }
if (bval[offset] != DbrValue.UNKNOWN) {
curVal = bval[offset];
}
diff --git a/SourceGen/DataAnalysis.cs b/SourceGen/DataAnalysis.cs
index 41272be..eff399a 100644
--- a/SourceGen/DataAnalysis.cs
+++ b/SourceGen/DataAnalysis.cs
@@ -681,8 +681,8 @@ namespace SourceGen {
offset++;
// Check to see if we just crossed an address change.
- if (offset < mAnattribs.Length &&
- !mProject.AddrMap.IsRangeUnbroken(offset - 1, 2)) {
+ if (offset < mAnattribs.Length && mAnattribs[offset].IsAddrRegionChange
+ /*!mProject.AddrMap.IsRangeUnbroken(offset - 1, 2)*/) {
// Must be an ORG here. End region and scan.
AnalyzeRange(startOffset, offset - 1);
startOffset = -1;
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 52a31e4..e621665 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -808,7 +808,7 @@ namespace SourceGen {
/// Task timestamp collection object.
public void Analyze(UndoableChange.ReanalysisScope reanalysisRequired,
CommonUtil.DebugLog debugLog, TaskTimer reanalysisTimer) {
- // This method doesn't report failures. It succeeds to the best of its ability,
+ // This method doesn't return an error code. It succeeds to the best of its ability,
// and handles problems by discarding bad data. The overall philosophy is that
// the program will never generate bad data, and any bad project file contents
// (possibly introduced by hand-editing) are identified at load time, called out
@@ -1530,7 +1530,7 @@ namespace SourceGen {
// Create a mapping from label (which must be unique) to file offset. This
// is different from UserLabels (which only has user-created labels, and is
// sorted by offset) and SymbolTable (which has constants and platform symbols,
- // and uses the address as value rather than the offset).
+ // and uses the address as the value rather than the offset).
SortedList labelList = new SortedList(mFileData.Length,
Asm65.Label.LABEL_COMPARER);
for (int offset = 0; offset < mAnattribs.Length; offset++) {
@@ -1596,6 +1596,15 @@ namespace SourceGen {
// Is this a reference to a label?
if (labelList.TryGetValue(dfd.SymbolRef.Label, out int symOffset)) {
+ if (mAnattribs[symOffset].IsNonAddressable) {
+ Messages.Add(new MessageList.MessageEntry(
+ MessageList.MessageEntry.SeverityLevel.Warning,
+ offset,
+ MessageList.MessageEntry.MessageType.NonAddrLabelRef,
+ dfd.SymbolRef.Label,
+ MessageList.MessageEntry.ProblemResolution.None));
+ }
+
// Compute adjustment.
int adj = 0;
if (operandOffset >= 0) {
diff --git a/SourceGen/DisplayList.cs b/SourceGen/DisplayList.cs
index 8bda1dd..cec5e43 100644
--- a/SourceGen/DisplayList.cs
+++ b/SourceGen/DisplayList.cs
@@ -345,7 +345,7 @@ namespace SourceGen {
}
///
- /// List elements. Instances are immutable.
+ /// List elements. Instances are immutable except for ListIndex.
///
public class FormattedParts {
public string Offset { get; private set; }
@@ -365,14 +365,32 @@ namespace SourceGen {
public bool IsVisualizationSet { get; private set; }
public Visualization[] VisualizationSet { get; private set; }
- // Set to true if we want to highlight the address and label fields.
+ // Set to true if we want to highlight the address and label fields. This is
+ // examined by a data trigger in CodeListItemStyle.xaml.
public bool HasAddrLabelHighlight { get; private set; }
// Set to true if the Flags field has been modified.
- public bool HasModifiedFlags { get; private set; }
+ public bool HasModifiedFlags {
+ get { return (mPartFlags & PartFlags.HasModifiedFlags) != 0; }
+ }
+ // Set to true if the address here is actually non-addressable.
+ public bool IsNonAddressable {
+ get { return (mPartFlags & PartFlags.IsNonAddressable) != 0; }
+ }
+ // List index, filled in on demand. If the list is regenerated we want to
+ // renumber elements without having to recreate them, so this field is mutable.
public int ListIndex { get; set; } = -1;
+ [Flags]
+ public enum PartFlags {
+ None = 0,
+ HasModifiedFlags = 1, // Flags field is non-default
+ IsNonAddressable = 1 << 1, // this is a non-addressable region
+ }
+ private PartFlags mPartFlags;
+
+
private static Color NoColor = CommonWPF.Helper.ZeroColor;
@@ -388,11 +406,10 @@ namespace SourceGen {
private static FormattedParts Clone(FormattedParts orig) {
FormattedParts newParts = FormattedParts.Create(orig.Offset, orig.Addr,
orig.Bytes, orig.Flags, orig.Attr, orig.Label, orig.Opcode, orig.Operand,
- orig.Comment);
+ orig.Comment, orig.mPartFlags);
newParts.IsLongComment = orig.IsLongComment;
newParts.HasAddrLabelHighlight = orig.HasAddrLabelHighlight;
- newParts.HasModifiedFlags = orig.HasModifiedFlags;
newParts.ListIndex = orig.ListIndex;
return newParts;
@@ -400,7 +417,7 @@ namespace SourceGen {
public static FormattedParts Create(string offset, string addr, string bytes,
string flags, string attr, string label, string opcode, string operand,
- string comment) {
+ string comment, PartFlags pflags) {
FormattedParts parts = new FormattedParts();
parts.Offset = offset;
parts.Addr = addr;
@@ -412,6 +429,7 @@ namespace SourceGen {
parts.Operand = operand;
parts.Comment = comment;
parts.IsLongComment = false;
+ parts.mPartFlags = pflags;
return parts;
}
@@ -489,12 +507,6 @@ namespace SourceGen {
return newParts;
}
- public static FormattedParts SetFlagsModified(FormattedParts orig) {
- FormattedParts newParts = Clone(orig);
- newParts.HasModifiedFlags = true;
- return newParts;
- }
-
public override string ToString() {
return "[Parts: index=" + ListIndex + " off=" + Offset + "]";
}
diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs
index 29911a5..5c0d572 100644
--- a/SourceGen/LineListGen.cs
+++ b/SourceGen/LineListGen.cs
@@ -554,7 +554,8 @@ namespace SourceGen {
// should have been done already
default:
Debug.Assert(false);
- parts = FormattedParts.Create("x", "x", "x", "x", "x", "x", "x", "x", "x");
+ parts = FormattedParts.Create("x", "x", "x", "x", "x", "x", "x", "x", "x",
+ FormattedParts.PartFlags.None);
break;
}
line.Parts = parts;
@@ -1083,8 +1084,8 @@ namespace SourceGen {
// TODO(org): pre-label (address / label only, logically part of ORG)
Line newLine = new Line(offset, 0, Line.Type.ArStartDirective, arSubLine++);
string addrStr;
- if (region.Address == AddressMap.NON_ADDR) {
- addrStr = "NA";
+ if (region.Address == Address.NON_ADDR) {
+ addrStr = Address.NON_ADDR_STR;
} else {
addrStr = mFormatter.FormatHexValue(region.Address, 4);
}
@@ -1265,7 +1266,18 @@ namespace SourceGen {
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 - 1);
+ if (change.Offset != offset - 1) {
+ Debug.Assert(false, "Bad offset: change.Offset=+" +
+ change.Offset.ToString("x6") + " offset-1=+" +
+ (offset - 1).ToString("x6"));
+ }
+
+ // 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.
+ if (change.Region.Length != 1) {
+ arSubLine = 0;
+ }
if (change.Region.Offset == 0 && hasPrgHeader) {
// Suppress the .arend at offset +000001.
@@ -1277,11 +1289,11 @@ namespace SourceGen {
if (!change.IsStart) {
// Show the start address to make it easier to pair them visually.
string addrStr;
- if (change.Region.Address == AddressMap.NON_ADDR) {
- addrStr = "(NA)";
+ if (change.Region.Address == Address.NON_ADDR) {
+ addrStr = "\u2191 " + Address.NON_ADDR_STR;
} else {
- addrStr = "(" + mFormatter.FormatHexValue(change.Region.Address, 4) +
- ")";
+ addrStr = "\u2191 " +
+ mFormatter.FormatHexValue(change.Region.Address, 4);
}
// Associate with offset of previous byte.
@@ -1369,7 +1381,6 @@ namespace SourceGen {
opcodeStr = opcodeStr + " \u25bc"; // BLACK DOWN-POINTING TRIANGLE
}
- string formattedOperand = null;
int operandLen = instrLen - 1;
PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.None;
@@ -1401,6 +1412,8 @@ namespace SourceGen {
operandForSymbol = attr.OperandAddress;
}
+ string formattedOperand;
+
// Check Length to watch for bogus descriptors. ApplyFormatDescriptors() should
// have discarded anything appropriate, so we might be able to eliminate this test.
if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length) {
@@ -1472,11 +1485,15 @@ namespace SourceGen {
}
string commentStr = mFormatter.FormatEolComment(eolComment);
- FormattedParts parts = FormattedParts.Create(offsetStr, addrStr, bytesStr,
- flagsStr, attrStr, labelStr, opcodeStr, operandStr, commentStr);
+ FormattedParts.PartFlags pflags = FormattedParts.PartFlags.None;
if (mProject.StatusFlagOverrides[offset] != StatusFlags.DefaultValue) {
- parts = FormattedParts.SetFlagsModified(parts);
+ pflags |= FormattedParts.PartFlags.HasModifiedFlags;
}
+ if (attr.IsNonAddressable) {
+ pflags |= FormattedParts.PartFlags.IsNonAddressable;
+ }
+ FormattedParts parts = FormattedParts.Create(offsetStr, addrStr, bytesStr,
+ flagsStr, attrStr, labelStr, opcodeStr, operandStr, commentStr, pflags);
return parts;
}
@@ -1527,8 +1544,12 @@ namespace SourceGen {
}
}
+ FormattedParts.PartFlags pflags = FormattedParts.PartFlags.None;
+ if (attr.IsNonAddressable) {
+ pflags |= FormattedParts.PartFlags.IsNonAddressable;
+ }
FormattedParts parts = FormattedParts.Create(offsetStr, addrStr, bytesStr,
- flagsStr, attrStr, labelStr, opcodeStr, operandStr, commentStr);
+ flagsStr, attrStr, labelStr, opcodeStr, operandStr, commentStr, pflags);
return parts;
}
@@ -1604,7 +1625,7 @@ namespace SourceGen {
return lvars[tableIndex];
}
- public AddressMap.AddressRegion GetAddrRegionFromLine(Line line) {
+ 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.
// 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.
@@ -1622,11 +1643,13 @@ namespace SourceGen {
while (addrIter.Current != null && addrIter.Current.Offset == offset) {
AddressMap.AddressChange change = addrIter.Current;
if (count == 0) {
+ isSynth = change.IsSynthetic;
return change.Region;
}
if (change.IsStart && !string.IsNullOrEmpty(change.Region.PreLabel)) {
count--;
if (count == 0) {
+ isSynth = change.IsSynthetic;
return change.Region;
}
}
@@ -1635,6 +1658,7 @@ namespace SourceGen {
addrIter.MoveNext();
}
+ isSynth = false;
return null;
}
@@ -1673,8 +1697,13 @@ namespace SourceGen {
operandStr = operands[subLineIndex];
+ FormattedParts.PartFlags pflags = FormattedParts.PartFlags.None;
+ if (attr.IsNonAddressable) {
+ pflags |= FormattedParts.PartFlags.IsNonAddressable;
+ }
FormattedParts parts = FormattedParts.Create(offsetStr, addrStr, bytesStr,
- /*flags*/string.Empty, attrStr, labelStr, opcodeStr, operandStr, commentStr);
+ /*flags*/string.Empty, attrStr, labelStr, opcodeStr, operandStr, commentStr,
+ pflags);
partsArray[subLineIndex] = parts;
}
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 11ddbd7..c8fbfd2 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -1660,7 +1660,8 @@ namespace SourceGen {
if (line.IsAddressRangeDirective) {
// TODO(someday): make this jump to the actual directive rather than nearby code
- AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line);
+ AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line,
+ out bool unused);
if (region == null) {
Debug.Assert(false);
return false;
@@ -1843,6 +1844,15 @@ 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
+ // 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.)
+ // Work around it here.
+ if (CodeLineList[lastIndex].LineType == LineListGen.Line.Type.ArEndDirective) {
+ nextOffset++;
+ }
+
// Compute length of selection. May be zero if it's entirely .arstart/.arend.
int selectedLen = nextOffset - firstOffset;
@@ -1850,10 +1860,19 @@ 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(CodeLineList[selIndex]);
+ curRegion = CodeLineList.GetAddrRegionFromLine(CodeLineList[selIndex],
+ out bool isSynth);
Debug.Assert(curRegion != null);
- Debug.WriteLine("Using region from " + CodeLineList[selIndex].LineType +
- ": " + curRegion);
+ if (isSynth) {
+ // Synthetic regions are created for non-addressable "holes" in the map.
+ // They're not part of the map, so this is a create operation rather than
+ // a resize operation.
+ curRegion = null;
+ Debug.WriteLine("Ignoring synthetic region");
+ } else {
+ Debug.WriteLine("Using region from " + CodeLineList[selIndex].LineType +
+ ": " + curRegion);
+ }
} else {
if (selectedLen == 0) {
// A length of zero is only possible if nothing but directives were selected,
@@ -1866,10 +1885,14 @@ namespace SourceGen {
AddressMap.AddressMapEntry newEntry = null;
if (curRegion == null) {
- // No entry, create a new one.
+ // No entry, create a new one. Use the current address as the default value,
+ // unless the region is non-addressable.
int addr = addrMap.OffsetToAddress(firstOffset);
- // Create a prototype entry with the various values.
+ if (addr == Address.NON_ADDR) {
+ addr = 0;
+ }
+ // Create a prototype entry with the various values.
newEntry = new AddressMap.AddressMapEntry(firstOffset,
selectedLen, addr, string.Empty, false);
Debug.WriteLine("New entry prototype: " + newEntry);
@@ -1970,7 +1993,11 @@ namespace SourceGen {
int offset = CodeLineList[selIndex].FileOffset;
Anattrib attr = mProject.GetAnattrib(offset);
- EditLabel dlg = new EditLabel(mMainWin, attr.Symbol, attr.Address, offset,
+ int addr = attr.Address;
+ if (attr.IsNonAddressable) {
+ addr = Address.NON_ADDR;
+ }
+ EditLabel dlg = new EditLabel(mMainWin, attr.Symbol, addr, offset,
mProject.SymbolTable, mFormatter);
if (dlg.ShowDialog() != true) {
return;
@@ -3849,7 +3876,12 @@ namespace SourceGen {
private void PopulateSymbolsList() {
mMainWin.SymbolsList.Clear();
foreach (Symbol sym in mProject.SymbolTable) {
- string valueStr = mFormatter.FormatHexValue(sym.Value, 0);
+ string valueStr;
+ if (sym.SymbolSource == Symbol.Source.User && sym.Value == Address.NON_ADDR) {
+ valueStr = Address.NON_ADDR_STR;
+ } else {
+ valueStr = mFormatter.FormatHexValue(sym.Value, 0);
+ }
string sourceTypeStr = sym.SourceTypeString;
if (sym is DefSymbol) {
DefSymbol defSym = (DefSymbol)sym;
@@ -3976,10 +4008,15 @@ namespace SourceGen {
if (line.LineType == LineListGen.Line.Type.ArStartDirective ||
line.LineType == LineListGen.Line.Type.ArEndDirective) {
- AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line);
+ AddressMap.AddressRegion region = CodeLineList.GetAddrRegionFromLine(line,
+ out bool isSynth);
+ if (region == null) {
+ Debug.Assert(false, "Unable to find region at: " + line);
+ return;
+ }
StringBuilder esb = new StringBuilder();
esb.Append("Address: ");
- if (region.Address == AddressMap.NON_ADDR) {
+ if (region.Address == Address.NON_ADDR) {
esb.Append("non-addressable");
} else {
esb.Append("$" +
@@ -3997,6 +4034,8 @@ namespace SourceGen {
esb.Append(" (floating)");
}
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,
diff --git a/SourceGen/MessageList.cs b/SourceGen/MessageList.cs
index 824309e..f4a2af6 100644
--- a/SourceGen/MessageList.cs
+++ b/SourceGen/MessageList.cs
@@ -51,6 +51,7 @@ namespace SourceGen {
HiddenLocalVariableTable,
HiddenVisualization,
UnresolvedWeakRef,
+ NonAddrLabelRef,
InvalidOffsetOrLength,
InvalidDescriptor,
BankOverrun,
@@ -165,6 +166,9 @@ namespace SourceGen {
case MessageEntry.MessageType.UnresolvedWeakRef:
problem = Res.Strings.MSG_UNRESOLVED_WEAK_REF;
break;
+ case MessageEntry.MessageType.NonAddrLabelRef:
+ problem = Res.Strings.MSG_NON_ADDR_LABEL_REF;
+ break;
case MessageEntry.MessageType.InvalidOffsetOrLength:
problem = Res.Strings.MSG_INVALID_OFFSET_OR_LENGTH;
break;
diff --git a/SourceGen/RenderAddressMap.cs b/SourceGen/RenderAddressMap.cs
index 9f6d6b1..bb77bf7 100644
--- a/SourceGen/RenderAddressMap.cs
+++ b/SourceGen/RenderAddressMap.cs
@@ -45,7 +45,9 @@ namespace SourceGen {
int prevAddr = 0;
int lastEndOffset = -1;
- sb.AppendLine("Map of address regions");
+ sb.AppendLine("Address region map for " + project.DataFileName);
+ sb.Append(CRLF);
+
IEnumerator iter = addrMap.AddressChangeIterator;
while (iter.MoveNext()) {
AddressChange change = iter.Current;
@@ -109,7 +111,10 @@ namespace SourceGen {
//sb.Append(")");
sb.Append(CRLF);
- PrintDepthLines(sb, depth, true);
+ // Add a blank line, but with the depth lines.
+ if (depth > 0) {
+ PrintDepthLines(sb, depth, true);
+ }
sb.Append(CRLF);
// Use offset+1 here so it lines up with start records.
@@ -139,8 +144,8 @@ namespace SourceGen {
PrintDepthLines(sb, depth, true);
sb.Append(' ');
- if (startAddr == AddressMap.NON_ADDR) {
- sb.Append("-NA-");
+ if (startAddr == Address.NON_ADDR) {
+ sb.Append("-" + Address.NON_ADDR_STR + "-");
} else {
PrintAddress(sb, formatter, startAddr, showBank);
sb.Append(" - ");
@@ -155,8 +160,8 @@ namespace SourceGen {
private static void PrintAddress(StringBuilder sb, Formatter formatter, int addr,
bool showBank) {
- if (addr == AddressMap.NON_ADDR) {
- sb.Append("-NA-");
+ if (addr == Address.NON_ADDR) {
+ sb.Append("-" + Address.NON_ADDR_STR + "-");
} else {
sb.Append("$");
sb.Append(formatter.FormatAddress(addr, showBank));
diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml
index a81acba..7d2e1a2 100644
--- a/SourceGen/Res/Strings.xaml
+++ b/SourceGen/Res/Strings.xaml
@@ -133,6 +133,7 @@ limitations under the License.
Invalid offset or len
Label ignored
LV table skipped over
+ Ref to non-addressable label
Ref'd symbol not found
Visualization ignored
no files available
diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs
index 764cfb1..acdcaf8 100644
--- a/SourceGen/Res/Strings.xaml.cs
+++ b/SourceGen/Res/Strings.xaml.cs
@@ -247,6 +247,8 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_MsgLabelIgnored");
public static string MSG_LOCAL_VARIABLE_TABLE_IGNORED =
(string)Application.Current.FindResource("str_MsgLocalVariableTableIgnored");
+ public static string MSG_NON_ADDR_LABEL_REF =
+ (string)Application.Current.FindResource("str_MsgNonAddrLabelRef");
public static string MSG_UNRESOLVED_WEAK_REF =
(string)Application.Current.FindResource("str_MsgUnresolvedWeakRef");
public static string MSG_VISUALIZATION_IGNORED =
diff --git a/SourceGen/Res/Theme_Dark.xaml b/SourceGen/Res/Theme_Dark.xaml
index a0a44df..c8ab073 100644
--- a/SourceGen/Res/Theme_Dark.xaml
+++ b/SourceGen/Res/Theme_Dark.xaml
@@ -21,6 +21,7 @@ limitations under the License.
+
#FF787D7F
#FF6A787F
diff --git a/SourceGen/Res/Theme_Light.xaml b/SourceGen/Res/Theme_Light.xaml
index 86ceb88..0c2f8dd 100644
--- a/SourceGen/Res/Theme_Light.xaml
+++ b/SourceGen/Res/Theme_Light.xaml
@@ -21,6 +21,7 @@ limitations under the License.
+
#FFF1FBFF
#FFD5F1FE
diff --git a/SourceGen/WpfGui/CodeListItemStyle.xaml b/SourceGen/WpfGui/CodeListItemStyle.xaml
index ce3aa52..aa6e30d 100644
--- a/SourceGen/WpfGui/CodeListItemStyle.xaml
+++ b/SourceGen/WpfGui/CodeListItemStyle.xaml
@@ -356,6 +356,9 @@ See also https://github.com/fadden/DisasmUiTest