1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-23 16:29:34 +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;
namespace Asm65 {
/// <summary>
/// Memory address primitives.
/// </summary>
public static class Address {
/// <summary>
/// Converts a 16- or 24-bit address to a string.
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
/// <param name="addr">Address</param>
/// <param name="always24">If true, force 24-bit output mode.</param>
/// <returns>Formatted string.</returns>
public static string AddressToString(int addr, bool always24) {
if (!always24 && addr < 65536) {
return addr.ToString("x4");

View File

@ -83,6 +83,11 @@ namespace CommonUtil {
/// </summary>
public const int NON_ADDR = -1025;
/// <summary>
/// Address value to use for an invalid address.
/// </summary>
public const int INVALID_ADDR = -2048;
#region Structural
/// <summary>
@ -141,6 +146,27 @@ namespace CommonUtil {
" addr=$" + Address.ToString("x4") + " preLab='" + PreLabel +
"' 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>
@ -154,52 +180,49 @@ namespace CommonUtil {
/// </summary>
public class AddressRegion : AddressMapEntry {
/// <summary>
/// Is the end point floating?
/// Actual length (after FLOATING_LEN is resolved).
/// </summary>
/// <remarks>
/// (In the structural list this is redundant with the FLOATING_LEN Length value,
/// but in the other structures a new instance that has the actual length is created.)
/// </remarks>
public bool IsFloating { get; private set; }
public int ActualLength { get; private set; }
/// <summary>
/// Address associated with pre-label.
/// </summary>
public int PreLabelAddress { get; private set; }
/// <summary>
/// Is the end point floating?
/// </summary>
public bool IsFloating {
get {
return Length == FLOATING_LEN;
}
}
/// <summary>
/// Full constructor.
/// </summary>
public AddressRegion(int offset, int len, int addr, bool isFloating,
string preLabel, int prevAddr, bool isRelative)
public AddressRegion(int offset, int len, int addr, string preLabel, bool isRelative,
int actualLen, int preLabelAddr)
: base(offset, len, addr, preLabel, isRelative) {
IsFloating = isFloating;
PreLabelAddress = prevAddr;
ActualLength = actualLen;
PreLabelAddress = preLabelAddr;
Debug.Assert(Length != FLOATING_LEN);
Debug.Assert(ActualLength != FLOATING_LEN);
}
/// <summary>
/// Basic constructor.
/// Basic constructor. Not for use when len==FLOATING_LEN.
/// </summary>
public AddressRegion(int offset, int len, int addr)
: this(offset, len, addr, addr == FLOATING_LEN ? true : false,
string.Empty, NON_ADDR, false) {
}
/// <summary>
/// Construct from AddressRegion.
/// </summary>
public AddressRegion(AddressMapEntry region)
: this(region.Offset, region.Length, region.Address,
region.Address == FLOATING_LEN ? true : false,
region.PreLabel, NON_ADDR, region.IsRelative) {
: this(offset, len, addr, string.Empty, false, len, NON_ADDR) {
Debug.Assert(len != FLOATING_LEN);
}
public override string ToString() {
return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" + Length.ToString("x4") +
" addr=$" + Address.ToString("x4") + " isFloat=" + IsFloating +
" isRel=" + IsRelative + "]";
return "[AddrRegion: +" + Offset.ToString("x6") + " len=$" +
Length.ToString("x4") + " addr=$" + Address.ToString("x4") +
" 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
// plugins, but... better safe.)
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);
if (result != AddResult.Okay) {
throw new Exception("Unable to add entry (" + result + "): " + ent);
@ -277,10 +300,10 @@ namespace CommonUtil {
/// <summary>
/// Number of entries in the address map.
/// </summary>
public int RegionCount { get { return mMapEntries.Count; } }
public int EntryCount { get { return mMapEntries.Count; } }
/// <summary>
/// Error codes for AddRegion().
/// Error codes for AddEntry().
/// </summary>
public enum AddResult {
Unknown = 0,
@ -319,18 +342,30 @@ namespace CommonUtil {
}
/// <summary>
/// Adds a new region.
/// Adds a new entry to the map.
/// </summary>
/// <param name="offset">File offset of region start.</param>
/// <param name="length">Length of region, or FLOATING_LEN for a floating end point.</param>
/// <param name="addr">Address of region start.</param>
/// <returns>Failure code.</returns>
public AddResult AddRegion(int offset, int length, int addr) {
return AddRegion(offset, length, addr, string.Empty, false);
public AddResult AddEntry(int offset, int length, int addr) {
return AddEntry(offset, length, addr, string.Empty, false);
}
/// <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>
/// <param name="offset">File offset of region start.</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
/// assembler directive operand.</param>
/// <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) {
if (!ValidateArgs(offset, length, addr, preLabel)) {
Debug.WriteLine("AddRegion: invalid arg");
Debug.WriteLine("AddEntry: invalid arg");
return AddResult.InvalidValue;
}
int insIdx;
@ -472,13 +507,13 @@ namespace CommonUtil {
/// <param name="preLabel">Pre-block label.</param>
/// <param name="isRelative">New value for IsRelative.</param>
/// <returns>True if a region was edited, false otherwise.</returns>
public bool EditRegion(int offset, int length, int addr, string preLabel, bool isRelative) {
public bool EditEntry(int offset, int length, int addr, string preLabel, bool isRelative) {
if (!ValidateArgs(offset, length, addr, preLabel)) {
throw new Exception("Bad EditRegion args +" + offset.ToString("x6") +
" " + length + " $" + addr);
}
int idx = FindRegion(offset, length);
int idx = FindEntry(offset, length);
if (idx < 0) {
return false;
}
@ -493,13 +528,13 @@ namespace CommonUtil {
/// <param name="offset">Offset of region to remove.</param>
/// <param name="length">Length of region to remove.</param>
/// <returns>True if a region was removed, false otherwise.</returns>
public bool RemoveRegion(int offset, int length) {
public bool RemoveEntry(int offset, int length) {
if (!ValidateArgs(offset, length, 0, string.Empty)) {
throw new Exception("Bad RemoveRegion args +" + offset.ToString("x6") +
" " + length);
}
int idx = FindRegion(offset, length);
int idx = FindEntry(offset, length);
if (idx < 0) {
return false;
}
@ -509,12 +544,12 @@ namespace CommonUtil {
}
/// <summary>
/// Finds a region with a matching offset and length.
/// Finds an entry with a matching offset and length.
/// </summary>
/// <param name="offset">Offset to match.</param>
/// <param name="length">Length to match (may be FLOATING_LEN).</param>
/// <returns>Index of matching region, or -1 if not found.</returns>
private int FindRegion(int offset, int length) {
/// <returns>Index of matching entry, or -1 if not found.</returns>
private int FindEntry(int offset, int length) {
for (int i = 0; i < mMapEntries.Count; i++) {
if (mMapEntries[i].Offset == offset && mMapEntries[i].Length == length) {
return i;
@ -531,26 +566,11 @@ namespace CommonUtil {
//}
/// <summary>
/// Gets the first region with the specified offset and length.
/// </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.
/// Gets a list of the entries with the specified offset value.
/// </summary>
/// <param name="offset">File offset.</param>
/// <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>();
for (int i = 0; i < mMapEntries.Count; i++) {
if (mMapEntries[i].Offset == offset) {
@ -653,7 +673,7 @@ namespace CommonUtil {
/// reconstructed whenever a change is made.
///
/// We need to resolve floating lengths and pre-label addresses, so the tree holds
/// AddressMapEntry rather than AddressRegion.
/// AddressRegion rather than AddressMapEntry.
/// </remarks>
private class TreeNode {
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
// explicitly is caught here. It also avoids the need to special-case the top
// part of the file.
AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, false,
string.Empty, NON_ADDR, false);
AddressRegion globalReg = new AddressRegion(0, mSpanLength, NON_ADDR, string.Empty,
false, mSpanLength, NON_ADDR);
TreeNode topNode = new TreeNode(globalReg, null);
// 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
// check for sub-regions.
int nextStart = parent.Region.Offset + parent.Region.Length;
int nextStart = parent.Region.Offset + parent.Region.ActualLength;
index++;
if (index < mMapEntries.Count) {
// Check next sibling.
@ -740,16 +760,16 @@ namespace CommonUtil {
}
}
AddressRegion fixedReg = new AddressRegion(childEnt.Offset,
nextStart - childEnt.Offset, childEnt.Address, true,
childEnt.PreLabel, preLabelAddr, childEnt.IsRelative);
FLOATING_LEN, childEnt.Address, childEnt.PreLabel, childEnt.IsRelative,
nextStart - childEnt.Offset, preLabelAddr);
children.Add(new TreeNode(fixedReg, parent));
// "index" now points to entry past the child we just added.
} else {
// Add this region to the list, and check for descendants.
AddressRegion newReg = new AddressRegion(childEnt.Offset,
childEnt.Length, childEnt.Address, false,
childEnt.PreLabel, preLabelAddr, childEnt.IsRelative);
childEnt.Length, childEnt.Address, childEnt.PreLabel,
childEnt.IsRelative, childEnt.Length, preLabelAddr);
TreeNode thisNode = new TreeNode(newReg, parent);
children.Add(thisNode);
@ -857,7 +877,7 @@ namespace CommonUtil {
// Non-addressable space.
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.
return -1;
}
@ -869,7 +889,7 @@ namespace CommonUtil {
foreach (TreeNode childNode in node.Children) {
AddressRegion childReg = childNode.Region;
int childStartPosn = childReg.Offset - region.Offset;
int childEndPosn = childStartPosn + childReg.Length;
int childEndPosn = childStartPosn + childReg.ActualLength;
if (childStartPosn > subPosn) {
// 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;
if (node.Region.Address != NON_ADDR) {
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;
}
@ -919,7 +939,8 @@ namespace CommonUtil {
if (node.Children != null) {
foreach (TreeNode child in node.Children) {
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.
return OffsetToNode(offset, child);
}
@ -928,6 +949,48 @@ namespace CommonUtil {
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>
/// 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
@ -955,9 +1018,9 @@ namespace CommonUtil {
TreeNode node = OffsetToNode(offset, mTopNode);
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
if (lastOffset >= region.Offset + region.Length) {
if (lastOffset >= region.Offset + region.ActualLength) {
// end of region is not in this node
return false;
}
@ -971,7 +1034,7 @@ namespace CommonUtil {
// Child is past the target, so range is not in a hole; no need to check
// additional children because the children are sorted by Offset.
break;
} else if (offset <= childReg.Offset + childReg.Length - 1 &&
} else if (offset <= childReg.Offset + childReg.ActualLength - 1 &&
lastOffset >= childReg.Offset) {
// Target is in a hole occupied by the child. No good.
return false;
@ -983,7 +1046,7 @@ namespace CommonUtil {
}
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");
return false;
}
@ -1006,19 +1069,19 @@ namespace CommonUtil {
private bool DebugValidateHierarchy(List<TreeNode> nodeList, int startOffset,
int nextOffset, ref int nodeCount) {
foreach (TreeNode node in nodeList) {
Debug.Assert(node.Region.Length >= 0); // no floaters
Debug.Assert(node.Region.ActualLength >= 0);
nodeCount++;
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");
return false;
}
if (node.Children != null) {
// Descend recursively.
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;
}
}
@ -1076,7 +1139,7 @@ namespace CommonUtil {
if (mTopNode.Children != null) {
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) {
// Insert a no-address zone here.
@ -1090,7 +1153,7 @@ namespace CommonUtil {
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>
private void AddChangeEntry(List<AddressChange> changeList, TreeNode node,
int parentStartAddr) {
Debug.Assert(node.Region.Length != FLOATING_LEN);
Debug.Assert(node.Region.ActualLength != FLOATING_LEN);
int nextAddr = NON_ADDR;
if (parentStartAddr != NON_ADDR) {
nextAddr = parentStartAddr + node.Region.Length;
nextAddr = parentStartAddr + node.Region.ActualLength;
}
AddressChange startChange = new AddressChange(true,
node.Region.Offset, node.Region.Address, node.Region);
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);
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() {
const int mapLen = 0x8000;
AddressMap map = new AddressMap(mapLen);
@ -1294,27 +1431,27 @@ namespace CommonUtil {
const int adr2 = 0x1700;
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, len0, adr0));
map.AddEntry(off0, len0, adr0));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off1, len1, adr1));
map.AddEntry(off1, len1, adr1));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off2, len2, adr2));
map.AddEntry(off2, len2, adr2));
result &= map.DebugValidate();
Test_Expect(AddResult.OverlapExisting, ref result,
map.AddRegion(off0, len0, 0x1000));
map.AddEntry(off0, len0, 0x1000));
Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(off0, FLOATING_LEN, 0x1000));
map.AddEntry(off0, FLOATING_LEN, 0x1000));
Test_Expect(AddResult.StraddleExisting, ref result,
map.AddRegion(off0 + 1, len0, 0x1000));
map.AddEntry(off0 + 1, len0, 0x1000));
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.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, mapLen, 0x0000));
Test_Expect(true, ref result, map.RemoveRegion(off0, mapLen));
Test_Expect(false, ref result, map.RemoveRegion(off0, mapLen));
map.AddEntry(off0, mapLen, 0x0000));
Test_Expect(true, ref result, map.RemoveEntry(off0, mapLen));
Test_Expect(false, ref result, map.RemoveEntry(off0, mapLen));
Test_Expect(adr0, ref result, map.OffsetToAddress(off0));
Test_Expect(adr1, ref result, map.OffsetToAddress(off1));
@ -1354,16 +1491,16 @@ namespace CommonUtil {
const int adr2 = NON_ADDR;
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off0, len0, adr0));
map.AddEntry(off0, len0, adr0));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(off1, len1, adr1));
map.AddEntry(off1, len1, adr1));
// 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.
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(adr1, ref result, map.OffsetToAddress(off1));
@ -1390,56 +1527,56 @@ namespace CommonUtil {
bool result = true;
// Nested with shared start offset.
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,
map.AddRegion(0x000100, 0x0100, 0x7000, "preA1", false));
map.AddEntry(0x000100, 0x0100, 0x7000, "preA1", false));
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,
map.AddRegion(0x000100, 0x0200, 0x6000, "preA3", false));
map.AddEntry(0x000100, 0x0200, 0x6000, "preA3", false));
// Add a couple of floaters.
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,
map.AddRegion(0x000101, FLOATING_LEN, 0x3101, "preA5", false));
map.AddEntry(0x000101, FLOATING_LEN, 0x3101, "preA5", false));
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.
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,
map.AddRegion(0x001200, 0x0200, 0x6000, "preB1", false));
map.AddEntry(0x001200, 0x0200, 0x6000, "preB1", false));
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,
map.AddRegion(0x001100, 0x0300, 0x5000, "preB3", false));
map.AddEntry(0x001100, 0x0300, 0x5000, "preB3", false));
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.
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,
map.AddRegion(0x0013ff, 1, 0x83ff, "preB6", false));
map.AddEntry(0x0013ff, 1, 0x83ff, "preB6", false));
// Nested with no common edge, building from outside-in.
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x0800, 0x4000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002300, 0x0200, 0x7000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002000, 0x0800, 0x4000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002300, 0x0200, 0x7000));
// 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.AddRegion(0x003200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003000, 0x0800, 0x4000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003300, 0x0200, 0x7000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003200, 0x0400, 0x6000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003100, 0x0600, 0x5000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x003000, 0x0800, 0x4000));
// Try floater then overlap.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x004000, FLOATING_LEN, 0x8000));
map.AddEntry(0x004000, FLOATING_LEN, 0x8000));
Test_Expect(AddResult.OverlapFloating, ref result,
map.AddRegion(0x004000, 0x100, 0x8000));
Test_Expect(true, ref result, map.RemoveRegion(0x004000, FLOATING_LEN));
map.AddEntry(0x004000, 0x100, 0x8000));
Test_Expect(true, ref result, map.RemoveEntry(0x004000, FLOATING_LEN));
Test_Expect(0x30ff, ref result, map.OffsetToAddress(0x0000ff));
Test_Expect(0x7000, ref result, map.OffsetToAddress(0x000100));
@ -1466,10 +1603,10 @@ namespace CommonUtil {
AddressMap map = new AddressMap(mapLen);
bool result = true;
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x000000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x002100, 0x0200, 0xe100));
Test_Expect(AddResult.Okay, ref result, map.AddRegion(0x003100, 0x0200, 0xf100));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002000, 0x2000, 0x8000));
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x002100, 0x0200, 0xe100));
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(0x002100, 0xf105));
@ -1504,19 +1641,19 @@ namespace CommonUtil {
// Pyramid shape, all regions start at same address except last.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x000000, 0x6000, 0x8000));
map.AddEntry(0x000000, 0x6000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x001000, 0x4000, 0x8000));
map.AddEntry(0x001000, 0x4000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x002000, 0x2000, 0x7fff));
map.AddEntry(0x002000, 0x2000, 0x7fff));
// Second pyramid.
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x006000, 0x6000, 0x8000));
map.AddEntry(0x006000, 0x6000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x007000, 0x4000, 0x8000));
map.AddEntry(0x007000, 0x4000, 0x8000));
Test_Expect(AddResult.Okay, ref result,
map.AddRegion(0x008000, 0x2000, 0x8000));
map.AddEntry(0x008000, 0x2000, 0x8000));
// Children take priority over the start node.
Test_Expect(0x002001, ref result, map.AddressToOffset(0x000000, 0x8000));
@ -1559,6 +1696,8 @@ namespace CommonUtil {
public static bool Test() {
bool ok = true;
ok &= Test_Primitives();
ok &= Test_Find();
ok &= Test_SimpleLinear();
ok &= Test_SimpleFloatGap();
ok &= Test_Nested();

View File

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

View File

@ -106,7 +106,8 @@ namespace SourceGen.AsmGen {
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "=" },
{ "VarDirective", ".set" },
{ "OrgDirective", ".org" },
{ "ArStartDirective", ".org" },
//ArEndDirective
//RegWidthDirective // .a8, .a16, .i8, .i16
//DataBankDirective
{ "DefineData1", ".byte" },
@ -250,20 +251,10 @@ namespace SourceGen.AsmGen {
sw.WriteLine("MEMORY {");
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("SEGMENTS {");
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("FEATURES {}");
@ -493,24 +484,6 @@ namespace SourceGen.AsmGen {
OutputLine(labelStr, opcodeStr, operandStr, commentStr);
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>
@ -561,30 +534,12 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
if (!isStart) {
return;
}
//// Linear search for offset. List should be small, so this should be quick.
//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),
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);
}

View File

@ -100,7 +100,8 @@ namespace SourceGen.AsmGen {
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "equ" },
{ "VarDirective", "equ" },
{ "OrgDirective", "org" },
{ "ArStartDirective", "org" },
//ArEndDirective
//RegWidthDirective
//DataBankDirective
{ "DefineData1", "dfb" },
@ -484,10 +485,12 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputOrgDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
public void OutputArDirective(AddressMap.AddressRegion addrEntry, bool isStart) {
if (isStart) {
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);
OutputLine(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> {
{ "EquDirective", "=" },
{ "VarDirective", ".var" },
{ "OrgDirective", ".logical" },
{ "ArStartDirective", ".logical" },
{ "ArEndDirective", ".here" },
//RegWidthDirective // .as, .al, .xs, .xl
//DataBankDirective
{ "DefineData1", ".byte" },
@ -158,7 +159,6 @@ namespace SourceGen.AsmGen {
//StrLen16
{ "StrDci", ".shift" }
});
private const string HERE_PSEUDO_OP = ".here";
// IGenerator
@ -653,7 +653,7 @@ namespace SourceGen.AsmGen {
}
// 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
// into the generated binary, and "program counter", which determines the code
// 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(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4), string.Empty);
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(addrEntry.Address, 4),
string.Empty);
mPcDepth++;
} else {
mPcDepth--;
if (mPcDepth > 0 || !mFirstIsOpen) {
// close previous block
OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(HERE_PSEUDO_OP),
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective),
string.Empty, string.Empty);
} else {
// 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;
while (change != null && change.Offset == offset) {
gen.OutputOrgDirective(change.Region, change.IsStart);
addrIter.MoveNext();
change = addrIter.Current;
if (change.IsStart) {
gen.OutputArDirective(change.Region, change.IsStart);
addrIter.MoveNext();
change = addrIter.Current;
} else {
break;
}
}
List<DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset);
@ -130,6 +134,22 @@ namespace SourceGen.AsmGen {
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.
int curProgress = (offset * 10) / proj.FileData.Length;
if (lastProgress != curProgress) {
@ -143,19 +163,7 @@ namespace SourceGen.AsmGen {
}
}
// Close off any open ORG blocks.
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;
}
Debug.Assert(offset == proj.FileDataLength);
}
private static void GenerateHeader(IGenerator gen, StreamWriter sw) {
@ -565,11 +573,11 @@ namespace SourceGen.AsmGen {
return false;
}
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");
}
// 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");
return false;
}

