1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-27 18:29:30 +00:00

ORG rework, part 3

Split ".org" into ".arstart" and ".arend" (address range start/end).
Address range ends are now shown in the code list view, and the
pseudo-op can be edited in app settings.  Address range starts are
now shown after notes and long comments, rather than before, which
brings the on-screen display in sync with generated code.

Reworked the address range editor UI to include the new features.
The implementation is fully broken.

More changes to the AddressMap API, putting the resolved region length
into a separate ActualLength field.  Added FindRegion().  Renamed
some things.

Code generation changed slightly: the blank line before a region-end
line now comes after it, and ACME's "} ;!pseudopc" is now just "}".
This required minor updates to some of the regression test results.
This commit is contained in:
Andy McFadden 2021-09-22 14:39:39 -07:00
parent d4c481839e
commit 5f472b60cf
40 changed files with 830 additions and 586 deletions

View File

@ -17,12 +17,16 @@ using System;
using System.Diagnostics; using System.Diagnostics;
namespace Asm65 { namespace Asm65 {
/// <summary>
/// Memory address primitives.
/// </summary>
public static class Address { public static class Address {
/// <summary> /// <summary>
/// Converts a 16- or 24-bit address to a string. /// Converts a 16- or 24-bit address to a string.
/// </summary> /// </summary>
/// <param name="addr"></param> /// <param name="addr">Address</param>
/// <returns></returns> /// <param name="always24">If true, force 24-bit output mode.</param>
/// <returns>Formatted string.</returns>
public static string AddressToString(int addr, bool always24) { public static string AddressToString(int addr, bool always24) {
if (!always24 && addr < 65536) { if (!always24 && addr < 65536) {
return addr.ToString("x4"); return addr.ToString("x4");

View File

@ -83,6 +83,11 @@ namespace CommonUtil {
/// </summary> /// </summary>
public const int NON_ADDR = -1025; public const int NON_ADDR = -1025;
/// <summary>
/// Address value to use for an invalid address.
/// </summary>
public const int INVALID_ADDR = -2048;
#region Structural #region Structural
/// <summary> /// <summary>
@ -141,6 +146,27 @@ namespace CommonUtil {
" addr=$" + Address.ToString("x4") + " preLab='" + PreLabel + " addr=$" + Address.ToString("x4") + " preLab='" + PreLabel +
"' isRel=" + IsRelative + "]"; "' isRel=" + IsRelative + "]";
} }
public static bool operator ==(AddressMapEntry a, AddressMapEntry b) {
if (ReferenceEquals(a, b)) {
return true; // same object, or both null
}
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
return false; // one is null
}
// All fields must be equal.
return a.Offset == b.Offset && a.Length == b.Length && a.Address == b.Address &&
a.PreLabel == b.PreLabel && a.IsRelative == b.IsRelative;
}
public static bool operator !=(AddressMapEntry a, AddressMapEntry b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is AddressMapEntry && this == (AddressMapEntry)obj;
}
public override int GetHashCode() {
return Offset ^ Length ^ Address ^ PreLabel.GetHashCode() ^ (IsRelative ? 1 : 0);
}
} }
/// <summary> /// <summary>
@ -154,52 +180,49 @@ namespace CommonUtil {
/// </summary> /// </summary>
public class AddressRegion : AddressMapEntry { public class AddressRegion : AddressMapEntry {
/// <summary> /// <summary>
/// Is the end point floating? /// Actual length (after FLOATING_LEN is resolved).
/// </summary> /// </summary>
/// <remarks> public int ActualLength { get; private set; }
/// (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> /// <summary>
/// Address associated with pre-label. /// Address associated with pre-label.
/// </summary> /// </summary>
public int PreLabelAddress { get; private set; } public int PreLabelAddress { get; private set; }
/// <summary>
/// Is the end point floating?
/// </summary>
public bool IsFloating {
get {
return Length == FLOATING_LEN;
}
}
/// <summary> /// <summary>
/// Full constructor. /// Full constructor.
/// </summary> /// </summary>
public AddressRegion(int offset, int len, int addr, bool isFloating, public AddressRegion(int offset, int len, int addr, string preLabel, bool isRelative,
string preLabel, int prevAddr, bool isRelative) int actualLen, int preLabelAddr)
: base(offset, len, addr, preLabel, isRelative) { : base(offset, len, addr, preLabel, isRelative) {
IsFloating = isFloating; ActualLength = actualLen;
PreLabelAddress = prevAddr; PreLabelAddress = preLabelAddr;
Debug.Assert(Length != FLOATING_LEN); Debug.Assert(ActualLength != FLOATING_LEN);
} }
/// <summary> /// <summary>
/// Basic constructor. /// Basic constructor. Not for use when len==FLOATING_LEN.
/// </summary> /// </summary>
public AddressRegion(int offset, int len, int addr) public AddressRegion(int offset, int len, int addr)
: this(offset, len, addr, addr == FLOATING_LEN ? true : false, : this(offset, len, addr, string.Empty, false, len, NON_ADDR) {
string.Empty, NON_ADDR, false) { Debug.Assert(len != FLOATING_LEN);
}
/// <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() { public override string ToString() {
return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") + return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" +
" addr=$" + Address.ToString("x4") + " isFloat=" + IsFloating + Length.ToString("x4") + " addr=$" + Address.ToString("x4") +
" isRel=" + IsRelative + "]"; " actualLen=$" + ActualLength.ToString("x4") + " isRel=" + IsRelative + "]";
} }
} }
@ -235,7 +258,7 @@ namespace CommonUtil {
// (Shouldn't be necessary since we're only doing this to pass the address map to // (Shouldn't be necessary since we're only doing this to pass the address map to
// plugins, but... better safe.) // plugins, but... better safe.)
foreach (AddressMapEntry ent in entries) { foreach (AddressMapEntry ent in entries) {
AddResult result = AddRegion(ent.Offset, ent.Length, ent.Address, ent.PreLabel, AddResult result = AddEntry(ent.Offset, ent.Length, ent.Address, ent.PreLabel,
ent.IsRelative); ent.IsRelative);
if (result != AddResult.Okay) { if (result != AddResult.Okay) {
throw new Exception("Unable to add entry (" + result + "): " + ent); throw new Exception("Unable to add entry (" + result + "): " + ent);
@ -277,10 +300,10 @@ namespace CommonUtil {
/// <summary> /// <summary>
/// Number of entries in the address map. /// Number of entries in the address map.
/// </summary> /// </summary>
public int RegionCount { get { return mMapEntries.Count; } } public int EntryCount { get { return mMapEntries.Count; } }
/// <summary> /// <summary>
/// Error codes for AddRegion(). /// Error codes for AddEntry().
/// </summary> /// </summary>
public enum AddResult { public enum AddResult {
Unknown = 0, Unknown = 0,
@ -319,18 +342,30 @@ namespace CommonUtil {
} }
/// <summary> /// <summary>
/// Adds a new region. /// Adds a new entry to the map.
/// </summary> /// </summary>
/// <param name="offset">File offset of region start.</param> /// <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="length">Length of region, or FLOATING_LEN for a floating end point.</param>
/// <param name="addr">Address of region start.</param> /// <param name="addr">Address of region start.</param>
/// <returns>Failure code.</returns> /// <returns>Failure code.</returns>
public AddResult AddRegion(int offset, int length, int addr) { public AddResult AddEntry(int offset, int length, int addr) {
return AddRegion(offset, length, addr, string.Empty, false); return AddEntry(offset, length, addr, string.Empty, false);
} }
/// <summary> /// <summary>
/// Adds a new region. /// Adds a new entry to the map.
/// </summary>
/// <param name="entry">Entry object.</param>
/// <returns>Failure code.</returns>
public AddResult AddEntry(AddressMapEntry entry) {
// Slightly inefficient to extract the fields and reassemble them, which we can
// avoid since instances are immutable, but it's not worth coding around.
return AddEntry(entry.Offset, entry.Length, entry.Address, entry.PreLabel,
entry.IsRelative);
}
/// <summary>
/// Adds a new entry to the map.
/// </summary> /// </summary>
/// <param name="offset">File offset of region start.</param> /// <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="length">Length of region, or FLOATING_LEN for a floating end point.</param>
@ -339,10 +374,10 @@ namespace CommonUtil {
/// <param name="isRelative">True if code generator should output relative /// <param name="isRelative">True if code generator should output relative
/// assembler directive operand.</param> /// assembler directive operand.</param>
/// <returns>Failure code.</returns> /// <returns>Failure code.</returns>
public AddResult AddRegion(int offset, int length, int addr, string preLabel, public AddResult AddEntry(int offset, int length, int addr, string preLabel,
bool isRelative) { bool isRelative) {
if (!ValidateArgs(offset, length, addr, preLabel)) { if (!ValidateArgs(offset, length, addr, preLabel)) {
Debug.WriteLine("AddRegion: invalid arg"); Debug.WriteLine("AddEntry: invalid arg");
return AddResult.InvalidValue; return AddResult.InvalidValue;
} }
int insIdx; int insIdx;
@ -472,13 +507,13 @@ namespace CommonUtil {
/// <param name="preLabel">Pre-block label.</param> /// <param name="preLabel">Pre-block label.</param>
/// <param name="isRelative">New value for IsRelative.</param> /// <param name="isRelative">New value for IsRelative.</param>
/// <returns>True if a region was edited, false otherwise.</returns> /// <returns>True if a region was edited, false otherwise.</returns>
public bool EditRegion(int offset, int length, int addr, string preLabel, bool isRelative) { public bool EditEntry(int offset, int length, int addr, string preLabel, bool isRelative) {
if (!ValidateArgs(offset, length, addr, preLabel)) { if (!ValidateArgs(offset, length, addr, preLabel)) {
throw new Exception("Bad EditRegion args +" + offset.ToString("x6") + throw new Exception("Bad EditRegion args +" + offset.ToString("x6") +
" " + length + " $" + addr); " " + length + " $" + addr);
} }
int idx = FindRegion(offset, length); int idx = FindEntry(offset, length);
if (idx < 0) { if (idx < 0) {
return false; return false;
} }
@ -493,13 +528,13 @@ namespace CommonUtil {
/// <param name="offset">Offset of region to remove.</param> /// <param name="offset">Offset of region to remove.</param>
/// <param name="length">Length of region to remove.</param> /// <param name="length">Length of region to remove.</param>
/// <returns>True if a region was removed, false otherwise.</returns> /// <returns>True if a region was removed, false otherwise.</returns>
public bool RemoveRegion(int offset, int length) { public bool RemoveEntry(int offset, int length) {
if (!ValidateArgs(offset, length, 0, string.Empty)) { if (!ValidateArgs(offset, length, 0, string.Empty)) {
throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") + throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") +
" " + length); " " + length);
} }
int idx = FindRegion(offset, length); int idx = FindEntry(offset, length);
if (idx < 0) { if (idx < 0) {
return false; return false;
} }
@ -509,12 +544,12 @@ namespace CommonUtil {
} }
/// <summary> /// <summary>
/// Finds a region with a matching offset and length. /// Finds an entry with a matching offset and length.
/// </summary> /// </summary>
/// <param name="offset">Offset to match.</param> /// <param name="offset">Offset to match.</param>
/// <param name="length">Length to match (may be FLOATING_LEN).</param> /// <param name="length">Length to match (may be FLOATING_LEN).</param>
/// <returns>Index of matching region, or -1 if not found.</returns> /// <returns>Index of matching entry, or -1 if not found.</returns>
private int FindRegion(int offset, int length) { private int FindEntry(int offset, int length) {
for (int i = 0; i < mMapEntries.Count; i++) { for (int i = 0; i < mMapEntries.Count; i++) {
if (mMapEntries[i].Offset == offset && mMapEntries[i].Length == length) { if (mMapEntries[i].Offset == offset && mMapEntries[i].Length == length) {
return i; return i;
@ -531,26 +566,11 @@ namespace CommonUtil {
//} //}
/// <summary> /// <summary>
/// Gets the first region with the specified offset and length. /// Gets a list of the entries with the specified offset value.
/// </summary>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <returns></returns>
//public AddressMapEntry GetFirstRegion(int offset, int length) {
// int idx = FindRegion(offset, length);
// if (idx < 0) {
// return null;
// } else {
// return mRegionList[idx];
// }
//}
/// <summary>
/// Gets a list of the regions with the specified offset value.
/// </summary> /// </summary>
/// <param name="offset">File offset.</param> /// <param name="offset">File offset.</param>
/// <returns>List of entries; may be empty.</returns> /// <returns>List of entries; may be empty.</returns>
public List<AddressMapEntry> GetRegions(int offset) { public List<AddressMapEntry> GetEntries(int offset) {
List<AddressMapEntry> regions = new List<AddressMapEntry>(); List<AddressMapEntry> regions = new List<AddressMapEntry>();
for (int i = 0; i < mMapEntries.Count; i++) { for (int i = 0; i < mMapEntries.Count; i++) {
if (mMapEntries[i].Offset == offset) { if (mMapEntries[i].Offset == offset) {
@ -653,7 +673,7 @@ namespace CommonUtil {
/// reconstructed whenever a change is made. /// reconstructed whenever a change is made.
/// ///
/// We need to resolve floating lengths and pre-label addresses, so the tree holds /// We need to resolve floating lengths and pre-label addresses, so the tree holds
/// AddressMapEntry rather than AddressRegion. /// AddressRegion rather than AddressMapEntry.
/// </remarks> /// </remarks>
private class TreeNode { private class TreeNode {
public AddressRegion Region { get; set; } public AddressRegion Region { get; set; }
@ -682,8 +702,8 @@ namespace CommonUtil {
// Create a "fake" node that spans the file, so that any region not covered // 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 // explicitly is caught here. It also avoids the need to special-case the top
// part of the file. // part of the file.
AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, false, AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, string.Empty,
string.Empty, NON_ADDR, false); false, mSpanLength, NON_ADDR);
TreeNode topNode = new TreeNode(globalReg, null); TreeNode topNode = new TreeNode(globalReg, null);
// Generate the children of this node. // Generate the children of this node.
@ -730,7 +750,7 @@ namespace CommonUtil {
// //
// Regions with floating ends can't have children, so we don't need to // Regions with floating ends can't have children, so we don't need to
// check for sub-regions. // check for sub-regions.
int nextStart = parent.Region.Offset + parent.Region.Length; int nextStart = parent.Region.Offset + parent.Region.ActualLength;
index++; index++;
if (index < mMapEntries.Count) { if (index < mMapEntries.Count) {
// Check next sibling. // Check next sibling.
@ -740,16 +760,16 @@ namespace CommonUtil {
} }
} }
AddressRegion fixedReg = new AddressRegion(childEnt.Offset, AddressRegion fixedReg = new AddressRegion(childEnt.Offset,
nextStart - childEnt.Offset, childEnt.Address, true, FLOATING_LEN, childEnt.Address, childEnt.PreLabel, childEnt.IsRelative,
childEnt.PreLabel, preLabelAddr, childEnt.IsRelative); nextStart - childEnt.Offset, preLabelAddr);
children.Add(new TreeNode(fixedReg, parent)); children.Add(new TreeNode(fixedReg, parent));
// "index" now points to entry past the child we just added. // "index" now points to entry past the child we just added.
} else { } else {
// Add this region to the list, and check for descendants. // Add this region to the list, and check for descendants.
AddressRegion newReg = new AddressRegion(childEnt.Offset, AddressRegion newReg = new AddressRegion(childEnt.Offset,
childEnt.Length, childEnt.Address, false, childEnt.Length, childEnt.Address, childEnt.PreLabel,
childEnt.PreLabel, preLabelAddr, childEnt.IsRelative); childEnt.IsRelative, childEnt.Length, preLabelAddr);
TreeNode thisNode = new TreeNode(newReg, parent); TreeNode thisNode = new TreeNode(newReg, parent);
children.Add(thisNode); children.Add(thisNode);
@ -857,7 +877,7 @@ namespace CommonUtil {
// Non-addressable space. // Non-addressable space.
return -1; return -1;
} }
if (targetAddr < region.Address || targetAddr >= region.Address + region.Length) { if (targetAddr < region.Address || targetAddr >= region.Address + region.ActualLength) {
// Outside our range of addresses, return failure. // Outside our range of addresses, return failure.
return -1; return -1;
} }
@ -869,7 +889,7 @@ namespace CommonUtil {
foreach (TreeNode childNode in node.Children) { foreach (TreeNode childNode in node.Children) {
AddressRegion childReg = childNode.Region; AddressRegion childReg = childNode.Region;
int childStartPosn = childReg.Offset - region.Offset; int childStartPosn = childReg.Offset - region.Offset;
int childEndPosn = childStartPosn + childReg.Length; int childEndPosn = childStartPosn + childReg.ActualLength;
if (childStartPosn > subPosn) { if (childStartPosn > subPosn) {
// Child is past the target, it's not in a hole; no need to check // Child is past the target, it's not in a hole; no need to check
@ -904,7 +924,7 @@ namespace CommonUtil {
int ourAddr = NON_ADDR; int ourAddr = NON_ADDR;
if (node.Region.Address != NON_ADDR) { if (node.Region.Address != NON_ADDR) {
ourAddr = node.Region.Address + (offset - node.Region.Offset); ourAddr = node.Region.Address + (offset - node.Region.Offset);
Debug.Assert(ourAddr < node.Region.Address + node.Region.Length); Debug.Assert(ourAddr < node.Region.Address + node.Region.ActualLength);
} }
return ourAddr; return ourAddr;
} }
@ -919,7 +939,8 @@ namespace CommonUtil {
if (node.Children != null) { if (node.Children != null) {
foreach (TreeNode child in node.Children) { foreach (TreeNode child in node.Children) {
AddressRegion childReg = child.Region; AddressRegion childReg = child.Region;
if (offset >= childReg.Offset && offset < childReg.Offset + childReg.Length) { if (offset >= childReg.Offset &&
offset < childReg.Offset + childReg.ActualLength) {
// It's in or below this child. Check it with tail recursion. // It's in or below this child. Check it with tail recursion.
return OffsetToNode(offset, child); return OffsetToNode(offset, child);
} }
@ -928,6 +949,48 @@ namespace CommonUtil {
return node; return node;
} }
/// <summary>
/// Finds a region with a matching offset and length.
/// </summary>
/// <remarks>
/// We want the AddressRegion object, not the AddressMapEntry, so we need to walk through
/// the tree to find it.
/// </remarks>
/// <param name="offset">Region start offset.</param>
/// <param name="length">Region length. May be FLOATING_LEN.</param>
/// <returns>Region found, or null if not.</returns>
public AddressRegion FindRegion(int offset, int length) {
if (!ValidateArgs(offset, length, 0, string.Empty)) {
Debug.Assert(false, "Invalid args to FindRegion");
return null;
}
TreeNode curNode = mTopNode;
while (curNode != null) {
// Check for exact match.
if (curNode.Region.Offset == offset && curNode.Region.Length == length) {
// found it
return curNode.Region;
}
// Look for a child that includes the offset.
if (curNode.Children == null) {
// Not found, bail.
break;
}
TreeNode foundNode = null;
foreach (TreeNode childNode in curNode.Children) {
if (offset >= childNode.Region.Offset &&
offset < childNode.Region.Offset + childNode.Region.ActualLength) {
foundNode = childNode;
break;
}
}
curNode = foundNode;
}
return null;
}
/// <summary> /// <summary>
/// Checks to see if the specified range of offsets is in an uninterrupted address /// Checks to see if the specified range of offsets is in an uninterrupted address
/// range. Use this to see if something crosses an address-change boundary. This /// range. Use this to see if something crosses an address-change boundary. This
@ -955,9 +1018,9 @@ namespace CommonUtil {
TreeNode node = OffsetToNode(offset, mTopNode); TreeNode node = OffsetToNode(offset, mTopNode);
AddressRegion region = node.Region; AddressRegion region = node.Region;
Debug.Assert(offset >= region.Offset && offset < region.Offset + region.Length); Debug.Assert(offset >= region.Offset && offset < region.Offset + region.ActualLength);
int lastOffset = offset + length - 1; // offset of last byte in range int lastOffset = offset + length - 1; // offset of last byte in range
if (lastOffset >= region.Offset + region.Length) { if (lastOffset >= region.Offset + region.ActualLength) {
// end of region is not in this node // end of region is not in this node
return false; return false;
} }
@ -971,7 +1034,7 @@ namespace CommonUtil {
// Child is past the target, so range is not in a hole; no need to check // 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. // additional children because the children are sorted by Offset.
break; break;
} else if (offset <= childReg.Offset + childReg.Length - 1 && } else if (offset <= childReg.Offset + childReg.ActualLength - 1 &&
lastOffset >= childReg.Offset) { lastOffset >= childReg.Offset) {
// Target is in a hole occupied by the child. No good. // Target is in a hole occupied by the child. No good.
return false; return false;
@ -983,7 +1046,7 @@ namespace CommonUtil {
} }
private bool DebugValidateHierarchical() { private bool DebugValidateHierarchical() {
if (mTopNode.Region.Offset != 0 || mTopNode.Region.Length != mSpanLength) { if (mTopNode.Region.Offset != 0 || mTopNode.Region.ActualLength != mSpanLength) {
Debug.WriteLine("Malformed top node"); Debug.WriteLine("Malformed top node");
return false; return false;
} }
@ -1006,19 +1069,19 @@ namespace CommonUtil {
private bool DebugValidateHierarchy(List<TreeNode> nodeList, int startOffset, private bool DebugValidateHierarchy(List<TreeNode> nodeList, int startOffset,
int nextOffset, ref int nodeCount) { int nextOffset, ref int nodeCount) {
foreach (TreeNode node in nodeList) { foreach (TreeNode node in nodeList) {
Debug.Assert(node.Region.Length >= 0); // no floaters Debug.Assert(node.Region.ActualLength >= 0);
nodeCount++; nodeCount++;
if (node.Region.Offset < startOffset || if (node.Region.Offset < startOffset ||
node.Region.Offset + node.Region.Length > nextOffset) { node.Region.Offset + node.Region.ActualLength > nextOffset) {
Debug.WriteLine("Child node did not fit in parent bounds"); Debug.WriteLine("Child node did not fit in parent bounds");
return false; return false;
} }
if (node.Children != null) { if (node.Children != null) {
// Descend recursively. // Descend recursively.
if (!DebugValidateHierarchy(node.Children, node.Region.Offset, if (!DebugValidateHierarchy(node.Children, node.Region.Offset,
node.Region.Offset + node.Region.Length, ref nodeCount)) { node.Region.Offset + node.Region.ActualLength, ref nodeCount)) {
return false; return false;
} }
} }
@ -1076,7 +1139,7 @@ namespace CommonUtil {
if (mTopNode.Children != null) { if (mTopNode.Children != null) {
foreach (TreeNode node in mTopNode.Children) { foreach (TreeNode node in mTopNode.Children) {
Debug.Assert(node.Region.Length > 0); // all floaters should be resolved Debug.Assert(node.Region.ActualLength > 0);
if (node.Region.Offset != startOffset) { if (node.Region.Offset != startOffset) {
// Insert a no-address zone here. // Insert a no-address zone here.
@ -1090,7 +1153,7 @@ namespace CommonUtil {
AddChangeEntry(changeList, node, NON_ADDR); AddChangeEntry(changeList, node, NON_ADDR);
startOffset = node.Region.Offset + node.Region.Length; startOffset = node.Region.Offset + node.Region.ActualLength;
} }
} }
@ -1125,15 +1188,15 @@ namespace CommonUtil {
/// parent's region.</param> /// parent's region.</param>
private void AddChangeEntry(List<AddressChange> changeList, TreeNode node, private void AddChangeEntry(List<AddressChange> changeList, TreeNode node,
int parentStartAddr) { int parentStartAddr) {
Debug.Assert(node.Region.Length != FLOATING_LEN); Debug.Assert(node.Region.ActualLength != FLOATING_LEN);
int nextAddr = NON_ADDR; int nextAddr = NON_ADDR;
if (parentStartAddr != NON_ADDR) { if (parentStartAddr != NON_ADDR) {
nextAddr = parentStartAddr + node.Region.Length; nextAddr = parentStartAddr + node.Region.ActualLength;
} }
AddressChange startChange = new AddressChange(true, AddressChange startChange = new AddressChange(true,
node.Region.Offset, node.Region.Address, node.Region); node.Region.Offset, node.Region.Address, node.Region);
AddressChange endChange = new AddressChange(false, AddressChange endChange = new AddressChange(false,
node.Region.Offset + node.Region.Length, nextAddr, node.Region); node.Region.Offset + node.Region.ActualLength, nextAddr, node.Region);
changeList.Add(startChange); changeList.Add(startChange);
int curAddr = node.Region.Address; int curAddr = node.Region.Address;
@ -1278,6 +1341,80 @@ namespace CommonUtil {
} }
} }
private static bool Test_Primitives() {
bool result = true;
AddressMapEntry ent1 = new AddressMapEntry(0, 1, 2, "three", true);
AddressMapEntry ent2 = new AddressMapEntry(0, 1, 2, "three", true);
AddressMapEntry ent3 = new AddressMapEntry(0, 1, 2, "three-A", true);
result &= ent1 == ent2;
result &= ent1 != ent3;
result &= ent1.Equals(ent2);
Test_Expect(true, ref result, result);
AddressRegion reg1 = new AddressRegion(ent1.Offset, ent1.Length, ent1.Address,
ent1.PreLabel, ent1.IsRelative, ent1.Length, 0);
AddressRegion reg2 = new AddressRegion(ent2.Offset, ent2.Length, ent2.Address,
ent2.PreLabel, ent2.IsRelative, ent2.Length, 0);
AddressRegion reg3 = new AddressRegion(ent3.Offset, ent3.Length, ent3.Address,
ent3.PreLabel, ent3.IsRelative, ent3.Length, 0);
result &= reg1 == reg2;
result &= reg1 == ent1;
result &= reg1 == ent2;
result &= reg1 != ent3;
result &= reg3 != ent1;
result &= reg1.Equals(ent2);
result &= ent3 != reg1;
Test_Expect(true, ref result, result);
result &= ent1.GetHashCode() == ent2.GetHashCode();
result &= ent2.GetHashCode() != ent3.GetHashCode();
Test_Expect(true, ref result, result);
return result;
}
private static bool Test_Find() {
const int mapLen = 0x1000;
AddressMap map = new AddressMap(mapLen);
bool result = true;
const int off0 = 0x000100;
const int len0 = 0x0f00;
const int adr0 = 0x2100;
const int off1 = 0x000200;
const int len1 = 0x0400;
const int adr1 = 0x2200;
const int off2 = 0x000400;
const int len2 = FLOATING_LEN;
const int adr2 = 0x2400;
AddressMapEntry ent0 = new AddressMapEntry(off0, len0, adr0, string.Empty, false);
AddressMapEntry ent1 = new AddressMapEntry(off1, len1, adr1, string.Empty, false);
AddressMapEntry ent2 = new AddressMapEntry(off2, len2, adr2, string.Empty, false);
Test_Expect(AddResult.Okay, ref result, map.AddEntry(ent0));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(ent1));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(ent2));
AddressRegion reg;
reg = map.FindRegion(off0, len0);
Test_Expect(true, ref result, reg == ent0);
reg = map.FindRegion(off1, len1);
Test_Expect(true, ref result, reg == ent1);
reg = map.FindRegion(off2, len2);
Test_Expect(true, ref result, reg == ent2);
// Look for non-existent regions.
reg = map.FindRegion(0x000000, 0x100);
Test_Expect(true, ref result, reg == null);
reg = map.FindRegion(off0, len1);
Test_Expect(true, ref result, reg == null);
return result;
}
private static bool Test_SimpleLinear() { private static bool Test_SimpleLinear() {
const int mapLen = 0x8000; const int mapLen = 0x8000;
AddressMap map = new AddressMap(mapLen); AddressMap map = new AddressMap(mapLen);
@ -1294,27 +1431,27 @@ namespace CommonUtil {
const int adr2 = 0x1700; const int adr2 = 0x1700;
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, len0, adr0)); map.AddEntry(off0, len0, adr0));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off1, len1, adr1)); map.AddEntry(off1, len1, adr1));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off2, len2, adr2)); map.AddEntry(off2, len2, adr2));
result &= map.DebugValidate(); result &= map.DebugValidate();
Test_Expect(AddResult.OverlapExisting, ref result, Test_Expect(AddResult.OverlapExisting, ref result,
map.AddRegion(off0, len0, 0x1000)); map.AddEntry(off0, len0, 0x1000));
Test_Expect(AddResult.OverlapFloating, ref result, Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(off0, FLOATING_LEN, 0x1000)); map.AddEntry(off0, FLOATING_LEN, 0x1000));
Test_Expect(AddResult.StraddleExisting, ref result, Test_Expect(AddResult.StraddleExisting, ref result,
map.AddRegion(off0 + 1, len0, 0x1000)); map.AddEntry(off0 + 1, len0, 0x1000));
Test_Expect(AddResult.InvalidValue, ref result, Test_Expect(AddResult.InvalidValue, ref result,
map.AddRegion(off0, mapLen + 1, 0x1000)); map.AddEntry(off0, mapLen + 1, 0x1000));
// One region to wrap them all. Add then remove. // One region to wrap them all. Add then remove.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, mapLen, 0x0000)); map.AddEntry(off0, mapLen, 0x0000));
Test_Expect(true, ref result, map.RemoveRegion(off0, mapLen)); Test_Expect(true, ref result, map.RemoveEntry(off0, mapLen));
Test_Expect(false, ref result, map.RemoveRegion(off0, mapLen)); Test_Expect(false, ref result, map.RemoveEntry(off0, mapLen));
Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); Test_Expect(adr0, ref result, map.OffsetToAddress(off0));
Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); Test_Expect(adr1, ref result, map.OffsetToAddress(off1));
@ -1354,16 +1491,16 @@ namespace CommonUtil {
const int adr2 = NON_ADDR; const int adr2 = NON_ADDR;
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, len0, adr0)); map.AddEntry(off0, len0, adr0));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off1, len1, adr1)); map.AddEntry(off1, len1, adr1));
// Try to remove the implicit no-address zone. // Try to remove the implicit no-address zone.
Test_Expect(false, ref result, map.RemoveRegion(0, off0)); Test_Expect(false, ref result, map.RemoveEntry(0, off0));
// Add non-addressable area into the middle of the second region. // Add non-addressable area into the middle of the second region.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off2, len2, adr2)); map.AddEntry(off2, len2, adr2));
Test_Expect(adr0, ref result, map.OffsetToAddress(off0)); Test_Expect(adr0, ref result, map.OffsetToAddress(off0));
Test_Expect(adr1, ref result, map.OffsetToAddress(off1)); Test_Expect(adr1, ref result, map.OffsetToAddress(off1));
@ -1390,56 +1527,56 @@ namespace CommonUtil {
bool result = true; bool result = true;
// Nested with shared start offset. // Nested with shared start offset.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0400, 0x4000, "preA0", false)); map.AddEntry(0x000100, 0x0400, 0x4000, "preA0", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0100, 0x7000, "preA1", false)); map.AddEntry(0x000100, 0x0100, 0x7000, "preA1", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0300, 0x5000, "preA2", false)); map.AddEntry(0x000100, 0x0300, 0x5000, "preA2", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000100, 0x0200, 0x6000, "preA3", false)); map.AddEntry(0x000100, 0x0200, 0x6000, "preA3", false));
// Add a couple of floaters. // Add a couple of floaters.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x0000ff, FLOATING_LEN, 0x30ff, "preA4", false)); map.AddEntry(0x0000ff, FLOATING_LEN, 0x30ff, "preA4", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000101, FLOATING_LEN, 0x3101, "preA5", false)); map.AddEntry(0x000101, FLOATING_LEN, 0x3101, "preA5", false));
Test_Expect(AddResult.OverlapFloating, ref result, Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(0x000100, FLOATING_LEN, 0x3100, "preA6", false)); map.AddEntry(0x000100, FLOATING_LEN, 0x3100, "preA6", false));
// Nested with shared end offset. // Nested with shared end offset.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000fff, FLOATING_LEN, 0x3fff, "preB0", false)); map.AddEntry(0x000fff, FLOATING_LEN, 0x3fff, "preB0", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001200, 0x0200, 0x6000, "preB1", false)); map.AddEntry(0x001200, 0x0200, 0x6000, "preB1", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001000, 0x0400, 0x4000, "preB2", false)); map.AddEntry(0x001000, 0x0400, 0x4000, "preB2", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001100, 0x0300, 0x5000, "preB3", false)); map.AddEntry(0x001100, 0x0300, 0x5000, "preB3", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001300, 0x0100, 0x7000, "preB4", false)); map.AddEntry(0x001300, 0x0100, 0x7000, "preB4", false));
// Single-byte region at start and end. // Single-byte region at start and end.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001200, 1, 0x8200, "preB5", false)); map.AddEntry(0x001200, 1, 0x8200, "preB5", false));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x0013ff, 1, 0x83ff, "preB6", false)); map.AddEntry(0x0013ff, 1, 0x83ff, "preB6", false));
// Nested with no common edge, building from outside-in. // Nested with no common edge, building from outside-in.
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x0800, 0x4000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002000, 0x0800, 0x4000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0600, 0x5000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002200, 0x0400, 0x6000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002300, 0x0200, 0x7000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002300, 0x0200, 0x7000));
// Nested with no common edge, building from inside-out. // Nested with no common edge, building from inside-out.
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003300, 0x0200, 0x7000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003300, 0x0200, 0x7000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003200, 0x0400, 0x6000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0600, 0x5000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003000, 0x0800, 0x4000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003000, 0x0800, 0x4000));
// Try floater then overlap. // Try floater then overlap.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x004000, FLOATING_LEN, 0x8000)); map.AddEntry(0x004000, FLOATING_LEN, 0x8000));
Test_Expect(AddResult.OverlapFloating, ref result, Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(0x004000, 0x100, 0x8000)); map.AddEntry(0x004000, 0x100, 0x8000));
Test_Expect(true, ref result, map.RemoveRegion(0x004000, FLOATING_LEN)); Test_Expect(true, ref result, map.RemoveEntry(0x004000, FLOATING_LEN));
Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff)); Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff));
Test_Expect(0x7000, ref result, map.OffsetToAddress(0x000100)); Test_Expect(0x7000, ref result, map.OffsetToAddress(0x000100));
@ -1466,10 +1603,10 @@ namespace CommonUtil {
AddressMap map = new AddressMap(mapLen); AddressMap map = new AddressMap(mapLen);
bool result = true; bool result = true;
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x000000, 0x2000, 0x8000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x2000, 0x8000)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0200, 0xe100)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002100, 0x0200, 0xe100));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0200, 0xf100)); Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003100, 0x0200, 0xf100));
Test_Expect(0x003105, ref result, map.AddressToOffset(0x000000, 0xf105)); Test_Expect(0x003105, ref result, map.AddressToOffset(0x000000, 0xf105));
Test_Expect(0x003105, ref result, map.AddressToOffset(0x002100, 0xf105)); Test_Expect(0x003105, ref result, map.AddressToOffset(0x002100, 0xf105));
@ -1504,19 +1641,19 @@ namespace CommonUtil {
// Pyramid shape, all regions start at same address except last. // Pyramid shape, all regions start at same address except last.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000000, 0x6000, 0x8000)); map.AddEntry(0x000000, 0x6000, 0x8000));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001000, 0x4000, 0x8000)); map.AddEntry(0x001000, 0x4000, 0x8000));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002000, 0x2000, 0x7fff)); map.AddEntry(0x002000, 0x2000, 0x7fff));
// Second pyramid. // Second pyramid.
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x006000, 0x6000, 0x8000)); map.AddEntry(0x006000, 0x6000, 0x8000));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x007000, 0x4000, 0x8000)); map.AddEntry(0x007000, 0x4000, 0x8000));
Test_Expect(AddResult.Okay, ref result, Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x008000, 0x2000, 0x8000)); map.AddEntry(0x008000, 0x2000, 0x8000));
// Children take priority over the start node. // Children take priority over the start node.
Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000)); Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000));
@ -1559,6 +1696,8 @@ namespace CommonUtil {
public static bool Test() { public static bool Test() {
bool ok = true; bool ok = true;
ok &= Test_Primitives();
ok &= Test_Find();
ok &= Test_SimpleLinear(); ok &= Test_SimpleLinear();
ok &= Test_SimpleFloatGap(); ok &= Test_SimpleFloatGap();
ok &= Test_Nested(); ok &= Test_Nested();

View File

@ -37,7 +37,6 @@ namespace SourceGen.AsmGen {
// makefile rules. Since ".S" is pretty universal for assembly language sources, // makefile rules. Since ".S" is pretty universal for assembly language sources,
// I'm sticking with that. // I'm sticking with that.
private const string ASM_FILE_SUFFIX = "_acme.S"; // must start with underscore private const string ASM_FILE_SUFFIX = "_acme.S"; // must start with underscore
private const string CLOSE_PSEUDOPC = "} ;!pseudopc";
// IGenerator // IGenerator
public DisasmProject Project { get; private set; } public DisasmProject Project { get; private set; }
@ -124,7 +123,8 @@ namespace SourceGen.AsmGen {
new PseudoOp.PseudoOpNames(new Dictionary<string, string> { new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" }, { "EquDirective", "=" },
//VarDirective //VarDirective
{ "OrgDirective", "!pseudopc" }, { "ArStartDirective", "!pseudopc" },
{ "ArEndDirective", "}" },
//RegWidthDirective // !al, !as, !rl, !rs //RegWidthDirective // !al, !as, !rl, !rs
//DataBankDirective //DataBankDirective
{ "DefineData1", "!byte" }, { "DefineData1", "!byte" },
@ -286,12 +286,12 @@ namespace SourceGen.AsmGen {
// don't try // don't try
OutputLine(SourceFormatter.FullLineCommentDelimiter + OutputLine(SourceFormatter.FullLineCommentDelimiter +
"ACME can't handle 65816 code that lives outside bank zero"); "ACME can't handle 65816 code that lives outside bank zero");
int orgAddr = Project.AddrMap.OffsetToAddress(0); int firstAddr = Project.AddrMap.OffsetToAddress(0);
AddressMap.AddressRegion fakeEnt = new AddressMap.AddressRegion(0, AddressMap.AddressRegion fakeRegion = new AddressMap.AddressRegion(0,
Project.FileData.Length, orgAddr); Project.FileData.Length, firstAddr);
OutputOrgDirective(fakeEnt, true); OutputArDirective(fakeRegion, true);
OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty); OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty);
OutputOrgDirective(fakeEnt, false); OutputArDirective(fakeRegion, false);
} else { } else {
GenCommon.Generate(this, sw, worker); GenCommon.Generate(this, sw, worker);
} }
@ -568,7 +568,7 @@ namespace SourceGen.AsmGen {
} }
// IGenerator // IGenerator
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
// This is similar in operation to the AsmTass64 implementation. See comments there. // This is similar in operation to the AsmTass64 implementation. See comments there.
Debug.Assert(mPcDepth >= 0); Debug.Assert(mPcDepth >= 0);
if (isStart) { if (isStart) {
@ -587,15 +587,19 @@ namespace SourceGen.AsmGen {
OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty);
} }
} }
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), OutputLine(string.Empty,
SourceFormatter.FormatHexValue(addrEntry.Address, 4) + " {", string.Empty); SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4) + " {",
string.Empty);
mPcDepth++; mPcDepth++;
} else { } else {
mPcDepth--; mPcDepth--;
if (mPcDepth > 0 || !mFirstIsOpen) { if (mPcDepth > 0 || !mFirstIsOpen) {
// close previous block // close previous block
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(CLOSE_PSEUDOPC), OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective),
string.Empty, string.Empty); string.Empty, string.Empty);
//";" + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective));
} else { } else {
// mark initial "*=" region as closed, but don't output anything // mark initial "*=" region as closed, but don't output anything
mFirstIsOpen = false; mFirstIsOpen = false;

View File

@ -106,7 +106,8 @@ namespace SourceGen.AsmGen {
new PseudoOp.PseudoOpNames(new Dictionary<string, string> { new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" }, { "EquDirective", "=" },
{ "VarDirective", ".set" }, { "VarDirective", ".set" },
{ "OrgDirective", ".org" }, { "ArStartDirective", ".org" },
//ArEndDirective
//RegWidthDirective // .a8, .a16, .i8, .i16 //RegWidthDirective // .a8, .a16, .i8, .i16
//DataBankDirective //DataBankDirective
{ "DefineData1", ".byte" }, { "DefineData1", ".byte" },
@ -250,20 +251,10 @@ namespace SourceGen.AsmGen {
sw.WriteLine("MEMORY {"); sw.WriteLine("MEMORY {");
sw.WriteLine(" MAIN: file=%O, start=%S, size=65536;"); sw.WriteLine(" MAIN: file=%O, start=%S, size=65536;");
//int i = 0;
//foreach (AddressMap.AddressMapEntry ame in Project.AddrMap) {
// sw.WriteLine(string.Format("# MEM{0:D3}: file=%O, start=${1:x4}, size={2};",
// i, ame.Address, ame.Length));
// i++;
//}
sw.WriteLine("}"); sw.WriteLine("}");
sw.WriteLine("SEGMENTS {"); sw.WriteLine("SEGMENTS {");
sw.WriteLine(" CODE: load=MAIN, type=rw;"); sw.WriteLine(" CODE: load=MAIN, type=rw;");
//foreach (AddressMap.AddressMapEntry ame in Project.AddrMap) {
// sw.WriteLine(string.Format("# SEG{0:D3}: load=MEM{0:D3}, type=rw;", i));
// i++;
//}
sw.WriteLine("}"); sw.WriteLine("}");
sw.WriteLine("FEATURES {}"); sw.WriteLine("FEATURES {}");
@ -493,24 +484,6 @@ namespace SourceGen.AsmGen {
OutputLine(labelStr, opcodeStr, operandStr, commentStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr);
labelStr = commentStr = string.Empty; labelStr = commentStr = string.Empty;
} }
//StringBuilder sb = new StringBuilder(MAX_OPERAND_LEN);
//int maxPerLine = MAX_OPERAND_LEN / 4;
//int numChunks = (length + maxPerLine - 1) / maxPerLine;
//for (int chunk = 0; chunk < numChunks; chunk++) {
// int chunkStart = chunk * maxPerLine;
// int chunkEnd = Math.Min((chunk + 1) * maxPerLine, length);
// for (int i = chunkStart; i < chunkEnd; i++) {
// if (i != chunkStart) {
// sb.Append(',');
// }
// sb.Append(formatter.FormatHexValue(data[offset + i], 2));
// }
// OutputLine(labelStr, opcodeStr, sb.ToString(), commentStr);
// labelStr = commentStr = string.Empty;
// sb.Clear();
//}
} }
/// <summary> /// <summary>
@ -561,30 +534,12 @@ namespace SourceGen.AsmGen {
} }
// IGenerator // IGenerator
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
if (!isStart) { if (!isStart) {
return; return;
} }
//// Linear search for offset. List should be small, so this should be quick. OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
//int index = 0;
//foreach (AddressMap.AddressMapEntry ame in Project.AddrMap) {
// if (ame.Offset == addrEntry.Offset && ame.Length == addrEntry.Length) {
// break;
// }
// index++;
//}
//mLineBuilder.Clear();
//TextUtil.AppendPaddedString(mLineBuilder, ";", 0);
//// using +1 to make it look like the comment ';' shifted it over
//TextUtil.AppendPaddedString(mLineBuilder, SourceFormatter.FormatPseudoOp(".segment"),
// mColumnWidths[0] + 1);
//TextUtil.AppendPaddedString(mLineBuilder, string.Format("\"SEG{0:D3}\"", index),
// mColumnWidths[0] + mColumnWidths[1] + 1);
//OutputLine(mLineBuilder.ToString());
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);
} }

View File

@ -100,7 +100,8 @@ namespace SourceGen.AsmGen {
new PseudoOp.PseudoOpNames(new Dictionary<string, string> { new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "equ" }, { "EquDirective", "equ" },
{ "VarDirective", "equ" }, { "VarDirective", "equ" },
{ "OrgDirective", "org" }, { "ArStartDirective", "org" },
//ArEndDirective
//RegWidthDirective //RegWidthDirective
//DataBankDirective //DataBankDirective
{ "DefineData1", "dfb" }, { "DefineData1", "dfb" },
@ -484,10 +485,12 @@ namespace SourceGen.AsmGen {
} }
// IGenerator // IGenerator
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
if (isStart) { if (isStart) {
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), OutputLine(string.Empty,
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4),
string.Empty);
} }
} }

