diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index f8e815743..3d248e3ed 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -1025,9 +1025,27 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, // 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") + asmgen.out(" tay | cpy #0") + } + RegisterOrPair.AY -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + asmgen.out(" ldy #0 | cmp #0") + } + RegisterOrPair.AX -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX) + asmgen.out(" ldx #0 | cmp #0") + } + RegisterOrPair.XY -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY) + asmgen.out(" ldy #0 | cpx #0") + } + in Cx16VirtualRegisters -> { + asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister) + val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY) + zero.parent=fcall + assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE) + asmgen.out(" lda cx16.r0L") } else -> throw AssemblyError("invalid reg") } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 735d88d00..436b6c602 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -43,12 +43,12 @@ internal class AssignmentAsmGen(private val program: PtProgram, val variable = assign.source.asmVarname when (assign.target.datatype) { DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable) - DataType.WORD -> assignVariableWord(assign.target, variable) + DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype) DataType.UWORD -> { if(assign.source.datatype in PassByReferenceDatatypes) assignAddressOf(assign.target, variable) else - assignVariableWord(assign.target, variable) + assignVariableWord(assign.target, variable, assign.source.datatype) } DataType.FLOAT -> assignVariableFloat(assign.target, variable) DataType.STR -> assignVariableString(assign.target, variable) @@ -277,20 +277,45 @@ internal class AssignmentAsmGen(private val program: PtProgram, } is PtPrefix -> { if(assign.target.array==null) { - // First assign the value to the target then apply the operator in place on the target. - // This saves a temporary variable - translateNormalAssignment( - AsmAssignment( - AsmAssignSource.fromAstSource(value.value, program, asmgen), - assign.target, program.memsizer, assign.position - ), scope - ) - when (value.operator) { - "+" -> {} - "-" -> inplaceNegate(assign, true, scope) - "~" -> inplaceInvert(assign, scope) - "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") - else -> throw AssemblyError("invalid prefix operator") + if(assign.source.datatype==assign.target.datatype) { + // First assign the value to the target then apply the operator in place on the target. + // This saves a temporary variable + translateNormalAssignment( + AsmAssignment( + AsmAssignSource.fromAstSource(value.value, program, asmgen), + assign.target, program.memsizer, assign.position + ), scope + ) + when (value.operator) { + "+" -> {} + "-" -> inplaceNegate(assign, true, scope) + "~" -> inplaceInvert(assign, scope) + "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") + else -> throw AssemblyError("invalid prefix operator") + } + } else { + // use a temporary variable + val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1" + assignExpressionToVariable(value.value, tempvar, value.type) + when (value.operator) { + "+" -> {} + "-", "~" -> { + val assignTempvar = AsmAssignment( + AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar), + AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar), + program.memsizer, assign.position) + if(value.operator=="-") + inplaceNegate(assignTempvar, true, scope) + else + inplaceInvert(assignTempvar, scope) + } + "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") + else -> throw AssemblyError("invalid prefix operator") + } + if(value.type in ByteDatatypes) + assignVariableByte(assign.target, tempvar) + else + assignVariableWord(assign.target, tempvar, value.type) } } else { assignPrefixedExpressionToArrayElt(assign, scope) @@ -325,7 +350,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, asmgen.translateNormalAssignment(assignToTempvar, scope) when(assign.target.datatype) { in ByteDatatypes -> assignVariableByte(assign.target, tempvar) - in WordDatatypes -> assignVariableWord(assign.target, tempvar) + in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype) DataType.FLOAT -> assignVariableFloat(assign.target, tempvar) else -> throw AssemblyError("weird dt") } @@ -422,7 +447,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, DataType.UWORD -> { asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.out(" jsr math.divmod_uw_asm") - assignVariableWord(target, "P8ZP_SCRATCH_W2") + assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.UWORD) return true } else -> return false @@ -1932,19 +1957,29 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) { + private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) { + require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE) when(target.kind) { TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda $sourceName - ldy $sourceName+1 - sta ${target.asmVarname} - sty ${target.asmVarname}+1""") + if(sourceDt==DataType.UBYTE) { + asmgen.out(" lda $sourceName | sta ${target.asmVarname}") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz ${target.asmVarname}") + else + asmgen.out(" lda #0 | sta ${target.asmVarname}") + } + else + asmgen.out(""" + lda $sourceName + ldy $sourceName+1 + sta ${target.asmVarname} + sty ${target.asmVarname}+1""") } TargetStorageKind.MEMORY -> { throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast") } TargetStorageKind.ARRAY -> { + if(sourceDt==DataType.UBYTE) TODO("assign byte to word array") target.array!! if(target.constArrayIndexValue!=null) { val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt() @@ -2020,20 +2055,36 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } TargetStorageKind.REGISTER -> { - when(target.register!!) { - RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName") - RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName") - RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName") - in Cx16VirtualRegisters -> { - asmgen.out( - """ - lda $sourceName - sta cx16.${target.register.toString().lowercase()} - lda $sourceName+1 - sta cx16.${target.register.toString().lowercase()}+1 - """) + if(sourceDt==DataType.UBYTE) { + when(target.register!!) { + RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName") + RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName") + RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName") + in Cx16VirtualRegisters -> { + asmgen.out(" lda $sourceName | sta cx16.${target.register.toString().lowercase()}") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1") + else + asmgen.out(" lda #0 | sta cx16.${target.register.toString().lowercase()}+1") + } + else -> throw AssemblyError("can't load word in a single 8-bit register") + } + } else { + when(target.register!!) { + RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName") + RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName") + RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName") + in Cx16VirtualRegisters -> { + asmgen.out( + """ + lda $sourceName + sta cx16.${target.register.toString().lowercase()} + lda $sourceName+1 + sta cx16.${target.register.toString().lowercase()}+1 + """) + } + else -> throw AssemblyError("can't load word in a single 8-bit register") } - else -> throw AssemblyError("can't load word in a single 8-bit register") } } } diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index e61e53aeb..0516e4537 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -1,7 +1,9 @@ package prog8tests.codegencpu6502 import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual import io.kotest.matchers.shouldBe import prog8.code.SymbolTableMaker import prog8.code.ast.* @@ -107,9 +109,19 @@ class TestCodegen: FunSpec({ test("64tass assembler available? - if this fails you need to install 64tass in the path") { val command = mutableListOf("64tass", "--version") shouldNotThrowAny { - val proc = ProcessBuilder(command).inheritIO().start() + val proc = ProcessBuilder(command).start() + val output = String(proc.inputStream.readBytes()) val result = proc.waitFor() result.shouldBe(0) + val (_, version) = output.split('V') + val (major, minor, _) = version.split('.') + val majorNum = major.toInt() + val minorNum = minor.toInt() + withClue("64tass version should be 1.58 or newer") { + majorNum shouldBeGreaterThanOrEqual 1 + if (majorNum == 1) + minorNum shouldBeGreaterThanOrEqual 58 + } } } }) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 37a010d05..eb981c751 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -396,42 +396,35 @@ class IRCodeGen( } private fun translate(whenStmt: PtWhen): IRCodeChunks { - if(whenStmt.choices.children.isEmpty()) - return emptyList() val result = mutableListOf() val valueDt = irType(whenStmt.value.type) val valueTr = expressionEval.translateExpression(whenStmt.value) addToResult(result, valueTr, valueTr.resultReg, -1) - val choices = whenStmt.choices.children.map {it as PtWhenChoice } + + val choices = mutableListOf>() val endLabel = createLabelName() - for (choice in choices) { + whenStmt.choices.children.forEach { + val choice = it as PtWhenChoice if(choice.isElse) { result += translateNode(choice.statements) + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) } else { - val skipLabel = createLabelName() - val values = choice.values.children.map {it as PtNumber} - if(values.size==1) { - val chunk = IRCodeChunk(null, null) - chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel) - result += chunk - result += translateNode(choice.statements) - if(choice.statements.children.last() !is PtReturn) - addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) - } else { - val matchLabel = createLabelName() - val chunk = IRCodeChunk(null, null) - for (value in values) { - chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel) - } - chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel) - result += chunk - result += labelFirstChunk(translateNode(choice.statements), matchLabel) - if(choice.statements.children.last() !is PtReturn) - addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) + val choiceLabel = createLabelName() + choices.add(choiceLabel to choice) + choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value -> + addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null) } - result += IRCodeChunk(skipLabel, null) } } + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) + + choices.forEach { (label, choice) -> + result += labelFirstChunk(translateNode(choice.statements), label) + val lastStatement = choice.statements.children.last() + if(lastStatement !is PtReturn && lastStatement !is PtJump) + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) + } + result += IRCodeChunk(endLabel, null) return result } diff --git a/compiler/test/codegeneration/TestVariousCodeGen.kt b/compiler/test/codegeneration/TestVariousCodeGen.kt index aeb5c4465..54de41379 100644 --- a/compiler/test/codegeneration/TestVariousCodeGen.kt +++ b/compiler/test/codegeneration/TestVariousCodeGen.kt @@ -285,4 +285,37 @@ derp { compileText(VMTarget(), true, src, writeAssembly = true) shouldNotBe null compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null } + + test("prefix expressions with typecasting") { + val src=""" +main +{ + sub start() + { + uword uw = 54321 + ubyte ub = 123 + word sw = -12345 + byte sb = -123 + + func_uw(~ub as uword) + func_ub(~uw as ubyte) + func_uw(~sb as uword) + func_ub(~sw as ubyte) + func_w(-sb as word) + func_b(-sw as byte) + } + + sub func_uw(uword arg) { + } + sub func_w(word arg) { + } + sub func_ub(ubyte arg) { + } + sub func_b(byte arg) { + } +}""" + + compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null + compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null + } }) \ No newline at end of file diff --git a/examples/test.p8 b/examples/test.p8 index 6298b9e33..89dba048d 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,32 +5,22 @@ main { sub start() { - uword zc = 54321 - ubyte zb = 123 - ubyte shift = 2 + uword uw = 54321 + ubyte ub = 123 + word sw = -12345 + byte sb = -123 - txt.print_uw(zc<>shift) + txt.print_ub(~uw as ubyte) ;206 txt.nl() - txt.print_ub(zb<>shift) + txt.print_ub(~sw as ubyte) ;56 txt.nl() - - word szc = -12345 - byte szb = -123 - txt.print_w(szc<>shift) + txt.print_b(-sw as byte) ;57 txt.nl() - txt.print_b(szb<>shift) - txt.nl() - - -; cx16.r1L = (zc<