View File

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

View File

@ -271,7 +271,7 @@ namespace SourceGen {
AddrMap = new AddressMap(fileDataLen);
// set default load address to $1000; override later
AddrMap.AddRegion(0x000000, fileDataLen, 0x1000);
AddrMap.AddEntry(0x000000, fileDataLen, 0x1000);
// Default value is "no tag".
AnalyzerTags = new CodeAnalysis.AnalyzerTag[fileDataLen];
@ -400,9 +400,9 @@ namespace SourceGen {
int loadAddr = RawData.GetWord(mFileData, 0, 2, false);
// TODO(org): use NON_ADDR for first two bytes
AddressMap.AddResult addRes =
AddrMap.AddRegion(0, 2, loadAddr < 2 ? 0 : loadAddr - 2);
AddrMap.AddEntry(0, 2, loadAddr < 2 ? 0 : loadAddr - 2);
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);
OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE,
@ -413,7 +413,7 @@ namespace SourceGen {
} else {
int loadAddr = SystemDefaults.GetLoadAddress(sysDef);
AddressMap.AddResult addRes =
AddrMap.AddRegion(0, mFileData.Length, loadAddr);
AddrMap.AddEntry(0, mFileData.Length, loadAddr);
Debug.Assert(addRes == AddressMap.AddResult.Okay);
}
@ -756,16 +756,19 @@ namespace SourceGen {
}
/// <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>
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;
while (addrIter.MoveNext()) {
AddressMap.AddressChange change = addrIter.Current;
if (!change.IsStart) {
continue;
}
AddressMap.AddressRegion region = change.Region;
if (change.IsStart &&
(region.Address & 0xff0000) != ((region.Address + region.Length - 1) & 0xff0000)) {
if ((region.Address & 0xff0000) != ((region.Address + region.ActualLength - 1) & 0xff0000)) {
string fmt = Res.Strings.MSG_BANK_OVERRUN_DETAIL_FMT;
int firstNext = (region.Address & 0xff0000) + 0x010000;
int badOffset = region.Offset + (firstNext - region.Address);
@ -2149,18 +2152,18 @@ namespace SourceGen {
AddressMap addrMap = AddrMap;
if ((int)oldValue == AddressMap.NON_ADDR) {
// adding new entry
if (addrMap.AddRegion(offset, AddressMap.FLOATING_LEN,
if (addrMap.AddEntry(offset, AddressMap.FLOATING_LEN,
(int)newValue) != AddressMap.AddResult.Okay) {
Debug.Assert(false, "failed adding region");
}
} else if ((int)newValue == AddressMap.NON_ADDR) {
// removing existing entry
if (!addrMap.RemoveRegion(offset, AddressMap.FLOATING_LEN)) {
if (!addrMap.RemoveEntry(offset, AddressMap.FLOATING_LEN)) {
Debug.Assert(false, "failed removing region");
}
} else {
// updating existing entry
if (!addrMap.EditRegion(offset, AddressMap.FLOATING_LEN,
if (!addrMap.EditEntry(offset, AddressMap.FLOATING_LEN,
(int) newValue, string.Empty, false)) {
Debug.Assert(false, "failed editing region");
}

View File

@ -353,10 +353,12 @@ namespace SourceGen {
case LineListGen.Line.Type.EquDirective:
case LineListGen.Line.Type.RegWidthDirective:
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:
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]);
break;
}
@ -714,7 +716,8 @@ namespace SourceGen {
case LineListGen.Line.Type.EquDirective:
case LineListGen.Line.Type.RegWidthDirective:
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:
if (parts.IsLongComment) {
// 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
/// all of the output without needing to refer back to the project data. (Currently
/// making an exception for some selection-dependent field highlighting.)
///
///
/// Base fields are immutable, but the Parts property is set after creation.
/// </summary>
public class Line {
@ -116,14 +116,15 @@ namespace SourceGen {
Blank = 1 << 4,
// Assembler directives.
OrgDirective = 1 << 5,
EquDirective = 1 << 6,
RegWidthDirective = 1 << 7,
DataBankDirective = 1 << 8,
ArStartDirective = 1 << 5,
ArEndDirective = 1 << 6,
EquDirective = 1 << 7,
RegWidthDirective = 1 << 8,
DataBankDirective = 1 << 9,
// Additional metadata.
LocalVariableTable = 1 << 9,
VisualizationSet = 1 << 10,
LocalVariableTable = 1 << 10,
VisualizationSet = 1 << 11,
}
/// <summary>
@ -535,7 +536,8 @@ namespace SourceGen {
// Nothing to do.
parts = FormattedParts.CreateBlankLine();
break;
case Line.Type.OrgDirective:
case Line.Type.ArStartDirective:
case Line.Type.ArEndDirective:
case Line.Type.RegWidthDirective:
case Line.Type.DataBankDirective:
case Line.Type.LongComment:
@ -989,29 +991,53 @@ namespace SourceGen {
int offset = startOffset;
while (offset <= endOffset) {
bool blankAdded = false;
bool spaceAdded = false;
Anattrib attr = mProject.GetAnattrib(offset);
if (attr.IsInstructionStart && offset > 0 &&
mProject.GetAnattrib(offset - 1).IsData) {
// Transition from data to code. (Don't add blank line for inline data.)
lines.Add(GenerateBlankLine(offset));
blankAdded = true;
spaceAdded = true;
} else if (mProject.VisualizationSets.ContainsKey(offset) && !addBlank) {
// Blank line before visualization set helps keep image visually grouped
// with its data.
lines.Add(GenerateBlankLine(offset));
blankAdded = true;
spaceAdded = true;
} else if (addBlank) {
// Previous instruction wanted to be followed by a blank line.
lines.Add(GenerateBlankLine(offset));
blankAdded = true;
spaceAdded = true;
}
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) {
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;
if (region.Offset == 0 && AsmGen.GenCommon.HasPrgHeader(mProject)) {
@ -1024,40 +1050,22 @@ namespace SourceGen {
if (change.IsStart) {
// Blank line above ORG directive, except at top of file or when we've
// already added one for another reason.
if (region.Offset != 0 && !blankAdded) {
if (region.Offset != 0 && !spaceAdded) {
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)
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);
newLine.Parts = FormattedParts.CreateDirective(
mFormatter.FormatPseudoOp(mPseudoOpNames.OrgDirective), addrStr);
mFormatter.FormatPseudoOp(mPseudoOpNames.ArStartDirective), addrStr);
lines.Add(newLine);
addrIter.MoveNext();
} 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.
@ -1210,6 +1218,26 @@ namespace SourceGen {
}
offset += attr.Length;
}
// Check for address region ends, which will be positioned at the updated offset
// (unless they somehow got embedded inside something else).
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);
}
break;
case LineListGen.Line.Type.OrgDirective:
case LineListGen.Line.Type.ArStartDirective:
if (CanEditAddress()) {
EditAddress();
}
// TODO(org): handle ArEndDirective
break;
case LineListGen.Line.Type.RegWidthDirective:
if (CanEditStatusFlags()) {
@ -1771,7 +1772,8 @@ namespace SourceGen {
LineListGen.Line selLine = CodeLineList[selIndex];
if (selLine.LineType != LineListGen.Line.Type.Code &&
selLine.LineType != LineListGen.Line.Type.Data &&
selLine.LineType != LineListGen.Line.Type.OrgDirective) {
selLine.LineType != LineListGen.Line.Type.ArStartDirective) {
// TODO(org): handle ArEnd
return false;
}
@ -1803,55 +1805,31 @@ namespace SourceGen {
int lastIndex = mMainWin.CodeListView_GetLastSelectedIndex();
int firstOffset = CodeLineList[selIndex].FileOffset;
int lastOffset = CodeLineList[lastIndex].FileOffset;
int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
int nextAddr;
//int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
AddressMap addrMap = mProject.AddrMap;
// TODO(org): rewrite this - need to identify the specific .ORG statement since
// there can now be several at a single offset
if (firstOffset == lastOffset || nextOffset == mProject.FileDataLength) {
// Single item (which might not be a single *line*) is selected, or the
// last selected item is the end of the file.
nextOffset = -1;
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
int addr = addrMap.OffsetToAddress(firstOffset);
int newLen = lastOffset - firstOffset;
if (newLen == 0) {
newLen = 123;
}
EditAddress dlg = new EditAddress(mMainWin, firstOffset, nextOffset, nextAddr,
AddressMap.AddressMapEntry newEntry = new AddressMap.AddressMapEntry(firstOffset,
newLen /*DEBUG - replace*/, addr, string.Empty, false);
EditAddress dlg = new EditAddress(mMainWin, newEntry, true, newLen,
mProject, mFormatter);
if (dlg.ShowDialog() != true) {
return;
}
if (firstOffset == 0 && dlg.NewAddress < 0) {
// Not allowed. The AddressMap will just put it back, which confuses
// the undo operation.
Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000");
return;
}
//if (firstOffset == 0 && dlg.NewAddress < 0) {
// // Not allowed. The AddressMap will just put it back, which confuses
// // the undo operation.
// Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000");
// return;
//}
ChangeSet cs = new ChangeSet(1);
@ -3899,8 +3877,11 @@ namespace SourceGen {
case LineListGen.Line.Type.Blank:
lineTypeStr = "blank line";
break;
case LineListGen.Line.Type.OrgDirective:
lineTypeStr = "address directive";
case LineListGen.Line.Type.ArStartDirective:
lineTypeStr = "address range start directive";
break;
case LineListGen.Line.Type.ArEndDirective:
lineTypeStr = "address range end directive";
break;
case LineListGen.Line.Type.RegWidthDirective:
lineTypeStr = "register width directive";

View File

@ -673,7 +673,7 @@ namespace SourceGen {
// TODO(org): serialize length, isRelative, and preLabel
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);
if (addResult != CommonUtil.AddressMap.AddResult.Okay) {
string msg = "off=+" + addr.Offset.ToString("x6") + " addr=$" +

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,60 +19,99 @@ limitations under the License.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Set Address"
Title="Edit Address Range"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
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 Orientation="Horizontal">
<TextBlock Text="Editing address at offset: "/>
<TextBlock Margin="0,2,0,0" Text="{Binding FirstOffsetStr}"
<TextBlock Name="headerText" Text="{StaticResource str_HdrCreate}"/>
<Grid>
<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}"/>
<TextBlock Margin="16,0,0,0" Text="(load address: "/>
<TextBlock Margin="0,2,0,0" Text="{Binding LoadAddressText, FallbackValue=$1234}"
<TextBlock Grid.Column="0" Grid.Row="1" Margin="8,0,0,0" Text="End:"/>
<TextBlock Grid.Column="1" Grid.Row="1" Margin="4,2,0,0"
Text="{Binding RegionEndOffsetStr, FallbackValue=+123456}"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=")"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Text="Address (hex):"/>
<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>
<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"
<TextBlock Grid.Column="0" Grid.Row="2" Margin="8,0,0,0" Text="Length:"/>
<StackPanel Grid.Column="1" Grid.Row="2" Margin="4,0,0,0" Orientation="Horizontal">
<TextBlock Margin="0,2,0,0"
Text="{Binding RegionLengthStr, FallbackValue=23456 / $1234}"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text="(floating)" Margin="4,0,0,0" FontWeight="Bold"
Visibility="{Binding FloatTextVis}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Offset after last selected item ("/>
<TextBlock Text="{Binding NextOffsetStr, FallbackValue=+001234}" Margin="0,2,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text=") will resume at address "/>
<TextBlock Text="{Binding NextAddressStr, FallbackValue=$abcd}" Margin="0,2,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Text="."/>
</Grid>
<!-- failure message goes here -->
<StackPanel IsEnabled="{Binding IsRegionValid}">
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Text="Address (hex):"/>
<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>
<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>
<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">
<Button Content="OK" IsDefault="True" Width="70"
IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>

View File

@ -14,71 +14,58 @@
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using Asm65;
using CommonUtil;
namespace SourceGen.WpfGui {
/// <summary>
/// Edit Address dialog.
/// Edit Address Region dialog.
/// </summary>
public partial class EditAddress : Window, INotifyPropertyChanged {
/// <summary>
/// Address typed by user. Only valid after the dialog returns OK. Will be set to
/// AddressMap.NO_ENTRY_ADDR if the user is attempting to delete the address.
/// Updated address map entry. Will be null if we want to delete the entry.
/// </summary>
public int NewAddress { get; private set; }
public AddressMap.AddressMapEntry NewEntry { get; private set; }
/// <summary>
/// Offset being edited.
/// </summary>
private int mFirstOffset;
private int mRegionStartOffset;
public string RegionStartOffsetStr {
get { return mFormatter.FormatOffset24(mRegionStartOffset); }
}
/// <summary>
/// Offset after the end of the selection, or -1 if only one line is selected.
/// </summary>
private int mNextOffset;
/// <summary>
/// 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); }
private int mRegionEndOffset;
public string RegionEndOffsetStr {
get { return mFormatter.FormatOffset24(mRegionEndOffset); }
}
public string NextOffsetStr {
get { return mFormatter.FormatOffset24(mNextOffset); }
}
public string NextAddressStr {
get { return '$' + mFormatter.FormatAddress(mNextAddress, mNextAddress > 0xffff); }
}
public string BytesSelectedStr {
public string RegionLengthStr {
get {
int count = mNextOffset - mFirstOffset;
int count = mRegionEndOffset - mRegionStartOffset;
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>
/// Address input TextBox.
/// </summary>
@ -88,6 +75,21 @@ namespace SourceGen.WpfGui {
}
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>
/// Set to true when input is valid. Controls whether the OK button is enabled.
/// </summary>
@ -97,17 +99,23 @@ namespace SourceGen.WpfGui {
}
private bool mIsValid;
public Visibility NextAddressVis {
get { return mNextAddressVis; }
set { mNextAddressVis = value; OnPropertyChanged(); }
/// <summary>
/// Set to true when requested region is valid. Everything but the cancel button is
/// 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 {
get { return mLoadAddressText; }
set { mLoadAddressText = value; OnPropertyChanged(); }
/// <summary>
/// Determines whether the "(floating)" message appears next to the length.
/// </summary>
public Visibility FloatTextVis {
get { return mFloatTextVis; }
}
public string mLoadAddressText = string.Empty;
private Visibility mFloatTextVis;
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
@ -115,46 +123,96 @@ namespace SourceGen.WpfGui {
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>
/// Constructor.
/// </summary>
/// <param name="owner">Parent window.</param>
/// <param name="firstOffset">Offset at top of selection.</param>
/// <param name="nextOffset">Offset past bottom of selection, or -1 if only one
/// line is selected.</param>
/// <param name="entry">Map entry definition. This may be an existing entry, or values
/// representing the selection.</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="formatter">Text formatter object.</param>
public EditAddress(Window owner, int firstOffset, int nextOffset, int nextAddr,
DisasmProject project, Formatter formatter) {
public EditAddress(Window owner, AddressMap.AddressMapEntry entry, bool isNew,
int newLength, DisasmProject project, Formatter formatter) {
InitializeComponent();
Owner = owner;
DataContext = this;
mFirstOffset = firstOffset;
mNextOffset = nextOffset;
mNextAddress = nextAddr;
mFormatter = formatter;
mProject = project;
mMaxAddressValue = project.CpuDef.MaxAddressValue;
mFormatter = formatter;
// Compute load address, i.e. where the byte would have been placed if the entire
// 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);
Configure(entry, isNew, newLength);
}
int firstAddr = project.GetAnattrib(firstOffset).Address;
Debug.Assert(project.AddrMap.OffsetToAddress(firstOffset) == firstAddr);
private void Configure(AddressMap.AddressMapEntry entry, bool isNew, int newLength) {
mRegionStartOffset = mRegionEndOffset = entry.Offset;
mPreLabelAddress = 0;
IsRegionValid = false;
AddressText = Asm65.Address.AddressToString(firstAddr, false);
LoadAddressText = '$' + mFormatter.FormatAddress(mBaseAddr, mBaseAddr > 0xffff);
if (nextOffset >= 0) {
NextAddressVis = Visibility.Visible;
// 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
// 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.
List<AddressMap.AddressMapEntry> entries;
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) {
@ -163,12 +221,15 @@ namespace SourceGen.WpfGui {
}
private void OkButton_Click(object sender, RoutedEventArgs e) {
if (AddressText.Length == 0) {
NewAddress = CommonUtil.AddressMap.NON_ADDR;
bool ok = ParseAddress(out int addr);
Debug.Assert(ok);
if (addr == AddressMap.INVALID_ADDR) {
// field was blank, want to delete the entry
NewEntry = null;
} else {
bool ok = Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out int addr);
Debug.Assert(ok);
NewAddress = addr;
NewEntry = new AddressMap.AddressMapEntry(mNewRegion.Offset,
mNewRegion.Length, addr, PreLabelText,
UseRelativeAddressing);
}
DialogResult = true;
}
@ -181,32 +242,32 @@ namespace SourceGen.WpfGui {
/// for TextBox is LostFocus.
/// </remarks>
private void UpdateControls() {
IsValid = (AddressText.Length == 0) ||
Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out int unused);
IsValid = IsRegionValid && ParseAddress(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}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="orgDirectiveTextBox" 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"
<TextBox Name="regWidthDirectiveTextBox" Grid.Column="5" Grid.Row="0"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- second row -->
<StackPanel Grid.Column="0" Grid.Row="1" VerticalAlignment="Center">
<TextBlock Text="Little-endian" HorizontalAlignment="Right"/>
<TextBlock Text="data, one byte:" HorizontalAlignment="Right"/>
<TextBlock Text="Address range" HorizontalAlignment="Right"/>
<TextBlock Text="start:" HorizontalAlignment="Right"/>
</StackPanel>
<TextBox Name="defineData1TextBox" Grid.Column="1" Grid.Row="1"
<TextBox Name="arStartDirectiveTextBox" Grid.Column="1" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="1" Text="Two bytes:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="defineData2TextBox" Grid.Column="3" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"
<StackPanel Grid.Column="2" Grid.Row="1" VerticalAlignment="Center">
<TextBlock Text="Address range" HorizontalAlignment="Right"/>
<TextBlock Text="end:" HorizontalAlignment="Right"/>
</StackPanel>
<TextBox Name="arEndDirectiveTextBox" Grid.Column="3" Grid.Row="1"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- third row -->
<StackPanel Grid.Column="2" Grid.Row="2" VerticalAlignment="Center">
<TextBlock Text="Big-endian data," HorizontalAlignment="Right"/>
<TextBlock Text="two bytes:" HorizontalAlignment="Right"/>
<StackPanel Grid.Column="0" Grid.Row="2" VerticalAlignment="Center">
<TextBlock Text="Little-endian" HorizontalAlignment="Right"/>
<TextBlock Text="data, one byte:" HorizontalAlignment="Right"/>
</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}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- fourth row -->
<TextBlock Grid.Column="0" Grid.Row="3" Text="Fill:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="fillTextBox" Grid.Column="1" Grid.Row="3"
<StackPanel Grid.Column="2" Grid.Row="3" VerticalAlignment="Center">
<TextBlock Text="Big-endian data," HorizontalAlignment="Right"/>
<TextBlock Text="two bytes:" HorizontalAlignment="Right"/>
</StackPanel>
<TextBox Name="defineBigData2TextBox" Grid.Column="3" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="denseTextBox" Grid.Column="3" 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"
<TextBox Name="denseTextBox" Grid.Column="7" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- 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"/>
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="4"
<TextBox Name="fillTextBox" Grid.Column="1" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="4"
<TextBox Name="junkTextBox" Grid.Column="5" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="strNullTermTextBox" Grid.Column="5" 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"
<TextBox Name="alignTextBox" Grid.Column="7" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- 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"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="5"
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="strLen16TextBox" Grid.Column="3" Grid.Row="5"
<TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<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}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>

View File

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

View File

@ -222,10 +222,6 @@ namespace SourceGen.WpfGui {
SetControlsFromDescriptor(mFirstFormatDescriptor);
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?
Debug.WriteLine("NOTE: preferred format unavailable");
}