View File

@ -137,7 +137,8 @@ namespace SourceGen.AsmGen {
new PseudoOp.PseudoOpNames(new Dictionary<string, string> { new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" }, { "EquDirective", "=" },
{ "VarDirective", ".var" }, { "VarDirective", ".var" },
{ "OrgDirective", ".logical" }, { "ArStartDirective", ".logical" },
{ "ArEndDirective", ".here" },
//RegWidthDirective // .as, .al, .xs, .xl //RegWidthDirective // .as, .al, .xs, .xl
//DataBankDirective //DataBankDirective
{ "DefineData1", ".byte" }, { "DefineData1", ".byte" },
@ -158,7 +159,6 @@ namespace SourceGen.AsmGen {
//StrLen16 //StrLen16
{ "StrDci", ".shift" } { "StrDci", ".shift" }
}); });
private const string HERE_PSEUDO_OP = ".here";
// IGenerator // IGenerator
@ -653,7 +653,7 @@ namespace SourceGen.AsmGen {
} }
// IGenerator // IGenerator
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) { public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
// 64tass separates the "compile offset", which determines where the output fits // 64tass separates the "compile offset", which determines where the output fits
// into the generated binary, and "program counter", which determines the code // into the generated binary, and "program counter", which determines the code
// the assembler generates. Since we need to explicitly specify every byte in // the assembler generates. Since we need to explicitly specify every byte in
@ -692,14 +692,17 @@ namespace SourceGen.AsmGen {
//OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); //OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty);
} }
} }
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), OutputLine(string.Empty,
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty); SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4),
string.Empty);
mPcDepth++; mPcDepth++;
} else { } else {
mPcDepth--; mPcDepth--;
if (mPcDepth > 0 || !mFirstIsOpen) { if (mPcDepth > 0 || !mFirstIsOpen) {
// close previous block // close previous block
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(HERE_PSEUDO_OP), OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective),
string.Empty, string.Empty); string.Empty, string.Empty);
} else { } else {
// mark initial "*=" region as closed, but don't output anything // mark initial "*=" region as closed, but don't output anything

View File

@ -76,12 +76,16 @@ namespace SourceGen.AsmGen {
} }
} }
// Check for address changes. There may be more than one at a given offset. // Check for address range starts. There may be more than one at a given offset.
AddressMap.AddressChange change = addrIter.Current; AddressMap.AddressChange change = addrIter.Current;
while (change != null && change.Offset == offset) { while (change != null && change.Offset == offset) {
gen.OutputOrgDirective(change.Region, change.IsStart); if (change.IsStart) {
addrIter.MoveNext(); gen.OutputArDirective(change.Region, change.IsStart);
change = addrIter.Current; addrIter.MoveNext();
change = addrIter.Current;
} else {
break;
}
} }
List<DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset); List<DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset);
@ -130,6 +134,22 @@ namespace SourceGen.AsmGen {
offset += attr.Length; offset += attr.Length;
} }
// Check for address region ends. There may be more than one at a given offset.
// The end-region offset is listed as the byte *following* the instruction
// or data item, so it should match the updated offset.
//
// If we encounter a region start, we'll handle that at the top of the next
// loop iteration.
while (change != null && change.Offset == offset) {
if (!change.IsStart) {
gen.OutputArDirective(change.Region, change.IsStart);
addrIter.MoveNext();
change = addrIter.Current;
} else {
break;
}
}
// Update progress meter. We don't want to spam it, so just ping it 10x. // Update progress meter. We don't want to spam it, so just ping it 10x.
int curProgress = (offset * 10) / proj.FileData.Length; int curProgress = (offset * 10) / proj.FileData.Length;
if (lastProgress != curProgress) { if (lastProgress != curProgress) {
@ -143,19 +163,7 @@ namespace SourceGen.AsmGen {
} }
} }
// Close off any open ORG blocks. Debug.Assert(offset == proj.FileDataLength);
while (addrIter.Current != null) {
AddressMap.AddressChange change = addrIter.Current;
if (change.Offset != offset) {
Debug.WriteLine("Closing offset mismatch: change=+" +
change.Offset.ToString("x6") + ", cur offset=+" + offset.ToString("x6"));
Debug.Assert(false);
}
Debug.Assert(change.Offset == offset);
gen.OutputOrgDirective(change.Region, change.IsStart);
addrIter.MoveNext();
change = addrIter.Current;
}
} }
private static void GenerateHeader(IGenerator gen, StreamWriter sw) { private static void GenerateHeader(IGenerator gen, StreamWriter sw) {
@ -565,11 +573,11 @@ namespace SourceGen.AsmGen {
return false; return false;
} }
AddressMap.AddressChange change = iter.Current; AddressMap.AddressChange change = iter.Current;
if (change.Region.Length != 2) { if (change.Region.ActualLength != 2) {
Debug.WriteLine("PRG test: first entry is not a two-byte region"); Debug.WriteLine("PRG test: first entry is not a two-byte region");
} }
// Confirm there's an address map entry at offset 2. // Confirm there's an address map entry at offset 2.
if (project.AddrMap.GetRegions(0x000002).Count == 0) { if (project.AddrMap.GetEntries(0x000002).Count == 0) {
//Debug.WriteLine("PRG test: no ORG at +2"); //Debug.WriteLine("PRG test: no ORG at +2");
return false; return false;
} }

View File

@ -166,11 +166,11 @@ namespace SourceGen.AsmGen {
LocalVariableTable allDefs); LocalVariableTable allDefs);
/// <summary> /// <summary>
/// Outputs a code origin directive. /// Outputs an address region directive.
/// </summary> /// </summary>
/// <param name="addrEntry">Address map entry object.</param> /// <param name="region">Address region object.</param>
/// <param name="isStart">True if we're outputing a region-start directive.</param> /// <param name="isStart">True if this is the start of a region.</param>
void OutputOrgDirective(CommonUtil.AddressMap.AddressRegion addrEntry, bool isStart); void OutputArDirective(CommonUtil.AddressMap.AddressRegion region, bool isStart);
/// <summary> /// <summary>
/// Notify the assembler of a change in register width. /// Notify the assembler of a change in register width.

