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 9d1c9e5a6..cead1b897 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -120,17 +120,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 -> { -// if(assign.target.kind == TargetStorageKind.STACK) { -// asmgen.translateExpression(value) -// assignStackValue(assign.target) -// } else { -// println("!!!!TYPECAST to ${assign.target.kind} $value") -// // TODO maybe we can do the typecast on the target directly instead of on the stack? -// asmgen.translateExpression(value) -// assignStackValue(assign.target) -// } -// } + is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign) // is FunctionCall -> { // if (assign.target.kind == TargetStorageKind.STACK) { // asmgen.translateExpression(value) @@ -161,6 +151,52 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } + private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) { + val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT) + when(value) { + is IdentifierReference -> { + if (valueDt == DataType.UBYTE || valueDt == DataType.BYTE) { + if(targetDt in WordDatatypes) { + assignVariableByteIntoWord(target, value, valueDt) + return + } + } + } + is DirectMemoryRead -> { + if(targetDt in WordDatatypes) { + if (value.addressExpression is NumericLiteralValue) { + val address = (value.addressExpression as NumericLiteralValue).number.toInt() + assignMemoryByteIntoWord(target, address, null) + return + } + else if (value.addressExpression is IdentifierReference) { + assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference) + return + } + } + } + is NumericLiteralValue -> throw AssemblyError("a cast of a literal value should have been const-folded away") + else -> {} + } + + when(value) { + is PrefixExpression -> {} + is BinaryExpression -> {} + is ArrayIndexedExpression -> {} + is TypecastExpression -> {} + is RangeExpr -> {} + is FunctionCall -> {} + else -> { + // TODO optimize the others further? + println("warning: slow stack evaluation used for typecast: into $targetDt at ${value.position}") + } + } + + // give up, do it via eval stack + asmgen.translateExpression(origAssign.source.expression!!) + assignStackValue(target) + } + private fun assignStackValue(target: AsmAssignTarget) { when(target.kind) { TargetStorageKind.VARIABLE -> { @@ -520,6 +556,59 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } + private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference, valueDt: DataType) { + if(valueDt == DataType.BYTE) + TODO("sign extend byte to word") + + val sourceName = asmgen.asmVariableName(bytevar) + when(wordtarget.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.out(""" + lda $sourceName + sta ${wordtarget.asmVarname} + lda #0 + sta ${wordtarget.asmVarname}+1 + """) + } + TargetStorageKind.ARRAY -> { + val index = wordtarget.array!!.arrayspec.index + when { + wordtarget.constArrayIndexValue!=null -> { + val scaledIdx = wordtarget.constArrayIndexValue!! * 2 + asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1") + } + index is IdentifierReference -> { + asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y) + asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y") + } + else -> { + asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex") + asmgen.translateExpression(index) + asmgen.out(" inx | lda P8ESTACK_LO,x") + popAndWriteArrayvalueWithUnscaledIndexA(wordtarget.datatype, wordtarget.asmVarname) + } + } + } + TargetStorageKind.REGISTER -> { + when(wordtarget.register!!) { + RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0") + RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0") + RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0") + else -> throw AssemblyError("only reg pairs are words") + } + } + TargetStorageKind.STACK -> { + asmgen.out(""" + lda #$sourceName + sta P8ESTACK_LO,x + lda #0 + sta P8ESTACK_HI,x + dex""") + } + else -> throw AssemblyError("other types aren't word") + } + } + private fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) { require(target.datatype in ByteDatatypes) when(target.kind) { @@ -900,6 +989,63 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } + private fun assignMemoryByteIntoWord(wordtarget: AsmAssignTarget, address: Int?, identifier: IdentifierReference?) { + if (address != null) { + when(wordtarget.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.out(""" + lda ${address.toHex()} + sta ${wordtarget.asmVarname} + lda #0 + sta ${wordtarget.asmVarname}+1 + """) + } + TargetStorageKind.ARRAY -> { + throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}") + } + TargetStorageKind.REGISTER -> when(wordtarget.register!!) { + RegisterOrPair.AX -> asmgen.out(" lda ${address.toHex()} | ldx #0") + RegisterOrPair.AY -> asmgen.out(" lda ${address.toHex()} | ldy #0") + RegisterOrPair.XY -> asmgen.out(" ldy ${address.toHex()} | ldy #0") + else -> throw AssemblyError("word regs can only be pair") + } + TargetStorageKind.STACK -> { + asmgen.out(""" + lda ${address.toHex()} + sta P8ESTACK_LO,x + lda #0 + sta P8ESTACK_HI,x + dex""") + } + else -> throw AssemblyError("other types aren't word") + } + } else if (identifier != null) { + when(wordtarget.kind) { + TargetStorageKind.VARIABLE -> { + asmgen.loadByteFromPointerIntoA(identifier) + asmgen.out(" sta ${wordtarget.asmVarname} | lda #0 | sta ${wordtarget.asmVarname}+1") + } + TargetStorageKind.ARRAY -> { + throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ") + } + TargetStorageKind.REGISTER -> { + asmgen.loadByteFromPointerIntoA(identifier) + when(wordtarget.register!!) { + RegisterOrPair.AX -> asmgen.out(" ldx #0") + RegisterOrPair.AY -> asmgen.out(" ldy #0") + RegisterOrPair.XY -> asmgen.out(" tax | ldy #0") + else -> throw AssemblyError("word regs can only be pair") + } + } + TargetStorageKind.STACK -> { + asmgen.loadByteFromPointerIntoA(identifier) + asmgen.out(" sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex") + } + else -> throw AssemblyError("other types aren't word") + } + } + } + private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) { val addressExpr = memoryAddress.addressExpression val addressLv = addressExpr as? NumericLiteralValue 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 e8b5f2c46..022867178 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -218,7 +218,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } private fun tryRemoveRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean { - // TODO doesn't always remove casts for instance uword xx = xb generates complex stack based type cast evaluation... if (target.datatype == value.type) { val childDt = value.expression.inferType(program).typeOrElse(DataType.STRUCT) if (value.type.equalsSize(childDt) || value.type.largerThan(childDt)) { @@ -1315,7 +1314,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) { val outerCastDt = cast.type val innerCastDt = (cast.expression as? TypecastExpression)?.type - if (innerCastDt == null) { // simple typecast where the value is the target when (target.datatype) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 87242508c..e3c2c5f2f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,7 @@ TODO ==== - get rid of all other TODO's in the code ;-) -- gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed? +- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed? - compiler errors and warnings in standard format so the IDE shows them as clickable links; ./test.asm:2578:3: blablabla - further optimize assignment codegeneration - auto select correct library to import based on target, instead of having c64- and cx16- prefix variants diff --git a/examples/test.p8 b/examples/test.p8 index 72fec038d..c2e8a5a71 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -9,24 +9,13 @@ main { sub start() { - ubyte b - if b > 15 { - b = 99 - } else { - ; nothing - } + ubyte ub =9 + uword yy = 9999 ; this is okay (no 0-initialization generated) but... the next: + uword xx = ub ; TODO don't generate xx = 0 assignment if it's initialized with something else... - if b > 15 { - ; nothing - } else { - b = 99 - } - - if b > 15 { - ; nothing - } else { - ; nothing - } + ub++ + xx++ + yy++ ;asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { ...} ; TODO dont cause name conflict if we define sub or sub with param 'color' or even a var 'color' later.