1
0
mirror of https://github.com/fadden/6502bench.git synced 2025-02-07 14:31:00 +00:00

Address region isolation, part 3 (of 3)

If an address resolves to a user label in an isolated region, we
don't want to use it.  However, we still want to try to match it
to a project/platform symbol.

For example, suppose the isolated code wants to reference address
$1C00, which is a memory-mapped I/O location in one area, but a
regular bunch of code in the other.  We don't want it to map to
the regular code, but we do want it to resolve to our table of
platform I/O addresses.

We now handle this correctly.  The regression test has been updated
to check this.  The current implementation does a linear scan through
the symbol table, but I'm hoping this is not a common situation.

The reference manual has been updated to describe the new feature.
This commit is contained in:
Andy McFadden 2024-05-21 14:14:32 -07:00
parent e137db2b5c
commit 7a7ff44d3a
13 changed files with 376 additions and 101 deletions

View File

@ -1445,12 +1445,12 @@ namespace SourceGen {
//
// It might seem unwise to examine the full symbol table, because it has
// non-project non-platform symbols in it. However, any matching user
// labels would have been applied already. Also, we want to ensure that
// conflicting user labels take precedence, e.g. creating a user label "COUT"
// will prevent a platform symbol with the same name from being visible.
// Using the full symbol table is potentially a tad less efficient than
// looking for a match exclusively in project/platform symbols, but it's
// the correct thing to do.
// labels would have been applied already (unless blocked by address region
// isolation). Also, we want to ensure that conflicting user labels take
// precedence, e.g. creating a user label "COUT" will prevent a platform
// symbol with the same name from being visible. Using the full symbol
// table is potentially a tad less efficient than looking for a match
// exclusively in project/platform symbols, but it's the correct thing to do.
OpDef op = CpuDef.GetOpDef(FileData[offset]);
accType = op.MemEffect;
address = attr.OperandAddress;
@ -1508,7 +1508,11 @@ namespace SourceGen {
} else {
Debug.WriteLine("GeneratePlatform not applying at +" +
offset.ToString("x6") + ": " + sym);
sym = null;
// Do a secondary scan, looking only at project/platform/pre-labels.
sym = SymbolTable.FindProjPlatPreByAddress(address, accType);
if (sym != null) {
Debug.WriteLine(" ...found matching proj/plat: " + sym);
}
}
}

View File

