From da91f8604361677b291ae0dbf023f49666046e53 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 24 Oct 2018 14:57:09 -0700 Subject: [PATCH] Get 64tass expressions working We now insert parenthesis as needed. This can cause problems in some situations, so we always prefix parenthetical expressions with "0+", which looks goofy and is unnecessary for immediate operands. But it does generate working source code. Renamed the "simple" expression mode to "common", as it's not particularly simple but is what you'd expect most assemblers to do. (OTOH, life has been full of surprises.) (issue #16) --- Asm65/Formatter.cs | 4 +- SourceGen/AsmGen/AsmTass64.cs | 2 +- SourceGen/PseudoOp.cs | 75 +++++-- .../Expected/2007-labels-and-symbols_64tass.S | 209 ++++++++++++++++++ .../Expected/2009-branches-and-banks_64tass.S | 8 +- 5 files changed, 268 insertions(+), 30 deletions(-) create mode 100644 SourceGen/SGTestData/Expected/2007-labels-and-symbols_64tass.S diff --git a/Asm65/Formatter.cs b/Asm65/Formatter.cs index 31eb551..b7d9f0c 100644 --- a/Asm65/Formatter.cs +++ b/Asm65/Formatter.cs @@ -81,12 +81,12 @@ namespace Asm65 { public CharConvMode mHexDumpCharConvMode; // character conversion mode for dumps // Hopefully we don't need a separate mode for every assembler in existence. - public enum ExpressionMode { Unknown = 0, Simple, Cc65, Merlin }; + public enum ExpressionMode { Unknown = 0, Common, Cc65, Merlin }; public ExpressionMode mExpressionMode; // symbol rendering mode // Deserialization helper. public static ExpressionMode ParseExpressionMode(string str) { - ExpressionMode em = ExpressionMode.Simple; + ExpressionMode em = ExpressionMode.Common; if (!string.IsNullOrEmpty(str)) { if (Enum.TryParse(str, out ExpressionMode pem)) { em = pem; diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 56231b5..888855d 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -185,7 +185,7 @@ namespace SourceGen.AsmGen { config.mFullLineCommentDelimiterBase = ";"; config.mBoxLineCommentDelimiter = ";"; config.mAllowHighAsciiCharConst = false; - config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Simple; + config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common; } // IGenerator diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs index 63ae82a..ce98f1a 100644 --- a/SourceGen/PseudoOp.cs +++ b/SourceGen/PseudoOp.cs @@ -537,8 +537,8 @@ namespace SourceGen { StringBuilder sb = new StringBuilder(); switch (formatter.ExpressionMode) { - case Formatter.FormatConfig.ExpressionMode.Simple: - FormatNumericSymbolSimple(formatter, sym, labelMap, + case Formatter.FormatConfig.ExpressionMode.Common: + FormatNumericSymbolCommon(formatter, sym, labelMap, dfd, operandValue, operandLen, isPcRel, sb); break; case Formatter.FormatConfig.ExpressionMode.Cc65: @@ -568,7 +568,7 @@ namespace SourceGen { /// /// Format the symbol and adjustment using common expression syntax. /// - private static void FormatNumericSymbolSimple(Formatter formatter, Symbol sym, + private static void FormatNumericSymbolCommon(Formatter formatter, Symbol sym, Dictionary labelMap, FormatDescriptor dfd, int operandValue, int operandLen, bool isPcRel, StringBuilder sb) { // We could have some simple code that generated correct output, shifting and @@ -584,7 +584,8 @@ namespace SourceGen { } if (operandLen == 1) { - // Use the byte-selection operator to get the right piece. + // Use the byte-selection operator to get the right piece. In 64tass the + // selection operator has a very low precedence, similar to Merlin 32. string selOp; if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) { symbolValue = (sym.Value >> 16) & 0xff; @@ -604,25 +605,36 @@ namespace SourceGen { selOp = "<"; } } - sb.Append(selOp); - sb.Append(symLabel); operandValue &= 0xff; + + if (operandValue != symbolValue && + dfd.SymbolRef.ValuePart != WeakSymbolRef.Part.Low) { + // Adjustment is required to an upper-byte part. + sb.Append('('); + sb.Append(selOp); + sb.Append(symLabel); + sb.Append(')'); + } else { + // no adjustment required + sb.Append(selOp); + sb.Append(symLabel); + } } else if (operandLen <= 4) { // Operands and values should be 8/16/24 bit unsigned quantities. 32-bit // support is really there so you can have a 24-bit pointer in a 32-bit hole. // Might need to adjust this if 32-bit signed quantities become interesting. uint mask = 0xffffffff >> ((4 - operandLen) * 8); - string shOp; + int rightShift; if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) { symbolValue = (sym.Value >> 16); - shOp = " >> 16"; + rightShift = 16; } else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) { symbolValue = (sym.Value >> 8); - shOp = " >> 8"; + rightShift = 8; } else { symbolValue = sym.Value; - shOp = ""; + rightShift = 0; } if (isPcRel) { @@ -634,20 +646,41 @@ namespace SourceGen { symbolValue &= 0xffff; } - sb.Append(symLabel); - sb.Append(shOp); + bool needMask = false; if (symbolValue > mask) { // Post-shift value won't fit in an operand-size box. symbolValue = (int) (symbolValue & mask); - sb.Append(" & "); - sb.Append(formatter.FormatHexValue((int)mask, 2)); - } - - if (sb.Length != symLabel.Length) { - sb.Append(' '); + needMask = true; } operandValue = (int)(operandValue & mask); + + // Generate one of: + // label [+ adj] + // (label >> rightShift) [+ adj] + // (label & mask) [+ adj] + // ((label >> rightShift) & mask) [+ adj] + + if (rightShift != 0 || needMask) { + if (rightShift != 0 && needMask) { + sb.Append("0+(("); + } else { + sb.Append("0+("); + } + } + sb.Append(symLabel); + + if (rightShift != 0) { + sb.Append(" >> "); + sb.Append(rightShift.ToString()); + sb.Append(')'); + } + + if (needMask) { + sb.Append(" & "); + sb.Append(formatter.FormatHexValue((int)mask, 2)); + sb.Append(')'); + } } else { Debug.Assert(false, "bad numeric len"); sb.Append("?????"); @@ -704,9 +737,6 @@ namespace SourceGen { operandValue &= 0xff; } else if (operandLen <= 4) { - // Operands and values should be 8/16/24 bit unsigned quantities. 32-bit - // support is really there so you can have a 24-bit pointer in a 32-bit hole. - // Might need to adjust this if 32-bit signed quantities become interesting. uint mask = 0xffffffff >> ((4 - operandLen) * 8); string shOp; if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) { @@ -737,12 +767,11 @@ namespace SourceGen { sb.Append(" & "); sb.Append(formatter.FormatHexValue((int)mask, 2)); } + operandValue = (int)(operandValue & mask); if (sb.Length != symLabel.Length) { sb.Append(' '); } - - operandValue = (int)(operandValue & mask); } else { Debug.Assert(false, "bad numeric len"); sb.Append("?????"); diff --git a/SourceGen/SGTestData/Expected/2007-labels-and-symbols_64tass.S b/SourceGen/SGTestData/Expected/2007-labels-and-symbols_64tass.S new file mode 100644 index 0000000..9400b7e --- /dev/null +++ b/SourceGen/SGTestData/Expected/2007-labels-and-symbols_64tass.S @@ -0,0 +1,209 @@ +;Project was edited to add a label in the middle of a dense hex region, and add +;a duplicate label. + .cpu "65816" +zip = $cd +absl = $1029 +plataddr = $3000 ;address only in platform file +projalsa = $3200 ;same val as projalso +absh = $feed +biggie = $123456 +thirty2 = $12345678 ;32-bit constant test + +* = $012345 + .as + .xs +start clc + xce + sep #$30 + lda #zip + lda #zip+16 + lda #zip-192 + lda #absh + lda #absh)+1 + lda #absl + lda #absl)-1 + lda #start + lda #`start + pea $feed + pea 0+(start & $ffff) + pea $0001 + pea $3456 + pea $0012 + pea absh + pea 0+(start & $ffff) + pea 0+(start >> 16) + pea 0+(biggie & $ffff) + pea 0+(biggie >> 16) + lda zip+1 + lda @wzip+1 + lda @lzip+1 + lda absh-1 + lda @labsh-1 + lda absh+1 + lda @labsh+1 + lda 0+(start & $ffff)+1 + lda start+1 + lda 0+(start & $ffff)-1 + lda start-1 + lda 0+(biggie & $ffff)+1 + lda biggie+1 + lda 0+(biggie & $ffff)-1 + lda biggie-1 + rep #$30 + .al + .xl + lda #zip + lda #zip+16 + lda #zip+64 + lda #absl + lda #0+(absl >> 8) + lda #absl-4096 + lda #0+(absl >> 8)-16 + lda #0+(absl >> 16) + lda #absh + lda #0+(absh >> 8) + lda #absh-61440 + lda #0+(absh >> 8)+16 + lda #0+(absh >> 16)+1 + lda #0+(start & $ffff) + lda #0+(start >> 8) + lda #0+(start >> 16) + lda #0+(biggie & $ffff) + lda #0+(biggie >> 8) + lda #0+(biggie >> 16) + bra skipdata + + .byte zip + .byte absh + .byte start + .byte `start + .word zip + .word absl + .word 0+(absl >> 8) + .word absl-4096 + .word 0+(absl >> 8)-16 + .word absh + .word 0+(absh >> 8) + .word absh-61440 + .word 0+(absh >> 8)+16 + .word 0+(start & $ffff) + .word 0+(start >> 8) + .word 0+(start >> 16) + .word 0+(start & $ffff)+1 + .word 0+(start >> 8) + .word 0+(start >> 16) + .byte $fe,$ed + .long zip + .long absh + .long 0+(absh >> 8) + .long start + .long 0+(start >> 8) + .long 0+(start >> 16) + .dword zip + .dword absh + .dword 0+(absh >> 8) + .dword start-1 + .dword 0+(start >> 8) + .dword 0+(start >> 16) + +skipdata lda #0+(biggie >> 16)-1 + mvn `biggie,(`biggie)-17 + mvp `start,(`start)+17 + mvn 18,1 + mvp %00000001,%00010010 + per skipdata + brl nextchunk + +nextchunk jml L1000_1 + + .logical $1000 +L1000_1 nop +L1000 nop +L1000_0 nop +l1000 sep #$30 + .as + .xs + lda plataddr + lda $3100 + lda projalsa + lda $3300 + bra calls + + nop +targ nop + nop +L1016 per targ-1 + per targ + per targ+1 + jsr targ-1 + jsr targ + jsr targ+1 +L1028 bra targ-1 + +L102A bra targ + +L102C bra targ+1 + +L102E brl targ-1 + +L1031 brl targ + +L1034 brl targ+1 + +L1037 jmp targ-1 + +L103A jmp targ + +L103D jmp targ+1 + +L1040 jml targ-1 + + jml targ + + jml targ+1 + +calls jsr L1016 + jsr L1028 + jsr L102A + jsr L102C + jsr L102E + jsr L1031 + jsr L1034 + jsr L1037 + jsr L103A + jsr L103D + jsr L1040 + jsr $1044 + jsr $1048 + brl L118E + +bulk .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f ;bulky + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f +string .text "This is a long string. Put a label and comment on it to confir" ;stringy + .text "m that the label and comment only appear on the first line. T" + .text "he quick brown fox jumps over the lazy dogs." + +L118E lda #thirty2)+3 + lda #`thirty2 + rep #$30 + .al + .xl + lda #0+(thirty2 & $ffff)+3 + lda #0+((thirty2 >> 8) & $ffff)+4 + lda #0+(thirty2 >> 16) + rts + + .here diff --git a/SourceGen/SGTestData/Expected/2009-branches-and-banks_64tass.S b/SourceGen/SGTestData/Expected/2009-branches-and-banks_64tass.S index c80bd6a..a676e7e 100644 --- a/SourceGen/SGTestData/Expected/2009-branches-and-banks_64tass.S +++ b/SourceGen/SGTestData/Expected/2009-branches-and-banks_64tass.S @@ -40,14 +40,14 @@ LFFC3 brl L0003 .logical $440000 L440000 cmp L440000 L440004 lda L440000 - lda @wL440000 & $ffff + lda @w0+(L440000 & $ffff) lda L0000 bmi L440004 per high44 bne high44 brl L44FFC0 -dat44 .word dat44 & $ffff +dat44 .word 0+(dat44 & $ffff) .long dat44 .here @@ -62,8 +62,8 @@ L44FFCB jml L2000 .here .logical $2000 L2000 bit L2000 - pea dat44 & $ffff - pea dat44 >> 16 + pea 0+(dat44 & $ffff) + pea 0+(dat44 >> 16) bne L200E jml [lodat]