diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 6bfc22461..50aaa979b 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -400,7 +400,9 @@ enum class RegisterOrPair { FAC2, // cx16 virtual registers: R0, R1, R2, R3, R4, R5, R6, R7, - R8, R9, R10, R11, R12, R13, R14, R15; + R8, R9, R10, R11, R12, R13, R14, R15, + // combined virtual registers to store 32 bits longs: + R0R1_32, R2R3_32, R4R5_32, R6R7_32, R8R9_32, R10R11_32, R12R13_32, R14R15_32; companion object { val names by lazy { entries.map { it.toString()} } @@ -427,12 +429,13 @@ enum class RegisterOrPair { BaseDataType.BYTE -> "sL" BaseDataType.WORD -> "s" BaseDataType.UWORD, null -> "" - else -> throw IllegalArgumentException("invalid register param type") + else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg") } return listOf("cx16", name.lowercase()+suffix) } fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters + fun isLong() = this in combinedLongRegisters } // only used in parameter and return value specs in asm subroutines @@ -469,6 +472,17 @@ val Cx16VirtualRegisters = arrayOf( RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 ) +val combinedLongRegisters = arrayOf( + RegisterOrPair.R0R1_32, + RegisterOrPair.R2R3_32, + RegisterOrPair.R4R5_32, + RegisterOrPair.R6R7_32, + RegisterOrPair.R8R9_32, + RegisterOrPair.R10R11_32, + RegisterOrPair.R12R13_32, + RegisterOrPair.R14R15_32 +) + val CpuRegisters = arrayOf( RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y, RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 8a8412bdc..ea7757e07 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -914,8 +914,7 @@ class AsmGen6502Internal ( when (target.kind) { TargetStorageKind.VARIABLE -> { out(" lda $valuesym | sta ${target.asmVarname}") - signExtendVariableLsb(target.asmVarname, value.value.type.base) - signExtendLongVariableMsw(target.asmVarname, value.value.type.base) + signExtendLongVariable(target.asmVarname, value.value.type.base) } TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") @@ -931,11 +930,20 @@ class AsmGen6502Internal ( sta ${target.asmVarname} lda $valuesym+1 sta ${target.asmVarname}+1""") - signExtendLongVariableMsw(target.asmVarname, value.value.type.base) + signExtendLongVariable(target.asmVarname, value.value.type.base) } TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") - TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}") + TargetStorageKind.REGISTER -> { + require(target.register in combinedLongRegisters) + val startreg = target.register.toString().take(2).lowercase() + out(""" + lda $valuesym + sta cx16.$startreg + lda $valuesym+1 + sta cx16.$startreg+1""") + signExtendLongVariable("cx16.$startreg", value.value.type.base) + } TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}") TargetStorageKind.VOID -> { /* do nothing */ } } @@ -1384,7 +1392,7 @@ $repeatLabel""") } } - internal fun signExtendLongVariableMsw(asmvar: String, valueDt: BaseDataType) { + internal fun signExtendLongVariable(asmvar: String, valueDt: BaseDataType) { // sign extend signed word in a var to a full long in that variable when(valueDt) { BaseDataType.UBYTE -> { @@ -1421,7 +1429,7 @@ $repeatLabel""") + sta $asmvar+2 sta $asmvar+3""") } - else -> throw AssemblyError("need byte type") + else -> throw AssemblyError("need byte or word type") } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index 8c39eb101..626427146 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -133,6 +133,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, val dt = if(signed) DataType.WORD else DataType.UWORD AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) } + + RegisterOrPair.R0R1_32, + RegisterOrPair.R2R3_32, + RegisterOrPair.R4R5_32, + RegisterOrPair.R6R7_32, + RegisterOrPair.R8R9_32, + RegisterOrPair.R10R11_32, + RegisterOrPair.R12R13_32, + RegisterOrPair.R14R15_32 -> { + val dt = if(signed) DataType.LONG else TODO("unsigned long") + AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) + } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 52c5d8ec9..82bdcb37d 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -656,7 +656,7 @@ internal class AssignmentAsmGen( } returnDt== BaseDataType.LONG -> { // longs are in R0:R1 (r0=lsw, r1=msw) - assignRegisterLong(target, RegisterOrPair.R0, RegisterOrPair.R1) + assignRegisterLong(target, RegisterOrPair.R0R1_32) } returnDt==BaseDataType.FLOAT -> { // float result from function sits in FAC1 @@ -1244,7 +1244,7 @@ internal class AssignmentAsmGen( asmgen.out(" jsr prog8_math.asl_byte_A") assignRegisterByte(target, CpuRegister.A, signed, true) return true - } else { + } else if(dt.isWord) { assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed) asmgen.restoreRegisterStack(CpuRegister.X, true) if(expr.operator==">>") @@ -1256,6 +1256,12 @@ internal class AssignmentAsmGen( asmgen.out(" jsr prog8_math.asl_word_AY") assignRegisterpairWord(target, RegisterOrPair.AY) return true + } else if(dt.isLong) { + assignExpressionToRegister(expr.left, RegisterOrPair.R0R1_32, signed) + asmgen.out(" pla | sta P8ZP_SCRATCH_REG") + augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r0", expr.operator, "P8ZP_SCRATCH_REG") + assignRegisterLong(target, RegisterOrPair.R0R1_32) + return true } } else { @@ -1375,6 +1381,11 @@ internal class AssignmentAsmGen( return true } } + } else if(dt.isLong) { + assignExpressionToRegister(expr.left, RegisterOrPair.R0R1_32, signed) + augmentableAsmGen.inplacemodificationLongWithLiteralval("cx16.r0", expr.operator, shifts) + assignRegisterLong(target, RegisterOrPair.R0R1_32) + return true } } return false @@ -1787,7 +1798,11 @@ internal class AssignmentAsmGen( } } else -> { - TODO("add/subtract long ${target.position} - use simple expressions and temporary variables for now") + assignExpressionToRegister(expr.left, RegisterOrPair.R2R3_32, expr.left.type.isSigned) + assignExpressionToRegister(expr.right, RegisterOrPair.R0R1_32, expr.right.type.isSigned) + augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r2", expr.operator, "cx16.r0") + assignRegisterLong(target, RegisterOrPair.R2R3_32) + return true } } } @@ -2519,8 +2534,8 @@ $endLabel""") } if(targetDt.isInteger && valueDt.isByteOrBool && valueDt.isAssignableTo(targetDt)) { - require(targetDt.isWord) { - "should be byte to word assignment ${origTypeCastExpression.position}" + require(targetDt.isWord || targetDt.isLong) { + "should be byte to word or long assignment ${origTypeCastExpression.position}" } when(target.kind) { // TargetStorageKind.VARIABLE -> { @@ -2541,7 +2556,7 @@ $endLabel""") return } TargetStorageKind.REGISTER -> { - // byte to word, just assign to registers + // byte to word or long, just assign to registers assignExpressionToRegister(value, target.register!!, targetDt.isSigned) return } @@ -2686,7 +2701,7 @@ $endLabel""") BaseDataType.LONG -> { asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName") asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE) - asmgen.signExtendLongVariableMsw(targetAsmVarName, BaseDataType.WORD) + asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD) } BaseDataType.FLOAT -> { asmgen.out(""" @@ -2752,7 +2767,7 @@ $endLabel""") sta $targetAsmVarName lda $sourceAsmVarName+1 sta $targetAsmVarName+1""") - asmgen.signExtendLongVariableMsw(targetAsmVarName, BaseDataType.WORD) + asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD) } BaseDataType.FLOAT -> { asmgen.out(""" @@ -3124,22 +3139,89 @@ $endLabel""") } private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) { - require(sourceDt.isLong) + require(sourceDt.isByte || sourceDt.isWord || sourceDt.isLong) { + "need byte/word/long as source value to assign to long variable $varName ${target.position}" + } when(target.kind) { TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda $varName - sta ${target.asmVarname} - lda $varName+1 - sta ${target.asmVarname}+1 - lda $varName+2 - sta ${target.asmVarname}+2 - lda $varName+3 - sta ${target.asmVarname}+3""") + when(sourceDt) { + DataType.BYTE -> { + TODO("signed byte to long var") + } + DataType.UBYTE -> { + TODO("ubyte to long var") + } + DataType.WORD -> { + TODO("signed word to long var") + } + DataType.UWORD -> { + TODO("ubyte to long var") + } + DataType.LONG -> { + asmgen.out(""" + lda $varName + sta ${target.asmVarname} + lda $varName+1 + sta ${target.asmVarname}+1 + lda $varName+2 + sta ${target.asmVarname}+2 + lda $varName+3 + sta ${target.asmVarname}+3""") + } + else -> throw AssemblyError("wrong dt ${target.position}") + } } TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") - TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}") + TargetStorageKind.REGISTER -> { + require(target.register in combinedLongRegisters) + val regstart = target.register.toString().take(2).lowercase() + when(sourceDt) { + DataType.BYTE -> { + asmgen.out(" lda $varName | sta cx16.$regstart") + asmgen.signExtendLongVariable("cx16.$regstart", sourceDt.base) + } + DataType.UBYTE -> { + asmgen.out(""" + lda $varName + sta cx16.$regstart + lda #0 + sta cx16.$regstart+1 + sta cx16.$regstart+2 + sta cx16.$regstart+3""") + } + DataType.WORD -> { + asmgen.out(""" + lda $varName + sta cx16.$regstart + lda $varName+1 + sta cx16.$regstart+1""") + asmgen.signExtendLongVariable("cx16.$regstart", sourceDt.base) + } + DataType.UWORD -> { + asmgen.out(""" + lda $varName + sta cx16.$regstart + lda $varName+1 + sta cx16.$regstart+1 + lda #0 + sta cx16.$regstart+2 + sta cx16.$regstart+3""") + } + DataType.LONG -> { + asmgen.out(""" + lda $varName + sta cx16.$regstart + lda $varName+1 + sta cx16.$regstart+1 + lda $varName+2 + sta cx16.$regstart+2 + lda $varName+3 + sta cx16.$regstart+3""") + } + else -> throw AssemblyError("wrong dt ${target.position}") + } + } TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}") TargetStorageKind.VOID -> { /* do nothing */ } } @@ -3624,18 +3706,22 @@ $endLabel""") } } - internal fun assignRegisterLong(target: AsmAssignTarget, lsw: RegisterOrPair, msw: RegisterOrPair) { + internal fun assignRegisterLong(target: AsmAssignTarget, pairedRegisters: RegisterOrPair) { when(target.kind) { TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda cx16.r0L - sta ${target.asmVarname} - lda cx16.r0H - sta ${target.asmVarname}+1 - lda cx16.r1L - sta ${target.asmVarname}+2 - lda cx16.r1H - sta ${target.asmVarname}+3""") + if(pairedRegisters in combinedLongRegisters) { + val startreg = pairedRegisters.name.take(2).lowercase() + asmgen.out(""" + lda cx16.$startreg + sta ${target.asmVarname} + lda cx16.$startreg+1 + sta ${target.asmVarname}+1 + lda cx16.$startreg+2 + sta ${target.asmVarname}+2 + lda cx16.$startreg+3 + sta ${target.asmVarname}+3""") + } + else throw AssemblyError("only combined vreg allowed as long target ${target.position}") } TargetStorageKind.ARRAY -> { TODO("assign 32 bits int in R0:R1 into array ${target.position}") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index 2e70f1b7e..fb55d5f7f 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -553,7 +553,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } - private fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) { + internal fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) { when(operator) { "+" -> { asmgen.out(""" @@ -658,7 +658,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } - private fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) { + internal fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) { fun inplaceLongShiftLeft() { when { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 55755ea1b..49ea500c9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -797,11 +797,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } BaseDataType.UWORD -> { actualResultReg2 = codeGen.registers.next(IRDataType.LONG) - addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null) + addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null) } BaseDataType.WORD -> { actualResultReg2 = codeGen.registers.next(IRDataType.LONG) - addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null) + addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null) } else -> throw AssemblyError("weird cast value type ${cast.position}") } diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index a1525b8a2..ed76e491f 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -35,11 +35,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors: override fun after(numLiteral: NumericLiteral, parent: Node): Iterable { - if(numLiteral.type==BaseDataType.LONG && parent !is Assignment && parent !is VarDecl) { + if(numLiteral.type==BaseDataType.LONG) { // see if LONG values may be reduced to something smaller val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position) if(smaller.type!=BaseDataType.LONG) { - return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent)) + if(parent !is Assignment || !parent.target.inferType(program).isLong) { + // do NOT reduce the type if the target of the assignment is a long + return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent)) + } } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 7738f9c74..23a223e1d 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1554,7 +1554,11 @@ internal class AstChecker(private val program: Program, } else if(leftDt.isBool && rightDt.isByte || leftDt.isByte && rightDt.isBool) { // expression with one side BOOL other side (U)BYTE is allowed; bool==byte } else if((expr.operator == "<<" || expr.operator == ">>") - && (leftDt.isWord && rightDt.isByte || leftDt.isLong && rightDt.isByte || leftDt.isLong && rightDt.isWord)) { + && (leftDt.isByte && rightDt.isByte || + leftDt.isWord && rightDt.isByte || + leftDt.isWord && rightDt.isWord || + leftDt.isLong && rightDt.isByte || + leftDt.isLong && rightDt.isWord)) { // exception allowed: shifting a word by a byte, long by a word or byte } else if((expr.operator in BitwiseOperators) && (leftDt.isInteger && rightDt.isInteger)) { // exception allowed: bitwise operations with any integers diff --git a/docs/source/todo.rst b/docs/source/todo.rst index b1310e0dd..b069497ae 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,8 +4,7 @@ TODO LONG TYPE --------- - call convention: return long -> return it in R0+R1.... because AY is only 16 bits... -- call convention: long param -> passed as regular variable NOT via R0:R1 asmsubs don't have syntax for this so use explicit separate msw() and lsw() arguments... -- implement << >> expressions on longs +- call convention: long param -> passed as regular variable NOT via R0:R1? asmsubs don't have syntax for this so use explicit separate msw() and lsw() arguments... Or introduce new syntax for R0+R1 combo's? - implement the bitwise & | ^ operations as expressions on longs (all types args) - make sure == and != work with longs against byte and words as well signed and unsigned - how hard is it to also implement the other comparison operators on longs? diff --git a/examples/test.p8 b/examples/test.p8 index b034d4d01..2b1144521 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -15,7 +15,16 @@ main { txt.print_ulhex(lv1, false) txt.nl() - txt.print_l(lv1<<3) ; TODO fix compiler error + lv1 = 999999 + txt.print_l(lv1<<3) + txt.nl() + cx16.r4sL = -55 + lv1 = 999999 + txt.print_l(lv1+cx16.r4sL) + txt.nl() + cx16.r4s = -5555 + lv1 = 999999 + txt.print_l(lv1+cx16.r4s) txt.nl() lv2 <<= cx16.r0L @@ -51,6 +60,7 @@ main { b2 = lv2 != cx16.r0s b1 = lv1 != lv2 } +} /* sub start2() { @@ -174,4 +184,4 @@ main { } */ -} +;}