View File

@ -271,7 +271,7 @@ namespace SourceGen {
AddrMap = new AddressMap(fileDataLen); AddrMap = new AddressMap(fileDataLen);
// set default load address to $1000; override later // set default load address to $1000; override later
AddrMap.AddRegion(0x000000, fileDataLen, 0x1000); AddrMap.AddEntry(0x000000, fileDataLen, 0x1000);
// Default value is "no tag". // Default value is "no tag".
AnalyzerTags = new CodeAnalysis.AnalyzerTag[fileDataLen]; AnalyzerTags = new CodeAnalysis.AnalyzerTag[fileDataLen];
@ -400,9 +400,9 @@ namespace SourceGen {
int loadAddr = RawData.GetWord(mFileData, 0, 2, false); int loadAddr = RawData.GetWord(mFileData, 0, 2, false);
// TODO(org): use NON_ADDR for first two bytes // TODO(org): use NON_ADDR for first two bytes
AddressMap.AddResult addRes = AddressMap.AddResult addRes =
AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2); AddrMap.AddEntry(0, 2, loadAddr < 2 ? 0 : loadAddr - 2);
Debug.Assert(addRes == AddressMap.AddResult.Okay); Debug.Assert(addRes == AddressMap.AddResult.Okay);
addRes = AddrMap.AddRegion(2, mFileData.Length - 2, loadAddr); addRes = AddrMap.AddEntry(2, mFileData.Length - 2, loadAddr);
Debug.Assert(addRes == AddressMap.AddResult.Okay); Debug.Assert(addRes == AddressMap.AddResult.Okay);
OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE, OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE,
@ -413,7 +413,7 @@ namespace SourceGen {
} else { } else {
int loadAddr = SystemDefaults.GetLoadAddress(sysDef); int loadAddr = SystemDefaults.GetLoadAddress(sysDef);
AddressMap.AddResult addRes = AddressMap.AddResult addRes =
AddrMap.AddRegion(0, mFileData.Length, loadAddr); AddrMap.AddEntry(0, mFileData.Length, loadAddr);
Debug.Assert(addRes == AddressMap.AddResult.Okay); Debug.Assert(addRes == AddressMap.AddResult.Okay);
} }
@ -756,16 +756,19 @@ namespace SourceGen {
} }
/// <summary> /// <summary>
/// Checks to see if any part of the address map runs across a bank boundary. /// Checks to see if any section of the address map runs across a bank boundary.
/// </summary> /// </summary>
private void ValidateAddressMap() { private void ValidateAddressMap() {
// Use the change list, because the map entry list can have "floating" length values. // Use the change list, because the map entry list can have "floating" length values
// (which don't automatically stop at bank boundaries).
IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator; IEnumerator<AddressMap.AddressChange> addrIter = AddrMap.AddressChangeIterator;
while (addrIter.MoveNext()) { while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current; AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart) {
continue;
}
AddressMap.AddressRegion region = change.Region; AddressMap.AddressRegion region = change.Region;
if (change.IsStart && if ((region.Address & 0xff0000) != ((region.Address + region.ActualLength - 1) & 0xff0000)) {
(region.Address & 0xff0000) != ((region.Address + region.Length - 1) & 0xff0000)) {
string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT; string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT;
int firstNext = (region.Address & 0xff0000) + 0x010000; int firstNext = (region.Address & 0xff0000) + 0x010000;
int badOffset = region.Offset + (firstNext - region.Address); int badOffset = region.Offset + (firstNext - region.Address);
@ -2149,18 +2152,18 @@ namespace SourceGen {
AddressMap addrMap = AddrMap; AddressMap addrMap = AddrMap;
if ((int)oldValue == AddressMap.NON_ADDR) { if ((int)oldValue == AddressMap.NON_ADDR) {
// adding new entry // adding new entry
if (addrMap.AddRegion(offset, AddressMap.FLOATING_LEN, if (addrMap.AddEntry(offset, AddressMap.FLOATING_LEN,
(int)newValue) != AddressMap.AddResult.Okay) { (int)newValue) != AddressMap.AddResult.Okay) {
Debug.Assert(false, "failed adding region"); Debug.Assert(false, "failed adding region");
} }
} else if ((int)newValue == AddressMap.NON_ADDR) { } else if ((int)newValue == AddressMap.NON_ADDR) {
// removing existing entry // removing existing entry
if (!addrMap.RemoveRegion(offset, AddressMap.FLOATING_LEN)) { if (!addrMap.RemoveEntry(offset, AddressMap.FLOATING_LEN)) {
Debug.Assert(false, "failed removing region"); Debug.Assert(false, "failed removing region");
} }
} else { } else {
// updating existing entry // updating existing entry
if (!addrMap.EditRegion(offset, AddressMap.FLOATING_LEN, if (!addrMap.EditEntry(offset, AddressMap.FLOATING_LEN,
(int) newValue, string.Empty, false)) { (int) newValue, string.Empty, false)) {
Debug.Assert(false, "failed editing region"); Debug.Assert(false, "failed editing region");
} }

View File

@ -353,10 +353,12 @@ namespace SourceGen {
case LineListGen.Line.Type.EquDirective: case LineListGen.Line.Type.EquDirective:
case LineListGen.Line.Type.RegWidthDirective: case LineListGen.Line.Type.RegWidthDirective:
case LineListGen.Line.Type.DataBankDirective: case LineListGen.Line.Type.DataBankDirective:
case LineListGen.Line.Type.OrgDirective: case LineListGen.Line.Type.ArStartDirective:
case LineListGen.Line.Type.ArEndDirective:
case LineListGen.Line.Type.LocalVariableTable: case LineListGen.Line.Type.LocalVariableTable:
if (parts.IsLongComment) { if (parts.IsLongComment) {
// This happens for long comments embedded in LV tables. // This happens for long comments generated for LV tables (e.g. "empty
// variable table").
TextUtil.AppendPaddedString(sb, parts.Comment, mColStart[(int)Col.Label]); TextUtil.AppendPaddedString(sb, parts.Comment, mColStart[(int)Col.Label]);
break; break;
} }
@ -714,7 +716,8 @@ namespace SourceGen {
case LineListGen.Line.Type.EquDirective: case LineListGen.Line.Type.EquDirective:
case LineListGen.Line.Type.RegWidthDirective: case LineListGen.Line.Type.RegWidthDirective:
case LineListGen.Line.Type.DataBankDirective: case LineListGen.Line.Type.DataBankDirective:
case LineListGen.Line.Type.OrgDirective: case LineListGen.Line.Type.ArStartDirective:
case LineListGen.Line.Type.ArEndDirective:
case LineListGen.Line.Type.LocalVariableTable: case LineListGen.Line.Type.LocalVariableTable:
if (parts.IsLongComment) { if (parts.IsLongComment) {
// This happens for long comments embedded in LV tables, e.g. // This happens for long comments embedded in LV tables, e.g.

View File

@ -92,7 +92,7 @@ namespace SourceGen {
/// One of these per line of output in the display. It should be possible to draw /// One of these per line of output in the display. It should be possible to draw
/// all of the output without needing to refer back to the project data. (Currently /// all of the output without needing to refer back to the project data. (Currently
/// making an exception for some selection-dependent field highlighting.) /// making an exception for some selection-dependent field highlighting.)
/// ///
/// Base fields are immutable, but the Parts property is set after creation. /// Base fields are immutable, but the Parts property is set after creation.
/// </summary> /// </summary>
public class Line { public class Line {
@ -116,14 +116,15 @@ namespace SourceGen {
Blank = 1 << 4, Blank = 1 << 4,
// Assembler directives. // Assembler directives.
OrgDirective = 1 << 5, ArStartDirective = 1 << 5,
EquDirective = 1 << 6, ArEndDirective = 1 << 6,
RegWidthDirective = 1 << 7, EquDirective = 1 << 7,
DataBankDirective = 1 << 8, RegWidthDirective = 1 << 8,
DataBankDirective = 1 << 9,
// Additional metadata. // Additional metadata.
LocalVariableTable = 1 << 9, LocalVariableTable = 1 << 10,
VisualizationSet = 1 << 10, VisualizationSet = 1 << 11,
} }
/// <summary> /// <summary>
@ -535,7 +536,8 @@ namespace SourceGen {
// Nothing to do. // Nothing to do.
parts = FormattedParts.CreateBlankLine(); parts = FormattedParts.CreateBlankLine();
break; break;
case Line.Type.OrgDirective: case Line.Type.ArStartDirective:
case Line.Type.ArEndDirective:
case Line.Type.RegWidthDirective: case Line.Type.RegWidthDirective:
case Line.Type.DataBankDirective: case Line.Type.DataBankDirective:
case Line.Type.LongComment: case Line.Type.LongComment:
@ -989,29 +991,53 @@ namespace SourceGen {
int offset = startOffset; int offset = startOffset;
while (offset <= endOffset) { while (offset <= endOffset) {
bool blankAdded = false; bool spaceAdded = false;
Anattrib attr = mProject.GetAnattrib(offset); Anattrib attr = mProject.GetAnattrib(offset);
if (attr.IsInstructionStart && offset > 0 && if (attr.IsInstructionStart && offset > 0 &&
mProject.GetAnattrib(offset - 1).IsData) { mProject.GetAnattrib(offset - 1).IsData) {
// Transition from data to code. (Don't add blank line for inline data.) // Transition from data to code. (Don't add blank line for inline data.)
lines.Add(GenerateBlankLine(offset)); lines.Add(GenerateBlankLine(offset));
blankAdded = true; spaceAdded = true;
} else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank) { } else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank) {
// Blank line before visualization set helps keep image visually grouped // Blank line before visualization set helps keep image visually grouped
// with its data. // with its data.
lines.Add(GenerateBlankLine(offset)); lines.Add(GenerateBlankLine(offset));
blankAdded = true; spaceAdded = true;
} else if (addBlank) { } else if (addBlank) {
// Previous instruction wanted to be followed by a blank line. // Previous instruction wanted to be followed by a blank line.
lines.Add(GenerateBlankLine(offset)); lines.Add(GenerateBlankLine(offset));
blankAdded = true; spaceAdded = true;
} }
addBlank = false; addBlank = false;
// Start with address region changes. // 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.
//
// TODO: integrate into FormattedOperandCache so we don't have to
// regenerate them unless they change. Use the MLC as the dependency.
if (mProject.Notes.TryGetValue(offset, out MultiLineComment noteData)) {
List<string> formatted = noteData.FormatText(mFormatter, "NOTE: ");
StringListToLines(formatted, offset, Line.Type.Note,
noteData.BackgroundColor, NoteColorMultiplier, lines);
spaceAdded = true;
}
if (mProject.LongComments.TryGetValue(offset, out MultiLineComment longComment)) {
List<string> formatted = longComment.FormatText(mFormatter, string.Empty);
StringListToLines(formatted, offset, Line.Type.LongComment,
longComment.BackgroundColor, NoteColorMultiplier, lines);
spaceAdded = true;
}
if (mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet visSet)) {
lines.Add(new Line(offset, 0, Line.Type.VisualizationSet));
spaceAdded = true;
}
// Handle address region starts.
while (addrIter.Current != null && addrIter.Current.Offset <= offset) { while (addrIter.Current != null && addrIter.Current.Offset <= offset) {
AddressMap.AddressChange change = addrIter.Current; AddressMap.AddressChange change = addrIter.Current;
Debug.Assert(change.Offset == offset); // shouldn't be embedded in something // Range starts/ends shouldn't be embedded in something.
Debug.Assert(change.Offset == offset);
AddressMap.AddressRegion region = change.Region; AddressMap.AddressRegion region = change.Region;
if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) { if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) {
@ -1024,40 +1050,22 @@ namespace SourceGen {
if (change.IsStart) { if (change.IsStart) {
// Blank line above ORG directive, except at top of file or when we've // Blank line above ORG directive, except at top of file or when we've
// already added one for another reason. // already added one for another reason.
if (region.Offset != 0 && !blankAdded) { if (region.Offset != 0 && !spaceAdded) {
lines.Add(GenerateBlankLine(offset)); lines.Add(GenerateBlankLine(offset));
} }
blankAdded = false; // next one will need a blank line spaceAdded = false; // next one will need a blank line
// TODO(org): pre-label (address / label only, logically part of ORG) // TODO(org): pre-label (address / label only, logically part of ORG)
Line newLine = new Line(offset, 0, Line.Type.OrgDirective); Line newLine = new Line(offset, 0, Line.Type.ArStartDirective);
string addrStr = mFormatter.FormatHexValue(region.Address, 4); string addrStr = mFormatter.FormatHexValue(region.Address, 4);
newLine.Parts = FormattedParts.CreateDirective( newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr); mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective), addrStr);
lines.Add(newLine); lines.Add(newLine);
addrIter.MoveNext();
} else { } else {
// TODO(org) // Next entry is an end marker.
break;
} }
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
// regenerate them unless they change. Use the MLC as the dependency.
if (mProject.Notes.TryGetValue(offset, out MultiLineComment noteData)) {
List<string> formatted = noteData.FormatText(mFormatter, "NOTE: ");
StringListToLines(formatted, offset, Line.Type.Note,
noteData.BackgroundColor, NoteColorMultiplier, lines);
}
if (mProject.LongComments.TryGetValue(offset, out MultiLineComment longComment)) {
List<string> formatted = longComment.FormatText(mFormatter, string.Empty);
StringListToLines(formatted, offset, Line.Type.LongComment,
longComment.BackgroundColor, NoteColorMultiplier, lines);
}
if (mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet visSet)) {
lines.Add(new Line(offset, 0, Line.Type.VisualizationSet));
} }
// Local variable tables come next. Defer rendering. // Local variable tables come next. Defer rendering.
@ -1210,6 +1218,26 @@ namespace SourceGen {
} }
offset += attr.Length; offset += attr.Length;
} }
// Check for address region ends, which will be positioned at the updated offset
// (unless they somehow got embedded inside something else).
while (addrIter.Current != null && addrIter.Current.Offset <= offset) {
AddressMap.AddressChange change = addrIter.Current;
// Range starts/ends shouldn't be embedded in something.
Debug.Assert(change.Offset == offset);
if (!change.IsStart) {
// NOTE: last end(s) are at an offset outside file bounds.
Line newLine = new Line(offset, 0, Line.Type.ArEndDirective);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.ArEndDirective),
string.Empty);
lines.Add(newLine);
addrIter.MoveNext();
} else {
break;
}
}
} }
} }

View File

@ -1530,10 +1530,11 @@ namespace SourceGen {
EditProjectSymbol((CodeListColumn)col); EditProjectSymbol((CodeListColumn)col);
} }
break; break;
case LineListGen.Line.Type.OrgDirective: case LineListGen.Line.Type.ArStartDirective:
if (CanEditAddress()) { if (CanEditAddress()) {
EditAddress(); EditAddress();
} }
// TODO(org): handle ArEndDirective
break; break;
case LineListGen.Line.Type.RegWidthDirective: case LineListGen.Line.Type.RegWidthDirective:
if (CanEditStatusFlags()) { if (CanEditStatusFlags()) {
@ -1771,7 +1772,8 @@ namespace SourceGen {
LineListGen.Line selLine = CodeLineList[selIndex]; LineListGen.Line selLine = CodeLineList[selIndex];
if (selLine.LineType != LineListGen.Line.Type.Code && if (selLine.LineType != LineListGen.Line.Type.Code &&
selLine.LineType != LineListGen.Line.Type.Data && selLine.LineType != LineListGen.Line.Type.Data &&
selLine.LineType != LineListGen.Line.Type.OrgDirective) { selLine.LineType != LineListGen.Line.Type.ArStartDirective) {
// TODO(org): handle ArEnd
return false; return false;
} }
@ -1803,55 +1805,31 @@ namespace SourceGen {
int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex(); int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex();
int firstOffset = CodeLineList[selIndex].FileOffset; int firstOffset = CodeLineList[selIndex].FileOffset;
int lastOffset = CodeLineList[lastIndex].FileOffset; int lastOffset = CodeLineList[lastIndex].FileOffset;
int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan; //int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
int nextAddr;
AddressMap addrMap = mProject.AddrMap; AddressMap addrMap = mProject.AddrMap;
// TODO(org): rewrite this - need to identify the specific .ORG statement since // TODO(org): rewrite this - need to identify the specific .ORG statement since
// there can now be several at a single offset // there can now be several at a single offset
if (firstOffset == lastOffset || nextOffset == mProject.FileDataLength) { int addr = addrMap.OffsetToAddress(firstOffset);
// Single item (which might not be a single *line*) is selected, or the int newLen = lastOffset - firstOffset;
// last selected item is the end of the file. if (newLen == 0) {
nextOffset = -1; newLen = 123;
nextAddr = AddressMap.NON_ADDR;
} else {
// Compute "nextAddr". If there's an existing entry at nextOffset, we use
// that. If not, we use the "load address", which is determined by the very
// first address.
//
// I tried this by just removing the selected entry and seeing what the address
// would be without it, useful for relocations inside relocations. This worked
// poorly when relocations were chained, i.e. two consecutive blocks were
// relocated to different places. The end address of the second block gets
// set based on the first address of the first block, which doesn't seem useful.
#if false
nextAddr = mProject.AddrMap.Get(nextOffset);
if (nextAddr == AddressMap.NO_ENTRY_ADDR) {
AddressMap cloneMap = new AddressMap(mProject.AddrMap.GetEntryList());
if (firstOffset != 0) {
cloneMap.Remove(firstOffset);
}
nextAddr = cloneMap.OffsetToAddress(nextOffset);
}
#else
int fileStartAddr = addrMap.OffsetToAddress(0);
nextAddr = ((fileStartAddr + nextOffset) & 0xffff) | (fileStartAddr & 0xff0000);
#endif
} }
AddressMap.AddressMapEntry newEntry = new AddressMap.AddressMapEntry(firstOffset,
EditAddress dlg = new EditAddress(mMainWin, firstOffset, nextOffset, nextAddr, newLen /*DEBUG - replace*/, addr, string.Empty, false);
EditAddress dlg = new EditAddress(mMainWin, newEntry, true, newLen,
mProject, mFormatter); mProject, mFormatter);
if (dlg.ShowDialog() != true) { if (dlg.ShowDialog() != true) {
return; return;
} }
if (firstOffset == 0 && dlg.NewAddress < 0) { //if (firstOffset == 0 && dlg.NewAddress < 0) {
// Not allowed. The AddressMap will just put it back, which confuses // // Not allowed. The AddressMap will just put it back, which confuses
// the undo operation. // // the undo operation.
Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000"); // Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000");
return; // return;
} //}
ChangeSet cs = new ChangeSet(1); ChangeSet cs = new ChangeSet(1);
@ -3899,8 +3877,11 @@ namespace SourceGen {
case LineListGen.Line.Type.Blank: case LineListGen.Line.Type.Blank:
lineTypeStr = "blank line"; lineTypeStr = "blank line";
break; break;
case LineListGen.Line.Type.OrgDirective: case LineListGen.Line.Type.ArStartDirective:
lineTypeStr = "address directive"; lineTypeStr = "address range start directive";
break;
case LineListGen.Line.Type.ArEndDirective:
lineTypeStr = "address range end directive";
break; break;
case LineListGen.Line.Type.RegWidthDirective: case LineListGen.Line.Type.RegWidthDirective:
lineTypeStr = "register width directive"; lineTypeStr = "register width directive";

View File

@ -673,7 +673,7 @@ namespace SourceGen {
// TODO(org): serialize length, isRelative, and preLabel // TODO(org): serialize length, isRelative, and preLabel
int length = CommonUtil.AddressMap.FLOATING_LEN; int length = CommonUtil.AddressMap.FLOATING_LEN;
AddressMap.AddResult addResult = proj.AddrMap.AddRegion(addr.Offset, AddressMap.AddResult addResult = proj.AddrMap.AddEntry(addr.Offset,
length, addr.Addr); length, addr.Addr);
if (addResult != CommonUtil.AddressMap.AddResult.Okay) { if (addResult != CommonUtil.AddressMap.AddResult.Okay) {
string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" + string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" +

View File

@ -64,7 +64,8 @@ namespace SourceGen {
public class PseudoOpNames { public class PseudoOpNames {
public string EquDirective { get; private set; } public string EquDirective { get; private set; }
public string VarDirective { get; private set; } public string VarDirective { get; private set; }
public string OrgDirective { get; private set; } public string ArStartDirective { get; private set; }
public string ArEndDirective { get; private set; }
public string RegWidthDirective { get; private set; } public string RegWidthDirective { get; private set; }
public string DataBankDirective { get; private set; } public string DataBankDirective { get; private set; }
@ -115,7 +116,8 @@ namespace SourceGen {
} }
return a.EquDirective == b.EquDirective && return a.EquDirective == b.EquDirective &&
a.VarDirective == b.VarDirective && a.VarDirective == b.VarDirective &&
a.OrgDirective == b.OrgDirective && a.ArStartDirective == b.ArStartDirective &&
a.ArEndDirective == b.ArEndDirective &&
a.RegWidthDirective == b.RegWidthDirective && a.RegWidthDirective == b.RegWidthDirective &&
a.DataBankDirective == b.DataBankDirective && a.DataBankDirective == b.DataBankDirective &&
a.DefineData1 == b.DefineData1 && a.DefineData1 == b.DefineData1 &&
@ -145,7 +147,7 @@ namespace SourceGen {
public override int GetHashCode() { public override int GetHashCode() {
// should be enough // should be enough
return (EquDirective == null ? 0 : EquDirective.GetHashCode()) ^ return (EquDirective == null ? 0 : EquDirective.GetHashCode()) ^
(OrgDirective == null ? 0 : OrgDirective.GetHashCode()) ^ (ArStartDirective == null ? 0 : ArStartDirective.GetHashCode()) ^
(DefineData1 == null ? 0 : DefineData1.GetHashCode()) ^ (DefineData1 == null ? 0 : DefineData1.GetHashCode()) ^
(Fill == null ? 0 : Fill.GetHashCode()); (Fill == null ? 0 : Fill.GetHashCode());
} }
@ -226,7 +228,8 @@ namespace SourceGen {
new PseudoOpNames(new Dictionary<string, string> { new PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", ".eq" }, { "EquDirective", ".eq" },
{ "VarDirective", ".var" }, { "VarDirective", ".var" },
{ "OrgDirective", ".org" }, { "ArStartDirective", ".arstart" },
{ "ArEndDirective", ".arend" },
{ "RegWidthDirective", ".rwid" }, { "RegWidthDirective", ".rwid" },
{ "DataBankDirective", ".dbank" }, { "DataBankDirective", ".dbank" },

View File

@ -63,7 +63,7 @@ LABEL !hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
!pseudopc $1408 { !pseudopc $1408 {
!fill 8,$82 ;note no-op .ORG !fill 8,$82 ;note no-op .ORG
!fill 8,$83 !fill 8,$83
} ;!pseudopc }
!pseudopc $1428 { !pseudopc $1428 {
!fill 8,$83 ;meaningful .ORG !fill 8,$83 ;meaningful .ORG
!fill 8,$84 !fill 8,$84
@ -84,4 +84,4 @@ L14A8 !fill 8,$8b
!fill 8,$8c !fill 8,$8c
!byte %10001100 !byte %10001100
!fill 7,$8c !fill 7,$8c
} ;!pseudopc }

View File

@ -106,4 +106,4 @@ calls jsr L1015
L1160 adc #BMI1 L1160 adc #BMI1
rts rts
} ;!pseudopc }

View File

@ -23,4 +23,4 @@
!hex 656172206f6e20746865206669727374206c696e652e20205468652071756963 !hex 656172206f6e20746865206669727374206c696e652e20205468652071756963
!hex 6b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920 !hex 6b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920
!hex 646f67732ea97aa959a934c230a97b56a95a34a9341260 !hex 646f67732ea97aa959a934c230a97b56a95a34a9341260
} ;!pseudopc }

View File

@ -57,8 +57,8 @@ L2080 bit L2080
lda $00 lda $00
beq _L2100 beq _L2100
.byte $ad .byte $ad
.here .here
.logical $2100 .logical $2100
_L2100 nop _L2100 nop
nop nop
@ -72,8 +72,8 @@ _L2100 nop
.here .here
.logical $2820 .logical $2820
.fill 18,$00 .fill 18,$00
.here .here
.logical $3000 .logical $3000
_L3000 bit _L3000 _L3000 bit _L3000
lda #$44 lda #$44
@ -110,8 +110,8 @@ _L3182 bit _L3182
label1 .byte $ea label1 .byte $ea
.byte $ea .byte $ea
.here .here
.logical $3200 .logical $3200
L3200 bit L3200 L3200 bit L3200
.byte $00 .byte $00

View File

@ -7,7 +7,7 @@
jsr L1107 jsr L1107
jmp L2000 jmp L2000
} ;!pseudopc }
!pseudopc $1100 { !pseudopc $1100 {
L1100 bit L1100 L1100 bit L1100
L1103 lda #$11 L1103 lda #$11
@ -16,7 +16,7 @@ L1107 ldy #$11
clv clv
bvc L1103 bvc L1103
} ;!pseudopc }
!pseudopc $1100 { !pseudopc $1100 {
@L1100_0 bit @L1100_0 @L1100_0 bit @L1100_0
lda #$22 lda #$22
@ -24,7 +24,7 @@ L1107 ldy #$11
ldy #$22 ldy #$22
jmp @L1105 jmp @L1105
} ;!pseudopc }
!pseudopc $1100 { !pseudopc $1100 {
@L1100_1 bit @L1100_1 @L1100_1 bit @L1100_1
lda #$33 lda #$33
@ -33,20 +33,20 @@ L1107 ldy #$11
sec sec
bcs @L1107_0 bcs @L1107_0
} ;!pseudopc }
!pseudopc $2000 { !pseudopc $2000 {
L2000 bit L2000 L2000 bit L2000
beq $2018 beq $2018
bne @L2020 bne @L2020
} ;!pseudopc }
!pseudopc $2020 { !pseudopc $2020 {
@L2020 bit @L2020 @L2020 bit @L2020
beq $2028 beq $2028
bne L2080 bne L2080
offend nop offend nop
} ;!pseudopc }
!pseudopc $2080 { !pseudopc $2080 {
L2080 bit L2080 L2080 bit L2080
lda offend lda offend
@ -60,23 +60,23 @@ L2080 bit L2080
lda $00 lda $00
beq @L2100 beq @L2100
!byte $ad !byte $ad
}
} ;!pseudopc
!pseudopc $2100 { !pseudopc $2100 {
@L2100 nop @L2100 nop
nop nop
jmp @L3000 jmp @L3000
} ;!pseudopc }
!pseudopc $2800 { !pseudopc $2800 {
!byte $00 !byte $00
!byte $28 !byte $28
!fill 14,$00 !fill 14,$00
} ;!pseudopc }
!pseudopc $2820 { !pseudopc $2820 {
!fill 18,$00 !fill 18,$00
}
} ;!pseudopc
!pseudopc $3000 { !pseudopc $3000 {
@L3000 bit @L3000 @L3000 bit @L3000
lda #$44 lda #$44
@ -86,7 +86,7 @@ L2080 bit L2080
ulabel !byte $00 ulabel !byte $00
!byte $01 !byte $01
} ;!pseudopc }
!pseudopc $3100 { !pseudopc $3100 {
!byte $02 !byte $02
@ -99,7 +99,7 @@ fwd bit fwd
beq @L3182 beq @L3182
!byte $ea !byte $ea
!byte $ea !byte $ea
} ;!pseudopc }
!pseudopc $3180 { !pseudopc $3180 {
!byte $00 !byte $00
!byte $01 !byte $01
@ -113,10 +113,10 @@ fwd bit fwd
label1 !byte $ea label1 !byte $ea
!byte $ea !byte $ea
}
} ;!pseudopc
!pseudopc $3200 { !pseudopc $3200 {
L3200 bit L3200 L3200 bit L3200
!byte $00 !byte $00
!byte $01 ;execution continues off end of file !byte $01 ;execution continues off end of file
} ;!pseudopc }

View File

@ -63,8 +63,8 @@ _L22080 bit _L22080 & $ffff
lda $00 lda $00
beq _L22100 beq _L22100
.byte $ad .byte $ad
.here .here
.logical $022100 .logical $022100
_L22100 nop _L22100 nop
nop nop
@ -78,8 +78,8 @@ _L22100 nop
.here .here
.logical $022820 .logical $022820
.fill 18,$00 .fill 18,$00
.here .here
.logical $023000 .logical $023000
_L23000 bit _L23000 & $ffff _L23000 bit _L23000 & $ffff
lda #$44 lda #$44
@ -115,8 +115,8 @@ _L23182 bit _L23182 & $ffff
_label1 .byte $ea _label1 .byte $ea
.byte $ea .byte $ea
.here .here
.logical $023200 .logical $023200
_L23200 bit _L23200 & $ffff _L23200 bit _L23200 & $ffff
.byte $00 .byte $00

View File

@ -8,4 +8,4 @@
!hex 0000000000000000000000000000002c0030a944a244a04482f5000001022c01 !hex 0000000000000000000000000000002c0030a944a244a04482f5000001022c01
!hex 31ad0c30ad0d30ad0e30ad0f30ad0031f06deaea00012c8231ad9031ad9131ad !hex 31ad0c30ad0d30ad0e30ad0f30ad0031f06deaea00012c8231ad9031ad9131ad
!hex 00328070eaea2c00320001 !hex 00328070eaea2c00320001
} ;!pseudopc }

View File

@ -19,12 +19,12 @@ L0012 lda+1 lodat+1
clc clc
bcc LFFC0 bcc LFFC0
} ;!pseudopc }
!pseudopc $0080 { !pseudopc $0080 {
L0080 bit+2 L0080 L0080 bit+2 L0080
jmp LFFC6 jmp LFFC6
} ;!pseudopc }
!pseudopc $ffc0 { !pseudopc $ffc0 {
LFFC0 bit LFFC0 LFFC0 bit LFFC0
LFFC3 clc LFFC3 clc
@ -32,4 +32,4 @@ LFFC3 clc
LFFC6 rts LFFC6 rts
} ;!pseudopc }

View File

@ -26,8 +26,8 @@ L440004 lda L440000
dat44 .word dat44 & $ffff dat44 .word dat44 & $ffff
.long dat44 .long dat44
.here .here
.logical $44ffc0 .logical $44ffc0
L44FFC0 cmp L44FFC0 L44FFC0 cmp L44FFC0
high44 beq _L44FFCB high44 beq _L44FFCB

View File

@ -81,4 +81,4 @@ load11 lda #$11
lda @L2017 lda @L2017
rts rts
} ;!pseudopc }

View File

@ -299,4 +299,4 @@ L0080 bit+1 @L0082
@L0082 bit+1 @L0082 @L0082 bit+1 @L0082
bit+1 @L0082 bit+1 @L0082
L0086 bit+2 L0086 L0086 bit+2 L0086
} ;!pseudopc }

View File

@ -291,4 +291,4 @@ L0080 bit+1 @L0082
bit+1 @L0082 bit+1 @L0082
L0086 bit+2 L0086 L0086 bit+2 L0086
L0089 lda+3 L0089 L0089 lda+3 L0089
} ;!pseudopc }

