From 2395863e7efd3643f1292fef51c958ba94d17945 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 29 Jan 2021 01:52:49 +0100 Subject: [PATCH] asmsubs: fix clobbering and optimize register usage for loading the arguments --- .../compiler/target/c64/codegen/AsmGen.kt | 6 +- .../c64/codegen/BuiltinFunctionsAsmGen.kt | 286 +++++++++++++----- .../target/c64/codegen/ExpressionsAsmGen.kt | 2 +- .../target/c64/codegen/FunctionCallAsmGen.kt | 118 ++++---- .../codegen/assignment/AssignmentAsmGen.kt | 53 ++-- docs/source/todo.rst | 4 +- examples/test.p8 | 24 +- 7 files changed, 324 insertions(+), 169 deletions(-) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index cb2a988c0..b68364251 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -43,12 +43,12 @@ internal class AsmGen(private val program: Program, private val globalFloatConsts = mutableMapOf() // all float values in the entire program (value -> varname) private val allocatedZeropageVariables = mutableMapOf>() private val breakpointLabels = mutableListOf() - private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this) private val forloopsAsmGen = ForLoopsAsmGen(program, this) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val expressionsAsmGen = ExpressionsAsmGen(program, this) private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen) + private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen) internal val loopEndLabels = ArrayDeque() private val blockLevelVarInits = mutableMapOf>() internal val slabs = mutableMapOf() @@ -782,8 +782,8 @@ internal class AsmGen(private val program: Program, internal fun translateExpression(indexer: ArrayIndex) = expressionsAsmGen.translateExpression(indexer) - internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean) = - builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack) + internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) = + builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack, resultRegister) internal fun translateFunctionCall(functionCall: FunctionCall) = functioncallAsmGen.translateFunctionCall(functionCall) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 18eafcc8c..80348575d 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -10,23 +10,24 @@ import prog8.compiler.AssemblyError import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget import prog8.compiler.target.c64.codegen.assignment.AsmAssignment +import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind import prog8.compiler.target.subroutineFloatEvalResultVar2 import prog8.compiler.toHex import prog8.functions.FSignature -internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) { +internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) { - internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature, resultToStack: Boolean) { - translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack) + internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) } internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FSignature) { - translateFunctioncall(fcall, func, discardResult = true, resultToStack = false) + translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null) } - private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean) { + private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { if (discardResult && func.pure) return // can just ignore the whole function call altogether @@ -34,36 +35,37 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val throw AssemblyError("cannot both discard the result AND put it onto stack") val sscope = (fcall as Node).definingSubroutine() + when (func.name) { - "msb" -> funcMsb(fcall, resultToStack) - "lsb" -> funcLsb(fcall, resultToStack) - "mkword" -> funcMkword(fcall, resultToStack) - "abs" -> funcAbs(fcall, func, resultToStack, sscope) + "msb" -> funcMsb(fcall, resultToStack, resultRegister) + "lsb" -> funcLsb(fcall, resultToStack, resultRegister) + "mkword" -> funcMkword(fcall, resultToStack, resultRegister) + "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) "swap" -> funcSwap(fcall) - "min", "max" -> funcMinMax(fcall, func, resultToStack) - "sum" -> funcSum(fcall, resultToStack) - "any", "all" -> funcAnyAll(fcall, func, resultToStack) + "min", "max" -> funcMinMax(fcall, func, resultToStack, resultRegister, sscope) + "sum" -> funcSum(fcall, resultToStack, resultRegister, sscope) + "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) "sin8", "sin8u", "sin16", "sin16u", - "cos8", "cos8u", "cos16", "cos16u" -> funcSinCosInt(fcall, func, resultToStack, sscope) - "sgn" -> funcSgn(fcall, func, resultToStack, sscope) + "cos8", "cos8u", "cos16", "cos16u" -> funcSinCosInt(fcall, func, resultToStack, resultRegister, sscope) + "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) "sin", "cos", "tan", "atan", "ln", "log2", "sqrt", "rad", "deg", "round", "floor", "ceil", - "rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, sscope) - "rnd", "rndw" -> funcRnd(func, resultToStack) - "sqrt16" -> funcSqrt16(fcall, func, resultToStack, sscope) + "rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope) + "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope) + "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) "rol" -> funcRol(fcall) "rol2" -> funcRol2(fcall) "ror" -> funcRor(fcall) "ror2" -> funcRor2(fcall) "sort" -> funcSort(fcall) "reverse" -> funcReverse(fcall) - "memory" -> funcMemory(fcall, discardResult, resultToStack) + "memory" -> funcMemory(fcall, discardResult, resultToStack, resultRegister) else -> TODO("missing asmgen for builtin func ${func.name}") } } - private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean) { + private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { if(discardResult || fcall !is FunctionCall) throw AssemblyError("should not discard result of memory allocation at $fcall") val scope = fcall.definingScope() @@ -82,7 +84,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val if(resultToStack) AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null) else - AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen) + AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen) val assign = AsmAssignment(src, target, false, fcall.position) asmgen.translateNormalAssignment(assign) @@ -92,22 +94,30 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.slabs[name] = size } - private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { + private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { translateArguments(fcall.args, func, scope) if(resultToStack) asmgen.out(" jsr prog8_lib.func_sqrt16_stack") - else + else { asmgen.out(" jsr prog8_lib.func_sqrt16_into_A") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) + } } - private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { + private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { translateArguments(fcall.args, func, scope) if(resultToStack) asmgen.out(" jsr prog8_lib.func_${func.name}_stack") else when(func.name) { - "sin8", "sin8u", "cos8", "cos8u" -> asmgen.out(" jsr prog8_lib.func_${func.name}_into_A") - "sin16", "sin16u", "cos16", "cos16u" -> asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY") + "sin8", "sin8u", "cos8", "cos8u" -> { + asmgen.out(" jsr prog8_lib.func_${func.name}_into_A") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) + } + "sin16", "sin16u", "cos16", "cos16u" -> { + asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY) + } } } @@ -390,15 +400,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.assignExpressionToVariable(indexerExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null) } - private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { + private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { translateArguments(fcall.args, func, scope) if(resultToStack) asmgen.out(" jsr floats.func_${func.name}_stack") - else + else { asmgen.out(" jsr floats.func_${func.name}_fac1") + assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen)) + } } - private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { + private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { translateArguments(fcall.args, func, scope) val dt = fcall.args.single().inferType(program) if(resultToStack) { @@ -419,10 +431,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A") else -> throw AssemblyError("weird type $dt") } + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) } } - private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean) { + private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { outputAddressAndLenghtOfArray(fcall.args[0]) val dt = fcall.args.single().inferType(program) if(resultToStack) { @@ -439,10 +452,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A") else -> throw AssemblyError("weird type $dt") } + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) } } - private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean) { + private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { outputAddressAndLenghtOfArray(fcall.args[0]) val dt = fcall.args.single().inferType(program) if(resultToStack) { @@ -456,17 +470,32 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } else { when (dt.typeOrElse(DataType.STRUCT)) { - DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A") - DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A") - DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY") - DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_fac1") + DataType.ARRAY_UB, DataType.STR -> { + asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) + } + DataType.ARRAY_B -> { + asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) + } + DataType.ARRAY_UW -> { + asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.ARRAY_W -> { + asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.ARRAY_F -> { + asmgen.out(" jsr floats.func_${function.name}_f_fac1") + assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen)) + } else -> throw AssemblyError("weird type $dt") } } } - private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean) { + private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { outputAddressAndLenghtOfArray(fcall.args[0]) val dt = fcall.args.single().inferType(program) if(resultToStack) { @@ -480,11 +509,26 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } else { when (dt.typeOrElse(DataType.STRUCT)) { - DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY") - DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_into_AY") - DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY") - DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_into_AY") - DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_fac1") + DataType.ARRAY_UB, DataType.STR -> { + asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.ARRAY_B -> { + asmgen.out(" jsr prog8_lib.func_sum_b_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.ARRAY_UW -> { + asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.ARRAY_W -> { + asmgen.out(" jsr prog8_lib.func_sum_w_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.ARRAY_F -> { + asmgen.out(" jsr floats.func_sum_f_fac1") + assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen)) + } else -> throw AssemblyError("weird type $dt") } } @@ -868,7 +912,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } - private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { + private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { translateArguments(fcall.args, func, scope) val dt = fcall.args.single().inferType(program).typeOrElse(DataType.STRUCT) if(resultToStack) { @@ -880,40 +924,91 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } else { when (dt) { - in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_into_A") - in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w_into_AY") - DataType.FLOAT -> asmgen.out(" jsr floats.abs_f_fac1") + in ByteDatatypes -> { + asmgen.out(" jsr prog8_lib.abs_b_into_A") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), CpuRegister.A) + } + in WordDatatypes -> { + asmgen.out(" jsr prog8_lib.abs_w_into_AY") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.AY) + } + DataType.FLOAT -> { + asmgen.out(" jsr floats.abs_f_fac1") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.FAC1) + } else -> throw AssemblyError("weird type") } } } - private fun funcRnd(func: FSignature, resultToStack: Boolean) { + private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { when(func.name) { "rnd" -> { if(resultToStack) asmgen.out(" jsr prog8_lib.func_rnd_stack") - else + else { asmgen.out(" jsr math.randbyte") + assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A) + } } "rndw" -> { if(resultToStack) asmgen.out(" jsr prog8_lib.func_rndw_stack") - else + else { asmgen.out(" jsr math.randword") + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY) + } } else -> throw AssemblyError("wrong func") } } - private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean) { - asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb - asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb - if(resultToStack) + private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + if(resultToStack) { + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb + asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") + } else { + val reg = resultRegister ?: RegisterOrPair.AY + val needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteralValue || fcall.args[0] is IdentifierReference) + when(reg) { + RegisterOrPair.AX -> { + asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb + if(needAsave) + asmgen.out(" pha") + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb + if(needAsave) + asmgen.out(" pla") + } + RegisterOrPair.AY -> { + asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb + if(needAsave) + asmgen.out(" pha") + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb + if(needAsave) + asmgen.out(" pla") + } + RegisterOrPair.XY -> { + asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb + if(needAsave) + asmgen.out(" pha") + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb + if(needAsave) + asmgen.out(" pla") + asmgen.out(" tax") + } + in Cx16VirtualRegisters -> { + asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb + asmgen.out(" sta cx16.${reg.toString().toLowerCase()}") + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb + asmgen.out(" sta cx16.${reg.toString().toLowerCase()}+1") + } + else -> throw AssemblyError("invalid mkword target reg") + } + } } - private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean) { + private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { val arg = fcall.args.single() if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes) throw AssemblyError("msb required word argument") @@ -921,19 +1016,43 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val throw AssemblyError("msb(const) should have been const-folded away") if (arg is IdentifierReference) { val sourceName = asmgen.asmVariableName(arg) - asmgen.out(" lda $sourceName+1") - if (resultToStack) - asmgen.out(" sta P8ESTACK_LO,x | dex") + if(resultToStack) { + asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex") + } else { + when(resultRegister) { + null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1") + RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1") + RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1") + else -> throw AssemblyError("invalid reg") + } + } } else { - asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) - if (resultToStack) + if(resultToStack) { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) asmgen.out(" tya | sta P8ESTACK_LO,x | dex") - else - asmgen.out(" tya") + } else { + when(resultRegister) { + null, RegisterOrPair.A -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + asmgen.out(" tya") + } + RegisterOrPair.X -> { + asmgen.out(" pha") + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX) + asmgen.out(" pla") + } + RegisterOrPair.Y -> { + asmgen.out(" pha") + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + asmgen.out(" pla") + } + else -> throw AssemblyError("invalid reg") + } + } } } - private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean) { + private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { val arg = fcall.args.single() if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes) throw AssemblyError("lsb required word argument") @@ -942,16 +1061,45 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val if (arg is IdentifierReference) { val sourceName = asmgen.asmVariableName(arg) - asmgen.out(" lda $sourceName") - if (resultToStack) - asmgen.out(" sta P8ESTACK_LO,x | dex") + if(resultToStack) { + asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex") + } else { + when(resultRegister) { + null, RegisterOrPair.A -> asmgen.out(" lda $sourceName") + RegisterOrPair.X -> asmgen.out(" ldx $sourceName") + RegisterOrPair.Y -> asmgen.out(" ldy $sourceName") + else -> throw AssemblyError("invalid reg") + } + } } else { - asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) - // NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction. - // this is required because the compiler assumes the status bits are set according to what A is (lsb) - // and will not generate another cmp when lsb() is directly used inside a comparison expression. - if (resultToStack) + if(resultToStack) { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + // NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction. + // this is required because the compiler assumes the status bits are set according to what A is (lsb) + // and will not generate another cmp when lsb() is directly used inside a comparison expression. asmgen.out(" sta P8ESTACK_LO,x | dex") + } else { + when(resultRegister) { + null, RegisterOrPair.A -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + // NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction. + // this is required because the compiler assumes the status bits are set according to what A is (lsb) + // and will not generate another cmp when lsb() is directly used inside a comparison expression. + } + RegisterOrPair.X -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY) + // NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction. + // this is required because the compiler assumes the status bits are set according to what X is (lsb) + // and will not generate another cmp when lsb() is directly used inside a comparison expression. + } + RegisterOrPair.Y -> { + asmgen.out(" pha") + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + asmgen.out(" tay | pla | cpy #0") + } + else -> throw AssemblyError("invalid reg") + } + } } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index ca8fe0965..9fdb109ca 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -1330,7 +1330,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge val sub = call.target.targetStatement(program.namespace) if(sub is BuiltinFunctionStatementPlaceholder) { val builtinFunc = BuiltinFunctions.getValue(sub.name) - asmgen.translateBuiltinFunctionCallExpression(call, builtinFunc, true) + asmgen.translateBuiltinFunctionCallExpression(call, builtinFunc, true, null) } else { sub as Subroutine asmgen.saveXbeforeCall(call) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index 4584515a5..cfcae2607 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -90,8 +90,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg stmt.args.all {isNoClobberRisk(it)} -> { // There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values. val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters) - val (vregsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters } - for(arg in vregsArgsInfo) + val (cx16virtualRegsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters } + for(arg in cx16virtualRegsArgsInfo) argumentViaRegister(sub, arg.first.first, arg.first.second) for(arg in otherRegsArgsInfo) argumentViaRegister(sub, arg.first.first, arg.first.second) @@ -256,71 +256,67 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg if(valueDt largerThan requiredDt) throw AssemblyError("can only convert byte values to word param types") } - when { - statusflag!=null -> { - if(requiredDt!=valueDt) - throw AssemblyError("for statusflag, byte value is required") - if (statusflag == Statusflag.Pc) { - // this param needs to be set last, right before the jsr - // for now, this is already enforced on the subroutine definition by the Ast Checker - when(value) { - is NumericLiteralValue -> { - val carrySet = value.number.toInt() != 0 - asmgen.out(if(carrySet) " sec" else " clc") - } - is IdentifierReference -> { - val sourceName = asmgen.asmVariableName(value) - asmgen.out(""" - pha - lda $sourceName - beq + - sec - bcs ++ -+ clc -+ pla -""") - } - else -> { - asmgen.assignExpressionToRegister(value, RegisterOrPair.A) - asmgen.out(""" - beq + - sec - bcs ++ -+ clc -+""") - } + if (statusflag!=null) { + if(requiredDt!=valueDt) + throw AssemblyError("for statusflag, byte value is required") + if (statusflag == Statusflag.Pc) { + // this param needs to be set last, right before the jsr + // for now, this is already enforced on the subroutine definition by the Ast Checker + when(value) { + is NumericLiteralValue -> { + val carrySet = value.number.toInt() != 0 + asmgen.out(if(carrySet) " sec" else " clc") + } + is IdentifierReference -> { + val sourceName = asmgen.asmVariableName(value) + asmgen.out(""" + pha + lda $sourceName + beq + + sec + bcs ++ + + clc + + pla + """) + } + else -> { + asmgen.assignExpressionToRegister(value, RegisterOrPair.A) + asmgen.out(""" + beq + + sec + bcs ++ + + clc + +""") } } - else throw AssemblyError("can only use Carry as status flag parameter") - } - else -> { - // via register or register pair - register!! - if(requiredDt largerThan valueDt) { - // we need to sign extend the source, do this via temporary word variable - val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1") - asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub) - asmgen.signExtendVariableLsb(scratchVar, valueDt) - asmgen.assignVariableToRegister(scratchVar, register) - } - else { - val target: AsmAssignTarget = - if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) - AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register) - else - AsmAssignTarget.fromRegisters(register, sub, program, asmgen) - val src = if(valueDt in PassByReferenceDatatypes) { - if(value is IdentifierReference) { - val addr = AddressOf(value, Position.DUMMY) - AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target) - } else { - AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) - } + } else throw AssemblyError("can only use Carry as status flag parameter") + } + else { + // via register or register pair + register!! + if(requiredDt largerThan valueDt) { + // we need to sign extend the source, do this via temporary word variable + val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1") + asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub) + asmgen.signExtendVariableLsb(scratchVar, valueDt) + asmgen.assignVariableToRegister(scratchVar, register) + } else { + val target: AsmAssignTarget = + if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) + AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register) + else + AsmAssignTarget.fromRegisters(register, sub, program, asmgen) + val src = if(valueDt in PassByReferenceDatatypes) { + if(value is IdentifierReference) { + val addr = AddressOf(value, Position.DUMMY) + AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target) } else { AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) } - asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY)) + } else { + AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) } + asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY)) } } } 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 c6e91e637..e9a08e28c 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -209,34 +209,37 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } is BuiltinFunctionStatementPlaceholder -> { val signature = BuiltinFunctions.getValue(sub.name) - asmgen.translateBuiltinFunctionCallExpression(value, signature, false) - val returntype = builtinFunctionReturnType(sub.name, value.args, program) - if(!returntype.isKnown) - throw AssemblyError("unknown dt") - when(returntype.typeOrElse(DataType.STRUCT)) { - in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A - in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY - DataType.STR -> { - when (assign.target.datatype) { - DataType.STR -> { - asmgen.out(""" - pha - lda #<${assign.target.asmVarname} - sta P8ZP_SCRATCH_W1 - lda #>${assign.target.asmVarname} - sta P8ZP_SCRATCH_W1+1 - pla - jsr prog8_lib.strcpy""") + asmgen.translateBuiltinFunctionCallExpression(value, signature, false, assign.target.register) + if(assign.target.register==null) { + // still need to assign the result to the target variable/etc. + val returntype = builtinFunctionReturnType(sub.name, value.args, program) + if(!returntype.isKnown) + throw AssemblyError("unknown dt") + when(returntype.typeOrElse(DataType.STRUCT)) { + in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A + in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY + DataType.STR -> { + when (assign.target.datatype) { + DataType.STR -> { + asmgen.out(""" + pha + lda #<${assign.target.asmVarname} + sta P8ZP_SCRATCH_W1 + lda #>${assign.target.asmVarname} + sta P8ZP_SCRATCH_W1+1 + pla + jsr prog8_lib.strcpy""") + } + DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) + else -> throw AssemblyError("str return value type mismatch with target") } - DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) - else -> throw AssemblyError("str return value type mismatch with target") } + DataType.FLOAT -> { + // float result from function sits in FAC1 + assignFAC1float(assign.target) + } + else -> throw AssemblyError("weird result type") } - DataType.FLOAT -> { - // float result from function sits in FAC1 - assignFAC1float(assign.target) - } - else -> throw AssemblyError("weird result type") } } else -> { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7ad210537..2140ff6f4 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -5,9 +5,9 @@ TODO - optimize for loop iterations better to allow proper inx, cpx #value, bne loop instructions (like repeat loop) - optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2)) - implement the linked_list millfork benchmark +- [in progress] implement highres 4 color mode in gfx2 +- [in progress] make a retro Amiga Workbench "simulator" using that new gfx mode -- implement highres 4 color mode in gfx2 -- make a retro Amiga Workbench "simulator" using that new gfx mode - use the 65c02 bit clear/set/test instructions for single-bit operations - add a flood fill routine to gfx2 - can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50 diff --git a/examples/test.p8 b/examples/test.p8 index b4142d6ca..3faf7792f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,4 +1,5 @@ %import textio +%import palette %import syslib %zeropage basicsafe @@ -6,15 +7,22 @@ main { sub start() { - ubyte value - ubyte bb1 + uword screencolorRGB + uword drawcolorRGB + ubyte ll + ubyte hh - ; TODO why is this generating so much larger code: (only with asmsub btw) - value = cx16.vpeek(lsb(cx16.r0), mkword(value, bb1)) - value = cx16.vpeek(lsb(cx16.r0), mkword(value, bb1)) + cx16.vpoke(1, mkword(hh, ll), lsb(screencolorRGB)) - ubyte lx = lsb(cx16.r0) - value = cx16.vpeek(lx, mkword(value, bb1)) - value = cx16.vpeek(lx, mkword(value, bb1)) + +; ubyte value +; ubyte bb1 +; +; value = cx16.vpeek(lsb(cx16.r0), mkword(value, bb1)) +; value = cx16.vpeek(lsb(cx16.r0), mkword(value, bb1)) +; +; ubyte lx = lsb(cx16.r0) +; value = cx16.vpeek(lx, mkword(value, bb1)) +; value = cx16.vpeek(lx, mkword(value, bb1)) } }