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

ORG rework, part 7

Implemented "is relative" flag.  This only affects source code
generation, replacing ".arstart <addr>" with ".arstart *+<value>".
Only output by 64tass and ACME generators.

Added a bold-text summary to radio buttons in address region edit
dialog.  This makes it much easier to see what you're doing.  Added
a warning to the label edit dialog when a label is being placed in
a non-addressable region.

Modified double-click behavior for .arstart/.arend to jump to the
other end when the opcode is clicked on.  This matches the behavior
of instructions with address operands.

Reordered Actions menu, putting "edit operand" at the top.

Fixed AddressMap entry collision testing.
Fixed PRG issue with multiple address regions at offset +000002.

Added regression tests.  Most of the complicated stuff with regions
is tested by unit tests inside AddressMap, but we still need to
exercise nested region code generation.
This commit is contained in:
Andy McFadden 2021-10-02 13:47:05 -07:00
parent e6c5c7f8df
commit e8608770b9
30 changed files with 1149 additions and 105 deletions

View File

@ -256,6 +256,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) {
// TODO(maybe): suppress Regenerate() call in AddEntry while we work
AddResult result = AddEntry(ent.Offset, ent.Length, ent.Address, ent.PreLabel,
ent.IsRelative);
if (result != AddResult.Okay) {
@ -357,7 +358,7 @@ namespace CommonUtil {
}
/// <summary>
/// Adds a new entry to the map.
/// Adds a new entry to the map. Uses defaults for PreLabel and IsRelative.
/// </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>
@ -423,14 +424,16 @@ namespace CommonUtil {
return AddResult.Okay;
}
// Find insertion point.
int insIdx;
for (insIdx = 0; insIdx < mMapEntries.Count; insIdx++) {
AddressMapEntry ent = mMapEntries[insIdx];
if (ent.Offset > offset) {
// Insert before this one.
break;
} else if (ent.Offset == offset) {
// Find the insertion point, and check for conflicts.
//
// If we know where to insert the new entry, we only need to check the previous
// node, following node, and parents. However, we may not have regenerated the
// tree structure since the previous add, so we can't rely on that. We're expecting
// the list to be short, so checking all entries shouldn't be prohibitive.
int insIdx = -1;
for (int i = 0; i < mMapEntries.Count; i++) {
AddressMapEntry ent = mMapEntries[i];
if (ent.Offset == offset) {
// We share a start point with this entry. See if we fit inside it or
// wrap around it.
if (length == FLOATING_LEN || ent.Length == FLOATING_LEN) {
@ -441,33 +444,18 @@ namespace CommonUtil {
return AddResult.OverlapExisting;
} else if (ent.Length < length) {
// New region is larger, would become parent, so insert before this.
break;
if (insIdx < 0) {
insIdx = i;
}
} else {
// New region is smaller and will be a child of this entry, so we want
// to insert *after* this point. Loop again to see if the following
// entry is also a parent for this new one.
Debug.Assert(ent.Length > length);
}
}
}
// The insertion index indicates the entry we want to insert before. We need to
// confirm that the new block doesn't straddle the blocks on either side. If we're
// inserting into a bunch of blocks with coincident start points, it's possible for
// the blocks appearing before and after to share the same start offset.
if (insIdx > 0) {
// Check previous block. We know that its offset is <= the new offset, so
// either its a parent or a sibling.
AddressMapEntry ent = mMapEntries[insIdx - 1];
if (ent.Offset == offset) {
// Previous is our parent. These things were checked earlier.
Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN);
Debug.Assert(ent.Length > length);
} else {
} else if (ent.Offset < offset) {
// Existing block starts before this new one. The existing block must either
// be floating, be completely before this, or completely envelop this.
Debug.Assert(ent.Offset < offset);
if (ent.Length == FLOATING_LEN) {
// sibling -- must end before us
} else if (ent.Offset + ent.Length <= offset) {
@ -480,20 +468,12 @@ namespace CommonUtil {
// whoops
return AddResult.StraddleExisting;
}
}
}
if (insIdx < mMapEntries.Count) {
// Check following block. We know that its offset is >= the new offset, so it's
// either a child or a sibling.
AddressMapEntry ent = mMapEntries[insIdx];
if (ent.Offset == offset) {
// Following block is our child. These things were checked earlier.
Debug.Assert(length != FLOATING_LEN && ent.Length != FLOATING_LEN);
Debug.Assert(ent.Length < length);
} else {
// Existing block starts after this new one. The existing block must either
// be floating, be completely after this, or be completely enveloped by this.
Debug.Assert(ent.Offset > offset);
if (insIdx < 0) {
insIdx = i;
}
if (ent.Length == FLOATING_LEN) {
// child or sibling, depending on start offset
} else if (offset + length <= ent.Offset) {
@ -509,6 +489,10 @@ namespace CommonUtil {
}
}
if (insIdx < 0) {
insIdx = mMapEntries.Count;
}
outInsIdx = insIdx;
return AddResult.Okay;
}
@ -549,13 +533,6 @@ namespace CommonUtil {
return -1;
}
// Returns true if adding the specified region is a valid action.
// ??? do we want to do this, or just ask "does region exist"? Depends on
// flow in edit dialog.
//public bool CanAddRegion(int offset, int length) {
// return false;
//}
/// <summary>
/// Gets a list of the entries with the specified offset value.
/// </summary>
@ -1097,7 +1074,7 @@ namespace CommonUtil {
/// <remarks>
/// We use inclusive Offset values for both start and end. If we don't do this, the
/// offset for end records will be outside the file bounds. It also gets a bit painful
/// when the display list tries to update [M,N] if put the end at N+1.
/// when the display list tries to update [M,N] if the end is actually held at N+1.
/// </remarks>
public class AddressChange {
// True if this is a region start, false if a region end.
@ -1451,6 +1428,8 @@ namespace CommonUtil {
map.AddEntry(off0 + 1, len0, 0x1000));
Test_Expect(AddResult.InvalidValue, ref result,
map.AddEntry(off0, mapLen + 1, 0x1000));
Test_Expect(AddResult.StraddleExisting, ref result,
map.AddEntry(off0 + 1, off2 - off0, 0x1000));
// One region to wrap them all. Add then remove.
Test_Expect(AddResult.Okay, ref result,
@ -1596,9 +1575,6 @@ namespace CommonUtil {
Test_Expect(0x002100, ref result, map.AddressToOffset(0x002300, 0x5000));
Test_Expect(0x003100, ref result, map.AddressToOffset(0x003000, 0x5000));
string mapStr = map.FormatAddressMap(); // DEBUG - format the map and
Debug.WriteLine(mapStr); // DEBUG - print it to the console
result &= map.DebugValidate();
return result;
}
@ -1699,6 +1675,39 @@ namespace CommonUtil {
return result;
}
private static bool Test_OddOverlap() {
const int mapLen = 0x1000;
AddressMap map = new AddressMap(mapLen);
bool result = true;
// Top region spans full map.
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000000, mapLen, 0x1000));
// Parent region covers next two.
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000000, 0x0400, 0x1000));
// Floating region.
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000100, FLOATING_LEN, 0x2000));
// Fixed region follows.
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000200, 0x0100, 0x3000));
string mapStr = map.FormatAddressMap(); // DEBUG - format the map and
Debug.WriteLine(mapStr); // DEBUG - print it to the console
// Add a region that starts in the middle of the floating region (becoming
// a sibling), and ends after the fixed region (becoming its parent).
Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000180, 0x0200, 0x4000));
// Remove it.
Test_Expect(true, ref result, map.RemoveEntry(0x000180, 0x0200));
// Add a region that starts in the middle of the floating region and ends after
// the parent. Since this crosses the parent's end boundary and doesn't share
// the parent's start offset, this is invalid.
Test_Expect(AddResult.StraddleExisting, ref result,
map.AddEntry(0x000180, 0x0400, 0x4000));
result &= map.DebugValidate();
return result;
}
public static bool Test() {
bool ok = true;
ok &= Test_Primitives();
@ -1708,6 +1717,7 @@ namespace CommonUtil {
ok &= Test_Nested();
ok &= Test_Cross();
ok &= Test_Pyramids();
ok &= Test_OddOverlap();
Debug.WriteLine("AddressMap: test complete (ok=" + ok + ")");
return ok;

View File

@ -594,9 +594,24 @@ namespace SourceGen.AsmGen {
OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty);
}
}
AddressMap.AddressRegion region = change.Region;
string addrStr;
if (region.IsRelative && region.PreLabelAddress != Address.NON_ADDR) {
int diff = nextAddress - region.PreLabelAddress;
string pfxStr;
if (diff >= 0) {
pfxStr = "*+";
} else {
pfxStr = "*-";
diff = -diff;
}
addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4);
} else {
addrStr = SourceFormatter.FormatHexValue(nextAddress, 4);
}
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(nextAddress, 4) + " {",
addrStr + " {",
string.Empty);
mPcDepth++;
} else {

View File

@ -550,6 +550,7 @@ namespace SourceGen.AsmGen {
// IGenerator
public void FlushArDirectives() {
// TODO(someday): handle IsRelative
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4),

View File

@ -501,6 +501,7 @@ namespace SourceGen.AsmGen {
// IGenerator
public void FlushArDirectives() {
// TODO(someday): handle IsRelative
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(mNextAddress, 4),

View File

@ -697,9 +697,25 @@ namespace SourceGen.AsmGen {
//OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty);
}
}
AddressMap.AddressRegion region = change.Region;
string addrStr;
if (region.IsRelative && region.PreLabelAddress != Address.NON_ADDR) {
int diff = nextAddress - region.PreLabelAddress;
string pfxStr;
if (diff >= 0) {
pfxStr = "*+";
} else {
pfxStr = "*-";
diff = -diff;
}
addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4);
} else {
addrStr = SourceFormatter.FormatHexValue(nextAddress, 4);
}
OutputLine(string.Empty,
SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
SourceFormatter.FormatHexValue(nextAddress, 4),
addrStr,
string.Empty);
mPcDepth++;
} else {

View File

@ -584,9 +584,12 @@ namespace SourceGen.AsmGen {
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.GetEntries(0x000002).Count == 0) {
//Debug.WriteLine("PRG test: no ORG at +2");
// Confirm there's a single address map entry at offset 2. If there's more than
// one we likely have a situation where the first one is a "full-file" region, and
// the second determines the address. This weird scenario causes problems with
// code generation, so we just don't support it.
if (project.AddrMap.GetEntries(0x000002).Count != 1) {
//Debug.WriteLine("PRG test: wrong #of entries at +000002");
return false;
}
// See if the address at offset 2 matches the value at 0/1.

View File

@ -1086,6 +1086,17 @@ namespace SourceGen {
string addrStr;
if (region.Address == Address.NON_ADDR) {
addrStr = Address.NON_ADDR_STR;
} else if (change.Region.IsRelative &&
change.Region.PreLabelAddress != Address.NON_ADDR) {
int diff = region.Address - change.Region.PreLabelAddress;
string pfxStr;
if (diff >= 0) {
pfxStr = "*+";
} else {
pfxStr = "*-";
diff = -diff;
}
addrStr = pfxStr + mFormatter.FormatHexValue(diff, 4);
} else {
addrStr = mFormatter.FormatHexValue(region.Address, 4);
}

View File

@ -1532,7 +1532,9 @@ namespace SourceGen {
break;
case LineListGen.Line.Type.ArStartDirective:
case LineListGen.Line.Type.ArEndDirective:
if (CanEditAddress()) {
if ((CodeListColumn)col == CodeListColumn.Opcode) {
JumpToOperandTarget(line, false);
} else if (CanEditAddress()) {
EditAddress();
}
break;
@ -1807,9 +1809,10 @@ namespace SourceGen {
return false;
}
// If multiple lines with code/data are selected, there must not be an address change
// If multiple lines with code/data are selected, there must not be a .arstart
// between them unless we're resizing a region. Determining whether or not a resize
// is valid is left to the edit dialog.
// is valid is left to the edit dialog. It's okay for a .arend to be in the middle
// so long as the corresponding .arstart is at the current offset.
if (selLine.LineType == LineListGen.Line.Type.ArStartDirective) {
// Skip overlapping region check.
return true;
@ -1821,14 +1824,18 @@ namespace SourceGen {
return true;
}
// Compute exclusive end point of selected range.
int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
// Anything else is too complicated to be worth messing with here. We could do
// the work, but we have no good way of telling the user what went wrong.
// Let the dialog explain it.
if (!mProject.AddrMap.IsRangeUnbroken(firstOffset, nextOffset - firstOffset)) {
Debug.WriteLine("Found mid-selection AddressMap entry (len=" +
(nextOffset - firstOffset) + ")");
return false;
}
//// Compute exclusive end point of selected range.
//int nextOffset = lastOffset + CodeLineList[lastIndex].OffsetSpan;
//if (!mProject.AddrMap.IsRangeUnbroken(firstOffset, nextOffset - firstOffset)) {
// Debug.WriteLine("Found mid-selection AddressMap entry (len=" +
// (nextOffset - firstOffset) + ")");
// return false;
//}
//Debug.WriteLine("First +" + firstOffset.ToString("x6") +
// ", last +" + lastOffset.ToString("x6") + ",next +" + nextOffset.ToString("x6"));

View File

@ -107,6 +107,9 @@ namespace SourceGen {
sb.Append(formatter.FormatOffset24(change.Offset));
PrintDepthLines(sb, depth, false);
sb.Append("+- " + "end");
if (change.Region.IsFloating) {
sb.Append(" (floating)");
}
//PrintAddress(sb, formatter, change.Address, showBank);
//sb.Append(")");
sb.Append(CRLF);

Binary file not shown.

View File

@ -0,0 +1,161 @@
### 6502bench SourceGen dis65 v1.0 ###
{
"_ContentVersion":5,
"FileDataLength":156,
"FileDataCrc32":1681364707,
"ProjectProps":{
"CpuName":"6502",
"IncludeUndocumentedInstr":false,
"TwoByteBrk":false,
"EntryFlags":32702671,
"AutoLabelStyle":"Simple",
"AnalysisParams":{
"AnalyzeUncategorizedData":true,
"DefaultTextScanMode":"C64Petscii",
"MinCharsForString":4,
"SeekNearbyTargets":true,
"UseRelocData":false,
"SmartPlpHandling":false,
"SmartPlbHandling":true},
"PlatformSymbolFileIdentifiers":["RT:Commodore/C64-Kernal.sym65"],
"ExtensionScriptFileIdentifiers":["RT:Commodore/VisC64.cs"],
"ProjectSyms":{
}},
"AddressMap":[{
"Offset":2,
"Addr":4096,
"Length":154,
"PreLabel":"",
"IsRelative":true},
{
"Offset":2,
"Addr":8192,
"Length":18,
"PreLabel":"",
"IsRelative":true},
{
"Offset":2,
"Addr":12288,
"Length":12,
"PreLabel":"",
"IsRelative":true},
{
"Offset":26,
"Addr":-1025,
"Length":-1024,
"PreLabel":"",
"IsRelative":false},
{
"Offset":58,
"Addr":16384,
"Length":74,
"PreLabel":"",
"IsRelative":false},
{
"Offset":74,
"Addr":20480,
"Length":16,
"PreLabel":"",
"IsRelative":false},
{
"Offset":106,
"Addr":20488,
"Length":16,
"PreLabel":"",
"IsRelative":false},
{
"Offset":132,
"Addr":53248,
"Length":24,
"PreLabel":"",
"IsRelative":true},
{
"Offset":139,
"Addr":57344,
"Length":17,
"PreLabel":"",
"IsRelative":false},
{
"Offset":145,
"Addr":61440,
"Length":-1024,
"PreLabel":"",
"IsRelative":false}],
"TypeHints":[{
"Low":2,
"High":2,
"Hint":"Code"},
{
"Low":74,
"High":74,
"Hint":"Code"},
{
"Low":106,
"High":106,
"Hint":"Code"}],
"StatusFlagOverrides":{
},
"Comments":{
"0":"load address"},
"LongComments":{
},
"Notes":{
},
"UserLabels":{
},
"OperandFormats":{
"0":{
"Length":2,
"Format":"NumericLE",
"SubFormat":"None",
"SymbolRef":null},
"27":{
"Length":25,
"Format":"StringNullTerm",
"SubFormat":"C64Petscii",
"SymbolRef":null},
"53":{
"Length":2,
"Format":"NumericLE",
"SubFormat":"Address",
"SymbolRef":null},
"55":{
"Length":2,
"Format":"NumericLE",
"SubFormat":"Address",
"SymbolRef":null}},
"LvTables":{
},
"Visualizations":[],
"VisualizationAnimations":[],
"VisualizationSets":{
},
"RelocList":{
},
"DbrValues":{
}}

Binary file not shown.

View File

@ -0,0 +1,96 @@
### 6502bench SourceGen dis65 v1.0 ###
{
"_ContentVersion":5,
"FileDataLength":40,
"FileDataCrc32":-539266680,
"ProjectProps":{
"CpuName":"65816",
"IncludeUndocumentedInstr":false,
"TwoByteBrk":false,
"EntryFlags":32702671,
"AutoLabelStyle":"Simple",
"AnalysisParams":{
"AnalyzeUncategorizedData":true,
"DefaultTextScanMode":"LowHighAscii",
"MinCharsForString":4,
"SeekNearbyTargets":true,
"UseRelocData":false,
"SmartPlpHandling":false,
"SmartPlbHandling":true},
"PlatformSymbolFileIdentifiers":[],
"ExtensionScriptFileIdentifiers":[],
"ProjectSyms":{
}},
"AddressMap":[{
"Offset":0,
"Addr":69632,
"Length":40,
"PreLabel":"",
"IsRelative":true},
{
"Offset":0,
"Addr":135168,
"Length":24,
"PreLabel":"",
"IsRelative":true},
{
"Offset":0,
"Addr":200704,
"Length":16,
"PreLabel":"",
"IsRelative":true},
{
"Offset":32,
"Addr":-1025,
"Length":6,
"PreLabel":"",
"IsRelative":true}],
"TypeHints":[{
"Low":0,
"High":0,
"Hint":"Code"},
{
"Low":32,
"High":32,
"Hint":"Code"}],
"StatusFlagOverrides":{
},
"Comments":{
},
"LongComments":{
},
"Notes":{
},
"UserLabels":{
},
"OperandFormats":{
"34":{
"Length":4,
"Format":"NumericLE",
"SubFormat":"Address",
"SymbolRef":null}},
"LvTables":{
},
"Visualizations":[],
"VisualizationAnimations":[],
"VisualizationSets":{
},
"RelocList":{
},
"DbrValues":{
}}

View File

@ -0,0 +1,97 @@
.cpu "6502"
* = $0000
.word $3000 ;load address
.logical $1000
.logical *+$1000
.logical *+$1000
L3000 bit L3000
_L3003 lda _L3003
and _LD003
jmp _L200C
.here
_L200C bit _L200C
jmp _L1012
.here
_L1012 bit _L1012
jsr _L4000
.logical $0000
.byte $00
.null "Null-term PETSCII string"
.byte $80
.word _L3003
.word _LD003
.byte $80
.here
.logical $4000
_L4000 bit _L4000
bit _L5000
bit _L500F
bit _L500F
nop
jmp _L4020
.logical $5000
_L5000 bit _L5000
bit _L4000
nop
nop
_L5008 bit _L5008
bit _L5017
nop
_L500F rts
.here
_L4020 bit _L4020
bit _L500F
nop
nop
nop
nop
nop
nop
nop
jmp _L4040
.logical $5008
_L5008_0 bit _L5008_0
bit _L5000
nop
_L500F_0 bit _L500F_0
nop
nop
nop
nop
nop
_L5017 rts
.here
_L4040 bit _L4040
bit _L5017
nop
jmp _LD000
.here
.logical *+$bf7e
_LD000 bit _L200C
_LD003 nop
jmp _LE000
.logical $e000
_LE000 bit _LE000
jmp _LF000
.logical $f000
_LF000 bit _LF000
lda _L3003
and _LD003
nop
rts
.here
.here
.here
.here

View File

@ -0,0 +1,97 @@
!cpu 6502
* = $0000
!word $3000 ;load address
!pseudopc $1000 {
!pseudopc *+$1000 {
!pseudopc *+$1000 {
L3000 bit L3000
@L3003 lda @L3003
and @LD003
jmp @L200C
}
@L200C bit @L200C
jmp @L1012
}
@L1012 bit @L1012
jsr @L4000
!pseudopc $0000 {
!byte $00
!pet "Null-term PETSCII string",$00
!byte $80
!word @L3003
!word @LD003
!byte $80
}
!pseudopc $4000 {
@L4000 bit @L4000
bit @L5000
bit @L500F
bit @L500F
nop
jmp @L4020
!pseudopc $5000 {
@L5000 bit @L5000
bit @L4000
nop
nop
@L5008 bit @L5008
bit @L5017
nop
@L500F rts
}
@L4020 bit @L4020
bit @L500F
nop
nop
nop
nop
nop
nop
nop
jmp @L4040
!pseudopc $5008 {
@L5008_0 bit @L5008_0
bit @L5000
nop
@L500F_0 bit @L500F_0
nop
nop
nop
nop
nop
@L5017 rts
}
@L4040 bit @L4040
bit @L5017
nop
jmp @LD000
}
!pseudopc *+$bf7e {
@LD000 bit @L200C
@LD003 nop
jmp @LE000
!pseudopc $e000 {
@LE000 bit @LE000
jmp @LF000
!pseudopc $f000 {
@LF000 bit @LF000
lda @L3003
and @LD003
nop
rts
}
}
}
}

View File

@ -0,0 +1,90 @@
.setcpu "6502"
.org $0000
.word $3000 ;load address
.org $3000
L3000: bit L3000
@L3003: lda @L3003
and @LD003
jmp @L200C
.org $200c
@L200C: bit @L200C
jmp @L1012
.org $1012
@L1012: bit @L1012
jsr @L4000
.org $0000
.byte $00
.byte $ce,$55,$4c,$4c,$2d,$54,$45,$52,$4d,$20,$d0,$c5,$d4,$d3,$c3,$c9
.byte $c9,$20,$53,$54,$52,$49,$4e,$47,$00
.byte $80
.word @L3003
.word @LD003
.byte $80
.org $4000
@L4000: bit @L4000
bit @L5000
bit @L500F
bit @L500F
nop
jmp @L4020
.org $5000
@L5000: bit @L5000
bit @L4000
nop
nop
@L5008: bit @L5008
bit @L5017
nop
@L500F: rts
.org $4020
@L4020: bit @L4020
bit @L500F
nop
nop
nop
nop
nop
nop
nop
jmp @L4040
.org $5008
@L5008_0: bit @L5008_0
bit @L5000
nop
@L500F_0: bit @L500F_0
nop
nop
nop
nop
nop
@L5017: rts
.org $4040
@L4040: bit @L4040
bit @L5017
nop
jmp @LD000
.org $d000
@LD000: bit @L200C
@LD003: nop
jmp @LE000
.org $e000
@LE000: bit @LE000
jmp @LF000
.org $f000
@LF000: bit @LF000
lda @L3003
and @LD003
nop
rts

View File

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

View File

@ -0,0 +1,88 @@
org $0000
dw $3000 ;load address
org $3000
L3000 bit L3000
:L3003 lda :L3003
and :LD003
jmp :L200C
org $200c
:L200C bit :L200C
jmp :L1012
org $1012
:L1012 bit :L1012
jsr :L4000
org $0000
dfb $00
hex ce554c4c2d5445524d20d0c5d4d3c3c9c920535452494e4700
dfb $80
dw :L3003
dw :LD003
dfb $80
org $4000
:L4000 bit :L4000
bit :L5000
bit :L500F
bit :L500F
nop
jmp :L4020
org $5000
:L5000 bit :L5000
bit :L4000
nop
nop
:L5008 bit :L5008
bit :L5017
nop
:L500F rts
org $4020
:L4020 bit :L4020
bit :L500F
nop
nop
nop
nop
nop
nop
nop
jmp :L4040
org $5008
:L5008_0 bit :L5008_0
bit :L5000
nop
:L500F_0 bit :L500F_0
nop
nop
nop
nop
nop
:L5017 rts
org $4040
:L4040 bit :L4040
bit :L5017
nop
jmp :LD000
org $d000
:LD000 bit :L200C
:LD003 nop
jmp :LE000
org $e000
:LE000 bit :LE000
jmp :LF000
org $f000
:LF000 bit :LF000
lda :L3003
and :LD003
nop
rts

View File

@ -0,0 +1,30 @@
.cpu "65816"
.logical $011000
.logical *+$010000
.logical *+$010000
.as
.xs
L31000 lda L31000
_L31004 lda _L31004
and _L1101F
jml _L21010
.here
_L21010 lda _L21010
jml _L11018
.here
_L11018 lda _L11018
jmp _L11026
_L1101F .byte $80
.logical *-$011020
.byte $ea
.byte $60
.dword _L31004
.here
_L11026 nop
rts
.here

View File

@ -0,0 +1,6 @@
;ACME can't handle 65816 code that lives outside bank zero
* = $0000
!pseudopc $031000 {
!hex af001003af0410032f1f10015c101002af1010025c181001af1810014c261080
!hex ea6004100300ea60
}

View File

@ -0,0 +1,27 @@
.setcpu "65816"
.org $031000
.a8
.i8
L31000: lda L31000
@L31004: lda @L31004
and f:@L1101F
jml @L21010
.org $021010
@L21010: lda @L21010
jml @L11018
.org $011018
@L11018: lda @L11018
jmp @L11026 & $ffff
@L1101F: .byte $80
.org $0000
.byte $ea
.byte $60
.dword @L31004
.org $011026
@L11026: nop
rts

View File

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

View File

@ -0,0 +1,24 @@
org $031000
L31000 ldal L31000
:L31004 ldal :L31004
andl :L1101F
jml :L21010
org $021010
:L21010 ldal :L21010
jml :L11018
org $011018
:L11018 ldal :L11018
jmp :L11026
:L1101F dfb $80
org $0000
dfb $ea
dfb $60
adrl :L31004
org $011026
:L11026 nop
rts

View File

@ -0,0 +1,137 @@
; Copyright 2021 faddenSoft. All Rights Reserved.
; See the LICENSE.txt file for distribution terms (Apache 2.0).
;
; Test nested address regions.
;
; Assembler: 64tass
; % tass64 --ascii --case-sensitive --nostart 20250-nested-regions.S
.cpu "6502"
* = $1000
START
.word $3000
; NOTE: leave PRG header as a 2-byte hole with no explicit region
; Start with 3 nested regions that share a starting address.
; EDIT: create region starting at $1000, ending at REGION_END
; EDIT: make all 3 of these relative (especially the first one, which follows non-addr)
; EDIT: create these as fixed-end regions; first one spans full file
.logical $1000
.logical $2000
.logical $3000
part3k bit part3k
early lda early
and late
jmp part2k
.here ;$3000; now $20xx
part2k bit part2k
jmp part1k
.here ;$2000; now $10xx
part1k bit part1k
jsr overhole ;let execution try to fall into non-addr
REGION1_END
; EDIT: next chunk is a *floating* non-addr region
brk
.null "Null-term PETSCII string"
.byte $80
.word early
.word late
.byte $80
; 4000-40xx, with two overlapping regions in the $5000 area. Tests order
; of resolution and descent into children. (This is mostly tested internally
; by the AddressMap unit tests, so we don't need to go crazy here.)
.logical $4000
overhole
a4000 bit a4000
bit b5000
bit b500f ;CHECK: resolves to b500f
bit c500f ;CHECK: also resolves to b500f
nop
jmp a4020
; EDIT: set region, add code start tag
.logical $5000
b5000 bit b5000
b5003 bit a4000
nop
nop
b5008 bit b5008 ;CHECK: resolves locally
bit c5017 ;CHECK: resolves to other segment
nop
b500f rts
.here ;$5000, now at $4010
a4020 bit a4020
bit c500f
nop
nop
nop
nop
nop
nop
nop
jmp a4040
; EDIT: set region, add code start tag
.logical $5008
c5008 bit c5008 ;CHECK: resolves locally
bit b5000 ;CHECK: resolves to other segment
nop
c500f bit c500f
nop
nop
nop
nop
nop
c5017 rts
.here ;$5008
a4040 bit a4040
bit c5017
nop
jmp tailend
.here ;$4000
; EDIT: create region starting at $d000, ending at REGION2_END; relative
.logical $d000
tailend
partdk bit part2k
late nop
jmp partek
; EDIT: create
.logical $e000
partek bit partek
jmp partfk
; EDIT: create, make it floating
.logical $f000
partfk bit partfk
lda early
and late
end
nop
rts
REGION2_END
.here ;$f000
.here ;$e000
.here ;$d000
.here ;$1000

View File

@ -0,0 +1,47 @@
; Copyright 2021 faddenSoft. All Rights Reserved.
; See the LICENSE.txt file for distribution terms (Apache 2.0).
;
; Test nested address regions.
;
; Assembler: 64tass
; % tass64 --ascii --case-sensitive --nostart 20252-nested-regions.S
.cpu "65816"
* = $081000
START
; Start with 3 nested regions that share a starting address.
; EDIT: create these as fixed-end regions; first one spans full file
.logical $011000
.logical $021000
.logical $031000
part3k lda @lpart3k
early lda early
and late
jmp part2k
.here ;031000
part2k lda @lpart2k
jmp part1k
.here ;021000
part1k lda @lpart1k
jmp end
late .byte $80
; EIDT: make this a non-addressable region
; EDIT: put a code start tag here
tag nop
rts
.dword early
end
nop
rts
REGION2_END
.here ;$011000

View File

@ -33,25 +33,47 @@ limitations under the License.
<system:String x:Key="str_HdrCreate">Creating new address region</system:String>
<system:String x:Key="str_HdrEdit">Editing existing address region:</system:String>
<system:String x:Key="str_OptEditAsIsSummary">
Edit only.
</system:String>
<system:String x:Key="str_OptEditAsIs">
Edit the region's attributes. Region length will not be changed.
</system:String>
<system:String x:Key="str_OptEditAndFixSummary">
Set fixed size.
</system:String>
<system:String x:Key="str_OptEditAndFix">
Edit the region's attributes, and convert the floating end to a fixed end.
</system:String>
<system:String x:Key="str_OptResizeSummary">
Resize.
</system:String>
<system:String x:Key="str_OptResize">
Edit the region's attributes, and resize it to the selection. The new
end offset will be {0}, for a length of {1}.
</system:String>
<system:String x:Key="str_OptResizeFail">
Cannot resize to the selection.
Resize not available: cannot resize to the selection. {0}
</system:String>
<system:String x:Key="str_CreateFixedSummary">
Create fixed.
</system:String>
<system:String x:Key="str_CreateFixed">
Create a new region, with a fixed end point. The region will start at {0}
and have a length of {1}.
</system:String>
<system:String x:Key="str_CreateFixedAlreadyFixed">
Create fixed not available: can't convert to fixed end point because it's already fixed.
</system:String>
<system:String x:Key="str_CreateFixedFail">
Unable to create a new region with a fixed end point here.
Create fixed not available: unable to create a new region with a fixed end point here.
</system:String>
<system:String x:Key="str_CreateFixedFailStraddle">
Create fixed not available: unable to create a new region with a fixed end point
here, because it would straddle an existing region.
</system:String>
<system:String x:Key="str_CreateFloatingSummary">
Create floating.
</system:String>
<system:String x:Key="str_CreateFloating">
Create a new region, with a floating end point. The region will start at {0}
@ -59,13 +81,13 @@ limitations under the License.
may change as other regions are added and removed.
</system:String>
<system:String x:Key="str_CreateFloatingFail">
Unable to create a new region with a floating end point here.
Create floating not available: unable to create a new region with a floating end point here.
</system:String>
<system:String x:Key="str_ErrInternal">Internal error.</system:String>
<system:String x:Key="str_ErrInvalidValue">Internal error (invalid value).</system:String>
<system:String x:Key="str_ErrOverlapExisting">
The new region has the same start offset and length as an existing region.
The new region would have the same start offset and length as an existing region.
</system:String>
<system:String x:Key="str_ErrOverlapFloating">
The start offset of the new region is the same as the start offset of an existing
@ -77,7 +99,7 @@ limitations under the License.
</Window.Resources>
<StackPanel Margin="8">
<TextBlock Text="{Binding OperationStr, FallbackValue=Editing existing region}"/>
<TextBlock Text="{Binding OperationStr, FallbackValue=Editing existing address region:}"/>
<Grid Visibility="{Binding ShowExistingRegion, Converter={StaticResource BoolToVis}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@ -114,7 +136,7 @@ limitations under the License.
IsEnabled="{Binding EnableOption1}"
IsChecked="{Binding CheckOption1}">
<RadioButton.Content>
<TextBlock TextWrapping="Wrap" Text="{Binding Option1Str, FallbackValue=Option 1}"/>
<TextBlock Name="option1TextBlock" TextWrapping="Wrap" Text="Option 1"/>
</RadioButton.Content>
</RadioButton>
<RadioButton Name="radioOption2" Margin="0,4,0,0"
@ -122,7 +144,7 @@ limitations under the License.
IsEnabled="{Binding EnableOption2}"
IsChecked="{Binding CheckOption2}">
<RadioButton.Content>
<TextBlock TextWrapping="Wrap" Text="{Binding Option2Str, FallbackValue=Option 2}"/>
<TextBlock Name="option2TextBlock" TextWrapping="Wrap" Text="Option 2"/>
</RadioButton.Content>
</RadioButton>
@ -151,8 +173,8 @@ limitations under the License.
</TextBox>
</StackPanel>
<TextBlock Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00." Margin="0,4,0,0"/>
<TextBlock Text="• Enter 'NA' if the region is non-addressable."/>
<TextBlock Text="• Enter 16-bit or 24-bit address, e.g. $1000 or 01/be00" Margin="0,4,0,0"/>
<TextBlock Text="• Enter 'NA' to mark the region non-addressable"/>
</StackPanel>
<StackPanel IsEnabled="{Binding EnableAttributeControls}">
@ -173,9 +195,9 @@ limitations under the License.
<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 not appear if parent region is non-addressable."/>
<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 not appear if parent region is non-addressable"/>
</StackPanel>
</GroupBox>
</StackPanel>

View File

@ -18,7 +18,8 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using Asm65;
using CommonUtil;
@ -109,18 +110,6 @@ namespace SourceGen.WpfGui {
}
private bool mCheckOption2;
public string Option1Str {
get { return mOption1Str; }
set { mOption1Str = value; OnPropertyChanged(); }
}
private string mOption1Str;
public string Option2Str {
get { return mOption2Str; }
set { mOption2Str = value; OnPropertyChanged(); }
}
private string mOption2Str;
/// <summary>
/// Address at which a pre-label would be placed. This is determined by the parent
/// region, so its value is fixed.
@ -279,6 +268,11 @@ namespace SourceGen.WpfGui {
CheckOption1 = true;
EnableAttributeControls = true;
string option1Summ;
string option1Msg;
string option2Summ;
string option2Msg;
if (curRegion != null) {
// Editing an existing region.
CanDeleteRegion = true;
@ -304,14 +298,18 @@ namespace SourceGen.WpfGui {
mResultEntry1 = new AddressMap.AddressMapEntry(curRegion.Offset,
curRegion.Length, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
Option1Str = (string)FindResource("str_OptEditAsIs");
Option2Str = (string)FindResource("str_OptEditAndFix");
option1Summ = (string)FindResource("str_OptEditAsIsSummary");
option1Msg = (string)FindResource("str_OptEditAsIs");
if (curRegion.IsFloating) {
option2Summ = (string)FindResource("str_OptEditAndFixSummary");
option2Msg = (string)FindResource("str_OptEditAndFix");
mResultEntry2 = new AddressMap.AddressMapEntry(curRegion.Offset,
curRegion.ActualLength, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
} else {
option2Summ = string.Empty;
option2Msg = (string)FindResource("str_CreateFixedAlreadyFixed");
mResultEntry2 = null;
EnableOption2 = false; // show it, but disabled
}
@ -328,11 +326,13 @@ namespace SourceGen.WpfGui {
curRegion.Length, curRegion.Address, curRegion.PreLabel,
curRegion.IsRelative);
option1Summ = (string)FindResource("str_OptResizeSummary");
string fmt = (string)FindResource("str_OptResize");
Option1Str = string.Format(fmt,
option1Msg = string.Format(fmt,
mFormatter.FormatOffset24(curRegion.Offset + selectionLen - 1),
FormatLength(selectionLen));
Option2Str = (string)FindResource("str_OptEditAsIs");
option2Summ = (string)FindResource("str_OptEditAsIsSummary");
option2Msg = (string)FindResource("str_OptEditAsIs");
Debug.Assert(selectionLen > 0);
AddressMap.AddResult ares;
@ -340,7 +340,9 @@ namespace SourceGen.WpfGui {
curRegion.Address, out ares);
if (ares != AddressMap.AddResult.Okay) {
// Can't resize the new region, so disable that option (still visible).
Option1Str = (string)FindResource("str_OptResizeFail");
option1Summ = string.Empty;
string fmta = (string)FindResource("str_OptResizeFail");
option1Msg = string.Format(fmta, GetErrorString(ares));
EnableOption1 = false;
CheckOption2 = true;
}
@ -378,13 +380,19 @@ namespace SourceGen.WpfGui {
mResultEntry1 = new AddressMap.AddressMapEntry(newEntry.Offset,
newRegion1.ActualLength, newEntry.Address, string.Empty, false);
option1Summ = (string)FindResource("str_CreateFixedSummary");
string fmt = (string)FindResource("str_CreateFixed");
Option1Str = string.Format(fmt,
option1Msg = string.Format(fmt,
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion1.ActualLength));
mPreLabelAddress = newRegion1.PreLabelAddress;
} else {
Option1Str = (string)FindResource("str_CreateFixedFail");
option1Summ = string.Empty;
if (ares1 == AddressMap.AddResult.StraddleExisting) {
option1Msg = (string)FindResource("str_CreateFixedFailStraddle");
} else {
option1Msg = (string)FindResource("str_CreateFixedFail");
}
CheckOption2 = true;
EnableOption1 = false;
}
@ -392,13 +400,15 @@ namespace SourceGen.WpfGui {
mResultEntry2 = new AddressMap.AddressMapEntry(newEntry.Offset,
AddressMap.FLOATING_LEN, newEntry.Address, string.Empty, false);
option2Summ = (string)FindResource("str_CreateFloatingSummary");
string fmt = (string)FindResource("str_CreateFloating");
Option2Str = string.Format(fmt,
option2Msg = string.Format(fmt,
mFormatter.FormatOffset24(newEntry.Offset),
FormatLength(newRegion2.ActualLength));
mPreLabelAddress = newRegion2.PreLabelAddress;
} else {
Option2Str = (string)FindResource("str_CreateFloatingFail");
option2Summ = string.Empty;
option2Msg = (string)FindResource("str_CreateFloatingFail");
CheckOption1 = true;
CheckOption2 = false; // required for some reason
EnableOption2 = false;
@ -412,6 +422,20 @@ namespace SourceGen.WpfGui {
SetErrorString(ares1);
}
}
TextBlock tb1 = option1TextBlock;
tb1.Inlines.Clear();
if (!string.IsNullOrEmpty(option1Summ)) {
tb1.Inlines.Add(new Run(option1Summ + " ") { FontWeight = FontWeights.Bold });
}
tb1.Inlines.Add(option1Msg);
TextBlock tb2 = option2TextBlock;
tb2.Inlines.Clear();
if (!string.IsNullOrEmpty(option2Summ)) {
tb2.Inlines.Add(new Run(option2Summ + " ") { FontWeight = FontWeights.Bold });
}
tb2.Inlines.Add(option2Msg);
}
private string FormatLength(int len) {
@ -442,7 +466,7 @@ namespace SourceGen.WpfGui {
return newRegion;
}
private void SetErrorString(AddressMap.AddResult result) {
private string GetErrorString(AddressMap.AddResult result) {
string rsrc;
switch (result) {
case AddressMap.AddResult.InternalError:
@ -458,15 +482,18 @@ namespace SourceGen.WpfGui {
rsrc = "str_ErrOverlapFloating";
break;
case AddressMap.AddResult.StraddleExisting:
rsrc = "str_ErrStraddelExisting";
rsrc = "str_ErrStraddleExisting";
break;
default:
Debug.Assert(false);
rsrc = "str_ErrInternal";
break;
}
return(string)FindResource(rsrc); // throws exception on failure
}
ErrorMessageStr = (string)FindResource(rsrc); // throws exception on failure
private void SetErrorString(AddressMap.AddResult result) {
ErrorMessageStr = GetErrorString(result);
ShowErrorMessage = true;
}

View File

@ -51,6 +51,8 @@ limitations under the License.
<TextBlock Name="firstLetterLabel" Text="• Must start with a letter or underscore"/>
<TextBlock Name="validCharsLabel" Text="• Valid characters are ASCII letters, numbers, and underscore"/>
<TextBlock Name="notDuplicateLabel" Text="• Must not be a duplicate of an existing label"/>
<TextBlock Visibility="{Binding NonAddrWarningVis}" Foreground="Blue"
Text="• NOTE: label is in a non-addressable data area"/>
</StackPanel>
<GroupBox Grid.Column="0" Grid.Row="1" Header="Label Type" Padding="2,4" Margin="0,12,0,0">

View File

@ -121,6 +121,8 @@ namespace SourceGen.WpfGui {
set { mIsExportedEnabled = value; OnPropertyChanged(); }
}
public Visibility NonAddrWarningVis { get; private set; }
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
@ -142,6 +144,12 @@ namespace SourceGen.WpfGui {
string fmt = (string)FindResource("str_NonUniqueLocalFmt");
NonUniqueButtonLabel = string.Format(fmt, mFormatter.NonUniqueLabelPrefix);
if (mAddress == Address.NON_ADDR) {
NonAddrWarningVis = Visibility.Visible;
} else {
NonAddrWarningVis = Visibility.Collapsed;
}
}
private void Window_Loaded(object sender, RoutedEventArgs e) {

View File

@ -425,12 +425,12 @@ limitations under the License.
<MenuItem Command="{StaticResource ViewAddressMapCmd}"/>
</MenuItem>
<MenuItem Name="actionsMenu" Header="_Actions">
<MenuItem Command="{StaticResource EditAddressCmd}"/>
<MenuItem Command="{StaticResource EditStatusFlagsCmd}"/>
<MenuItem Command="{StaticResource EditLabelCmd}"/>
<MenuItem Command="{StaticResource EditOperandCmd}"/>
<MenuItem Command="{StaticResource EditLabelCmd}"/>
<MenuItem Command="{StaticResource EditCommentCmd}" InputGestureText="Ctrl+;"/>
<MenuItem Command="{StaticResource EditLongCommentCmd}"/>
<MenuItem Command="{StaticResource EditAddressCmd}"/>
<MenuItem Command="{StaticResource EditStatusFlagsCmd}"/>
<MenuItem Command="{StaticResource EditDataBankCmd}"/>
<MenuItem Command="{StaticResource EditNoteCmd}"/>
<MenuItem Command="{StaticResource EditProjectSymbolCmd}"/>