View File

@ -223,4 +223,4 @@ DPCODE nop
beq @SPLIT2 beq @SPLIT2
rts rts
} ;!pseudopc }

View File

@ -294,4 +294,4 @@ L00E6 bit .ARG+2
bit $0100 bit $0100
rts rts
} ;!pseudopc }

View File

@ -110,7 +110,7 @@ L1800 jsr PrintInlineNullString
!byte $6e !byte $6e
!byte $20 !byte $20
!byte $01 !byte $01
} ;!pseudopc }
!pseudopc $1840 { !pseudopc $1840 {
!text "string" !text "string"
!byte $00 !byte $00
@ -123,4 +123,4 @@ L1848 jsl PrintInlineL2String
L184F jsr PrintInlineNullString L184F jsr PrintInlineNullString
adc $6e adc $6e
!byte $64 !byte $64
} ;!pseudopc }

View File

@ -8,8 +8,8 @@
.logical $200a .logical $200a
.text "world" .text "world"
.byte $80 .byte $80
.here .here
.logical $2100 .logical $2100
L2100 lda #$00 L2100 lda #$00
sta addr1-1 ;edit this operand sta addr1-1 ;edit this operand

View File

@ -6,8 +6,8 @@
!pseudopc $200a { !pseudopc $200a {
!text "world" !text "world"
!byte $80 !byte $80
}
} ;!pseudopc
!pseudopc $2100 { !pseudopc $2100 {
L2100 lda #$00 L2100 lda #$00
sta addr1-1 ;edit this operand sta addr1-1 ;edit this operand
@ -20,4 +20,4 @@ addr1 !text "!?---"
L2121 rts L2121 rts
} ;!pseudopc }