@ -2,7 +2,7 @@
{
"_ContentVersion":6,
"FileDataLength":1537,
"FileDataCrc32":950074699,
"FileDataCrc32":-1543685807,
"ProjectProps":{
"CpuName":"6502",
"IncludeUndocumentedInstr":false,
@ -21,7 +21,90 @@
"PlatformSymbolFileIdentifiers":[],
"ExtensionScriptFileIdentifiers":[],
"ProjectSyms":{
}},
"IN_1":{
"DataDescriptor":{
"Length":256,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"",
"HasWidth":true,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"IN_1",
"Value":36864,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"},
"IN_2":{
"DataDescriptor":{
"Length":256,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"",
"HasWidth":true,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"IN_2",
"Value":40960,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"},
"IN_3":{
"DataDescriptor":{
"Length":256,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"",
"HasWidth":true,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"IN_3",
"Value":45056,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"},
"IN_4":{
"DataDescriptor":{
"Length":256,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"",
"HasWidth":true,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"IN_4",
"Value":49152,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"},
"THREE_K":{
"DataDescriptor":{
"Length":1,
"Format":"NumericLE",
"SubFormat":"Hex",
"SymbolRef":null},
"Comment":"project symbol test",
"HasWidth":false,
"Direction":"ReadWrite",
"MultiMask":null,
"Label":"THREE_K",
"Value":12288,
"Source":"Project",
"Type":"ExternalAddr",
"LabelAnno":"None"}}},
"AddressMap":[{
"Offset":0,
@ -303,6 +386,13 @@
"Value":2072,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"},
"1319":{
"Label":"_checkit",
"Value":49166,
"Source":"User",
"Type":"GlobalAddr",
"LabelAnno":"None"}},
"OperandFormats":{

View File

@ -1,26 +1,32 @@
.cpu "6502"
THREE_K = $3000 ;project symbol test
IN_1 = $9000
IN_2 = $a000
IN_3 = $b000
IN_4 = $c000
* = $0800
jsr region1
jsr region2
jsr $3000
jsr THREE_K
jsr $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda L9005
lda LA008
lda IN_3+11
lda IN_4+14
self .byte $ad,$ea
L081A nop
jsr altbnk1
jsr altbnk2
jmp done
altbnk1 bit $c080
altbnk1 bit $ffc0
lda self+1
bne L081A
rts
.logical *+$0000
altbnk2 bit $c080
altbnk2 bit $ffc0
lda $0819
bne $081a
ldx $081b
@ -106,7 +112,7 @@ inner1_pre
.logical $9000
inner1 ldx inner1
ldy #$aa
ldy finish1
L9005 ldy finish1
ldy finish2
ldy $302b
ldy $402b
@ -116,12 +122,12 @@ inner1 ldx inner1
finish1 ldy finish1
ldx region1
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda IN_3
lda IN_4
pla
rts
@ -146,7 +152,7 @@ inner2_pre
inner2 ldx inner2
ldy #$aa
ldy $102b
ldy finish2
LA008 ldy finish2
ldy $302b
ldy $402b
rts
@ -155,12 +161,16 @@ inner2 ldx inner2
finish2 ldy finish2
ldx $1000
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda IN_1
lda inner2
lda IN_3
lda IN_4
pla
rts
.fill 196,$00
.fill 184,$00
.here
.logical $3000
@ -192,10 +202,14 @@ finish3 ldy finish3
ldx region2
ldx region3
ldx $4000
lda inner1
lda inner2
lda inner3
lda IN_4
pla
rts
.fill 196,$00
.fill 184,$00
.here
.logical $4000
@ -218,19 +232,23 @@ inner4 ldx inner4
ldy $102b
ldy $202b
ldy $302b
ldy finish4
X_checkit ldy finish4
rts
.here
finish4 ldy finish4
ldx $1000
ldx $2000
ldx $3000
ldx THREE_K
ldx region4
lda IN_1
lda IN_2
lda IN_3
lda inner4
pla
rts
.fill 196,$00
.fill 184,$00
.here
.logical $0000
.byte $ff

View File

@ -1,26 +1,32 @@
!cpu 6502
THREE_K = $3000 ;project symbol test
IN_1 = $9000
IN_2 = $a000
IN_3 = $b000
IN_4 = $c000
* = $0800
jsr region1
jsr region2
jsr $3000
jsr THREE_K
jsr $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda L9005
lda LA008
lda IN_3+11
lda IN_4+14
self !byte $ad,$ea
L081A nop
jsr altbnk1
jsr altbnk2
jmp done
altbnk1 bit $c080
altbnk1 bit $ffc0
lda self+1
bne L081A
rts
!pseudopc *+$0000 {
altbnk2 bit $c080
altbnk2 bit $ffc0
lda $0819
bne $081a
ldx $081b
@ -106,7 +112,7 @@ inner1_pre
!pseudopc $9000 {
inner1 ldx inner1
ldy #$aa
ldy finish1
L9005 ldy finish1
ldy finish2
ldy $302b
ldy $402b
@ -116,12 +122,12 @@ inner1 ldx inner1
finish1 ldy finish1
ldx region1
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda IN_3
lda IN_4
pla
rts
@ -146,7 +152,7 @@ inner2_pre
inner2 ldx inner2
ldy #$aa
ldy $102b
ldy finish2
LA008 ldy finish2
ldy $302b
ldy $402b
rts
@ -155,12 +161,16 @@ inner2 ldx inner2
finish2 ldy finish2
ldx $1000
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda IN_1
lda inner2
lda IN_3
lda IN_4
pla
rts
!fill 196,$00
!fill 184,$00
}
!pseudopc $3000 {
@ -192,10 +202,14 @@ finish3 ldy finish3
ldx region2
ldx region3
ldx $4000
lda inner1
lda inner2
lda inner3
lda IN_4
pla
rts
!fill 196,$00
!fill 184,$00
}
!pseudopc $4000 {
@ -218,19 +232,23 @@ inner4 ldx inner4
ldy $102b
ldy $202b
ldy $302b
ldy finish4
_checkit ldy finish4
rts
}
finish4 ldy finish4
ldx $1000
ldx $2000
ldx $3000
ldx THREE_K
ldx region4
lda IN_1
lda IN_2
lda IN_3
lda inner4
pla
rts
!fill 196,$00
!fill 184,$00
}
!pseudopc $0000 {
!byte $ff

View File

@ -1,26 +1,32 @@
.setcpu "6502"
THREE_K = $3000 ;project symbol test
IN_1 = $9000
IN_2 = $a000
IN_3 = $b000
IN_4 = $c000
.org $0800
jsr region1
jsr region2
jsr $3000
jsr THREE_K
jsr $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda L9005
lda LA008
lda IN_3+11
lda IN_4+14
self: .byte $ad,$ea
L081A: nop
jsr altbnk1
jsr altbnk2
jmp done
altbnk1: bit $c080
altbnk1: bit $ffc0
lda self+1
bne L081A
rts
.org *+$0000
altbnk2: bit $c080
altbnk2: bit $ffc0
lda $0819
bne $081a
ldx $081b
@ -105,7 +111,7 @@ inner1_pre:
.org $9000
inner1: ldx inner1
ldy #$aa
ldy finish1
L9005: ldy finish1
ldy finish2
ldy $302b
ldy $402b
@ -115,12 +121,12 @@ inner1: ldx inner1
finish1: ldy finish1
ldx region1
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda IN_3
lda IN_4
pla
rts
@ -144,7 +150,7 @@ inner2_pre:
inner2: ldx inner2
ldy #$aa
ldy $102b
ldy finish2
LA008: ldy finish2
ldy $302b
ldy $402b
rts
@ -153,12 +159,16 @@ inner2: ldx inner2
finish2: ldy finish2
ldx $1000
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda IN_1
lda inner2
lda IN_3
lda IN_4
pla
rts
.res 196,$00
.res 184,$00
.org $3000
region3: lda region3
@ -189,10 +199,14 @@ finish3: ldy finish3
ldx region2
ldx region3
ldx $4000
lda inner1
lda inner2
lda inner3
lda IN_4
pla
rts
.res 196,$00
.res 184,$00
.org $4000
region4: lda region4
@ -214,18 +228,22 @@ inner4: ldx inner4
ldy $102b
ldy $202b
ldy $302b
ldy finish4
_checkit: ldy finish4
rts
.org $402b
finish4: ldy finish4
ldx $1000
ldx $2000
ldx $3000
ldx THREE_K
ldx region4
lda IN_1
lda IN_2
lda IN_3
lda inner4
pla
rts
.res 196,$00
.res 184,$00
.org $0000
.byte $ff

View File

@ -1,25 +1,31 @@
THREE_K equ $3000 ;project symbol test
IN_1 equ $9000
IN_2 equ $a000
IN_3 equ $b000
IN_4 equ $c000
org $0800
jsr region1
jsr region2
jsr $3000
jsr THREE_K
jsr $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda L9005
lda LA008
lda IN_3+11
lda IN_4+14
self dfb $ad,$ea
L081A nop
jsr altbnk1
jsr altbnk2
jmp done
altbnk1 bit $c080
altbnk1 bit $ffc0
lda self+1
bne L081A
rts
org *+$0000
altbnk2 bit $c080
altbnk2 bit $ffc0
lda $0819
bne $081a
ldx $081b
@ -104,7 +110,7 @@ inner1_pre
org $9000
inner1 ldx inner1
ldy #$aa
ldy finish1
L9005 ldy finish1
ldy finish2
ldy $302b
ldy $402b
@ -114,12 +120,12 @@ inner1 ldx inner1
finish1 ldy finish1
ldx region1
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda inner1
lda inner2
lda $b000
lda $c000
lda IN_3
lda IN_4
pla
rts
@ -143,7 +149,7 @@ inner2_pre
inner2 ldx inner2
ldy #$aa
ldy $102b
ldy finish2
LA008 ldy finish2
ldy $302b
ldy $402b
rts
@ -152,12 +158,16 @@ inner2 ldx inner2
finish2 ldy finish2
ldx $1000
ldx region2
ldx $3000
ldx THREE_K
ldx $4000
lda IN_1
lda inner2
lda IN_3
lda IN_4
pla
rts
ds 196
ds 184
org $3000
region3 lda region3
@ -188,10 +198,14 @@ finish3 ldy finish3
ldx region2
ldx region3
ldx $4000
lda inner1
lda inner2
lda inner3
lda IN_4
pla
rts
ds 196
ds 184
org $4000
region4 lda region4
@ -213,18 +227,22 @@ inner4 ldx inner4
ldy $102b
ldy $202b
ldy $302b
ldy finish4
_checkit ldy finish4
rts
org $402b
finish4 ldy finish4
ldx $1000
ldx $2000
ldx $3000
ldx THREE_K
ldx region4
lda IN_1
lda IN_2
lda IN_3
lda inner4
pla
rts
ds 196
ds 184
org $0000
dfb $ff

View File

@ -43,7 +43,7 @@ a way to tell the test harness to override the default. We do this by
creating project symbols:
| Name | Value | Description
| -------------------------- | ----- | -------------------------------------------|
| -------------------------- | ----- | -----------------------------------------|
| __ENABLE_LABEL_NEWLINE | any | Puts long labels on their own line |
| __ENABLE_ALL_LABEL_NEWLINE | any | Puts all labels on their own line |
| __ENABLE_CYCLE_COUNTS | any | Adds cycle count to end-of-line comments |
@ -90,6 +90,7 @@ If you want to add or update a test, follow these steps:
into the Expected directory, replacing any existing copies.
4. Run the test harness. This should now report success, and will
remove the tmpNNNNN directory.
5. Run "git diff" to verify that the changes match your expectations.
Be sure to have the version of the cross-assembler identified at the top
of this document configured.

View File

@ -14,16 +14,21 @@
; regions 1-4.
;
* = $0800
BANK = $c080
BANK = $ffc0
; EDIT: create project symbol THREE_K for $3000
jsr region1
jsr region2
jsr region3
jsr region4
lda inner1
lda inner2
lda inner3
lda inner4
; EDIT: create project symbols for $8000/9000/a000/b000, len=256
; These should resolve to the user labels (for 1/2) or the project
; symbols (for 3/4). We use an offset from the base address to ensure
; that the range is being taken into account.
lda inner1a
lda inner2a
lda inner3a
lda inner4a
; Local reference, can use "do not follow" to avoid mid-instruction branch.
self lda $eaea
@ -91,7 +96,7 @@ inner1_pre ;*
.logical region1+$8000 ;*
inner1 ldx inner1 ;*
ldy #$aa
ldy finish1
inner1a ldy finish1
ldy finish2
ldy finish3
ldy finish4
@ -137,7 +142,7 @@ inner2_pre ;*
inner2 ldx inner2 ;*
ldy #$aa
ldy finish1
ldy finish2
inner2a ldy finish2
ldy finish3
ldy finish4
rts
@ -149,6 +154,10 @@ finish2 ldy finish2 ;*
ldx region2
ldx region3
ldx region4
lda inner1
lda inner2
lda inner3
lda inner4
pla
rts
@ -179,7 +188,7 @@ inner3 ldx inner3 ;*
ldy #$aa
ldy finish1
ldy finish2
ldy finish3
inner3a ldy finish3
ldy finish4
rts
inner3_end ;*
@ -190,6 +199,10 @@ finish3 ldy finish3 ;*
ldx region2
ldx region3
ldx region4
lda inner1
lda inner2
lda inner3
lda inner4
pla
rts
@ -221,7 +234,7 @@ inner4 ldx inner4 ;*
ldy finish1
ldy finish2
ldy finish3
ldy finish4
inner4a ldy finish4
rts
inner4_end ;*
.endlogical
@ -231,6 +244,10 @@ finish4 ldy finish4 ;*
ldx region2
ldx region3
ldx region4
lda inner1
lda inner2
lda inner3
lda inner4
pla
rts

View File

@ -523,6 +523,7 @@ namespace SourceGen {
/// variables.
/// </summary>
/// <param name="addr">Address to find.</param>
/// <param name="effect">Memory effect type to match against.</param>
/// <returns>First matching symbol found, or null if nothing matched.</returns>
public Symbol FindNonVariableByAddress(int addr, OpDef.MemoryEffect effect) {
bool tryRead, tryWrite;
@ -540,6 +541,7 @@ namespace SourceGen {
return null;
}
// The mSymbolsBy{Read,Write}Address tables don't include local vars or constants.
Symbol sym = null;
if (tryRead) {
mSymbolsByReadAddress.TryGetValue(addr, out sym);
@ -549,11 +551,10 @@ namespace SourceGen {
}
if (sym == null) {
// Nothing matched, check the match groups.
// Nothing matched, check the mask groups.
foreach (KeyValuePair<DefSymbol.MultiAddressMask, MaskGroup> kvp in mMaskGroups) {
DefSymbol.MultiAddressMask multiMask = kvp.Key;
if ((addr & multiMask.CompareMask) == multiMask.CompareValue) {
MaskGroup group = kvp.Value;
DefSymbol defSym = kvp.Value.Find(addr, tryRead, tryWrite);
if (defSym != null) {
sym = defSym;
@ -565,6 +566,53 @@ namespace SourceGen {
return sym;
}
/// <summary>
/// Searches the table for project/platform symbols with matching address values. Ignores
/// constants and variables.
/// </summary>
/// <param name="addr">Address to find.</param>
/// <param name="effect">Memory effect type to match against.</param>
/// <returns>First matching symbol found, or null if nothing matched.</returns>
public Symbol FindProjPlatPreByAddress(int addr, OpDef.MemoryEffect effect) {
bool tryRead, tryWrite;
if (effect == OpDef.MemoryEffect.Read) {
tryRead = true;
tryWrite = false;
} else if (effect == OpDef.MemoryEffect.Write) {
tryRead = false;
tryWrite = true;
} else if (effect == OpDef.MemoryEffect.ReadModifyWrite ||
effect == OpDef.MemoryEffect.None) {
tryRead = tryWrite = true;
} else {
Debug.Assert(false);
return null;
}
// We don't have a pair of tables for this, so just do a linear walk through
// the symbol table. This is inefficient, but the current use case is very rare.
foreach (KeyValuePair<string,Symbol> kvp in mSymbols) {
if (!(kvp.Value is DefSymbol)) {
continue;
}
DefSymbol defSym = (DefSymbol)kvp.Value;
if (defSym.SymbolSource != Symbol.Source.Project &&
defSym.SymbolSource != Symbol.Source.Platform &&
defSym.SymbolSource != Symbol.Source.AddrPreLabel) {
continue;
}
if (addr >= defSym.Value && addr < defSym.Value + defSym.DataDescriptor.Length) {
if ((tryRead && (defSym.Direction & DefSymbol.DirectionFlags.Read) != 0) ||
(tryWrite && (defSym.Direction & DefSymbol.DirectionFlags.Write) != 0))
{
return defSym;
}
}
}
// Do we need to check the mask groups?
return null;
}
public override string ToString() {
return "SymbolTable: " + mSymbols.Count + " by label, " +
mSymbolsByReadAddress.Count + " by addr(r), " +

View File

@ -322,7 +322,9 @@ once, so there's no reason to treat them as separate projects. Currently,
the best way to deal with this is to concatenate the files into a single
file, and operate on that.</p>
<h2 id="overlap">Overlapping Address Spaces</h2>
<p>Some programs use memory overlays, where multiple parts of the
code run in the same address in RAM. Others use bank switching to access
parts of the program that reside in separate physical RAM or ROM,
@ -383,6 +385,16 @@ it is referring to.</p>
If we hit the top of the tree without finding a match, we conclude
that the reference is to an external address.</p>
<p>The tree search can be pruned with the
"disallow inbound address resolution" and
"disallow outbound address resolution" flags, which can be set in
the address region edit dialog
(<a href="intro-details.html#region-isolation">more info here</a>).
When inbound resolution is disabled,
parents and siblings will not search into a region. When outbound
resolution is disabled, the search will not ascend to the region's parent.
Note that neither option prevents descent into a region's children.</p>
<h2 id="reloc-data">OMF Relocation Dictionaries</h2>

View File

@ -30,9 +30,12 @@ would resolve to address $123456.
If you want to set the region to be non-addressable, enter
"<code>NA</code>".</p>
<p>You can also enter a <a href="intro-details.html#pre-labels">pre-label</a>
or specify that the operand should be formatted as a
<a href="intro-details.html#relative-addr">relative address</a>.
<p>You can also enter a <a href="intro-details.html#pre-labels">pre-label</a>,
specify that the operand should be formatted as a
<a href="intro-details.html#relative-addr">relative address</a>,
or
<a href="intro-details.html#region-isolation">disallow address resolution</a>
across region boundaries.</p>
<p>To delete a region, click the "Delete Region" button.</p>

View File

@ -653,8 +653,36 @@ within the file. The process is straightforward if the address only
appears once, but when overlays cause multiple parts of the file to have
the same address, the operand target may be in different places depending
on where the call is being made from.
The algorithm for resolving addresses is described
in the <a href="advanced.html#overlap">advanced topics</a> section.</p>
The algorithm for resolving addresses is described in the
<a href="advanced.html#overlap">advanced topics</a> section.</p>
<h3 id="region-isolation">Isolation</h3>
<p>Code in regions that have been relocated will usually be able to
access code and data in other regions. However, sometimes code is
destined to be executed in an independent address space, such as a
disk drive controller, or is part of a bank-switched ROM that puts
multiple regions at the same address. In such cases, you wouldn't
want the address operand of, say, a <code>JSR</code> to resolve to
a symbol in a different region.</p>
<p>The address resolution behavior for a given region can be modified
with the "disallow inbound address resolution" and
"disallow outbound address resolution" checkboxes. The former
prevents other regions from searching for matches in the current
region, and the latter prevents the current region from searching
other regions. (The algorithm is described in the
<a href="advanced.html#overlap">advanced topics</a> section.)</p>
<p>In some rare cases it may be useful to allow resolution to work
in one direction. For example, suppose ROM bank #1 has a table of
<code>JMP</code> instructions for bank #2 that it will copy out to
RAM. The table should be placed into its own address region, with
outbound resolution disabled, because we don't want it to try to
jump to locations in our bank #1 code. It's useful to leave inbound
resolution enabled so that we can reference the table from the code
that copies it to RAM.</p>
<h3 id="non-addr">Non-Addressable Areas</h3>