diff --git a/compiler/res/prog8lib/prog8_lib.asm b/compiler/res/prog8lib/prog8_lib.asm index 27cc09f9c..64031d216 100644 --- a/compiler/res/prog8lib/prog8_lib.asm +++ b/compiler/res/prog8lib/prog8_lib.asm @@ -1065,3 +1065,29 @@ _return_minusone lda #-1 rts .pend + + +sign_extend_stack_byte .proc + ; -- sign extend the (signed) byte on the stack to full 16 bits + lda P8ESTACK_LO+1,x + ora #$7f + bmi + + lda #0 ++ sta P8ESTACK_HI+1,x + rts + .pend + + +sign_extend_AY_byte .proc + ; -- sign extend the (signed) byte in AY to full 16 bits + pha + tya + and #$80 + beq + + ldy #$ff + pla + rts ++ ldy #0 + pla + rts + .pend diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 2186c004e..69ce363c5 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -1192,20 +1192,20 @@ $label nop""") assemblyLines.add(assembly) } + internal fun signExtendAYlsb(valueDt: DataType) { + // sign extend signed byte in AY to full word in AY + when(valueDt) { + DataType.UBYTE -> out(" ldy #0") + DataType.BYTE -> out(" jsr prog8_lib.sign_extend_AY_byte") + else -> throw AssemblyError("need byte type") + } + } + internal fun signExtendStackLsb(valueDt: DataType) { // sign extend signed byte on stack to signed word when(valueDt) { - DataType.UBYTE -> { - out(" lda #0 | sta P8ESTACK_HI+1,x") - } - DataType.BYTE -> { - out(""" - lda P8ESTACK_LO+1,x - ora #$7f - bmi + - lda #0 -+ sta P8ESTACK_HI+1,x""") - } + DataType.UBYTE -> out(" lda #0 | sta P8ESTACK_HI+1,x") + DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte") else -> throw AssemblyError("need byte type") } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt index 7c05ea2b3..9c416661d 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -138,7 +138,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen is IdentifierReference -> throw AssemblyError("source kind should have been variable") is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array") is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") - is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign) + is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value) is FunctionCall -> { when (val sub = value.target.targetStatement(program.namespace)) { is Subroutine -> { @@ -227,11 +227,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } - private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) { + private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) { val valueIDt = value.inferType(program) if(!valueIDt.isKnown) throw AssemblyError("unknown dt") val valueDt = valueIDt.typeOrElse(DataType.STRUCT) + if(valueDt==targetDt) + throw AssemblyError("type cast to identical dt should have been removed") + when(value) { is IdentifierReference -> { if(targetDt in WordDatatypes) { @@ -262,35 +265,26 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen else -> {} } - when(value) { - is IdentifierReference -> { - if(target.kind==TargetStorageKind.VARIABLE) { - val sourceDt = value.inferType(program).typeOrElse(DataType.STRUCT) - if (sourceDt != DataType.STRUCT) - return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), sourceDt, origAssign.source.expression!!) - } - } - is PrefixExpression -> {} - is BinaryExpression -> {} - is ArrayIndexedExpression -> {} - is TypecastExpression -> {} - is RangeExpr -> {} - is FunctionCall -> {} - else -> { - // TODO optimize the others further? - if(this.asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used for typecast: $value into $targetDt at ${value.position}") + + // special case optimizations + if (value is IdentifierReference) { + if(target.kind==TargetStorageKind.VARIABLE) { + if (valueDt != DataType.STRUCT) + return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt) } } // give up, do it via eval stack - asmgen.translateExpression(origAssign.source.expression!!) + // TODO optimize typecasts for more special cases? + // note: cannot use assignTypeCastedValue because that is ourselves :P + if(this.asmgen.options.slowCodegenWarnings) + println("warning: slow stack evaluation used for typecast: $value into $targetDt at ${value.position}") + asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast) assignStackValue(target) } private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType, - sourceAsmVarName: String, sourceDt: DataType, - origExpr: Expression) { + sourceAsmVarName: String, sourceDt: DataType) { if(sourceDt == targetDt) throw AssemblyError("typecast to identical value") diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt index 0ebbdfe6e..1104e8977 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -1100,11 +1100,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, throw AssemblyError("unknown dt") val valueDt = valueiDt.typeOrElse(DataType.STRUCT) - // TODO can use registers instead of stack value? - fun multiplyWord() { + fun multiplyVarByWordInAY() { asmgen.out(""" - lda P8ESTACK_LO+1,x - ldy P8ESTACK_HI+1,x sta P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1+1 lda $name @@ -1117,51 +1114,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, """) } - // TODO can use registers instead of stack value? - fun divideWord() { - if (dt == DataType.WORD) { - asmgen.out(""" + fun divideVarByWordInAY() { + asmgen.out(""" + pha lda $name - ldy $name+1 sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda P8ESTACK_LO+1,x - ldy P8ESTACK_HI+1,x - jsr math.divmod_w_asm - sta $name - sty $name+1 - """) - } else { - asmgen.out(""" - lda $name - ldy $name+1 - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda P8ESTACK_LO+1,x - ldy P8ESTACK_HI+1,x - jsr math.divmod_uw_asm - sta $name - sty $name+1 - """) - } + lda $name+1 + sta P8ZP_SCRATCH_W1+1 + pla""") + if (dt == DataType.WORD) + asmgen.out(" jsr math.divmod_w_asm") + else + asmgen.out(" jsr math.divmod_uw_asm") + asmgen.out(" sta $name | sty $name+1") } - // TODO can use registers instead of stack value? - fun remainderWord() { + fun remainderVarByWordInAY() { if(dt==DataType.WORD) throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") asmgen.out(""" + pha lda $name - ldy $name+1 sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda P8ESTACK_LO+1,x - ldy P8ESTACK_HI+1,x + lda $name+1 + sta P8ZP_SCRATCH_W1+1 + pla jsr math.divmod_uw_asm lda P8ZP_SCRATCH_W2 + ldy P8ZP_SCRATCH_W2+1 sta $name - lda P8ZP_SCRATCH_W2+1 - sta $name+1 + sty $name+1 """) } @@ -1171,14 +1153,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, when (operator) { // note: ** (power) operator requires floats. "+" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4): $name += ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) + asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null) if(valueDt==DataType.UBYTE) asmgen.out(""" lda $name clc - adc P8ESTACK_LO+1,x + adc P8ZP_SCRATCH_B1 sta $name bcc + inc $name+1 @@ -1186,7 +1166,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, else asmgen.out(""" ldy #0 - lda P8ESTACK_LO+1,x + lda P8ZP_SCRATCH_B1 bpl + dey ; sign extend + clc @@ -1195,17 +1175,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, tya adc $name+1 sta $name+1""") - asmgen.out(" inx") } "-" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4): $name -= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) + asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", valueDt, null) if(valueDt==DataType.UBYTE) asmgen.out(""" lda $name sec - sbc P8ESTACK_LO+1,x + sbc P8ZP_SCRATCH_REG sta $name bcs + dec $name+1 @@ -1213,45 +1190,38 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, else asmgen.out(""" ldy #0 - lda P8ESTACK_LO+1,x + lda P8ZP_SCRATCH_REG bpl + dey ; sign extend + sty P8ZP_SCRATCH_B1 lda $name sec - sbc P8ESTACK_LO+1,x + sbc P8ZP_SCRATCH_REG sta $name lda $name+1 sbc P8ZP_SCRATCH_B1 sta $name+1""") - asmgen.out(" inx") } "*" -> { // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4): $name *= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.signExtendStackLsb(valueDt) - multiplyWord() - asmgen.out(" inx") + // TODO use an optimized word * byte multiplication routine + asmgen.assignExpressionToRegister(value, RegisterOrPair.A) + asmgen.signExtendAYlsb(valueDt) + multiplyVarByWordInAY() } "/" -> { // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4): $name /= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.signExtendStackLsb(valueDt) - divideWord() - asmgen.out(" inx") + // TODO use an optimized word / byte divmod routine + asmgen.assignExpressionToRegister(value, RegisterOrPair.A) + asmgen.signExtendAYlsb(valueDt) + divideVarByWordInAY() } "%" -> { // stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4): $name %= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.signExtendStackLsb(valueDt) - remainderWord() - asmgen.out(" inx") + // TODO use an optimized word / byte divmod routine + asmgen.assignExpressionToRegister(value, RegisterOrPair.A) + asmgen.signExtendAYlsb(valueDt) + remainderVarByWordInAY() } "<<" -> { asmgen.assignExpressionToRegister(value, RegisterOrPair.Y) @@ -1306,56 +1276,37 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, when (operator) { // note: ** (power) operator requires floats. "+" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name += ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1 | inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1") } "-" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name -= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1 | inx") + asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt, null) + asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1") } "*" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name *= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - multiplyWord() - asmgen.out(" inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + multiplyVarByWordInAY() } "/" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name /= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - divideWord() - asmgen.out(" inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + divideVarByWordInAY() } "%" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name %= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - remainderWord() - asmgen.out(" inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + remainderVarByWordInAY() } "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "&" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name &= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1 | inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1") } "^" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name ^= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1 | inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1") } "|" -> { - if(asmgen.options.slowCodegenWarnings) - println("warning: slow stack evaluation used (4w): $name |r= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1 | inx") + asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) + asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1") } else -> throw AssemblyError("invalid operator for in-place modification $operator") } diff --git a/examples/test.p8 b/examples/test.p8 index e4d1c051b..1833706e3 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -10,6 +10,8 @@ main { uword uw = $c000 ubyte ub = 1 ubyte ub2 = 1 + uword uv1 = 1 + uword uv2 = 1 uw = 1000 uw += ub+ub2 @@ -46,6 +48,65 @@ main { txt.print_uw(uw) txt.chrout('\n') + uw = $1111 + uw &= (ub+ub2) | 15 + txt.print_uwhex(uw, 1) + txt.chrout('\n') + + uw = $1111 + uw |= (ub+ub2) | 15 + txt.print_uwhex(uw, 1) + txt.chrout('\n') + + uw = $1111 + uw ^= (ub+ub2) | 15 + txt.print_uwhex(uw, 1) + txt.chrout('\n') + + txt.chrout('\n') + + + + uw = 1000 + uw += uv1+uv2 + txt.print_uw(uw) + txt.chrout('\n') + + uw = 1000 + uw -= uv1+uv2 + txt.print_uw(uw) + txt.chrout('\n') + + uw = 1000 + uw *= uv1+uv2 + txt.print_uw(uw) + txt.chrout('\n') + + uw = 1000 + uw /= uv1+uv2 + txt.print_uw(uw) + txt.chrout('\n') + + uw = 1000 + uw %= 5*uv1+uv2+uv2 + txt.print_uw(uw) + txt.chrout('\n') + + uw = $1111 + uw &= (uv1+uv2) | 1023 + txt.print_uwhex(uw, 1) + txt.chrout('\n') + + uw = $1111 + uw |= (uv1+uv2) | 32768 + txt.print_uwhex(uw, 1) + txt.chrout('\n') + + uw = $1111 + uw ^= (uv1+uv2) | 32768 + txt.print_uwhex(uw, 1) + txt.chrout('\n') + test_stack.test() }