View File

@ -116,8 +116,8 @@ _L4FFE0 .long _L4FFE0
.byte $1e .byte $1e
.byte $1f .byte $1f
.text " !",$22,"#$%&'()*+,-./" .text " !",$22,"#$%&'()*+,-./"
.here .here
.logical $023456 .logical $023456
.as .as
.xs .xs

View File

@ -11,4 +11,4 @@
!hex 00000000ea60af000008ad1900eaaf000001af000002af000003af0000086b19 !hex 00000000ea60af000008ad1900eaaf000001af000002af000003af0000086b19
!hex 00080056340200800010080054686973206973206120746573742e0068656c6c !hex 00080056340200800010080054686973206973206120746573742e0068656c6c
!hex 6f2c20776f726c6421 !hex 6f2c20776f726c6421
} ;!pseudopc }

View File

@ -71,8 +71,8 @@ L2202E nop
rts rts
bank2addr .word L2202E & $ffff bank2addr .word L2202E & $ffff
.here .here
.logical $033000 .logical $033000
bank3 lda bank3 bank3 lda bank3
lda bank2 & $ffff lda bank2 & $ffff
@ -89,8 +89,8 @@ _L33024 lda $2030
rtl rtl
L33028 .word L33028 & $ffff L33028 .word L33028 & $ffff
.here .here
.logical $024000 .logical $024000
L24000 lda L24000 L24000 lda L24000
phb phb

