diff --git a/SourceGen/DataAnalysis.cs b/SourceGen/DataAnalysis.cs
index 7853a80..9f8bb81 100644
--- a/SourceGen/DataAnalysis.cs
+++ b/SourceGen/DataAnalysis.cs
@@ -165,6 +165,8 @@ namespace SourceGen {
// Check for a relocation. It'll be at offset+1 because it's on the operand,
// not the opcode byte. (Make sure to check the length, or an RTS followed
// by relocated data will freak out.)
+ // TODO(someday): this won't get the second byte of an MVN/MVP, which is fine
+ // since we don't currently support two formats on one instruction.
if (mAnalysisParams.UseRelocData) {
if (attr.Length > 1 && mProject.RelocList.TryGetValue(offset + 1,
out DisasmProject.RelocData reloc)) {
diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml
index ff3a8b8..0bf9313 100644
--- a/SourceGen/Res/Strings.xaml
+++ b/SourceGen/Res/Strings.xaml
@@ -130,7 +130,7 @@ limitations under the License.
Visualization ignored
no files available
No exported symbols found.
- Segment {0:D2}: Kind={1}, Attrs={2}, Name='{3}'
+ Segment {0:D2}: Kind={1}; Attrs={2}; Name='{3}'
Segment {0:D2}: {3} {1,-9} Name='{2}', Length={4}
Seg{0:D2}: {1} '{2}'
The file doesn't exist.
diff --git a/SourceGen/SGTestData/20212-reloc-data b/SourceGen/SGTestData/20212-reloc-data
new file mode 100644
index 0000000..05efc89
Binary files /dev/null and b/SourceGen/SGTestData/20212-reloc-data differ
diff --git a/SourceGen/SGTestData/20212-reloc-data.dis65 b/SourceGen/SGTestData/20212-reloc-data.dis65
new file mode 100644
index 0000000..c86fc02
--- /dev/null
+++ b/SourceGen/SGTestData/20212-reloc-data.dis65
@@ -0,0 +1,326 @@
+### 6502bench SourceGen dis65 v1.0 ###
+{
+"_ContentVersion":4,
+"FileDataLength":255,
+"FileDataCrc32":-527039020,
+"ProjectProps":{
+"CpuName":"65816",
+"IncludeUndocumentedInstr":false,
+"TwoByteBrk":false,
+"EntryFlags":12780031,
+"AutoLabelStyle":"Simple",
+"AnalysisParams":{
+"AnalyzeUncategorizedData":true,
+"DefaultTextScanMode":"LowHighAscii",
+"MinCharsForString":4,
+"SeekNearbyTargets":true,
+"UseRelocData":true,
+"SmartPlpHandling":true},
+
+"PlatformSymbolFileIdentifiers":["RT:Apple/Cxxx-IO.sym65",
+"RT:Apple/IIgs-ROM.sym65",
+"RT:Apple/GSOS.sym65"],
+"ExtensionScriptFileIdentifiers":["RT:Apple/GSOS.cs",
+"RT:Apple/IIgs-Toolbox.cs"],
+"ProjectSyms":{
+}},
+
+"AddressMap":[{
+"Offset":0,
+"Addr":196608},
+
+{
+"Offset":110,
+"Addr":327648},
+
+{
+"Offset":142,
+"Addr":327680},
+
+{
+"Offset":162,
+"Addr":144470},
+
+{
+"Offset":188,
+"Addr":524288},
+
+{
+"Offset":242,
+"Addr":393216}],
+"TypeHints":[{
+"Low":0,
+"High":0,
+"Hint":"Code"}],
+"StatusFlagOverrides":{
+},
+
+"Comments":{
+},
+
+"LongComments":{
+"0":{
+"Text":"Segment 02: Kind=Code; Attrs=NoSpecial; Name=\u0027 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"110":{
+"Text":"Segment 03: Kind=Data; Attrs=BankRel, Dynamic; Name=\u0027PosFFE0 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"162":{
+"Text":"Segment 04: Kind=Code; Attrs=NoSpecial; Name=\u0027Bank2 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"188":{
+"Text":"Segment 05: Kind=Code; Attrs=AbsBank, Dynamic; Name=\u0027Bank8 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"242":{
+"Text":"Segment 06: Kind=Data; Attrs=0; Name=\u0027Filler \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0}},
+
+"Notes":{
+"0":{
+"Text":"Seg02: 03/0000 \u0027 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"110":{
+"Text":"Seg03: 04/ffe0 \u0027PosFFE0 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"162":{
+"Text":"Seg04: 02/3456 \u0027Bank2 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"188":{
+"Text":"Seg05: 08/0000 \u0027Bank8 \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0},
+
+"242":{
+"Text":"Seg06: 06/0000 \u0027Filler \u0027",
+"BoxMode":false,
+"MaxWidth":80,
+"BackgroundColor":0}},
+
+"UserLabels":{
+},
+
+"OperandFormats":{
+},
+
+"LvTables":{
+},
+
+"Visualizations":[],
+"VisualizationAnimations":[],
+"VisualizationSets":{
+},
+
+"RelocList":{
+"82":{
+"Width":2,
+"Shift":0,
+"Value":196677},
+
+"91":{
+"Width":2,
+"Shift":0,
+"Value":196695},
+
+"94":{
+"Width":2,
+"Shift":0,
+"Value":200773},
+
+"98":{
+"Width":2,
+"Shift":0,
+"Value":196717},
+
+"5":{
+"Width":3,
+"Shift":0,
+"Value":196608},
+
+"85":{
+"Width":2,
+"Shift":-8,
+"Value":196677},
+
+"88":{
+"Width":1,
+"Shift":-16,
+"Value":196677},
+
+"10":{
+"Width":3,
+"Shift":0,
+"Value":144470},
+
+"14":{
+"Width":3,
+"Shift":0,
+"Value":524288},
+
+"18":{
+"Width":3,
+"Shift":0,
+"Value":144470},
+
+"31":{
+"Width":3,
+"Shift":0,
+"Value":524313},
+
+"102":{
+"Width":3,
+"Shift":0,
+"Value":327648},
+
+"105":{
+"Width":3,
+"Shift":0,
+"Value":327648},
+
+"100":{
+"Width":2,
+"Shift":0,
+"Value":327648},
+
+"22":{
+"Width":2,
+"Shift":0,
+"Value":144470},
+
+"63":{
+"Width":2,
+"Shift":0,
+"Value":144478},
+
+"35":{
+"Width":2,
+"Shift":0,
+"Value":524313},
+
+"47":{
+"Width":2,
+"Shift":0,
+"Value":524313},
+
+"60":{
+"Width":2,
+"Shift":0,
+"Value":524326},
+
+"76":{
+"Width":2,
+"Shift":0,
+"Value":524313},
+
+"25":{
+"Width":1,
+"Shift":0,
+"Value":144470},
+
+"27":{
+"Width":1,
+"Shift":-8,
+"Value":144470},
+
+"29":{
+"Width":1,
+"Shift":-16,
+"Value":144470},
+
+"38":{
+"Width":1,
+"Shift":0,
+"Value":524313},
+
+"40":{
+"Width":1,
+"Shift":-8,
+"Value":524313},
+
+"42":{
+"Width":1,
+"Shift":-16,
+"Value":524313},
+
+"50":{
+"Width":2,
+"Shift":-8,
+"Value":524313},
+
+"53":{
+"Width":1,
+"Shift":-16,
+"Value":524313},
+
+"66":{
+"Width":1,
+"Shift":-16,
+"Value":144478},
+
+"67":{
+"Width":1,
+"Shift":-16,
+"Value":524326},
+
+"79":{
+"Width":1,
+"Shift":-16,
+"Value":524313},
+
+"110":{
+"Width":3,
+"Shift":0,
+"Value":327648},
+
+"167":{
+"Width":2,
+"Shift":0,
+"Value":144494},
+
+"163":{
+"Width":3,
+"Shift":0,
+"Value":144470},
+
+"193":{
+"Width":2,
+"Shift":0,
+"Value":524313},
+
+"189":{
+"Width":3,
+"Shift":0,
+"Value":524288},
+
+"213":{
+"Width":3,
+"Shift":0,
+"Value":524313},
+
+"217":{
+"Width":3,
+"Shift":0,
+"Value":144470}}}
diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S b/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S
new file mode 100644
index 0000000..d3b51eb
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/20212-reloc-data_64tass.S
@@ -0,0 +1,140 @@
+ .cpu "65816"
+ .enc "sg_ascii"
+ .cdef $20,$7e,$20
+;Segment 02: Kind=Code; Attrs=NoSpecial; Name=' '
+* = $030000
+ .al
+ .xl
+L30000 clc
+ xce
+ .as
+ .xs
+ sep #$30
+ lda L30000
+ nop
+ jsl _L23456
+ jsl _L80000
+ lda _L23456
+ lda _L23456 & $ffff
+ lda #<_L23456
+ lda #>_L23456
+ lda #`_L23456
+ lda _L80019
+ lda @w_L80019 & $ffff
+ lda #<_L80019
+ lda #>_L80019
+ lda #`_L80019
+ nop
+ rep #$30
+ .al
+ .xl
+ lda #_L80019 & $ffff
+ lda #_L80019 >> 8
+ lda #_L80019 >> 16
+ nop
+ lda #$000f
+ ldx #_L80026 & $ffff
+ ldy #_L2345E & $ffff
+ mvn #(`_L2345E)+6,#`_L2345E
+ nop
+_L30045 pea $0000
+ pea $f000
+ pea _L80019 & $ffff
+ pea _L80019 >> 16
+ pea _L30045 & $ffff
+ pea _L30045 >> 8
+_L30057 pea _L30045 >> 16
+ pea _L30057 & $ffff
+ pea $1045
+ nop
+ jmp _L3006D
+
+ .word _L4FFE0 & $ffff
+ .long _L4FFE0
+ .long _L4FFE0
+ .byte $00
+
+_L3006D rts
+
+;Segment 03: Kind=Data; Attrs=BankRel, Dynamic; Name='PosFFE0 '
+ .logical $04ffe0
+_L4FFE0 .long _L4FFE0
+ .byte $00
+ .byte $00
+ .byte $01
+ .byte $02
+ .byte $03
+ .byte $04
+ .byte $05
+ .byte $06
+ .byte $07
+ .byte $08
+ .byte $09
+ .byte $0a
+ .byte $0b
+ .byte $0c
+ .byte $0d
+ .byte $0e
+ .byte $0f
+ .byte $10
+ .byte $11
+ .byte $12
+ .byte $13
+ .byte $14
+ .byte $15
+ .byte $16
+ .byte $17
+ .byte $18
+ .byte $19
+ .byte $1a
+ .byte $1b
+ .here
+ .logical $050000
+ .byte $1c
+ .byte $1d
+ .byte $1e
+ .byte $1f
+ .text " !",$22,"#$%&'()*+,-./"
+
+;Segment 04: Kind=Code; Attrs=NoSpecial; Name='Bank2 '
+ .here
+ .logical $023456
+ .as
+ .xs
+_L23456 lda _L23456
+ jsr _L2346E
+ rtl
+
+_L2345E .fill 16,$00
+
+_L2346E nop
+ rts
+
+;Segment 05: Kind=Code; Attrs=AbsBank, Dynamic; Name='Bank8 '
+ .here
+ .logical $080000
+_L80000 lda _L80000
+ lda @w_L80019 & $ffff
+ nop
+ lda $010000
+ lda $020000
+ lda L30000
+ lda _L80000
+ rtl
+
+_L80019 .long _L80019
+ .byte $00
+ .long _L23456
+ .byte $00
+ .byte $80
+ .byte $00
+ .byte $10
+ .byte $08
+ .byte $00
+_L80026 .text "This is a test."
+ .byte $00
+;Segment 06: Kind=Data; Attrs=0; Name='Filler '
+ .here
+ .logical $060000
+ .text "hello, world!"
+ .here
diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_Merlin32.S b/SourceGen/SGTestData/Expected/20212-reloc-data_Merlin32.S
new file mode 100644
index 0000000..12c8d8f
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/20212-reloc-data_Merlin32.S
@@ -0,0 +1,128 @@
+;Segment 02: Kind=Code; Attrs=NoSpecial; Name=' '
+ org $030000
+ mx %00
+L30000 clc
+ xce
+ mx %11
+ sep #$30
+ ldal L30000
+ nop
+ jsl :L23456
+ jsl :L80000
+ ldal :L23456
+ lda :L23456
+ lda #<:L23456
+ lda #>:L23456
+ lda #^:L23456
+ ldal :L80019
+ lda: :L80019
+ lda #<:L80019
+ lda #>:L80019
+ lda #^:L80019
+ nop
+ rep #$30
+ mx %00
+ lda #:L80019
+ lda #>:L80019
+ lda #^:L80019
+ nop
+ lda #$000f
+ ldx #:L80026
+ ldy #:L2345E
+ mvn #^:L2345E+$60000,#^:L2345E
+ nop
+:L30045 pea $0000
+ pea $f000
+ pea :L80019
+ pea ^:L80019
+ pea :L30045
+ pea >:L30045
+:L30057 pea ^:L30045
+ pea :L30057
+ pea $1045
+ nop
+ jmp :L3006D
+
+ dw :L4FFE0
+ adr :L4FFE0
+ adr :L4FFE0
+ dfb $00
+
+:L3006D rts
+
+;Segment 03: Kind=Data; Attrs=BankRel, Dynamic; Name='PosFFE0 '
+ org $04ffe0
+:L4FFE0 adr :L4FFE0
+ dfb $00
+ dfb $00
+ dfb $01
+ dfb $02
+ dfb $03
+ dfb $04
+ dfb $05
+ dfb $06
+ dfb $07
+ dfb $08
+ dfb $09
+ dfb $0a
+ dfb $0b
+ dfb $0c
+ dfb $0d
+ dfb $0e
+ dfb $0f
+ dfb $10
+ dfb $11
+ dfb $12
+ dfb $13
+ dfb $14
+ dfb $15
+ dfb $16
+ dfb $17
+ dfb $18
+ dfb $19
+ dfb $1a
+ dfb $1b
+ org $050000
+ dfb $1c
+ dfb $1d
+ dfb $1e
+ dfb $1f
+ asc ' !"#$%&',27,'()*+,-./'
+
+;Segment 04: Kind=Code; Attrs=NoSpecial; Name='Bank2 '
+ org $023456
+ mx %11
+:L23456 ldal :L23456
+ jsr :L2346E
+ rtl
+
+:L2345E ds 16
+
+:L2346E nop
+ rts
+
+;Segment 05: Kind=Code; Attrs=AbsBank, Dynamic; Name='Bank8 '
+ org $080000
+:L80000 ldal :L80000
+ lda: :L80019
+ nop
+ ldal $010000
+ ldal $020000
+ ldal L30000
+ ldal :L80000
+ rtl
+
+:L80019 adr :L80019
+ dfb $00
+ adr :L23456
+ dfb $00
+ dfb $80
+ dfb $00
+ dfb $10
+ dfb $08
+ dfb $00
+:L80026 asc 'This is a test.'
+ dfb $00
+;Segment 06: Kind=Data; Attrs=0; Name='Filler '
+ org $060000
+ asc 'hello, world!'
diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S b/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S
new file mode 100644
index 0000000..db1e891
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/20212-reloc-data_acme.S
@@ -0,0 +1,12 @@
+;ACME can't handle 65816 code that lives outside bank zero
+* = $0000
+ !pseudopc $030000 {
+ !hex 18fbe230af000003ea2256340222000008af563402ad5634a956a934a902af19
+ !hex 0008ad1900a919a900a908eac230a91900a90008a90800eaa90f00a22600a05e
+ !hex 34540208eaf40000f400f0f41900f40800f44500f40003f40300f45700f44510
+ !hex ea4c6d00e0ffe0ff04e0ff040060e0ff0400000102030405060708090a0b0c0d
+ !hex 0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d
+ !hex 2e2faf563402206e346b00000000000000000000000000000000ea60af000008
+ !hex ad1900eaaf000001af000002af000003af0000086b1900080056340200800010
+ !hex 080054686973206973206120746573742e0068656c6c6f2c20776f726c6421
+ } ;!pseudopc
diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S
new file mode 100644
index 0000000..60537eb
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.S
@@ -0,0 +1,139 @@
+ .setcpu "65816"
+;Segment 02: Kind=Code; Attrs=NoSpecial; Name=' '
+; .segment "SEG000"
+ .org $030000
+ .a16
+ .i16
+L30000: clc
+ xce
+ .a8
+ .i8
+ sep #$30
+ lda L30000
+ nop
+ jsl @L23456
+ jsl @L80000
+ lda f:@L23456
+ lda @L23456 & $ffff
+ lda #<@L23456
+ lda #>@L23456
+ lda #^@L23456
+ lda f:@L80019
+ lda a:@L80019 & $ffff
+ lda #<@L80019
+ lda #>@L80019
+ lda #^@L80019
+ nop
+ rep #$30
+ .a16
+ .i16
+ lda #@L80019 & $ffff
+ lda #@L80019 >> 8
+ lda #@L80019 >> 16
+ nop
+ lda #$000f
+ ldx #@L80026 & $ffff
+ ldy #@L2345E & $ffff
+ mvn #^@L2345E+6,#^@L2345E
+ nop
+@L30045: pea $0000
+ pea $f000
+ pea @L80019 & $ffff
+ pea @L80019 >> 16
+ pea @L30045 & $ffff
+ pea @L30045 >> 8
+@L30057: pea @L30045 >> 16
+ pea @L30057 & $ffff
+ pea $1045
+ nop
+ jmp @L3006D & $ffff
+
+ .word @L4FFE0 & $ffff
+ .faraddr @L4FFE0
+ .faraddr @L4FFE0
+ .byte $00
+
+@L3006D: rts
+
+;Segment 03: Kind=Data; Attrs=BankRel, Dynamic; Name='PosFFE0 '
+; .segment "SEG001"
+ .org $04ffe0
+@L4FFE0: .faraddr @L4FFE0
+ .byte $00
+ .byte $00
+ .byte $01
+ .byte $02
+ .byte $03
+ .byte $04
+ .byte $05
+ .byte $06
+ .byte $07
+ .byte $08
+ .byte $09
+ .byte $0a
+ .byte $0b
+ .byte $0c
+ .byte $0d
+ .byte $0e
+ .byte $0f
+ .byte $10
+ .byte $11
+ .byte $12
+ .byte $13
+ .byte $14
+ .byte $15
+ .byte $16
+ .byte $17
+ .byte $18
+ .byte $19
+ .byte $1a
+ .byte $1b
+; .segment "SEG002"
+ .org $050000
+ .byte $1c
+ .byte $1d
+ .byte $1e
+ .byte $1f
+ .byte " !",$22,"#$%&'()*+,-./"
+
+;Segment 04: Kind=Code; Attrs=NoSpecial; Name='Bank2 '
+; .segment "SEG003"
+ .org $023456
+ .a8
+ .i8
+@L23456: lda @L23456
+ jsr @L2346E & $ffff
+ rtl
+
+@L2345E: .res 16,$00
+
+@L2346E: nop
+ rts
+
+;Segment 05: Kind=Code; Attrs=AbsBank, Dynamic; Name='Bank8 '
+; .segment "SEG004"
+ .org $080000
+@L80000: lda @L80000
+ lda a:@L80019 & $ffff
+ nop
+ lda $010000
+ lda $020000
+ lda L30000
+ lda @L80000
+ rtl
+
+@L80019: .faraddr @L80019
+ .byte $00
+ .faraddr @L23456
+ .byte $00
+ .byte $80
+ .byte $00
+ .byte $10
+ .byte $08
+ .byte $00
+@L80026: .byte "This is a test."
+ .byte $00
+;Segment 06: Kind=Data; Attrs=0; Name='Filler '
+; .segment "SEG005"
+ .org $060000
+ .byte "hello, world!"
diff --git a/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg
new file mode 100644
index 0000000..1772cf0
--- /dev/null
+++ b/SourceGen/SGTestData/Expected/20212-reloc-data_cc65.cfg
@@ -0,0 +1,21 @@
+# 6502bench SourceGen generated linker script for 20212-reloc-data
+MEMORY {
+ MAIN: file=%O, start=%S, size=65536;
+# MEM000: file=%O, start=$30000, size=110;
+# MEM001: file=%O, start=$4ffe0, size=32;
+# MEM002: file=%O, start=$50000, size=20;
+# MEM003: file=%O, start=$23456, size=26;
+# MEM004: file=%O, start=$80000, size=54;
+# MEM005: file=%O, start=$60000, size=13;
+}
+SEGMENTS {
+ CODE: load=MAIN, type=rw;
+# SEG000: load=MEM000, type=rw;
+# SEG001: load=MEM001, type=rw;
+# SEG002: load=MEM002, type=rw;
+# SEG003: load=MEM003, type=rw;
+# SEG004: load=MEM004, type=rw;
+# SEG005: load=MEM005, type=rw;
+}
+FEATURES {}
+SYMBOLS {}
diff --git a/SourceGen/SGTestData/Source/20212-reloc-data-bank2.S b/SourceGen/SGTestData/Source/20212-reloc-data-bank2.S
new file mode 100644
index 0000000..6d3ab61
--- /dev/null
+++ b/SourceGen/SGTestData/Source/20212-reloc-data-bank2.S
@@ -0,0 +1,17 @@
+; Copyright 2020 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Segment #3 : code, load at specific address ($02/3456)
+
+ REL ;generate relocatable code
+
+BANK2_START ENT
+start ldal start
+ jsr later
+ rtl
+
+BANK2_MOV_DST ENT
+ ds 16
+
+later nop
+ rts
diff --git a/SourceGen/SGTestData/Source/20212-reloc-data-bank8.S b/SourceGen/SGTestData/Source/20212-reloc-data-bank8.S
new file mode 100644
index 0000000..b0b7a3f
--- /dev/null
+++ b/SourceGen/SGTestData/Source/20212-reloc-data-bank8.S
@@ -0,0 +1,30 @@
+; Copyright 2020 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+;
+; Segment #4 : code, loads somewhere in bank 8
+
+ REL ;generate relocatable code
+
+BANK2_START EXT
+
+BANK8_START ENT
+start ldal start
+ lda addr
+ nop
+ ldal $010000
+ ldal $020000
+ ldal $030000
+ ldal $080000
+ rtl
+
+BANK8_ADDR ENT
+addr
+ adrl addr
+ adrl BANK2_START
+ dfb $80
+ adrl $081000
+
+BANK8_MOV_SRC ENT
+ asc 'This is a test.',$00
diff --git a/SourceGen/SGTestData/Source/20212-reloc-data-filler.S b/SourceGen/SGTestData/Source/20212-reloc-data-filler.S
new file mode 100644
index 0000000..1d1ee40
--- /dev/null
+++ b/SourceGen/SGTestData/Source/20212-reloc-data-filler.S
@@ -0,0 +1,11 @@
+; Copyright 2020 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+;
+; Segment #5 : filler
+
+ REL ;generate relocatable code
+
+ asc 'hello, world!'
+
diff --git a/SourceGen/SGTestData/Source/20212-reloc-data-lnk.S b/SourceGen/SGTestData/Source/20212-reloc-data-lnk.S
new file mode 100644
index 0000000..81a207d
--- /dev/null
+++ b/SourceGen/SGTestData/Source/20212-reloc-data-lnk.S
@@ -0,0 +1,60 @@
+; Copyright 2020 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Merlin 32 link file.
+;
+; According to the documentation we can use ORG directives to set the segment
+; load addresses, but in practice Merlin 32 rejects them. Also, DS
+; directives are ignored, and KND uses a different default value than
+; what the documentation says.
+;
+; cf. https://github.com/apple2accumulator/merlin32/issues/39
+;
+; Instructions:
+; - Assemble this file with Merlin32. It will pull everything in.
+; - Open out.20212 with the segment viewer and find the start offsets
+; of the segments. Set the ORG field with a hex editor. It's a
+; 4-byte field at +$18. (Easy way in a v2.0 header: find the start
+; of the SEGNAME and back up $1e bytes.)
+; "PosFFE0" -> E0 FF 00
+; "Bank2" -> 56 34 02
+; "Bank8" -> 00 10 08
+; - Convert out.20212 to "20212-reloc-data" with OMF converter.
+; - The project file generally requires no edits, except to delete the
+; header comment. Leaving the segment-start comments in seems fine.
+
+ DSK out.20212 ;output file name, must be ProDOS-compat
+ TYP $b3 ;S16
+ XPL ;add ~ExpressLoad
+
+; Segment #1
+ ASM 20212-reloc-data-main.S
+ KND $1000 ;Code; NoSpec
+; SNA
+
+
+; Segment #2
+ ASM 20212-reloc-data-relover.S
+ KND $8101 ;Data; Dynamic, BankRel
+ ALI None
+; BSZ 0
+; ORG $00ffe0 ;load in any bank, at $ffe0
+ SNA PosFFE0
+
+; Segment #3
+ ASM 20212-reloc-data-bank2.S
+ KND $1000
+ DS 256
+; ORG $023456 ;load exactly here
+ SNA Bank2
+
+; Segment #4
+ ASM 20212-reloc-data-bank8.S
+ KND $8800 ;Code; Dynamic, AbsBank
+; ORG $081000 ;load in bank 8, at any address
+ SNA Bank8
+
+; Segment #5
+ ASM 20212-reloc-data-filler.S
+ KND $0001 ;Data
+ SNA Filler
diff --git a/SourceGen/SGTestData/Source/20212-reloc-data-main.S b/SourceGen/SGTestData/Source/20212-reloc-data-main.S
new file mode 100644
index 0000000..fd4f8d7
--- /dev/null
+++ b/SourceGen/SGTestData/Source/20212-reloc-data-main.S
@@ -0,0 +1,80 @@
+; Copyright 2020 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+;
+; Segment #1 : code; main segment, loads anywhere
+
+BANK2_START EXT
+BANK2_MOV_DST EXT
+BANK8_START EXT
+BANK8_ADDR EXT
+BANK8_MOV_SRC EXT
+ADDR_FFE0 EXT
+
+ REL ;generate relocatable code
+
+start
+ clc
+ xce
+ sep #$30
+ mx %11
+
+ ldal start
+ nop
+
+ jsl BANK2_START
+ jsl BANK8_START
+
+ ldal BANK2_START
+ lda BANK2_START
+ lda #BANK2_START
+ lda #^BANK2_START
+
+ ldal BANK8_ADDR
+ lda BANK8_ADDR
+ lda #BANK8_ADDR
+ lda #^BANK8_ADDR
+ nop
+
+; Do some stuff with 16-bit registers. Merlin 32 treats <>^ as shift
+; operations rather than byte selectors.
+ rep #$30
+ mx %00
+ lda #BANK8_ADDR
+ lda #^BANK8_ADDR
+ nop
+
+; Check MVN/MVP. Handing them correctly requires having two different
+; symbol refs on a single instruction.
+ lda #15
+ ldx #BANK8_MOV_SRC
+ ldy #BANK2_MOV_DST
+ mvn #^BANK8_MOV_SRC,#^BANK2_MOV_DST
+ nop
+
+check_pea
+ pea $0000
+ pea $f000
+ pea BANK8_ADDR
+ pea ^BANK8_ADDR
+ pea check_pea
+ pea >check_pea
+ pea ^check_pea
+ pea check_pea+18
+ pea check_pea+$1000
+ nop
+
+ jmp :skipdata
+
+; Generate 2/3/4-byte refs. The OMF reloc entry generated by Merlin32
+; for ADRL is only 3 bytes wide.
+ dw ADDR_FFE0
+ adr ADDR_FFE0
+ adrl ADDR_FFE0
+
+:skipdata
+ rts
diff --git a/SourceGen/SGTestData/Source/20212-reloc-data-relover.S b/SourceGen/SGTestData/Source/20212-reloc-data-relover.S
new file mode 100644
index 0000000..00fedca
--- /dev/null
+++ b/SourceGen/SGTestData/Source/20212-reloc-data-relover.S
@@ -0,0 +1,15 @@
+; Copyright 2020 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+;
+; Segment #2 : data, loads at $FFE0 in arbitrary bank and overflows
+
+ REL ;generate relocatable code
+
+ADDR_FFE0 ENT
+
+start adrl start
+ hex 000102030405060708090a0b0c0d0e0f
+ hex 101112131415161718191a1b1c1d1e1f
+ hex 202122232425262728292a2b2c2d2e2f
diff --git a/SourceGen/Tools/Omf/Loader.cs b/SourceGen/Tools/Omf/Loader.cs
index 8d9762e..db1716a 100644
--- a/SourceGen/Tools/Omf/Loader.cs
+++ b/SourceGen/Tools/Omf/Loader.cs
@@ -157,6 +157,9 @@ namespace SourceGen.Tools.Omf {
// would mean you can't set the "absolute bank" flag to position code or
// data in bank 0. I'm going to assume that's intentional, since people
// (a) shouldn't be doing that, and (b) can use DP/Stack instead (?).
+ //
+ // It also means that "bank relative" can't be used to set the position
+ // to zero, which is probably fine since you can do that with ALIGN=$10000.
continue;
}
@@ -195,15 +198,30 @@ namespace SourceGen.Tools.Omf {
int addr;
- if (omfSeg.Org != 0) {
- // Specific address requested.
+ // We want to put the segment at a specific offset in an arbitrary bank
+ // if ORG is nonzero, the BankRel flag is set, and the AbsBank flag is clear.
+ bool bankRel = omfSeg.Org != 0 &&
+ (omfSeg.Attrs & OmfSegment.SegmentAttribute.BankRel) != 0 &&
+ (omfSeg.Attrs & OmfSegment.SegmentAttribute.AbsBank) == 0;
+ // We want to put the segment at an arbitrary offset in a specific bank
+ // if ORG is nonzero, the BankRel flag is clear, and the AbsBank flag is set.
+ bool fixedBank = omfSeg.Org != 0 &&
+ (omfSeg.Attrs & OmfSegment.SegmentAttribute.BankRel) == 0 &&
+ (omfSeg.Attrs & OmfSegment.SegmentAttribute.AbsBank) != 0;
+ // We want to put the segment at a specific offset and bank
+ // if ORG is nonzero, and BankRel and FixedBank are either both set or
+ // both clear.
+ bool fixedAddr = omfSeg.Org != 0 && (bankRel ^ fixedBank) == false;
+
+ if (fixedAddr || fixedBank) {
+ // Specific bank requested.
addr = omfSeg.Org;
if ((omfSeg.Attrs & OmfSegment.SegmentAttribute.AbsBank) != 0) {
// just keep the bank
addr &= 0x00ff0000;
}
} else {
- // Find next available spot with enough space.
+ // Find next available bank with enough space.
while (true) {
while (nextBank < 256 && inUse[nextBank]) {
nextBank++;
@@ -225,11 +243,20 @@ namespace SourceGen.Tools.Omf {
}
addr = nextBank << 16;
+ if (bankRel) {
+ // TODO(maybe): reject if incompatible with BANKSIZE
+ addr |= omfSeg.Org & 0x0000ffff;
+ }
// Advance nextBank. We do this by identifying the last address touched,
- // and moving to the next bank.
+ // then incrementing the bank number.
int lastAddr = addr + omfSeg.Length - 1;
nextBank = (lastAddr >> 16) + 1;
+ if (nextBank >= 0x0100) {
+ // Overflowed the 65816 address space.
+ Debug.WriteLine("Bank exceeded $ff");
+ return false;
+ }
}
SegmentMapEntry ent = new SegmentMapEntry(omfSeg, addr);