diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index b3c60545a..16f60bed4 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -663,6 +663,33 @@ greatereq_w .proc .pend +shiftleft_b .proc + inx + ldy c64.ESTACK_LO,x + bne + + rts ++ lda c64.ESTACK_LO+1,x +- asl a + dey + bne - + sta c64.ESTACK_LO+1,x + rts + .pend + +shiftright_b .proc + inx + ldy c64.ESTACK_LO,x + bne + + rts ++ lda c64.ESTACK_LO+1,x +- lsr a + dey + bne - + sta c64.ESTACK_LO+1,x + rts + .pend + + orig_stackpointer .byte 0 ; stores the Stack pointer register at program start func_exit .proc diff --git a/compiler/res/version.txt b/compiler/res/version.txt index a3ec5a4bd..a534c681e 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -3.2 +3.3-SNAPSHOT diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 6e394d38d..6fb4cd2fb 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -771,12 +771,6 @@ internal class AstChecker(private val program: Program, if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes) errors.err("bitwise operator can only be used on integer operands", expr.right.position) } - "<<", ">>" -> { - // for now, bit-shifts can only shift by a constant number TODO remove this restriction - val constRight = expr.right.constValue(program) - if(constRight==null) - errors.err("bit-shift can only be done by a constant number (for now)", expr.right.position) - } } if(leftDt !in NumericDatatypes) diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index b07ac478d..92ec40761 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -336,8 +336,8 @@ open class Assignment(var target: AssignTarget, var value: Expression, override get() { val binExpr = value as? BinaryExpression if(binExpr!=null) { - if(binExpr.right !is BinaryExpression && binExpr.left isSameAs target) - return true // A = A v + if(binExpr.left isSameAs target) + return true // A = A Something if(binExpr.operator in associativeOperators) { if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt index 4a1a1a6e4..d11e5ec4a 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AugmentableAssignmentAsmGen.kt @@ -38,19 +38,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } private fun inplaceBinary(target: AssignTarget, binExpr: BinaryExpression, assign: Assignment) { - - if (binExpr.right !is BinaryExpression && binExpr.left isSameAs target) { - // A = A 5 + if (binExpr.left isSameAs target) { + // A = A Something return inplaceModification(target, binExpr.operator, binExpr.right, assign) } if (binExpr.operator in associativeOperators) { - val leftBinExpr = binExpr.left as? BinaryExpression - if (leftBinExpr != null && binExpr.right isSameAs target) { + if (binExpr.right isSameAs target) { // A = 5 A return inplaceModification(target, binExpr.operator, binExpr.left, assign) } + val leftBinExpr = binExpr.left as? BinaryExpression if (leftBinExpr?.operator == binExpr.operator) { // TODO better optimize the chained asm to avoid intermediate stores/loads? when { @@ -128,8 +127,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } in WordDatatypes -> { when { - valueLv != null -> inplaceModification_word_litval_to_variable(name, operator, valueLv.toInt()) - ident != null -> inplaceModification_word_variable_to_variable(name, operator, ident) + valueLv != null -> inplaceModification_word_litval_to_variable(name, dt, operator, valueLv.toInt()) + ident != null -> inplaceModification_word_variable_to_variable(name, dt, operator, ident) // TODO more specialized code for types such as memory read etc. // value is DirectMemoryRead -> { // println("warning: slow stack evaluation used (8): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO @@ -145,9 +144,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // } value is TypecastExpression -> { if (tryRemoveRedundantCast(value, target, operator, origAssign)) return - inplaceModification_word_value_to_variable(name, operator, value) + inplaceModification_word_value_to_variable(name, dt, operator, value) } - else -> inplaceModification_word_value_to_variable(name, operator, value) + else -> inplaceModification_word_value_to_variable(name, dt, operator, value) } } DataType.FLOAT -> { @@ -226,7 +225,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, val childDt = value.expression.inferType(program).typeOrElse(DataType.STRUCT) if (value.type.equalsSize(childDt) || value.type.largerThan(childDt)) { // this typecast is redundant here; the rest of the code knows how to deal with the uncasted value. - println("***(test) removing redundant typecast ${value.position} $childDt") // TODO inplaceModification(target, operator, value.expression, origAssign) return true } @@ -269,9 +267,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, ldx ${C64Zeropage.SCRATCH_REG_X} """) } - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier "/" -> { - TODO() + TODO("div") // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") } "%" -> { @@ -331,11 +329,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, ldx ${C64Zeropage.SCRATCH_REG_X} """) } - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier "/" -> { if (value == 0.0) throw AssemblyError("division by zero") - TODO() + TODO("div") // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") } "%" -> { @@ -370,16 +368,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, loadByteFromPointerIntoA() asmgen.out(" sec | sbc $ESTACK_LO_PLUS1_HEX,x | sta (${C64Zeropage.SCRATCH_W1}),y") } - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("byte remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // asmgen.out(" jsr prog8_lib.remainder_ub") } - "<<" -> TODO("byte asl") - ">>" -> TODO("byte lsr") + "<<" -> TODO("ubyte asl") + ">>" -> TODO("ubyte lsr") "&" -> { loadByteFromPointerIntoA() asmgen.out(" and $ESTACK_LO_PLUS1_HEX,x | sta (${C64Zeropage.SCRATCH_W1}),y") @@ -418,16 +416,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, loadByteFromPointerIntoA() asmgen.out(" sec | sbc $otherName | sta (${C64Zeropage.SCRATCH_W1}),y") } - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("byte remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // asmgen.out(" jsr prog8_lib.remainder_ub") } - "<<" -> TODO("byte asl") - ">>" -> TODO("byte lsr") + "<<" -> TODO("ubyte asl") + ">>" -> TODO("ubyte lsr") "&" -> { loadByteFromPointerIntoA() asmgen.out(" and $otherName | sta (${C64Zeropage.SCRATCH_W1}),y") @@ -464,8 +462,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, loadByteFromPointerInA() asmgen.out(" sec | sbc #$value | sta (${C64Zeropage.SCRATCH_W1}),y") } - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("byte remainder") // if(types==DataType.BYTE) @@ -473,16 +471,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // asmgen.out(" jsr prog8_lib.remainder_ub") } "<<" -> { - if (value > 1) { + if (value > 0) { loadByteFromPointerInA() - repeat(value.toInt()) { asmgen.out(" asl a") } + repeat(value) { asmgen.out(" asl a") } asmgen.out(" sta (${C64Zeropage.SCRATCH_W1}),y") } } ">>" -> { - if (value > 1) { + if (value > 0) { loadByteFromPointerInA() - repeat(value.toInt()) { asmgen.out(" lsr a") } + repeat(value) { asmgen.out(" lsr a") } asmgen.out(" sta (${C64Zeropage.SCRATCH_W1}),y") } } @@ -502,7 +500,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_word_litval_to_variable(name: String, operator: String, value: Int) { + private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) { when (operator) { // note: ** (power) operator requires floats. // TODO use the + and - optimizations in the expression asm code as well. @@ -577,17 +575,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // asmgen.out(" jsr prog8_lib.remainder_ub") } "<<" -> { - if (value > 1) { - asmgen.out(" lda $name") - TODO("word asl") - asmgen.out(" sta $name") - } + repeat(value) { asmgen.out(" asl $name | rol $name+1") } } ">>" -> { - if (value > 1) { - asmgen.out(" lda $name") - TODO("word lsr") - asmgen.out(" sta $name") + if (value > 0) { + if(dt==DataType.UWORD) { + repeat(value) { asmgen.out(" lsr $name+1 | ror $name")} + } else { + repeat(value) { asmgen.out(" lda $name+1 | asl a | ror $name+1 | ror $name") } + } } } "&" -> { @@ -615,7 +611,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_word_variable_to_variable(name: String, operator: String, ident: IdentifierReference) { + private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { val otherName = asmgen.asmIdentifierName(ident) val valueDt = ident.targetVarDecl(program.namespace)!!.datatype when (valueDt) { @@ -639,14 +635,39 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, bcs + dec $name+1 + """) - "*" -> TODO() - "/" -> TODO() + "*" -> TODO("mul") + "/" -> TODO("div") "%" -> TODO("word remainder") - "<<" -> TODO() - ">>" -> TODO() - "&" -> TODO() - "^" -> TODO() - "|" -> TODO() + "<<" -> { + asmgen.out(""" + ldy $otherName +- asl $name + rol $name+1 + dey + bne -""") + } + ">>" -> { + if(dt==DataType.UWORD) { + asmgen.out(""" + ldy $otherName +- lsr $name+1 + ror $name + dey + bne -""") + } else { + asmgen.out(""" + ldy $otherName +- lda $name+1 + asl a + ror $name+1 + ror $name + dey + bne -""") + } + } + "&" -> TODO("bitand") + "^" -> TODO("bitxor") + "|" -> TODO("bitor") else -> throw AssemblyError("invalid operator for in-place modification $operator") } } @@ -656,16 +677,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // note: ** (power) operator requires floats. "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1") "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1") - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("word remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // asmgen.out(" jsr prog8_lib.remainder_ub") } - "<<" -> TODO() - ">>" -> TODO() + "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") "^" -> asmgen.out(" lda $name | xor $otherName | sta $name | lda $name+1 | xor $otherName+1 | sta $name+1") "|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") @@ -678,7 +698,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } - private fun inplaceModification_word_value_to_variable(name: String, operator: String, value: Expression) { + private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { // this should be the last resort for code generation for this, // because the value is evaluated onto the eval stack (=slow). println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO @@ -706,14 +726,50 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, bcs + dec $name+1 + """) - "*" -> TODO() - "/" -> TODO() + "*" -> TODO("mul") + "/" -> TODO("div") "%" -> TODO("word remainder") - "<<" -> TODO() - ">>" -> TODO() - "&" -> TODO() - "^" -> TODO() - "|" -> TODO() + "<<" -> { + asmgen.translateExpression(value) + asmgen.out(""" + inx + ldy c64.ESTACK_LO,x + beq + +- asl $name + rol $name+1 + dey + bne - ++""") + } + ">>" -> { + asmgen.translateExpression(value) + if(dt==DataType.UWORD) { + asmgen.out(""" + inx + ldy c64.ESTACK_LO,x + beq + +- lsr $name+1 + ror $name + dey + bne - ++""") } + else { + asmgen.out(""" + inx + ldy c64.ESTACK_LO,x + beq + +- lda $name+1 + asl a + ror $name+1 + ror $name + dey + bne - ++""") + } + } + "&" -> TODO("bitand") + "^" -> TODO("bitxor") + "|" -> TODO("bitor") else -> throw AssemblyError("invalid operator for in-place modification $operator") } } @@ -723,16 +779,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // note: ** (power) operator requires floats. "+" -> asmgen.out(" lda $name | clc | adc $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | adc $ESTACK_HI_PLUS1_HEX,x | sta $name+1") "-" -> asmgen.out(" lda $name | sec | sbc $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | sbc $ESTACK_HI_PLUS1_HEX,x | sta $name+1") - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("word remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // asmgen.out(" jsr prog8_lib.remainder_ub") } - "<<" -> TODO() - ">>" -> TODO() + "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "&" -> asmgen.out(" lda $name | and $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | and $ESTACK_HI_PLUS1_HEX,x | sta $name+1") "^" -> asmgen.out(" lda $name | xor $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | xor $ESTACK_HI_PLUS1_HEX,x | sta $name+1") "|" -> asmgen.out(" lda $name | ora $ESTACK_LO_PLUS1_HEX,x | sta $name | lda $name+1 | ora $ESTACK_HI_PLUS1_HEX,x | sta $name+1") @@ -756,16 +811,49 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // note: ** (power) operator requires floats. "+" -> asmgen.out(" lda $name | clc | adc $ESTACK_LO_PLUS1_HEX,x | sta $name") "-" -> asmgen.out(" lda $name | sec | sbc $ESTACK_LO_PLUS1_HEX,x | sta $name") - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("byte remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // asmgen.out(" jsr prog8_lib.remainder_ub") } - "<<" -> TODO() - ">>" -> TODO() + "<<" -> { + asmgen.translateExpression(value) + asmgen.out(""" + inx + ldy c64.ESTACK_LO,x + beq + +- asl $name + dey + bne - ++""") + } + ">>" -> { + asmgen.translateExpression(value) + if(dt==DataType.UBYTE) { + asmgen.out(""" + inx + ldy c64.ESTACK_LO,x + beq + +- lsr $name + dey + bne - ++""") + } else { + asmgen.out(""" + inx + ldy c64.ESTACK_LO,x + beq + +- lda $name + asl a + ror $name + dey + bne - ++""") + } + } "&" -> asmgen.out(" lda $name | and $ESTACK_LO_PLUS1_HEX,x | sta $name") "^" -> asmgen.out(" lda $name | xor $ESTACK_LO_PLUS1_HEX,x | sta $name") "|" -> asmgen.out(" lda $name | ora $ESTACK_LO_PLUS1_HEX,x | sta $name") @@ -780,16 +868,38 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // note: ** (power) operator requires floats. "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name") "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name") - "*" -> TODO()// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO()// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") "%" -> { TODO("byte remainder") // if(types==DataType.BYTE) // throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") // asmgen.out(" jsr prog8_lib.remainder_ub") } - "<<" -> TODO() - ">>" -> TODO() + "<<" -> { + asmgen.out(""" + ldy $otherName +- asl $name + dey + bne -""") + } + ">>" -> { + if(dt==DataType.UBYTE) { + asmgen.out(""" + ldy $otherName +- lsr $name + dey + bne -""") + } else { + asmgen.out(""" + ldy $otherName +- lda $name + asl a + ror $name + dey + bne -""") + } + } "&" -> asmgen.out(" lda $name | and $otherName | sta $name") "^" -> asmgen.out(" lda $name | xor $otherName | sta $name") "|" -> asmgen.out(" lda $name | ora $otherName | sta $name") @@ -828,17 +938,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, // asmgen.out(" jsr prog8_lib.remainder_ub") } "<<" -> { - if (value > 1) { - asmgen.out(" lda $name") - repeat(value.toInt()) { asmgen.out(" asl a") } - asmgen.out(" sta $name") - } + repeat(value) { asmgen.out(" asl $name") } } ">>" -> { - if (value > 1) { - asmgen.out(" lda $name") - repeat(value.toInt()) { asmgen.out(" lsr a") } - asmgen.out(" sta $name") + if(value>0) { + if (dt == DataType.UBYTE) { + repeat(value) { asmgen.out(" lsr $name") } + } else { + repeat(value) { asmgen.out(" lda $name | asl a | ror $name") } + } } } "&" -> asmgen.out(" lda $name | and #$value | sta $name") diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index 72929a481..b63d6efb7 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -207,79 +207,78 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge // see if we can apply some optimized routines when(expr.operator) { ">>" -> { - // bit-shifts are always by a constant number (for now) translateExpression(expr.left) - val amount = expr.right.constValue(program)!!.number.toInt() - when (leftDt) { - DataType.UBYTE -> { - if(amount<=2) - repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") } - else { - asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x") - repeat(amount) { asmgen.out(" lsr a") } - asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") + val amount = expr.right.constValue(program)?.number?.toInt() + if(amount!=null) { + when (leftDt) { + DataType.UBYTE -> { + if (amount <= 2) + repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") } + else { + asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x") + repeat(amount) { asmgen.out(" lsr a") } + asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") + } } - } - DataType.BYTE -> { - if(amount<=2) - repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") } - else { - asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | sta ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") - repeat(amount) { asmgen.out(" asl a | ror ${C64MachineDefinition.C64Zeropage.SCRATCH_B1} | lda ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") } - asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") + DataType.BYTE -> { + if (amount <= 2) + repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") } + else { + asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | sta ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") + repeat(amount) { asmgen.out(" asl a | ror ${C64MachineDefinition.C64Zeropage.SCRATCH_B1} | lda ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") } + asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") + } } - } - DataType.UWORD -> { - var left = amount - while(left>=7) { - asmgen.out(" jsr math.shift_right_uw_7") - left -= 7 + DataType.UWORD -> { + var left = amount + while (left >= 7) { + asmgen.out(" jsr math.shift_right_uw_7") + left -= 7 + } + if (left in 0..2) + repeat(left) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") } + else + asmgen.out(" jsr math.shift_right_uw_$left") } - if (left in 0..2) - repeat(left) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") } - else - asmgen.out(" jsr math.shift_right_uw_$left") - } - DataType.WORD -> { - var left = amount - while(left>=7) { - asmgen.out(" jsr math.shift_right_w_7") - left -= 7 + DataType.WORD -> { + var left = amount + while (left >= 7) { + asmgen.out(" jsr math.shift_right_w_7") + left -= 7 + } + if (left in 0..2) + repeat(left) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") } + else + asmgen.out(" jsr math.shift_right_w_$left") } - if (left in 0..2) - repeat(left) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") } - else - asmgen.out(" jsr math.shift_right_w_$left") + else -> throw AssemblyError("weird type") } - else -> throw AssemblyError("weird type") } - return } "<<" -> { - // bit-shifts are always by a constant number (for now) translateExpression(expr.left) - val amount = expr.right.constValue(program)!!.number.toInt() - if (leftDt in ByteDatatypes) { - if(amount<=2) - repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") } - else { - asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x") - repeat(amount) { asmgen.out(" asl a") } - asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") + val amount = expr.right.constValue(program)?.number?.toInt() + if(amount!=null) { + if (leftDt in ByteDatatypes) { + if (amount <= 2) + repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") } + else { + asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x") + repeat(amount) { asmgen.out(" asl a") } + asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x") + } + } else { + var left = amount + while (left >= 7) { + asmgen.out(" jsr math.shift_left_w_7") + left -= 7 + } + if (left in 0..2) + repeat(left) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") } + else + asmgen.out(" jsr math.shift_left_w_$left") } } - else { - var left=amount - while(left>=7) { - asmgen.out(" jsr math.shift_left_w_7") - left -= 7 - } - if (left in 0..2) - repeat(left) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") } - else - asmgen.out(" jsr math.shift_left_w_$left") - } - return } "*" -> { val value = expr.right.constValue(program) @@ -433,7 +432,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge inx sta $ESTACK_LO_PLUS1_HEX,x """) - "<<", ">>" -> throw AssemblyError("bit-shifts not via stack") + "<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b") + ">>" -> asmgen.out(" jsr prog8_lib.shiftright_b") "<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b") ">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b") "<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b") diff --git a/docs/source/programming.rst b/docs/source/programming.rst index c27193b9d..8133da761 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -766,16 +766,6 @@ rndw() rndf() returns a pseudo-random float between 0.0 and 1.0 -lsl(x) - Shift the bits in x (byte or word) one position to the left. - Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag) - Modifies in-place, doesn't return a value (so can't be used in an expression). - -lsr(x) - Shift the bits in x (byte or word) one position to the right. - The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag) - Modifies in-place, doesn't return a value (so can't be used in an expression). - rol(x) Rotate the bits in x (byte or word) one position to the left. This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag, diff --git a/examples/test.p8 b/examples/test.p8 index d33c2febc..882232c77 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,7 +5,54 @@ main { + ; todo make it possible to use cpu opcodes as varnames such as 'nop' + sub start() { + byte ub + word uw + ubyte x = 1 + ubyte z = 0 + + ubyte[] array = [1,2,3] + + ubyte i + ub = %00100001 + for i in 0 to 9 { + c64scr.print_ubbin(ub as ubyte, true) + c64.CHROUT('\n') + ;ub <<= 1 + ;ub <<= x + ub <<= (z+1) + } + c64.CHROUT('\n') + ;ub = %11000110 ; -123 + ub = -123 + for i in 0 to 9 { + c64scr.print_ubbin(ub as ubyte, true) + c64.CHROUT('\n') + ;ub >>= 1 + ;ub >>= x + ub >>= (z+1) + } + + uw = %0011100000000011 + for i in 0 to 17 { + c64scr.print_uwbin(uw as uword, true) + c64.CHROUT('\n') + ;uw <<= 1 + ;uw <<= x + uw <<= (z+1) + } + c64.CHROUT('\n') + uw = -12345 + ;uw = %1110000011000100 + for i in 0 to 17 { + c64scr.print_uwbin(uw as uword, true) + c64.CHROUT('\n') + ;uw >>= 1 + ;uw >>= x + uw >>= (z+1) + } } }