View File

@ -3,25 +3,25 @@
!pseudopc $8000 { !pseudopc $8000 {
!byte $ea !byte $ea
!fill 8191,$00 !fill 8191,$00
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$01 !fill 8192,$01
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$02 !fill 8192,$02
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$03 !fill 8192,$03
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$04 !fill 8192,$04
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$05 !fill 8192,$05
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$06 !fill 8192,$06
} ;!pseudopc }
!pseudopc $8000 { !pseudopc $8000 {
!fill 8192,$07 !fill 8192,$07
} ;!pseudopc }

View File

@ -19,60 +19,99 @@ limitations under the License.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui" xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d" mc:Ignorable="d"
Title="Set Address" Title="Edit Address Range"
SizeToContent="WidthAndHeight" ResizeMode="NoResize" SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
ContentRendered="Window_ContentRendered"> ContentRendered="Window_ContentRendered">
<Window.Resources>
<system:String x:Key="str_HdrCreate">Creating new address range, with properties:</system:String>
<system:String x:Key="str_HdrEdit">Editing existing address range, with properties:</system:String>
<system:String x:Key="str_HdrResize">Resizing existing address range, with properties:</system:String>
</Window.Resources>
<StackPanel Margin="8"> <StackPanel Margin="8">
<StackPanel Orientation="Horizontal"> <TextBlock Name="headerText" Text="{StaticResource str_HdrCreate}"/>
<TextBlock Text="Editing address at offset: "/> <Grid>
<TextBlock Margin="0,2,0,0" Text="{Binding FirstOffsetStr}" <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="8,0,0,0" Text="Start:"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="4,2,0,0"
Text="{Binding RegionStartOffsetStr, FallbackValue=+123456}"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Margin="16,0,0,0" Text="(load address: "/> <TextBlock Grid.Column="0" Grid.Row="1" Margin="8,0,0,0" Text="End:"/>
<TextBlock Margin="0,2,0,0" Text="{Binding LoadAddressText, FallbackValue=$1234}" <TextBlock Grid.Column="1" Grid.Row="1" Margin="4,2,0,0"
Text="{Binding RegionEndOffsetStr, FallbackValue=+123456}"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=")"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0"> <TextBlock Grid.Column="0" Grid.Row="2" Margin="8,0,0,0" Text="Length:"/>
<TextBlock Text="Address (hex):"/> <StackPanel Grid.Column="1" Grid.Row="2" Margin="4,0,0,0" Orientation="Horizontal">
<TextBlock Margin="0,2,0,0"
<TextBox Name="addrTextBox" Width="100" Margin="4,1,0,0" Text="{Binding RegionLengthStr, FallbackValue=23456 / $1234}"
FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding Path=AddressText, UpdateSourceTrigger=PropertyChanged}"
IsInactiveSelectionHighlightEnabled="True">
<TextBox.Resources>
<!-- default non-focus highlight color is nearly invisible -->
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
Color="LightBlue"/>
</TextBox.Resources>
</TextBox>
</StackPanel>
<TextBlock Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00." Margin="0,4,0,0"/>
<TextBlock Text="• Leave the field blank to remove the address override."/>
<StackPanel Margin="0,8,0,0" Visibility="{Binding NextAddressVis}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Bytes spanned: "/>
<TextBlock Text="{Binding BytesSelectedStr, FallbackValue=123 ($123)}" Margin="0,2,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text="(floating)" Margin="4,0,0,0" FontWeight="Bold"
Visibility="{Binding FloatTextVis}"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> </Grid>
<TextBlock Text="Offset after last selected item ("/>
<TextBlock Text="{Binding NextOffsetStr, FallbackValue=+001234}" Margin="0,2,0,0" <!-- failure message goes here -->
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=") will resume at address "/> <StackPanel IsEnabled="{Binding IsRegionValid}">
<TextBlock Text="{Binding NextAddressStr, FallbackValue=$abcd}" Margin="0,2,0,0" <StackPanel Orientation="Horizontal" Margin="0,8,0,0">
FontFamily="{StaticResource GeneralMonoFont}"/> <TextBlock Text="Address (hex):"/>
<TextBlock Text="."/>
<TextBox Name="addrTextBox" Width="100" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding Path=AddressText, UpdateSourceTrigger=PropertyChanged}"
IsInactiveSelectionHighlightEnabled="True">
<TextBox.Resources>
<!-- default non-focus highlight color is nearly invisible -->
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
Color="LightBlue"/>
</TextBox.Resources>
</TextBox>
</StackPanel> </StackPanel>
<TextBlock Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00." Margin="0,4,0,0"/>
<TextBlock Text="• Leave the field blank to remove the address override."/>
<TextBlock Text="• Enter 'NA' if the region is non-addressable."/>
</StackPanel> </StackPanel>
<GroupBox Header="Advanced" Padding="2,4" Margin="0,12,0,0">
<StackPanel>
<CheckBox Content="Use relative addressing"
IsChecked="{Binding UseRelativeAddressing}"/>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0">
<TextBlock Text="Pre-label:"/>
<TextBox Name="preLabelTextBox" Width="100" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding Path=PreLabelText, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="(address: " Margin="8,0,0,0"/>
<TextBlock Text="{Binding Path=PreLabelAddressStr, FallbackValue=$1234}"
Margin="0,2,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=")"/>
</StackPanel>
<TextBlock Text="• Must be valid label syntax; may not be a local label." Margin="0,4,0,0"/>
<TextBlock Text="• Must not be a duplicate of an existing label."/>
<TextBlock Text="• Will be ignored if preceding region is non-addressable."/>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="OK" IsDefault="True" Width="70" <Button Content="OK" IsDefault="True" Width="70"
IsEnabled="{Binding IsValid}" Click="OkButton_Click"/> IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>

