1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-29 10:50:28 +00:00

ORG rework, part 2

AddressMap API reshuffle.  Added "pre-label" to class and API.  Split
AddressMapEntry into two parts to make it clear when FLOATING_LEN
has been resolved.

Updated display line list generator to use in-line linear map
traversal.  Previous approach was to walk through the list of regions
in a second pass, inserting .ORG directives, but that was awkward
and is no longer needed.
This commit is contained in:
Andy McFadden 2021-09-17 15:29:35 -07:00
parent 39b7b20144
commit d4c481839e
10 changed files with 397 additions and 282 deletions

View File

@ -63,6 +63,9 @@ namespace CommonUtil {
/// the CHR graphics block in NES programs). The most significant impact this has on
/// SourceGen is that we never resolve address-to-offset lookups in such a region.
///
/// It is valid for the map to be completely empty, or for there to be ranges of offsets
/// for which there is no entry. We can use a catch-all non-addressable region for this.
///
/// For design notes, see https://github.com/fadden/6502bench/issues/107
/// </remarks>
public class AddressMap : IEnumerable<AddressMap.AddressMapEntry> {
@ -83,48 +86,118 @@ namespace CommonUtil {
#region Structural
/// <summary>
/// Code starting at the specified offset will have the specified address.
/// Address map entry definition.
///
/// The entries are held in the list in order, sorted primarily by increasing start offset,
/// secondarily by decreasing end offset. If there are multiple regions at the same
/// offset, the larger (parent) region will appear first (convenient if you're pushing
/// things onto a stack as you traverse the list).
///
/// It is valid for the map to be completely empty, or for there to be ranges of offsets
/// for which there is no entry.
/// offset, the larger (parent) region will appear first.
///
/// Instances are immutable.
/// </summary>
[Serializable]
public class AddressMapEntry {
// Offset at which region starts.
/// <summary>
/// Offset at which region starts.
/// </summary>
public int Offset { get; private set; }
// Length of region; invalid for a "floating" end point.
/// <summary>
/// Length of region, or FLOATING_LEN if the end point is floating.
/// </summary>
public int Length { get; private set; }
// Address to map start of region to.
/// <summary>
/// Address to map start of region to.
/// </summary>
public int Address { get; private set; }
// Is the end point floating?
public bool IsFloating { get; private set; }
// Should we try to generate this with a relative .ORG statement? (This is strictly
// for code generation, and has no effect on anything here.)
/// <summary>
/// If non-empty, label to add right before the start block.
/// </summary>
public string PreLabel { get; private set; }
/// <summary>
/// Should we try to generate the directive with an operand that is relative to
/// the current PC?
/// </summary>
/// <remarks>
/// (This is strictly for code generation, and has no effect on anything here.)
/// </remarks>
public bool IsRelative { get; private set; }
// THOUGHT: we need to be able to do certain info queries from edit dialog. We
// can uniquely identify a node in the region list by offset/len, but that gets
// funny with floating lengths. We can use a "was floating" flag here to make
// it possible to match the offset/float_len to a node in the hierarchical tree.
// (add another constructor)
public AddressMapEntry(int offset, int len, int addr, bool isFloating,
bool isRelative) {
/// <summary>
/// Constructor.
/// </summary>
public AddressMapEntry(int offset, int len, int addr, string preLabel, bool isRelative) {
Offset = offset;
Length = len;
Address = addr;
IsFloating = isFloating;
PreLabel = preLabel;
IsRelative = isRelative;
}
public override string ToString() {
return "[AddrMapEnt: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") +
" addr=$" + Address.ToString("x4") + " preLab='" + PreLabel +
"' isRel=" + IsRelative + "]";
}
}
/// <summary>
/// Address map entry augmented with computed values. Instances of these are used for
/// the hierarchical and linear views.
///
/// The Length property always holds the actual length (never FLOATING_LEN). The
/// IsFloating property indicates whether it was initially floating.
///
/// Instances are immutable.
/// </summary>
public class AddressRegion : AddressMapEntry {
/// <summary>
/// Is the end point floating?
/// </summary>
/// <remarks>
/// (In the structural list this is redundant with the FLOATING_LEN Length value,
/// but in the other structures a new instance that has the actual length is created.)
/// </remarks>
public bool IsFloating { get; private set; }
/// <summary>
/// Address associated with pre-label.
/// </summary>
public int PreLabelAddress { get; private set; }
/// <summary>
/// Full constructor.
/// </summary>
public AddressRegion(int offset, int len, int addr, bool isFloating,
string preLabel, int prevAddr, bool isRelative)
: base(offset, len, addr, preLabel, isRelative) {
IsFloating = isFloating;
PreLabelAddress = prevAddr;
Debug.Assert(Length != FLOATING_LEN);
}
/// <summary>
/// Basic constructor.
/// </summary>
public AddressRegion(int offset, int len, int addr)
: this(offset, len, addr, addr == FLOATING_LEN ? true : false,
string.Empty, NON_ADDR, false) {
}
/// <summary>
/// Construct from AddressRegion.
/// </summary>
public AddressRegion(AddressMapEntry region)
: this(region.Offset, region.Length, region.Address,
region.Address == FLOATING_LEN ? true : false,
region.PreLabel, NON_ADDR, region.IsRelative) {
}
public override string ToString() {
return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") +
" addr=$" + Address.ToString("x4") + " isFloat=" + IsFloating +
" isRel=" + IsRelative + "]";
}
@ -138,7 +211,7 @@ namespace CommonUtil {
/// <summary>
/// List of definitions, in sorted order.
/// </summary>
private List<AddressMapEntry> mRegionList = new List<AddressMapEntry>();
private List<AddressMapEntry> mMapEntries = new List<AddressMapEntry>();
/// <summary>
@ -162,17 +235,18 @@ namespace CommonUtil {
// (Shouldn't be necessary since we're only doing this to pass the address map to
// plugins, but... better safe.)
foreach (AddressMapEntry ent in entries) {
AddResult result = AddRegion(ent.Offset, ent.Length, ent.Address, ent.IsRelative);
AddResult result = AddRegion(ent.Offset, ent.Length, ent.Address, ent.PreLabel,
ent.IsRelative);
if (result != AddResult.Okay) {
throw new Exception("Unable to add entry (" + result + "): " + ent);
}
}
Debug.Assert(entries.Count == mRegionList.Count);
Debug.Assert(entries.Count == mMapEntries.Count);
Regenerate();
}
public void Clear() {
mRegionList.Clear();
mMapEntries.Clear();
Regenerate();
}
@ -182,8 +256,8 @@ namespace CommonUtil {
/// <param name="spanLength">Receives the map's span length.</param>
/// <returns>Copy of list.</returns>
public List<AddressMapEntry> GetEntryList(out int spanLength) {
List<AddressMapEntry> newList = new List<AddressMapEntry>(mRegionList.Count);
foreach (AddressMapEntry ent in mRegionList) {
List<AddressMapEntry> newList = new List<AddressMapEntry>(mMapEntries.Count);
foreach (AddressMapEntry ent in mMapEntries) {
newList.Add(ent);
}
spanLength = mSpanLength;
@ -192,18 +266,18 @@ namespace CommonUtil {
// IEnumerable
public IEnumerator<AddressMapEntry> GetEnumerator() {
return ((IEnumerable<AddressMapEntry>)mRegionList).GetEnumerator();
return ((IEnumerable<AddressMapEntry>)mMapEntries).GetEnumerator();
}
// IEnumerable
IEnumerator IEnumerable.GetEnumerator() {
return ((IEnumerable<AddressMapEntry>)mRegionList).GetEnumerator();
return ((IEnumerable<AddressMapEntry>)mMapEntries).GetEnumerator();
}
/// <summary>
/// Number of entries in the address map.
/// </summary>
public int RegionCount { get { return mRegionList.Count; } }
public int RegionCount { get { return mMapEntries.Count; } }
/// <summary>
/// Error codes for AddRegion().
@ -231,10 +305,14 @@ namespace CommonUtil {
/// - either address is NON_ADDR, or:
/// - addr &gt; 0
/// - addr &lt;= ADDR_MAX
/// - preLabel is not null
///
/// We might want to limit the length to fit within a single 64K bank, unless it's
/// a non-addressable region. That would probably be better as a warning than an error.
/// </remarks>
/// <returns>True if everything looks good.</returns>
private bool ValidateArgs(int offset, int length, int addr) {
return offset >= 0 && offset < mSpanLength &&
private bool ValidateArgs(int offset, int length, int addr, string preLabel) {
return preLabel != null && offset >= 0 && offset < mSpanLength &&
(length != FLOATING_LEN ? offset + length <= mSpanLength : true) &&
((length > 0 && length <= mSpanLength) || length == FLOATING_LEN) &&
((addr >= 0 && addr <= ADDR_MAX) || addr == NON_ADDR);
@ -244,31 +322,61 @@ namespace CommonUtil {
/// Adds a new region.
/// </summary>
/// <param name="offset">File offset of region start.</param>
/// <param name="length">Length of region, or -1 for a floating end point.</param>
/// <param name="length">Length of region, or FLOATING_LEN for a floating end point.</param>
/// <param name="addr">Address of region start.</param>
/// <returns>Failure code.</returns>
public AddResult AddRegion(int offset, int length, int addr) {
return AddRegion(offset, length, addr, string.Empty, false);
}
/// <summary>
/// Adds a new region.
/// </summary>
/// <param name="offset">File offset of region start.</param>
/// <param name="length">Length of region, or FLOATING_LEN for a floating end point.</param>
/// <param name="addr">Address of region start.</param>
/// <param name="preLabel">Pre-region label.</param>
/// <param name="isRelative">True if code generator should output relative
/// assembler directive operand.</param>
/// <returns>Failure code.</returns>
public AddResult AddRegion(int offset, int length, int addr, bool isRelative) {
if (!ValidateArgs(offset, length, addr)) {
public AddResult AddRegion(int offset, int length, int addr, string preLabel,
bool isRelative) {
if (!ValidateArgs(offset, length, addr, preLabel)) {
Debug.WriteLine("AddRegion: invalid arg");
return AddResult.InvalidValue;
}
int insIdx;
AddResult result = FindAddIndex(offset, length, out insIdx);
if (result == AddResult.Okay) {
AddressMapEntry newEntry = new AddressMapEntry(offset, length, addr,
preLabel, isRelative);
mMapEntries.Insert(insIdx, newEntry);
Regenerate();
}
return result;
}
AddressMapEntry newEntry = new AddressMapEntry(offset, length, addr,
length == FLOATING_LEN, isRelative);
/// <summary>
/// Determines whether a new region with the specified offset and length can be added.
/// If it can, returns the list index at which it should be placed.
/// </summary>
/// <param name="offset">File offset of region start.</param>
/// <param name="length">Length of region, or -1 for a floating end point.</param>
/// <param name="outInsIdx">Index at which new region should be added.</param>
/// <returns>Failure code.</returns>
private AddResult FindAddIndex(int offset, int length, out int outInsIdx) {
outInsIdx = -1;
// Empty list?
if (mRegionList.Count == 0) {
mRegionList.Add(newEntry);
Regenerate();
if (mMapEntries.Count == 0) {
outInsIdx = 0;
return AddResult.Okay;
}
// Find insertion point.
int insIdx;
for (insIdx = 0; insIdx < mRegionList.Count; insIdx++) {
AddressMapEntry ent = mRegionList[insIdx];
for (insIdx = 0; insIdx < mMapEntries.Count; insIdx++) {
AddressMapEntry ent = mMapEntries[insIdx];
if (ent.Offset > offset) {
// Insert before this one.
break;
@ -301,7 +409,7 @@ namespace CommonUtil {
if (insIdx > 0) {
// Check previous block. We know that its offset is <= the new offset, so
// either its a parent or a sibling.
AddressMapEntry ent = mRegionList[insIdx - 1];
AddressMapEntry ent = mMapEntries[insIdx - 1];
if (ent.Offset == offset) {
// Previous is our parent. These things were checked earlier.
Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN);
@ -324,10 +432,10 @@ namespace CommonUtil {
}
}
}
if (insIdx < mRegionList.Count) {
if (insIdx < mMapEntries.Count) {
// Check following block. We know that its offset is >= the new offset, so it's
// either a child or a sibling.
AddressMapEntry ent = mRegionList[insIdx];
AddressMapEntry ent = mMapEntries[insIdx];
if (ent.Offset == offset) {
// Following block is our child. These things were checked earlier.
Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN);
@ -351,8 +459,7 @@ namespace CommonUtil {
}
}
mRegionList.Insert(insIdx, newEntry);
Regenerate();
outInsIdx = insIdx;
return AddResult.Okay;
}
@ -362,10 +469,11 @@ namespace CommonUtil {
/// <param name="offset">Offset of region to edit.</param>
/// <param name="length">Length of region to edit.</param>
/// <param name="addr">New value for address.</param>
/// <param name="preLabel">Pre-block label.</param>
/// <param name="isRelative">New value for IsRelative.</param>
/// <returns>True if a region was edited, false otherwise.</returns>
public bool EditRegion(int offset, int length, int addr, bool isRelative) {
if (!ValidateArgs(offset, length, addr)) {
public bool EditRegion(int offset, int length, int addr, string preLabel, bool isRelative) {
if (!ValidateArgs(offset, length, addr, preLabel)) {
throw new Exception("Bad EditRegion args +" + offset.ToString("x6") +
" " + length + " $" + addr);
}
@ -374,8 +482,7 @@ namespace CommonUtil {
if (idx < 0) {
return false;
}
mRegionList[idx] = new AddressMapEntry(offset, length, addr, length == FLOATING_LEN,
isRelative);
mMapEntries[idx] = new AddressMapEntry(offset, length, addr, preLabel, isRelative);
Regenerate();
return true;
}
@ -387,7 +494,7 @@ namespace CommonUtil {
/// <param name="length">Length of region to remove.</param>
/// <returns>True if a region was removed, false otherwise.</returns>
public bool RemoveRegion(int offset, int length) {
if (!ValidateArgs(offset, length, 0)) {
if (!ValidateArgs(offset, length, 0, string.Empty)) {
throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") +
" " + length);
}
@ -396,7 +503,7 @@ namespace CommonUtil {
if (idx < 0) {
return false;
}
mRegionList.RemoveAt(idx);
mMapEntries.RemoveAt(idx);
Regenerate();
return true;
}
@ -408,8 +515,8 @@ namespace CommonUtil {
/// <param name="length">Length to match (may be FLOATING_LEN).</param>
/// <returns>Index of matching region, or -1 if not found.</returns>
private int FindRegion(int offset, int length) {
for (int i = 0; i < mRegionList.Count; i++) {
if (mRegionList[i].Offset == offset && mRegionList[i].Length == length) {
for (int i = 0; i < mMapEntries.Count; i++) {
if (mMapEntries[i].Offset == offset && mMapEntries[i].Length == length) {
return i;
}
}
@ -445,11 +552,11 @@ namespace CommonUtil {
/// <returns>List of entries; may be empty.</returns>
public List<AddressMapEntry> GetRegions(int offset) {
List<AddressMapEntry> regions = new List<AddressMapEntry>();
for (int i = 0; i < mRegionList.Count; i++) {
if (mRegionList[i].Offset == offset) {
regions.Add(mRegionList[i]);
for (int i = 0; i < mMapEntries.Count; i++) {
if (mMapEntries[i].Offset == offset) {
regions.Add(mMapEntries[i]);
}
if (mRegionList[i].Offset > offset) {
if (mMapEntries[i].Offset > offset) {
// Regions are in sorted order, we're done.
break;
}
@ -479,8 +586,8 @@ namespace CommonUtil {
private bool DebugValidateStructural() {
int lastStart = -1;
int lastLength = OFFSET_MAX + 1;
for (int i = 0; i < mRegionList.Count; i++) {
AddressMapEntry ent = mRegionList[i];
for (int i = 0; i < mMapEntries.Count; i++) {
AddressMapEntry ent = mMapEntries[i];
// Do basic range checks on arguments.
if (ent.Offset < 0 || ent.Offset > OFFSET_MAX) {
@ -531,7 +638,7 @@ namespace CommonUtil {
}
public override string ToString() {
return "[AddressMap: " + mRegionList.Count + " entries]";
return "[AddressMap: " + mMapEntries.Count + " entries]";
}
#endregion Structural
@ -545,16 +652,16 @@ namespace CommonUtil {
/// Modifications are rare and trees are expected to be small, so the entire tree is
/// reconstructed whenever a change is made.
///
/// We can reference the AddressMapEntry objects from the structural list unless it
/// has a floating length. If it does, we create a new one with the actual length.
/// We need to resolve floating lengths and pre-label addresses, so the tree holds
/// AddressMapEntry rather than AddressRegion.
/// </remarks>
private class TreeNode {
public AddressMapEntry Entry { get; set; }
public AddressRegion Region { get; set; }
public TreeNode Parent { get; set; }
public List<TreeNode> Children { get; set; }
public TreeNode(AddressMapEntry entry, TreeNode parent) {
Entry = entry;
public TreeNode(AddressRegion region, TreeNode parent) {
Region = region;
Parent = parent;
// all other fields null/false
}
@ -562,7 +669,8 @@ namespace CommonUtil {
/// <summary>
/// Top of the hierarchy. The topmost node is a no-address node that spans the entire
/// file.
/// file. If the region list is empty or has holes, this catches everything that falls
/// through.
/// </summary>
private TreeNode mTopNode;
@ -574,14 +682,15 @@ namespace CommonUtil {
// Create a "fake" node that spans the file, so that any region not covered
// explicitly is caught here. It also avoids the need to special-case the top
// part of the file.
AddressMapEntry globalEnt = new AddressMapEntry(0, mSpanLength, NON_ADDR, false, false);
TreeNode topNode = new TreeNode(globalEnt, null);
AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, false,
string.Empty, NON_ADDR, false);
TreeNode topNode = new TreeNode(globalReg, null);
// Generate the children of this node.
int index = -1;
GenerateChildren(topNode, ref index);
if (index != mRegionList.Count) {
if (index != mMapEntries.Count) {
Debug.Assert(false, "Not all regions traversed");
}
@ -601,37 +710,47 @@ namespace CommonUtil {
List<TreeNode> children = new List<TreeNode>();
index++;
while (index < mRegionList.Count) {
AddressMapEntry childEnt = mRegionList[index];
while (index < mMapEntries.Count) {
AddressMapEntry childEnt = mMapEntries[index];
if (childEnt.Offset >= parent.Entry.Offset + parent.Entry.Length) {
// Starts after end of parent, not a child.
if (childEnt.Offset >= parent.Region.Offset + parent.Region.Length) {
// Starts after end of parent, is not a child. Done with children.
break;
}
// Compute the address for the pre-label.
int preLabelAddr = NON_ADDR;
if (parent != null && parent.Region.Address != NON_ADDR) {
preLabelAddr = parent.Region.Address + childEnt.Offset - parent.Region.Offset;
}
if (childEnt.Length == FLOATING_LEN) {
// Compute actual length. We stop at the end of the parent, or at the start
// of the following region, whichever comes first.
//
// Regions with floating ends can't have children, so we don't need to
// check for sub-regions.
int nextStart = parent.Entry.Offset + parent.Entry.Length;
int nextStart = parent.Region.Offset + parent.Region.Length;
index++;
if (index < mRegionList.Count) {
if (index < mMapEntries.Count) {
// Check next sibling.
int sibStart = mRegionList[index].Offset;
int sibStart = mMapEntries[index].Offset;
if (sibStart < nextStart) {
nextStart = sibStart;
}
}
AddressMapEntry fixedEnt = new AddressMapEntry(childEnt.Offset,
nextStart - childEnt.Offset, childEnt.Address, true, childEnt.IsRelative);
children.Add(new TreeNode(fixedEnt, parent));
AddressRegion fixedReg = new AddressRegion(childEnt.Offset,
nextStart - childEnt.Offset, childEnt.Address, true,
childEnt.PreLabel, preLabelAddr, childEnt.IsRelative);
children.Add(new TreeNode(fixedReg, parent));
// "index" now points to entry past the child we just added.
} else {
// Add this region to the list, and check for descendants.
TreeNode thisNode = new TreeNode(childEnt, parent);
AddressRegion newReg = new AddressRegion(childEnt.Offset,
childEnt.Length, childEnt.Address, false,
childEnt.PreLabel, preLabelAddr, childEnt.IsRelative);
TreeNode thisNode = new TreeNode(newReg, parent);
children.Add(thisNode);
// Check for grandchildren. "index" will point to first entry beyond this
@ -733,12 +852,12 @@ namespace CommonUtil {
}
// Wasn't in any of the children, see if it's in this node.
AddressMapEntry ent = node.Entry;
if (ent.Address == NON_ADDR) {
AddressRegion region = node.Region;
if (region.Address == NON_ADDR) {
// Non-addressable space.
return -1;
}
if (targetAddr < ent.Address || targetAddr >= ent.Address + ent.Length) {
if (targetAddr < region.Address || targetAddr >= region.Address + region.Length) {
// Outside our range of addresses, return failure.
return -1;
}
@ -746,11 +865,11 @@ namespace CommonUtil {
// We span the correct range of addresses. See if the requested address
// falls into a hole spanned by a child.
if (node.Children != null) {
int subPosn = targetAddr - ent.Address; // position of target inside node
int subPosn = targetAddr - region.Address; // position of target inside node
foreach (TreeNode childNode in node.Children) {
AddressMapEntry childEnt = childNode.Entry;
int childStartPosn = childEnt.Offset - ent.Offset;
int childEndPosn = childStartPosn + childEnt.Length;
AddressRegion childReg = childNode.Region;
int childStartPosn = childReg.Offset - region.Offset;
int childEndPosn = childStartPosn + childReg.Length;
if (childStartPosn > subPosn) {
// Child is past the target, it's not in a hole; no need to check
@ -762,7 +881,7 @@ namespace CommonUtil {
}
}
}
return ent.Offset + (targetAddr - ent.Address);
return region.Offset + (targetAddr - region.Address);
}
/// <summary>
@ -783,9 +902,9 @@ namespace CommonUtil {
// Calculate address in this node.
int ourAddr = NON_ADDR;
if (node.Entry.Address != NON_ADDR) {
ourAddr = node.Entry.Address + (offset - node.Entry.Offset);
Debug.Assert(ourAddr < node.Entry.Address + node.Entry.Length);
if (node.Region.Address != NON_ADDR) {
ourAddr = node.Region.Address + (offset - node.Region.Offset);
Debug.Assert(ourAddr < node.Region.Address + node.Region.Length);
}
return ourAddr;
}
@ -799,8 +918,8 @@ namespace CommonUtil {
private TreeNode OffsetToNode(int offset, TreeNode node) {
if (node.Children != null) {
foreach (TreeNode child in node.Children) {
AddressMapEntry childEnt = child.Entry;
if (offset >= childEnt.Offset && offset < childEnt.Offset + childEnt.Length) {
AddressRegion childReg = child.Region;
if (offset >= childReg.Offset && offset < childReg.Offset + childReg.Length) {
// It's in or below this child. Check it with tail recursion.
return OffsetToNode(offset, child);
}
@ -829,16 +948,16 @@ namespace CommonUtil {
/// <param name="length">Length of region.</param>
/// <returns>True if the range of offsets is unbroken.</returns>
public bool IsRangeUnbroken(int offset, int length) {
if (!ValidateArgs(offset, length, 0)) {
if (!ValidateArgs(offset, length, 0, string.Empty)) {
Debug.Assert(false, "Invalid args to IsUnbrokenRange");
return true; // most ranges are unbroken, so just go with that
}
TreeNode node = OffsetToNode(offset, mTopNode);
AddressMapEntry ent = node.Entry;
Debug.Assert(offset >= ent.Offset && offset < ent.Offset + ent.Length);
AddressRegion region = node.Region;
Debug.Assert(offset >= region.Offset && offset < region.Offset + region.Length);
int lastOffset = offset + length - 1; // offset of last byte in range
if (lastOffset >= ent.Offset + ent.Length) {
if (lastOffset >= region.Offset + region.Length) {
// end of region is not in this node
return false;
}
@ -846,14 +965,14 @@ namespace CommonUtil {
// The specified range fits inside this node. See if it's interrupted by a child.
if (node.Children != null) {
foreach (TreeNode childNode in node.Children) {
AddressMapEntry childEnt = childNode.Entry;
AddressRegion childReg = childNode.Region;
if (childEnt.Offset > lastOffset) {
if (childReg.Offset > lastOffset) {
// Child is past the target, so range is not in a hole; no need to check
// additional children because the children are sorted by Offset.
break;
} else if (offset <= childEnt.Offset + childEnt.Length - 1 &&
lastOffset >= childEnt.Offset) {
} else if (offset <= childReg.Offset + childReg.Length - 1 &&
lastOffset >= childReg.Offset) {
// Target is in a hole occupied by the child. No good.
return false;
}
@ -864,7 +983,7 @@ namespace CommonUtil {
}
private bool DebugValidateHierarchical() {
if (mTopNode.Entry.Offset != 0 || mTopNode.Entry.Length != mSpanLength) {
if (mTopNode.Region.Offset != 0 || mTopNode.Region.Length != mSpanLength) {
Debug.WriteLine("Malformed top node");
return false;
}
@ -876,9 +995,9 @@ namespace CommonUtil {
// Check node count. It should have one entry for every entry in the region list
// (we don't count mTopNode).
if (nodeCount != mRegionList.Count) {
if (nodeCount != mMapEntries.Count) {
Debug.WriteLine("Hierarchical is missing entries: nodeCount=" + nodeCount +
" regionCount=" + mRegionList.Count);
" regionCount=" + mMapEntries.Count);
return false;
}
return true;
@ -887,19 +1006,19 @@ namespace CommonUtil {
private bool DebugValidateHierarchy(List<TreeNode> nodeList, int startOffset,
int nextOffset, ref int nodeCount) {
foreach (TreeNode node in nodeList) {
Debug.Assert(node.Entry.Length >= 0); // no floaters
Debug.Assert(node.Region.Length >= 0); // no floaters
nodeCount++;
if (node.Entry.Offset < startOffset ||
node.Entry.Offset + node.Entry.Length > nextOffset) {
if (node.Region.Offset < startOffset ||
node.Region.Offset + node.Region.Length > nextOffset) {
Debug.WriteLine("Child node did not fit in parent bounds");
return false;
}
if (node.Children != null) {
// Descend recursively.
if (!DebugValidateHierarchy(node.Children, node.Entry.Offset,
node.Entry.Offset + node.Entry.Length, ref nodeCount)) {
if (!DebugValidateHierarchy(node.Children, node.Region.Offset,
node.Region.Offset + node.Region.Length, ref nodeCount)) {
return false;
}
}
@ -933,15 +1052,15 @@ namespace CommonUtil {
// in the parent's range.
public int Address { get; private set; }
// Reference to the AddressMapEntry that generated this entry. The reference
// Reference to the AddressRegion that generated this entry. The reference
// will be the same for the "start" and "end" entries.
public AddressMapEntry Entry { get; private set; }
public AddressRegion Region { get; private set; }
public AddressChange(bool isStart, int offset, int addr, AddressMapEntry ent) {
public AddressChange(bool isStart, int offset, int addr, AddressRegion region) {
IsStart = isStart;
Offset = offset;
Address = addr;
Entry = ent;
Region = region;
}
}
@ -957,36 +1076,36 @@ namespace CommonUtil {
if (mTopNode.Children != null) {
foreach (TreeNode node in mTopNode.Children) {
Debug.Assert(node.Entry.Length > 0); // all floaters should be resolved
Debug.Assert(node.Region.Length > 0); // all floaters should be resolved
if (node.Entry.Offset != startOffset) {
if (node.Region.Offset != startOffset) {
// Insert a no-address zone here.
Debug.Assert(node.Entry.Offset > startOffset);
AddressMapEntry tmpEnt = new AddressMapEntry(startOffset,
node.Entry.Offset - startOffset, NON_ADDR, false, false);
changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpEnt));
changeList.Add(new AddressChange(false, node.Entry.Offset, NON_ADDR, tmpEnt));
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));
extraNodes++;
}
AddChangeEntry(changeList, node, NON_ADDR);
startOffset = node.Entry.Offset + node.Entry.Length;
startOffset = node.Region.Offset + node.Region.Length;
}
}
// Finish with a no-address zone if there's a gap.
if (startOffset != mSpanLength) {
Debug.Assert(startOffset < mSpanLength);
AddressMapEntry tmpEnt = new AddressMapEntry(startOffset,
mSpanLength - startOffset, NON_ADDR, false, false);
changeList.Add(new AddressChange(true, startOffset, NON_ADDR, tmpEnt));
changeList.Add(new AddressChange(false, mSpanLength, NON_ADDR, tmpEnt));
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));
extraNodes++;
}
if (changeList.Count != (mRegionList.Count + extraNodes) * 2) {
Debug.Assert(false, "Incorrect linear count: regions*2=" + (mRegionList.Count * 2) +
if (changeList.Count != (mMapEntries.Count + extraNodes) * 2) {
Debug.Assert(false, "Incorrect linear count: regions*2=" + (mMapEntries.Count * 2) +
" extraNodes=" + extraNodes + " changeList=" + changeList.Count);
}
@ -1006,25 +1125,25 @@ namespace CommonUtil {
/// parent's region.</param>
private void AddChangeEntry(List<AddressChange> changeList, TreeNode node,
int parentStartAddr) {
Debug.Assert(node.Entry.Length != FLOATING_LEN);
Debug.Assert(node.Region.Length != FLOATING_LEN);
int nextAddr = NON_ADDR;
if (parentStartAddr != NON_ADDR) {
nextAddr = parentStartAddr + node.Entry.Length;
nextAddr = parentStartAddr + node.Region.Length;
}
AddressChange startChange = new AddressChange(true,
node.Entry.Offset, node.Entry.Address, node.Entry);
node.Region.Offset, node.Region.Address, node.Region);
AddressChange endChange = new AddressChange(false,
node.Entry.Offset + node.Entry.Length, nextAddr, node.Entry);
node.Region.Offset + node.Region.Length, nextAddr, node.Region);
changeList.Add(startChange);
int curAddr = node.Entry.Address;
int curAddr = node.Region.Address;
if (node.Children != null) {
foreach (TreeNode childNode in node.Children) {
int mySpaceAddr = NON_ADDR;
if (curAddr != NON_ADDR) {
// Adjust address in parent space by difference between start of
// parent and start of this node.
mySpaceAddr = curAddr + childNode.Entry.Offset - node.Entry.Offset;
mySpaceAddr = curAddr + childNode.Region.Offset - node.Region.Offset;
}
AddChangeEntry(changeList, childNode, mySpaceAddr);
}
@ -1051,13 +1170,26 @@ namespace CommonUtil {
if (prevChange != null && change.Offset != prevChange.Offset) {
// 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);
}
// Start following end, or start following start after a gap.
if (!string.IsNullOrEmpty(change.Region.PreLabel)) {
sb.Append(" ");
PrintDepthLines(sb, depth);
sb.Append("| pre='" + change.Region.PreLabel + "' ");
if (change.Region.PreLabelAddress != NON_ADDR) {
sb.Append("$" + change.Region.PreLabelAddress.ToString("x4"));
} else {
sb.Append("(non-addr)");
}
sb.Append(CRLF);
}
sb.Append("+" + change.Offset.ToString("x6"));
PrintDepthLines(sb, depth);
sb.Append("+- +" + change.Offset.ToString("x6") + " START (");
sb.Append("+- " + "START (");
PrintAddress(sb, change.Address);
sb.Append(")");
sb.Append(CRLF);
@ -1070,12 +1202,14 @@ namespace CommonUtil {
if (change.Offset != prevChange.Offset) {
// 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);
}
sb.Append("+" + change.Offset.ToString("x6"));
PrintDepthLines(sb, depth);
sb.Append("+- +" + change.Offset.ToString("x6") + " END (now ");
sb.Append("+- " + "END (now ");
PrintAddress(sb, change.Address);
sb.Append(")");
sb.Append(CRLF);
@ -1089,6 +1223,7 @@ namespace CommonUtil {
}
private static void PrintDepthLines(StringBuilder sb, int depth) {
sb.Append(" ");
while (depth-- > 0) {
sb.Append("| ");
}
@ -1159,25 +1294,25 @@ namespace CommonUtil {
const int adr2 = 0x1700;
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, len0, adr0, false));
map.AddRegion(off0, len0, adr0));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off1, len1, adr1, false));
map.AddRegion(off1, len1, adr1));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off2, len2, adr2, false));
map.AddRegion(off2, len2, adr2));
result &= map.DebugValidate();
Test_Expect(AddResult.OverlapExisting, ref result,
map.AddRegion(off0, len0, 0x1000, false));
map.AddRegion(off0, len0, 0x1000));
Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(off0, FLOATING_LEN, 0x1000, false));
map.AddRegion(off0, FLOATING_LEN, 0x1000));
Test_Expect(AddResult.StraddleExisting, ref result,
map.AddRegion(off0 + 1, len0, 0x1000, false));
map.AddRegion(off0 + 1, len0, 0x1000));
Test_Expect(AddResult.InvalidValue, ref result,
map.AddRegion(off0, mapLen + 1, 0x1000, false));
map.AddRegion(off0, mapLen + 1, 0x1000));
// One region to wrap them all. Add then remove.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, mapLen, 0x0000, false));
map.AddRegion(off0, mapLen, 0x0000));
Test_Expect(true, ref result, map.RemoveRegion(off0, mapLen));
Test_Expect(false, ref result, map.RemoveRegion(off0, mapLen));
@ -1219,16 +1354,16 @@ namespace CommonUtil {
const int adr2 = NON_ADDR;
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, len0, adr0, false));
map.AddRegion(off0, len0, adr0));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off1, len1, adr1, false));
map.AddRegion(off1, len1, adr1));
// Try to remove the implicit no-address zone.
Test_Expect(false, ref result, map.RemoveRegion(0, off0));
// Add non-addressable area into the middle of the second region.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off2, len2, adr2, false));
map.AddRegion(off2, len2, adr2));
Test_Expect(adr0, ref result, map.OffsetToAddress(off0));
Test_Expect(adr1, ref result, map.OffsetToAddress(off1));
@ -1255,63 +1390,55 @@ namespace CommonUtil {
bool result = true;
// Nested with shared start offset.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0400, 0x4000, false));
map.AddRegion(0x000100, 0x0400, 0x4000, "preA0", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0100, 0x7000, false));
map.AddRegion(0x000100, 0x0100, 0x7000, "preA1", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0300, 0x5000, false));
map.AddRegion(0x000100, 0x0300, 0x5000, "preA2", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0200, 0x6000, false));
map.AddRegion(0x000100, 0x0200, 0x6000, "preA3", false));
// Add a couple of floaters.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, false));
map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, "preA4", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000101, FLOATING_LEN, 0x3101, false));
map.AddRegion(0x000101, FLOATING_LEN, 0x3101, "preA5", false));
Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(0x000100, FLOATING_LEN, 0x3100, false));
map.AddRegion(0x000100, FLOATING_LEN, 0x3100, "preA6", false));
// Nested with shared end offset.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, false));
map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, "preB0", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001200, 0x0200, 0x6000, false));
map.AddRegion(0x001200, 0x0200, 0x6000, "preB1", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001000, 0x0400, 0x4000, false));
map.AddRegion(0x001000, 0x0400, 0x4000, "preB2", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001100, 0x0300, 0x5000, false));
map.AddRegion(0x001100, 0x0300, 0x5000, "preB3", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001300, 0x0100, 0x7000, false));
map.AddRegion(0x001300, 0x0100, 0x7000, "preB4", false));
// Single-byte region at start and end.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001200, 1, 0x8200, false));
map.AddRegion(0x001200, 1, 0x8200, "preB5", false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x0013ff, 1, 0x83ff, false));
map.AddRegion(0x0013ff, 1, 0x83ff, "preB6", false));
// Nested with no common edge, building from outside-in.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002000, 0x0800, 0x4000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002100, 0x0600, 0x5000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002200, 0x0400, 0x6000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002300, 0x0200, 0x7000, false));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x0800, 0x4000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002300, 0x0200, 0x7000));
// Nested with no common edge, building from inside-out.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x003300, 0x0200, 0x7000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x003200, 0x0400, 0x6000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x003100, 0x0600, 0x5000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x003000, 0x0800, 0x4000, false));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003300, 0x0200, 0x7000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003000, 0x0800, 0x4000));
// Try floater then overlap.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x004000, FLOATING_LEN, 0x8000, false));
map.AddRegion(0x004000, FLOATING_LEN, 0x8000));
Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(0x004000, 0x100, 0x8000, false));
map.AddRegion(0x004000, 0x100, 0x8000));
Test_Expect(true, ref result, map.RemoveRegion(0x004000, FLOATING_LEN));
Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff));
@ -1327,6 +1454,9 @@ namespace CommonUtil {
Test_Expect(0x002100, ref result, map.AddressToOffset(0x002300, 0x5000));
Test_Expect(0x003100, ref result, map.AddressToOffset(0x003000, 0x5000));
string mapStr = map.FormatAddressMap(); // DEBUG - format the map and
Debug.WriteLine(mapStr); // DEBUG - print it to the console
result &= map.DebugValidate();
return result;
}
@ -1336,14 +1466,10 @@ namespace CommonUtil {
AddressMap map = new AddressMap(mapLen);
bool result = true;
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000000, 0x2000, 0x8000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002000, 0x2000, 0x8000, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002100, 0x0200, 0xe100, false));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x003100, 0x0200, 0xf100, false));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x000000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0200, 0xe100));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0200, 0xf100));
Test_Expect(0x003105, ref result, map.AddressToOffset(0x000000, 0xf105));
Test_Expect(0x003105, ref result, map.AddressToOffset(0x002100, 0xf105));
@ -1378,22 +1504,19 @@ namespace CommonUtil {
// Pyramid shape, all regions start at same address except last.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000000, 0x6000, 0x8000, false));
map.AddRegion(0x000000, 0x6000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001000, 0x4000, 0x8000, false));
map.AddRegion(0x001000, 0x4000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002000, 0x2000, 0x7fff, false));
map.AddRegion(0x002000, 0x2000, 0x7fff));
// Second pyramid.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x006000, 0x6000, 0x8000, false));
map.AddRegion(0x006000, 0x6000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x007000, 0x4000, 0x8000, false));
map.AddRegion(0x007000, 0x4000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x008000, 0x2000, 0x8000, false));
string mapStr = map.FormatAddressMap(); // DEBUG - format the map and
Debug.WriteLine(mapStr); // DEBUG - print it to the console
map.AddRegion(0x008000, 0x2000, 0x8000));
// Children take priority over the start node.
Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000));

View File

@ -287,8 +287,8 @@ namespace SourceGen.AsmGen {
OutputLine(SourceFormatter.FullLineCommentDelimiter +
"ACME can't handle 65816 code that lives outside bank zero");
int orgAddr = Project.AddrMap.OffsetToAddress(0);
AddressMap.AddressMapEntry fakeEnt = new AddressMap.AddressMapEntry(0,
Project.FileData.Length, orgAddr, false, false);
AddressMap.AddressRegion fakeEnt = new AddressMap.AddressRegion(0,
Project.FileData.Length, orgAddr);
OutputOrgDirective(fakeEnt, true);
OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty);
OutputOrgDirective(fakeEnt, false);
@ -568,7 +568,7 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) {
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
// This is similar in operation to the AsmTass64 implementation. See comments there.
Debug.Assert(mPcDepth >= 0);
if (isStart) {

View File

@ -561,7 +561,7 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) {
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
if (!isStart) {
return;
}

View File

@ -484,7 +484,7 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) {
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
if (isStart) {
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);

View File

@ -653,7 +653,7 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputOrgDirective(AddressMap.AddressMapEntry addrEntry, bool isStart) {
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
// 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

View File

@ -79,7 +79,7 @@ namespace SourceGen.AsmGen {
// Check for address changes. There may be more than one at a given offset.
AddressMap.AddressChange change = addrIter.Current;
while (change != null && change.Offset == offset) {
gen.OutputOrgDirective(change.Entry, change.IsStart);
gen.OutputOrgDirective(change.Region, change.IsStart);
addrIter.MoveNext();
change = addrIter.Current;
}
@ -152,7 +152,7 @@ namespace SourceGen.AsmGen {
Debug.Assert(false);
}
Debug.Assert(change.Offset == offset);
gen.OutputOrgDirective(change.Entry, change.IsStart);
gen.OutputOrgDirective(change.Region, change.IsStart);
addrIter.MoveNext();
change = addrIter.Current;
}
@ -565,7 +565,7 @@ namespace SourceGen.AsmGen {
return false;
}
AddressMap.AddressChange change = iter.Current;
if (change.Entry.Length != 2) {
if (change.Region.Length != 2) {
Debug.WriteLine("PRG test: first entry is not a two-byte region");
}
// Confirm there's an address map entry at offset 2.

View File

@ -170,7 +170,7 @@ namespace SourceGen.AsmGen {
/// </summary>
/// <param name="addrEntry">Address map entry object.</param>
/// <param name="isStart">True if we're outputing a region-start directive.</param>
void OutputOrgDirective(CommonUtil.AddressMap.AddressMapEntry addrEntry, bool isStart);
void OutputOrgDirective(CommonUtil.AddressMap.AddressRegion addrEntry, bool isStart);
/// <summary>
/// Notify the assembler of a change in register width.

View File

@ -271,7 +271,7 @@ namespace SourceGen {
AddrMap = new AddressMap(fileDataLen);
// set default load address to $1000; override later
AddrMap.AddRegion(0x000000, fileDataLen, 0x1000, false);
AddrMap.AddRegion(0x000000, fileDataLen, 0x1000);
// Default value is "no tag".
AnalyzerTags = new CodeAnalysis.AnalyzerTag[fileDataLen];
@ -400,9 +400,9 @@ namespace SourceGen {
int loadAddr = RawData.GetWord(mFileData, 0, 2, false);
// TODO(org): use NON_ADDR for first two bytes
AddressMap.AddResult addRes =
AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2, false);
AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2);
Debug.Assert(addRes == AddressMap.AddResult.Okay);
addRes = AddrMap.AddRegion(2, mFileData.Length - 2, loadAddr, false);
addRes = AddrMap.AddRegion(2, mFileData.Length - 2, loadAddr);
Debug.Assert(addRes == AddressMap.AddResult.Okay);
OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE,
@ -413,7 +413,7 @@ namespace SourceGen {
} else {
int loadAddr = SystemDefaults.GetLoadAddress(sysDef);
AddressMap.AddResult addRes =
AddrMap.AddRegion(0, mFileData.Length, loadAddr, false);
AddrMap.AddRegion(0, mFileData.Length, loadAddr);
Debug.Assert(addRes == AddressMap.AddResult.Okay);
}
@ -759,19 +759,19 @@ namespace SourceGen {
/// Checks to see if any part of the address map runs across a bank boundary.
/// </summary>
private void ValidateAddressMap() {
// Use the change list, because the region list can have "floating" length values.
// Use the change list, because the map entry list can have "floating" length values.
IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current;
AddressMap.AddressMapEntry entry = change.Entry;
AddressMap.AddressRegion region = change.Region;
if (change.IsStart &&
(entry.Address & 0xff0000) != ((entry.Address + entry.Length - 1) & 0xff0000)) {
(region.Address & 0xff0000) != ((region.Address + region.Length - 1) & 0xff0000)) {
string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT;
int firstNext = (entry.Address & 0xff0000) + 0x010000;
int badOffset = entry.Offset + (firstNext - entry.Address);
int firstNext = (region.Address & 0xff0000) + 0x010000;
int badOffset = region.Offset + (firstNext - region.Address);
Messages.Add(new MessageList.MessageEntry(
MessageList.MessageEntry.SeverityLevel.Error,
entry.Offset,
region.Offset,
MessageList.MessageEntry.MessageType.BankOverrun,
string.Format(fmt, "+" + badOffset.ToString("x6")),
MessageList.MessageEntry.ProblemResolution.None));
@ -2150,7 +2150,7 @@ namespace SourceGen {
if ((int)oldValue == AddressMap.NON_ADDR) {
// adding new entry
if (addrMap.AddRegion(offset, AddressMap.FLOATING_LEN,
(int)newValue, false) != AddressMap.AddResult.Okay) {
(int)newValue) != AddressMap.AddResult.Okay) {
Debug.Assert(false, "failed adding region");
}
} else if ((int)newValue == AddressMap.NON_ADDR) {
@ -2161,7 +2161,7 @@ namespace SourceGen {
} else {
// updating existing entry
if (!addrMap.EditRegion(offset, AddressMap.FLOATING_LEN,
(int) newValue, false)) {
(int) newValue, string.Empty, false)) {
Debug.Assert(false, "failed editing region");
}
}

View File

@ -944,7 +944,8 @@ namespace SourceGen {
// Assume variables may have changed.
mLvLookup.Reset();
// Find the previous status flags for M/X tracking.
// Find the previous status flags for M/X tracking. This is for synthesizing
// long/short pseudo-op directives.
StatusFlags prevFlags = StatusFlags.AllIndeterminate;
if (mProject.CpuDef.HasEmuFlag) {
for (int scanoff = startOffset - 1; scanoff >= 0; scanoff--) {
@ -966,6 +967,8 @@ namespace SourceGen {
// a no-continue instruction (e.g. JMP) followed by an instruction with a label.
// When we rename the label, we don't want the blank to disappear during the
// partial-list generation.
//
// (Blank lines should always be added ABOVE things rather than AFTER things.)
bool addBlank = false;
if (startOffset > 0) {
int baseOff = DataAnalysis.GetBaseOperandOffset(mProject, startOffset - 1);
@ -976,25 +979,69 @@ 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) {
break;
}
}
int offset = startOffset;
while (offset <= endOffset) {
bool blankAdded = false;
Anattrib attr = mProject.GetAnattrib(offset);
if (attr.IsInstructionStart && offset > 0 &&
mProject.GetAnattrib(offset - 1).IsData) {
// Transition from data to code. (Don't add blank line for inline data.)
lines.Add(GenerateBlankLine(offset));
} else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank &&
offset != 0) {
blankAdded = true;
} else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank) {
// Blank line before visualization set helps keep image visually grouped
// with its data. (Slightly weird things happen with .ORG at the start of
// the file; don't try to add a blank there.)
// with its data.
lines.Add(GenerateBlankLine(offset));
blankAdded = true;
} else if (addBlank) {
// Previous instruction wanted to be followed by a blank line.
lines.Add(GenerateBlankLine(offset));
blankAdded = true;
}
addBlank = false;
// Start with address region changes.
while (addrIter.Current != null && addrIter.Current.Offset <= offset) {
AddressMap.AddressChange change = addrIter.Current;
Debug.Assert(change.Offset == offset); // shouldn't be embedded in something
AddressMap.AddressRegion region = change.Region;
if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) {
// Suppress the ORG at offset zero. We know there's another one
// at offset +000002, and that it matches the value at +0/1.
addrIter.MoveNext();
continue;
}
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 && !blankAdded) {
lines.Add(GenerateBlankLine(offset));
}
blankAdded = false; // next one will need a blank line
// TODO(org): pre-label (address / label only, logically part of ORG)
Line newLine = new Line(offset, 0, Line.Type.OrgDirective);
string addrStr = mFormatter.FormatHexValue(region.Address, 4);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr);
lines.Add(newLine);
} else {
// TODO(org)
}
addrIter.MoveNext();
}
// 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.
// TODO: integrate into FormattedOperandCache so we don't have to
@ -1164,61 +1211,6 @@ namespace SourceGen {
offset += attr.Length;
}
}
// See if there were any address shifts in this section. If so, go back and
// insert an ORG statement as the first entry for the offset. We're expecting to
// have very few AddressMap entries (usually just one), so it's more efficient to
// process them here and walk through the sub-list than it is to ping the address map
// at every line.
//
// It should not be possible for an address map change to appear in the middle
// of an instruction or data item.
foreach (AddressMap.AddressMapEntry ent in mProject.AddrMap) {
if (ent.Offset < startOffset || ent.Offset > endOffset) {
continue;
}
if (ent.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) {
// Suppress the ORG at offset zero. We know there's another one
// at offset +000002, and that it matches the value at +0/1.
continue;
}
int index = FindLineByOffset(lines, ent.Offset);
if (index < 0) {
Debug.WriteLine("Couldn't find offset " + ent.Offset +
" in range we just generated");
Debug.Assert(false);
continue;
}
if (lines[index].LineType == Line.Type.Blank) {
index++;
}
Line topLine = lines[index];
Line newLine = new Line(topLine.FileOffset, 0, Line.Type.OrgDirective);
string addrStr = mFormatter.FormatHexValue(ent.Address, 4);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr);
lines.Insert(index, newLine);
// Prepend a blank line if the previous line wasn't already blank, and this
// isn't the ORG at the start of the file. (This may temporarily do
// double-spacing if we do a partial update, because we won't be able to
// "see" the previous line. Harmless.)
// TODO(maybe): consider always adding blanks, and doing a fix-up pass afterward.
// (but keep in mind that blank lines should always come above things)
//
// Interesting case:
// .dd2 $1000
// <blank>
// .org $1234
// .dd2 $aabb ;comment
// We need to include "index == 0" or we'll lose the blank when the comment
// is edited.
if (ent.Offset != 0 &&
(index == 0 || (index > 0 && lines[index-1].LineType != Line.Type.Blank))){
Line blankLine = GenerateBlankLine(topLine.FileOffset);
lines.Insert(index, blankLine);
}
}
}
/// <summary>

View File

@ -670,11 +670,11 @@ namespace SourceGen {
// Deserialize address map.
proj.AddrMap.Clear();
foreach (SerAddressMap addr in spf.AddressMap) {
// TODO(org): serialize length and isRelative
// TODO(org): serialize length, isRelative, and preLabel
int length = CommonUtil.AddressMap.FLOATING_LEN;
AddressMap.AddResult addResult = proj.AddrMap.AddRegion(addr.Offset,
length, addr.Addr, false);
length, addr.Addr);
if (addResult != CommonUtil.AddressMap.AddResult.Okay) {
string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" +
addr.Addr.ToString("x4") + " len=" +