diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index bccf941..b083584 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -183,7 +183,7 @@ namespace CommonUtil { public int ActualLength { get; private set; } /// - /// Address associated with pre-label. + /// Address associated with pre-label and relative addressing. /// public int PreLabelAddress { get; private set; } @@ -207,6 +207,19 @@ namespace CommonUtil { } } + /// + /// Is this region validly marked "is relative"? + /// + /// + /// The relative address is determined by subtracting the Address from the + /// PreLabelAddress, so neither may be NON_ADDR. + /// + public bool HasValidIsRelative { + get { + return IsRelative && PreLabelAddress != NON_ADDR && Address != NON_ADDR; + } + } + /// /// Full constructor. diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index ad3d97f..3c7c72c 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -600,7 +600,7 @@ namespace SourceGen.AsmGen { } AddressMap.AddressRegion region = change.Region; string addrStr; - if (region.IsRelative && region.PreLabelAddress != Address.NON_ADDR) { + if (region.HasValidIsRelative) { int diff = nextAddress - region.PreLabelAddress; string pfxStr; if (diff >= 0) { diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index e2c4121..0ad57ba 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -96,6 +96,18 @@ namespace SourceGen.AsmGen { /// private int mNextAddress = -1; + /// + /// True if we've seen an "is relative" flag in a block of address region start directives. + /// + /// + /// The trick with IsRelative is that, if there are multiple arstarts at the same + /// offset, we need to output some or all of them, starting from the one just before + /// the first IsRelative start. We probably want to disable the use of Flush and + /// just generate them as they appear, using the next Flush as the signal to return + /// to standard behavior. + /// + bool mIsInRelative = false; + /// /// Holds detected version of configured assembler. /// @@ -540,34 +552,73 @@ namespace SourceGen.AsmGen { // IGenerator public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { - if (change.IsStart && change.Region.HasValidPreLabel) { - // Need to output the previous ORG, if any, then a label on a line by itself. - if (mNextAddress >= 0) { - OutputLine(string.Empty, - SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), - SourceFormatter.FormatHexValue(mNextAddress, 4), - string.Empty); - } - string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); - OutputLine(labelStr, string.Empty, string.Empty, string.Empty); - } - int nextAddress = change.Address; if (nextAddress == Address.NON_ADDR) { // Start non-addressable regions at zero to ensure they don't overflow bank. nextAddress = 0; } + + if (change.IsStart) { + AddressMap.AddressRegion region = change.Region; + if (region.HasValidPreLabel || region.HasValidIsRelative) { + // Need to output the previous ORG, if one is pending. + if (mNextAddress >= 0) { + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(mNextAddress, 4), + string.Empty); + } + } + if (region.HasValidPreLabel) { + string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); + OutputLine(labelStr, string.Empty, string.Empty, string.Empty); + } + if (region.HasValidIsRelative) { + // Found a valid IsRelative. Switch to "relative mode" if not there already. + mIsInRelative = true; + } + if (mIsInRelative) { + // Once we see a region with IsRelative set, we output regions as we + // find them until the next Flush. + string addrStr; + if (region.HasValidIsRelative) { + 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), + addrStr, string.Empty); + + mNextAddress = -1; + return; + } + } + mNextAddress = nextAddress; } // IGenerator public void FlushArDirectives() { - // TODO(someday): handle IsRelative - OutputLine(string.Empty, - SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), - SourceFormatter.FormatHexValue(mNextAddress, 4), - string.Empty); + // Output pending directives. There will always be something to do here unless + // we were in "relative" mode. + Debug.Assert(mNextAddress >= 0 || mIsInRelative); + if (mNextAddress >= 0) { + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(mNextAddress, 4), + string.Empty); + } mNextAddress = -1; + mIsInRelative = false; } // IGenerator diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 7662a29..3a69e67 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -91,6 +91,18 @@ namespace SourceGen.AsmGen { /// private int mNextAddress = -1; + /// + /// True if we've seen an "is relative" flag in a block of address region start directives. + /// + /// + /// The trick with IsRelative is that, if there are multiple arstarts at the same + /// offset, we need to output some or all of them, starting from the one just before + /// the first IsRelative start. We probably want to disable the use of Flush and + /// just generate them as they appear, using the next Flush as the signal to return + /// to standard behavior. + /// + bool mIsInRelative = false; + /// /// Holds detected version of configured assembler. /// @@ -491,35 +503,75 @@ namespace SourceGen.AsmGen { // IGenerator public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { - if (change.IsStart && change.Region.HasValidPreLabel) { - // Need to output the previous ORG, if any, then a label on a line by itself. - if (mNextAddress >= 0) { - OutputLine(string.Empty, - SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), - SourceFormatter.FormatHexValue(mNextAddress, 4), - string.Empty); - } - string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); - OutputLine(labelStr, string.Empty, string.Empty, string.Empty); - } - int nextAddress = change.Address; if (nextAddress == Address.NON_ADDR) { // Start non-addressable regions at zero to ensure they don't overflow bank. nextAddress = 0; } + + if (change.IsStart) { + AddressMap.AddressRegion region = change.Region; + if (region.HasValidPreLabel || region.HasValidIsRelative) { + // Need to output the previous ORG, if one is pending. + if (mNextAddress >= 0) { + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(mNextAddress, 4), + string.Empty); + } + } + if (region.HasValidPreLabel) { + string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); + OutputLine(labelStr, string.Empty, string.Empty, string.Empty); + } + if (region.HasValidIsRelative) { + // Found a valid IsRelative. Switch to "relative mode" if not there already. + mIsInRelative = true; + } + if (mIsInRelative) { + // Once we see a region with IsRelative set, we output regions as we + // find them until the next Flush. + string addrStr; + if (region.HasValidIsRelative) { + Debug.Assert(nextAddress != Address.NON_ADDR && + 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), + addrStr, string.Empty); + + mNextAddress = -1; + return; + } + } + mNextAddress = nextAddress; } // IGenerator public void FlushArDirectives() { - // TODO(someday): handle IsRelative - Debug.Assert(mNextAddress >= 0); - OutputLine(string.Empty, - SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), - SourceFormatter.FormatHexValue(mNextAddress, 4), - string.Empty); + // Output pending directives. There will always be something to do here unless + // we were in "relative" mode. + Debug.Assert(mNextAddress >= 0 || mIsInRelative); + if (mNextAddress >= 0) { + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), + SourceFormatter.FormatHexValue(mNextAddress, 4), + string.Empty); + } mNextAddress = -1; + mIsInRelative = false; } // IGenerator diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 3c406d0..01c71f1 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -709,7 +709,7 @@ namespace SourceGen.AsmGen { AddressMap.AddressRegion region = change.Region; string addrStr; - if (region.IsRelative && region.PreLabelAddress != Address.NON_ADDR) { + if (region.HasValidIsRelative) { int diff = nextAddress - region.PreLabelAddress; string pfxStr; if (diff >= 0) { diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index 2a41b25..04225c0 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -90,6 +90,7 @@ namespace SourceGen.AsmGen { } } + // Reached end of start directives. Write the last one. if (arDirectPending) { gen.FlushArDirectives(); arDirectPending = false; diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index eeef4b4..eaf6e7c 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -1095,8 +1095,7 @@ 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) { + } else if (change.Region.HasValidIsRelative) { int diff = region.Address - change.Region.PreLabelAddress; string pfxStr; if (diff >= 0) { diff --git a/SourceGen/SGTestData/20250-nested-regions b/SourceGen/SGTestData/20250-nested-regions index 9fd3fb4..e7ca6b8 100644 Binary files a/SourceGen/SGTestData/20250-nested-regions and b/SourceGen/SGTestData/20250-nested-regions differ diff --git a/SourceGen/SGTestData/20250-nested-regions.dis65 b/SourceGen/SGTestData/20250-nested-regions.dis65 index 9e9e35e..d460e2d 100644 --- a/SourceGen/SGTestData/20250-nested-regions.dis65 +++ b/SourceGen/SGTestData/20250-nested-regions.dis65 @@ -2,7 +2,7 @@ { "_ContentVersion":5, "FileDataLength":156, -"FileDataCrc32":1681364707, +"FileDataCrc32":367497130, "ProjectProps":{ "CpuName":"6502", "IncludeUndocumentedInstr":false, @@ -74,17 +74,17 @@ { "Offset":132, -"Addr":53248, +"Addr":57344, "Length":24, "PreLabel":"", "IsRelative":true}, { "Offset":139, -"Addr":57344, +"Addr":53248, "Length":17, "PreLabel":"", -"IsRelative":false}, +"IsRelative":true}, { "Offset":145, diff --git a/SourceGen/SGTestData/Expected/20250-nested-regions_64tass.S b/SourceGen/SGTestData/Expected/20250-nested-regions_64tass.S index 7aeb89a..3f438b0 100644 --- a/SourceGen/SGTestData/Expected/20250-nested-regions_64tass.S +++ b/SourceGen/SGTestData/Expected/20250-nested-regions_64tass.S @@ -7,7 +7,7 @@ .logical *+$1000 L3000 bit L3000 _L3003 lda _L3003 - and _LD003 + and _LE003 jmp _L200C .here @@ -22,7 +22,7 @@ _L1012 bit _L1012 .null "Null-term PETSCII string" .byte $80 .word _L3003 - .word _LD003 + .word _LE003 .byte $80 .here @@ -72,22 +72,22 @@ _L5017 rts _L4040 bit _L4040 bit _L5017 nop - jmp _LD000 - - .here - .logical *+$bf7e -_LD000 bit _L200C -_LD003 nop jmp _LE000 - .logical $e000 -_LE000 bit _LE000 + .here + .logical *+$cf7e +_LE000 bit _L200C +_LE003 nop + jmp _LD000 + + .logical *-$1007 +_LD000 bit _LD000 jmp _LF000 .logical $f000 _LF000 bit _LF000 lda _L3003 - and _LD003 + and _LE003 nop rts diff --git a/SourceGen/SGTestData/Expected/20250-nested-regions_acme.S b/SourceGen/SGTestData/Expected/20250-nested-regions_acme.S index c8bb315..fd03138 100644 --- a/SourceGen/SGTestData/Expected/20250-nested-regions_acme.S +++ b/SourceGen/SGTestData/Expected/20250-nested-regions_acme.S @@ -7,7 +7,7 @@ !pseudopc *+$1000 { L3000 bit L3000 @L3003 lda @L3003 - and @LD003 + and @LE003 jmp @L200C } @@ -22,7 +22,7 @@ L3000 bit L3000 !pet "Null-term PETSCII string",$00 !byte $80 !word @L3003 - !word @LD003 + !word @LE003 !byte $80 } @@ -72,22 +72,22 @@ L3000 bit L3000 @L4040 bit @L4040 bit @L5017 nop - jmp @LD000 - - } - !pseudopc *+$bf7e { -@LD000 bit @L200C -@LD003 nop jmp @LE000 - !pseudopc $e000 { -@LE000 bit @LE000 + } + !pseudopc *+$cf7e { +@LE000 bit @L200C +@LE003 nop + jmp @LD000 + + !pseudopc *-$1007 { +@LD000 bit @LD000 jmp @LF000 !pseudopc $f000 { @LF000 bit @LF000 lda @L3003 - and @LD003 + and @LE003 nop rts diff --git a/SourceGen/SGTestData/Expected/20250-nested-regions_cc65.S b/SourceGen/SGTestData/Expected/20250-nested-regions_cc65.S index 3f14590..dbc2d5a 100644 --- a/SourceGen/SGTestData/Expected/20250-nested-regions_cc65.S +++ b/SourceGen/SGTestData/Expected/20250-nested-regions_cc65.S @@ -2,10 +2,12 @@ .org $0000 .word $3000 ;load address - .org $3000 + .org $1000 + .org *+$1000 + .org *+$1000 L3000: bit L3000 @L3003: lda @L3003 - and @LD003 + and @LE003 jmp @L200C .org $200c @@ -21,7 +23,7 @@ L3000: bit L3000 .byte $c9,$20,$53,$54,$52,$49,$4e,$47,$00 .byte $80 .word @L3003 - .word @LD003 + .word @LE003 .byte $80 .org $4000 @@ -70,21 +72,22 @@ L3000: bit L3000 @L4040: bit @L4040 bit @L5017 nop - jmp @LD000 - - .org $d000 -@LD000: bit @L200C -@LD003: nop jmp @LE000 - .org $e000 -@LE000: bit @LE000 + .org $1082 + .org *+$cf7e +@LE000: bit @L200C +@LE003: nop + jmp @LD000 + + .org *-$1007 +@LD000: bit @LD000 jmp @LF000 .org $f000 @LF000: bit @LF000 lda @L3003 - and @LD003 + and @LE003 nop rts diff --git a/SourceGen/SGTestData/Expected/20250-nested-regions_merlin32.S b/SourceGen/SGTestData/Expected/20250-nested-regions_merlin32.S index 5986f41..4faca70 100644 --- a/SourceGen/SGTestData/Expected/20250-nested-regions_merlin32.S +++ b/SourceGen/SGTestData/Expected/20250-nested-regions_merlin32.S @@ -1,10 +1,12 @@ org $0000 dw $3000 ;load address - org $3000 + org $1000 + org *+$1000 + org *+$1000 L3000 bit L3000 :L3003 lda :L3003 - and :LD003 + and :LE003 jmp :L200C org $200c @@ -19,7 +21,7 @@ L3000 bit L3000 hex ce554c4c2d5445524d20d0c5d4d3c3c9c920535452494e4700 dfb $80 dw :L3003 - dw :LD003 + dw :LE003 dfb $80 org $4000 @@ -68,21 +70,22 @@ L3000 bit L3000 :L4040 bit :L4040 bit :L5017 nop - jmp :LD000 - - org $d000 -:LD000 bit :L200C -:LD003 nop jmp :LE000 - org $e000 -:LE000 bit :LE000 + org $1082 + org *+$cf7e +:LE000 bit :L200C +:LE003 nop + jmp :LD000 + + org *-$1007 +:LD000 bit :LD000 jmp :LF000 org $f000 :LF000 bit :LF000 lda :L3003 - and :LD003 + and :LE003 nop rts diff --git a/SourceGen/SGTestData/Expected/20252-nested-regions_64tass.S b/SourceGen/SGTestData/Expected/20252-nested-regions_64tass.S index f8d7751..761d57c 100644 --- a/SourceGen/SGTestData/Expected/20252-nested-regions_64tass.S +++ b/SourceGen/SGTestData/Expected/20252-nested-regions_64tass.S @@ -18,7 +18,7 @@ _L11018 lda _L11018 jmp _L11026 _L1101F .byte $80 - .logical *-$011020 + .logical $0000 .byte $ea .byte $60 .dword _L31004 diff --git a/SourceGen/SGTestData/Expected/20252-nested-regions_cc65.S b/SourceGen/SGTestData/Expected/20252-nested-regions_cc65.S index 58eb4c2..1a5e645 100644 --- a/SourceGen/SGTestData/Expected/20252-nested-regions_cc65.S +++ b/SourceGen/SGTestData/Expected/20252-nested-regions_cc65.S @@ -1,5 +1,7 @@ .setcpu "65816" - .org $031000 + .org $011000 + .org *+$010000 + .org *+$010000 .a8 .i8 L31000: lda L31000 diff --git a/SourceGen/SGTestData/Expected/20252-nested-regions_merlin32.S b/SourceGen/SGTestData/Expected/20252-nested-regions_merlin32.S index a03da82..7a79e8b 100644 --- a/SourceGen/SGTestData/Expected/20252-nested-regions_merlin32.S +++ b/SourceGen/SGTestData/Expected/20252-nested-regions_merlin32.S @@ -1,4 +1,6 @@ - org $031000 + org $011000 + org *+$010000 + org *+$010000 L31000 ldal L31000 :L31004 ldal :L31004 andl :L1101F diff --git a/SourceGen/SGTestData/Source/20250-nested-regions.S b/SourceGen/SGTestData/Source/20250-nested-regions.S index 57a1567..fb64237 100644 --- a/SourceGen/SGTestData/Source/20250-nested-regions.S +++ b/SourceGen/SGTestData/Source/20250-nested-regions.S @@ -107,17 +107,17 @@ a4040 bit a4040 .here ;$4000 +; EDIT: create region starting at $e000, ending at REGION2_END; relative + .logical $e000 +tailend +partek bit part2k +late nop + jmp partdk + ; 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 +partdk bit partdk jmp partfk ; EDIT: create, make it floating