View File

@ -14,71 +14,58 @@
* limitations under the License. * limitations under the License.
*/ */
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using Asm65; using Asm65;
using CommonUtil;
namespace SourceGen.WpfGui { namespace SourceGen.WpfGui {
/// <summary> /// <summary>
/// Edit Address dialog. /// Edit Address Region dialog.
/// </summary> /// </summary>
public partial class EditAddress : Window, INotifyPropertyChanged { public partial class EditAddress : Window, INotifyPropertyChanged {
/// <summary> /// <summary>
/// Address typed by user. Only valid after the dialog returns OK. Will be set to /// Updated address map entry. Will be null if we want to delete the entry.
/// AddressMap.NO_ENTRY_ADDR if the user is attempting to delete the address.
/// </summary> /// </summary>
public int NewAddress { get; private set; } public AddressMap.AddressMapEntry NewEntry { get; private set; }
/// <summary> /// <summary>
/// Offset being edited. /// Offset being edited.
/// </summary> /// </summary>
private int mFirstOffset; private int mRegionStartOffset;
public string RegionStartOffsetStr {
get { return mFormatter.FormatOffset24(mRegionStartOffset); }
}
/// <summary> /// <summary>
/// Offset after the end of the selection, or -1 if only one line is selected. /// Offset after the end of the selection, or -1 if only one line is selected.
/// </summary> /// </summary>
private int mNextOffset; private int mRegionEndOffset;
public string RegionEndOffsetStr {
/// <summary> get { return mFormatter.FormatOffset24(mRegionEndOffset); }
/// Address after the end of the selection, or -1 if only one line is selected.
/// </summary>
private int mNextAddress;
/// <summary>
/// Maximum allowed address value.
/// </summary>
private int mMaxAddressValue;
/// <summary>
/// What the address would be if there were no addresses set after the initial one.
/// </summary>
private int mBaseAddr;
/// <summary>
/// Text formatter.
/// </summary>
private Formatter mFormatter;
public string FirstOffsetStr {
get { return mFormatter.FormatOffset24(mFirstOffset); }
} }
public string NextOffsetStr {
get { return mFormatter.FormatOffset24(mNextOffset); } public string RegionLengthStr {
}
public string NextAddressStr {
get { return '$' + mFormatter.FormatAddress(mNextAddress, mNextAddress > 0xffff); }
}
public string BytesSelectedStr {
get { get {
int count = mNextOffset - mFirstOffset; int count = mRegionEndOffset - mRegionStartOffset;
return count.ToString() + " (" + mFormatter.FormatHexValue(count, 2) + ")"; return count.ToString() + " (" + mFormatter.FormatHexValue(count, 2) + ")";
} }
} }
/// <summary>
/// Address at which a pre-label would be placed. This is determined by the parent
/// region, so its value is fixed.
/// </summary>
private int mPreLabelAddress;
public string PreLabelAddressStr {
get { return mFormatter.FormatOffset24(mRegionEndOffset); }
}
/// <summary> /// <summary>
/// Address input TextBox. /// Address input TextBox.
/// </summary> /// </summary>
@ -88,6 +75,21 @@ namespace SourceGen.WpfGui {
} }
private string mAddressText; private string mAddressText;
public bool UseRelativeAddressing {
get { return mUseRelativeAddressing; }
set { mUseRelativeAddressing = value; OnPropertyChanged(); UpdateControls(); }
}
private bool mUseRelativeAddressing;
/// <summary>
/// Pre-label input TextBox.
/// </summary>
public string PreLabelText {
get { return mPreLabelText; }
set { mPreLabelText = value; OnPropertyChanged(); UpdateControls(); }
}
private string mPreLabelText;
/// <summary> /// <summary>
/// Set to true when input is valid. Controls whether the OK button is enabled. /// Set to true when input is valid. Controls whether the OK button is enabled.
/// </summary> /// </summary>
@ -97,17 +99,23 @@ namespace SourceGen.WpfGui {
} }
private bool mIsValid; private bool mIsValid;
public Visibility NextAddressVis { /// <summary>
get { return mNextAddressVis; } /// Set to true when requested region is valid. Everything but the cancel button is
set { mNextAddressVis = value; OnPropertyChanged(); } /// disabled if not.
/// </summary>
public bool IsRegionValid {
get { return mIsRegionValid; }
set { mIsRegionValid = value; OnPropertyChanged(); }
} }
public Visibility mNextAddressVis = Visibility.Collapsed; private bool mIsRegionValid;
public string LoadAddressText { /// <summary>
get { return mLoadAddressText; } /// Determines whether the "(floating)" message appears next to the length.
set { mLoadAddressText = value; OnPropertyChanged(); } /// </summary>
public Visibility FloatTextVis {
get { return mFloatTextVis; }
} }
public string mLoadAddressText = string.Empty; private Visibility mFloatTextVis;
// INotifyPropertyChanged implementation // INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
@ -115,46 +123,96 @@ namespace SourceGen.WpfGui {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
private AddressMap.AddressRegion mNewRegion;
/// <summary>
/// Maximum allowed address value, based on CPU type.
/// </summary>
private int mMaxAddressValue;
/// <summary>
/// Reference to project. We need the address map and symbol table.
/// </summary>
private DisasmProject mProject;
/// <summary>
/// Reference to text formatter.
/// </summary>
private Formatter mFormatter;
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
/// <param name="owner">Parent window.</param> /// <param name="owner">Parent window.</param>
/// <param name="firstOffset">Offset at top of selection.</param> /// <param name="entry">Map entry definition. This may be an existing entry, or values
/// <param name="nextOffset">Offset past bottom of selection, or -1 if only one /// representing the selection.</param>
/// line is selected.</param> /// <param name="newLength">Length of region. Only used if we're resizing an
/// existing region.</param>
/// <param name="project">Project reference.</param> /// <param name="project">Project reference.</param>
/// <param name="formatter">Text formatter object.</param> /// <param name="formatter">Text formatter object.</param>
public EditAddress(Window owner, int firstOffset, int nextOffset, int nextAddr, public EditAddress(Window owner, AddressMap.AddressMapEntry entry, bool isNew,
DisasmProject project, Formatter formatter) { int newLength, DisasmProject project, Formatter formatter) {
InitializeComponent(); InitializeComponent();
Owner = owner; Owner = owner;
DataContext = this; DataContext = this;
mFirstOffset = firstOffset; mProject = project;
mNextOffset = nextOffset;
mNextAddress = nextAddr;
mFormatter = formatter;
mMaxAddressValue = project.CpuDef.MaxAddressValue; mMaxAddressValue = project.CpuDef.MaxAddressValue;
mFormatter = formatter;
// Compute load address, i.e. where the byte would have been placed if the entire Configure(entry, isNew, newLength);
// file were loaded at the address of the first address map entry. We assume }
// offsets wrap at the bank boundary.
int fileStartAddr = project.AddrMap.OffsetToAddress(0);
mBaseAddr = ((fileStartAddr + firstOffset) & 0xffff) | (fileStartAddr & 0xff0000);
int firstAddr = project.GetAnattrib(firstOffset).Address; private void Configure(AddressMap.AddressMapEntry entry, bool isNew, int newLength) {
Debug.Assert(project.AddrMap.OffsetToAddress(firstOffset) == firstAddr); mRegionStartOffset = mRegionEndOffset = entry.Offset;
mPreLabelAddress = 0;
IsRegionValid = false;
AddressText = Asm65.Address.AddressToString(firstAddr, false); // The passed-in region could have Length=FLOATING_LEN, so we need to resolve
// that now. We also need to figure out if it's valid. The easiest way to do
LoadAddressText = '$' + mFormatter.FormatAddress(mBaseAddr, mBaseAddr > 0xffff); // that is to clone the address map, add the region to it, and see how the values
// resolve. This also gets us an address for the pre-label.
if (nextOffset >= 0) { List<AddressMap.AddressMapEntry> entries;
NextAddressVis = Visibility.Visible; int spanLength;
entries = mProject.AddrMap.GetEntryList(out spanLength);
AddressMap tmpMap = new AddressMap(spanLength, entries);
if (!isNew) {
// Remove the old entry.
if (!tmpMap.RemoveEntry(entry.Offset, entry.Length)) {
// Shouldn't happen.
Debug.Assert(false);
// TODO(org): some sort of failure indicator
return;
}
} }
NewAddress = -2; // Add the new / replacement entry.
AddressMap.AddResult result = tmpMap.AddEntry(entry);
if (result != AddressMap.AddResult.Okay) {
// TODO(org): various things with failures
Debug.Assert(false); // remove
} else {
// Find it in the region tree.
mNewRegion = tmpMap.FindRegion(entry.Offset, entry.Length);
if (mNewRegion == null) {
// Shouldn't happen.
Debug.Assert(false);
// TODO(org): some sort of failure indicator
return;
} else {
// Set offset / length values based on what we got.
IsRegionValid = true;
mRegionStartOffset = mNewRegion.Offset;
mRegionEndOffset = mNewRegion.Offset + mNewRegion.ActualLength;
mPreLabelAddress = mNewRegion.PreLabelAddress;
mFloatTextVis = mNewRegion.IsFloating ? Visibility.Visible : Visibility.Hidden;
// Init editable stuff.
AddressText = Asm65.Address.AddressToString(mNewRegion.Address, false);
PreLabelText = mNewRegion.PreLabel;
UseRelativeAddressing = mNewRegion.IsRelative;
}
}
} }
private void Window_ContentRendered(object sender, EventArgs e) { private void Window_ContentRendered(object sender, EventArgs e) {
@ -163,12 +221,15 @@ namespace SourceGen.WpfGui {
} }
private void OkButton_Click(object sender, RoutedEventArgs e) { private void OkButton_Click(object sender, RoutedEventArgs e) {
if (AddressText.Length == 0) { bool ok = ParseAddress(out int addr);
NewAddress = CommonUtil.AddressMap.NON_ADDR; Debug.Assert(ok);
if (addr == AddressMap.INVALID_ADDR) {
// field was blank, want to delete the entry
NewEntry = null;
} else { } else {
bool ok = Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out int addr); NewEntry = new AddressMap.AddressMapEntry(mNewRegion.Offset,
Debug.Assert(ok); mNewRegion.Length, addr, PreLabelText,
NewAddress = addr; UseRelativeAddressing);
} }
DialogResult = true; DialogResult = true;
} }
@ -181,32 +242,32 @@ namespace SourceGen.WpfGui {
/// for TextBox is LostFocus. /// for TextBox is LostFocus.
/// </remarks> /// </remarks>
private void UpdateControls() { private void UpdateControls() {
IsValid = (AddressText.Length == 0) || IsValid = IsRegionValid && ParseAddress(out int unused);
Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out int unused); // TODO(org): check pre-label syntax
}
private const string NON_ADDR_STR = "NA";
/// <summary>
/// Parses the address out of the AddressText text box.
/// </summary>
/// <param name="addr">Receives the parsed address. Will be NON_ADDR for "NA", and
/// INVALID_ADDR if blank.</param>
/// <returns>True if the string parsed successfully.</returns>
private bool ParseAddress(out int addr) {
// Left blank?
if (AddressText.Length == 0) {
addr = AddressMap.INVALID_ADDR;
return true;
}
// "NA" for non-addressable?
string upper = AddressText.ToUpper();
if (upper == NON_ADDR_STR) {
addr = AddressMap.NON_ADDR;
return true;
}
// Parse numerically.
return Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out addr);
} }
} }
// This might be better with validation rules, but it's sort of awkward to pass parameters
// (like MaxAddressValue) in.
// https://social.technet.microsoft.com/wiki/contents/articles/31422.wpf-passing-a-data-bound-value-to-a-validation-rule.aspx
//
// Speaking of awkward, updating the OK button's IsEnable value through validation
// requires MultiDataTrigger.
//public class AddressValidationRule : ValidationRule {
// public int MaxAddress { get; set; }
// public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
// string text = value.ToString();
// Debug.WriteLine("VALIDATE " + text);
// if ((text.Length == 0) ||
// Asm65.Address.ParseAddress(text, MaxAddress, out int unused)) {
// return new ValidationResult(true, null);
// } else {
// return new ValidationResult(false, "Invalid address");
// }
// }
//}
} }

