diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index c9d723afb..118fda7fe 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -4,14 +4,11 @@ import prog8.ast.IFunctionCall import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.statements.AssignTarget -import prog8.ast.statements.Assignment import prog8.ast.statements.Subroutine import prog8.ast.statements.SubroutineParameter import prog8.compiler.AssemblyError -import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX -import prog8.compiler.toHex +import prog8.compiler.target.c64.codegen.assignment.* internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { @@ -52,7 +49,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } else -> { // Risk of clobbering due to complex expression args. Work via the stack. - argsViaStackEvaluation(stmt, sub) + registerArgsViaStackEvaluation(stmt, sub) } } } @@ -64,34 +61,34 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again } - private fun argsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) { + private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) { + // this is called when one or more of the arguments are 'complex' and + // cannot be assigned to a register easily or risk clobbering other registers. for (arg in stmt.args.reversed()) asmgen.translateExpression(arg) for (regparam in sub.asmParameterRegisters) { - when (regparam.registerOrPair) { - RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x") - RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead") - RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x") - RegisterOrPair.AX -> throw AssemblyError("can't pop into X register - use a variable instead") - RegisterOrPair.AY -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x") - RegisterOrPair.XY -> throw AssemblyError("can't pop into X register - use a variable instead") - null -> { - } - } - when (regparam.statusflag) { - Statusflag.Pc -> asmgen.out(""" + when { + regparam.statusflag==Statusflag.Pc -> { + asmgen.out(""" inx pha lda $ESTACK_LO_HEX,x beq + sec bcs ++ - + clc - + pla - """) - null -> { ++ clc ++ pla""") } - else -> throw AssemblyError("can only use Carry as status flag parameter") + regparam.statusflag!=null -> { + throw AssemblyError("can only use Carry as status flag parameter") + } + regparam.registerOrPair!=null -> { + val tgt = AsmAssignTarget.fromRegisters(regparam.registerOrPair, program, asmgen) + val source = AsmAssignSource(SourceStorageKind.STACK, program, tgt.datatype) + val asgn = AsmAssignment(source, tgt, false, Position.DUMMY) + asmgen.translateNormalAssignment(asgn) + } + else -> {} } } } @@ -102,16 +99,16 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg if(!valueIDt.isKnown) throw AssemblyError("arg type unknown") val valueDt = valueIDt.typeOrElse(DataType.STRUCT) - if(!argumentTypeCompatible(valueDt, parameter.value.type)) + if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) throw AssemblyError("argument type incompatible") - val paramVar = parameter.value - - val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".") - val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) - val assign = Assignment(target, value, value.position) - assign.linkParents(value.parent) - asmgen.translate(assign) + val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".") + val identifier = IdentifierReference(scopedParamVar, sub.position) + identifier.linkParents(value.parent) + val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, variable = identifier) + val source = AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(tgt) + val asgn = AsmAssignment(source, tgt, false, Position.DUMMY) + asmgen.translateNormalAssignment(asgn) } private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue, value: Expression) { @@ -120,7 +117,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg if(!valueIDt.isKnown) throw AssemblyError("arg type unknown") val valueDt = valueIDt.typeOrElse(DataType.STRUCT) - if(!argumentTypeCompatible(valueDt, parameter.value.type)) + if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) throw AssemblyError("argument type incompatible") val paramRegister = sub.asmParameterRegisters[parameter.index] @@ -171,80 +168,22 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } else throw AssemblyError("can only use Carry as status flag parameter") } - register!=null && register.name.length==1 -> { - when (value) { - is NumericLiteralValue -> { - asmgen.out(" ld${register.name.toLowerCase()} #${value.number.toInt().toHex()}") - } - is IdentifierReference -> { - asmgen.out(" ld${register.name.toLowerCase()} ${asmgen.asmIdentifierName(value)}") - } - // TODO more special cases to optimize argument passing in registers without intermediate stack usage - else -> { - asmgen.translateExpression(value) - when(register) { - RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x") - RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead") - RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x") - else -> throw AssemblyError("cannot assign to register pair") - } - } - } - } - register!=null && register.name.length==2 -> { - // register pair as a 16-bit value (only possible for subroutine parameters) - when (value) { - is NumericLiteralValue -> { - // optimize when the argument is a constant literal - val hex = value.number.toHex() - when (register) { - RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex") - RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex") - RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex") - else -> {} - } - } - is AddressOf -> { - // optimize when the argument is an address of something - val sourceName = asmgen.asmIdentifierName(value.identifier) - when (register) { - RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName") - RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName") - RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName") - else -> {} - } - } - is IdentifierReference -> { - val sourceName = asmgen.asmIdentifierName(value) - if(valueDt in PassByReferenceDatatypes) { - when (register) { - RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName") - RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName") - RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName") - else -> {} - } - } else { - when (register) { - RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1") - RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1") - RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1") - else -> {} - } - } - } - else -> { - asmgen.translateExpression(value) - if (register == RegisterOrPair.AX || register == RegisterOrPair.XY) - throw AssemblyError("can't use X register here - use a variable") - else if (register == RegisterOrPair.AY) - asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x") - } + else -> { + // via register or register pair + val target = AsmAssignTarget.fromRegisters(register!!, program, asmgen) + val src = if(valueDt in PassByReferenceDatatypes) { + val addr = AddressOf(value as IdentifierReference, Position.DUMMY) + AsmAssignSource.fromAstSource(addr, program).adjustDataTypeToTarget(target) + } else { + AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(target) } + + asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY)) } } } - private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { + private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { if(argType isAssignableTo paramType) return true if(argType in ByteDatatypes && paramType in ByteDatatypes) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt index 759d7cca4..b9d6d2f89 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt @@ -32,12 +32,12 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, program: Program, asmgen: AsmGen, val datatype: DataType, - val variable: IdentifierReference?, - val array: ArrayIndexedExpression?, - val memory: DirectMemoryWrite?, - val register: RegisterOrPair?, - val origAstTarget: AssignTarget?, // TODO get rid of this eventually? - ) + val variable: IdentifierReference? = null, + val array: ArrayIndexedExpression? = null, + val memory: DirectMemoryWrite? = null, + val register: RegisterOrPair? = null, + val origAstTarget: AssignTarget? = null // TODO get rid of this eventually? + ) { val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0} val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() } @@ -62,12 +62,22 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) { val dt = inferType(program, assign).typeOrElse(DataType.STRUCT) when { - identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, identifier, null, null, null, this) - arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, null, arrayindexed, null, null, this) - memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, null, null, memoryAddress, null, this) + identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, variable=identifier, origAstTarget = this) + arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, array = arrayindexed, origAstTarget = this) + memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, memory = memoryAddress, origAstTarget = this) else -> throw AssemblyError("weird target") } } + + fun fromRegisters(registers: RegisterOrPair, program: Program, asmgen: AsmGen): AsmAssignTarget = + when(registers) { + RegisterOrPair.A, + RegisterOrPair.X, + RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, register = registers) + RegisterOrPair.AX, + RegisterOrPair.AY, + RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, register = registers) + } } } 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 58112a448..355452bca 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -283,9 +283,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } TargetStorageKind.REGISTER -> { when(target.register!!) { - RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName") - RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName") - RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName") + RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName") + RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName") + RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName") else -> throw AssemblyError("can't load address in a single 8-bit register") } } @@ -380,9 +380,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } TargetStorageKind.REGISTER -> { when(target.register!!) { - RegisterOrPair.AX -> asmgen.out(" lda #$sourceName | ldx #$sourceName+1") - RegisterOrPair.AY -> asmgen.out(" lda #$sourceName | ldy #$sourceName+1") - RegisterOrPair.XY -> asmgen.out(" ldx #$sourceName | ldy #$sourceName+1") + RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1") + RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1") + RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1") else -> throw AssemblyError("can't load word in a single 8-bit register") } } @@ -461,9 +461,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen RegisterOrPair.A -> asmgen.out(" lda $sourceName") RegisterOrPair.X -> asmgen.out(" ldx $sourceName") RegisterOrPair.Y -> asmgen.out(" ldy $sourceName") - RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0") - RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0") - RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0") + RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0") + RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0") + RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0") } } TargetStorageKind.STACK -> TODO() diff --git a/examples/test.p8 b/examples/test.p8 index 7e1a747e8..479d0306e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,51 +3,13 @@ main { - ubyte[256] sieve - ubyte candidate_prime = 2 ; is increased in the loop - sub start() { - ubyte x - for x in 0 to 255 { - sieve[x] = 0 - } - ; memset(sieve, 256, false) ; clear the sieve, to reset starting situation on subsequent runs + uword lines = 1 + uword score = $1000 - ; calculate primes - c64scr.print("prime numbers up to 255:\n\n") - ubyte amount=0 - repeat { - ubyte prime = find_next_prime() - if prime==0 - break - c64scr.print_ub(prime) - c64scr.print(", ") - amount++ - } + c64scr.print_uw(lines) c64.CHROUT('\n') - c64scr.print("number of primes (expected 54): ") - c64scr.print_ub(amount) + c64scr.print_uw(score) c64.CHROUT('\n') } - - - sub find_next_prime() -> ubyte { - - while sieve[candidate_prime] { - candidate_prime++ - if candidate_prime==0 - return 0 ; we wrapped; no more primes available in the sieve - } - - ; found next one, mark the multiples and return it. - sieve[candidate_prime] = true - uword multiple = candidate_prime - - - while multiple < len(sieve) { - sieve[lsb(multiple)] = true - multiple += candidate_prime - } - return candidate_prime - } }