From 387b50d827c3e917dcdc17e78dfb0b19e377c54c Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sat, 9 Oct 2021 09:58:37 -0700 Subject: [PATCH] Implement IsRelative for cc65/merlin32 Added support for "relative" address regions to the Merlin 32 and cc65 code generators. These generate "flat" address directives, and so were a little more complicated. Suppressed generation of relative operands for non-addressable regions. Also, tweaked the 20250-nested-regions test to include a negative relative region offset. --- CommonUtil/AddressMap.cs | 15 ++- SourceGen/AsmGen/AsmAcme.cs | 2 +- SourceGen/AsmGen/AsmCc65.cs | 85 +++++++++++++---- SourceGen/AsmGen/AsmMerlin32.cs | 88 ++++++++++++++---- SourceGen/AsmGen/AsmTass64.cs | 2 +- SourceGen/AsmGen/GenCommon.cs | 1 + SourceGen/LineListGen.cs | 3 +- SourceGen/SGTestData/20250-nested-regions | Bin 156 -> 156 bytes .../SGTestData/20250-nested-regions.dis65 | 8 +- .../Expected/20250-nested-regions_64tass.S | 22 ++--- .../Expected/20250-nested-regions_acme.S | 22 ++--- .../Expected/20250-nested-regions_cc65.S | 25 ++--- .../Expected/20250-nested-regions_merlin32.S | 25 ++--- .../Expected/20252-nested-regions_64tass.S | 2 +- .../Expected/20252-nested-regions_cc65.S | 4 +- .../Expected/20252-nested-regions_merlin32.S | 4 +- .../SGTestData/Source/20250-nested-regions.S | 16 ++-- 17 files changed, 225 insertions(+), 99 deletions(-) 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 9fd3fb4318e385effa8a23c15a68b1defdf100d8..e7ca6b804cf7b0a0e603acf8325c827b20e662c9 100644 GIT binary patch delta 50 zcmbQkIERs&!9a(>U@fzOF7tzl+`5bpCR%z)KG5M&c;&-zL5Jaj55or?h7S<=R|x=a CN)TlL delta 50 zcmbQkIERs&!9a(>U@fzOF7t(n+`5bxCR%z)UeMuDc;&