View File

@ -585,125 +585,138 @@ limitations under the License.
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="0" Text="Origin:" <TextBlock Grid.Column="4" Grid.Row="0" Text="Reg width:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="orgDirectiveTextBox" Grid.Column="5" Grid.Row="0" <TextBox Name="regWidthDirectiveTextBox" Grid.Column="5" Grid.Row="0"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="0" Text="Reg width:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="regWidthDirectiveTextBox" Grid.Column="7" Grid.Row="0"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- second row --> <!-- second row -->
<StackPanel Grid.Column="0" Grid.Row="1" VerticalAlignment="Center"> <StackPanel Grid.Column="0" Grid.Row="1" VerticalAlignment="Center">
<TextBlock Text="Little-endian" HorizontalAlignment="Right"/> <TextBlock Text="Address range" HorizontalAlignment="Right"/>
<TextBlock Text="data, one byte:" HorizontalAlignment="Right"/> <TextBlock Text="start:" HorizontalAlignment="Right"/>
</StackPanel> </StackPanel>
<TextBox Name="defineData1TextBox" Grid.Column="1" Grid.Row="1" <TextBox Name="arStartDirectiveTextBox" Grid.Column="1" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="1" Text="Two bytes:" <StackPanel Grid.Column="2" Grid.Row="1" VerticalAlignment="Center">
HorizontalAlignment="Right" VerticalAlignment="Center"/> <TextBlock Text="Address range" HorizontalAlignment="Right"/>
<TextBox Name="defineData2TextBox" Grid.Column="3" Grid.Row="1" <TextBlock Text="end:" HorizontalAlignment="Right"/>
VerticalAlignment="Center" Margin="{StaticResource TBS}" </StackPanel>
Text=".placeho" MaxLength="12" <TextBox Name="arEndDirectiveTextBox" Grid.Column="3" Grid.Row="1"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="1" Text="Three bytes:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="defineData3TextBox" Grid.Column="5" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="1" Text="Four bytes:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="defineData4TextBox" Grid.Column="7" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- third row --> <!-- third row -->
<StackPanel Grid.Column="2" Grid.Row="2" VerticalAlignment="Center"> <StackPanel Grid.Column="0" Grid.Row="2" VerticalAlignment="Center">
<TextBlock Text="Big-endian data," HorizontalAlignment="Right"/> <TextBlock Text="Little-endian" HorizontalAlignment="Right"/>
<TextBlock Text="two bytes:" HorizontalAlignment="Right"/> <TextBlock Text="data, one byte:" HorizontalAlignment="Right"/>
</StackPanel> </StackPanel>
<TextBox Name="defineBigData2TextBox" Grid.Column="3" Grid.Row="2" <TextBox Name="defineData1TextBox" Grid.Column="1" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="2" Text="Two bytes:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="defineData2TextBox" Grid.Column="3" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="2" Text="Three bytes:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="defineData3TextBox" Grid.Column="5" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="2" Text="Four bytes:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="defineData4TextBox" Grid.Column="7" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- fourth row --> <!-- fourth row -->
<TextBlock Grid.Column="0" Grid.Row="3" Text="Fill:" <StackPanel Grid.Column="2" Grid.Row="3" VerticalAlignment="Center">
HorizontalAlignment="Right" VerticalAlignment="Center"/> <TextBlock Text="Big-endian data," HorizontalAlignment="Right"/>
<TextBox Name="fillTextBox" Grid.Column="1" Grid.Row="3" <TextBlock Text="two bytes:" HorizontalAlignment="Right"/>
</StackPanel>
<TextBox Name="defineBigData2TextBox" Grid.Column="3" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="3" Text="Bulk data:" <TextBlock Grid.Column="6" Grid.Row="3" Text="Bulk data:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="denseTextBox" Grid.Column="3" Grid.Row="3" <TextBox Name="denseTextBox" Grid.Column="7" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="3" Text="Junk:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="junkTextBox" Grid.Column="5" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="3" Text="Align:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="alignTextBox" Grid.Column="7" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- fifth row --> <!-- fifth row -->
<TextBlock Grid.Column="0" Grid.Row="4" Text="Generic str:" <TextBlock Grid.Column="0" Grid.Row="4" Text="Fill:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="4" <TextBox Name="fillTextBox" Grid.Column="1" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="4" Text="Reverse str:" <!-- TODO: add uninitialized data op -->
<TextBlock Grid.Column="4" Grid.Row="4" Text="Junk:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="4" <TextBox Name="junkTextBox" Grid.Column="5" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="4" Text="Null-term str:" <TextBlock Grid.Column="6" Grid.Row="4" Text="Align:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strNullTermTextBox" Grid.Column="5" Grid.Row="4" <TextBox Name="alignTextBox" Grid.Column="7" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="4" Text="DCI str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strDciTextBox" Grid.Column="7" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- sixth row --> <!-- sixth row -->
<TextBlock Grid.Column="0" Grid.Row="5" Text="1-byte len str:" <TextBlock Grid.Column="0" Grid.Row="5" Text="Generic str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="5" <TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="5" Text="2-byte len str:" <TextBlock Grid.Column="2" Grid.Row="5" Text="Reverse str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen16TextBox" Grid.Column="3" Grid.Row="5" <TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="5" Text="Data bank:" <TextBlock Grid.Column="4" Grid.Row="5" Text="Null-term str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="dataBankTextBox" Grid.Column="7" Grid.Row="5" <TextBox Name="strNullTermTextBox" Grid.Column="5" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="5" Text="DCI str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strDciTextBox" Grid.Column="7" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- seventh row -->
<TextBlock Grid.Column="0" Grid.Row="6" Text="1-byte len str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="6"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="6" Text="2-byte len str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen16TextBox" Grid.Column="3" Grid.Row="6"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="6" Text="Data bank:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="dataBankTextBox" Grid.Column="7" Grid.Row="6"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>

View File

@ -1180,7 +1180,8 @@ namespace SourceGen.WpfGui {
mPseudoNameMap = new TextBoxPropertyMap[] { mPseudoNameMap = new TextBoxPropertyMap[] {
new TextBoxPropertyMap(equDirectiveTextBox, "EquDirective"), new TextBoxPropertyMap(equDirectiveTextBox, "EquDirective"),
new TextBoxPropertyMap(varDirectiveTextBox, "VarDirective"), new TextBoxPropertyMap(varDirectiveTextBox, "VarDirective"),
new TextBoxPropertyMap(orgDirectiveTextBox, "OrgDirective"), new TextBoxPropertyMap(arStartDirectiveTextBox, "ArStartDirective"),
new TextBoxPropertyMap(arEndDirectiveTextBox, "ArEndDirective"),
new TextBoxPropertyMap(regWidthDirectiveTextBox, "RegWidthDirective"), new TextBoxPropertyMap(regWidthDirectiveTextBox, "RegWidthDirective"),
new TextBoxPropertyMap(dataBankTextBox, "DataBankDirective"), new TextBoxPropertyMap(dataBankTextBox, "DataBankDirective"),
new TextBoxPropertyMap(defineData1TextBox, "DefineData1"), new TextBoxPropertyMap(defineData1TextBox, "DefineData1"),

View File

@ -222,10 +222,6 @@ namespace SourceGen.WpfGui {
SetControlsFromDescriptor(mFirstFormatDescriptor); SetControlsFromDescriptor(mFirstFormatDescriptor);
if (mPreferredFormatUnavailable) { if (mPreferredFormatUnavailable) {
// This can happen when e.g. a bunch of stuff is formatted as null-terminated
// strings. We don't recognize a lone zero as a string, but we allow it if
// it's next to a bunch of others. If you come back later and try to format
// just that one byte, you end up here.
// TODO(maybe): make it more obvious what's going on? // TODO(maybe): make it more obvious what's going on?
Debug.WriteLine("NOTE: preferred format unavailable"); Debug.WriteLine("NOTE: preferred format unavailable");
} }