From 96bed8f57f11d45831e2a438d0bcabcd0f227a0c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 5 Dec 2024 23:43:42 +0100 Subject: [PATCH] tweaks --- codeCore/src/prog8/code/core/Enumerations.kt | 2 +- codeCore/src/prog8/code/core/MemoryRegions.kt | 2 +- codeCore/src/prog8/code/core/Operators.kt | 10 +- .../prog8/code/target/atari/AtariZeropage.kt | 2 +- .../prog8/code/target/c128/C128Zeropage.kt | 10 +- .../src/prog8/code/target/c64/C64Zeropage.kt | 10 +- .../src/prog8/codegen/cpu6502/AsmGen.kt | 162 +++++- .../prog8/codegen/cpu6502/AssemblyProgram.kt | 4 +- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 4 +- .../src/prog8/codegen/cpu6502/IfElseAsmGen.kt | 534 +----------------- .../codegen/cpu6502/IfExpressionAsmGen.kt | 370 ++++++++++++ .../compiler/astprocessing/AstChecker.kt | 8 +- .../src/prog8/vm/VmProgramLoader.kt | 1 - 13 files changed, 570 insertions(+), 549 deletions(-) create mode 100644 codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 5ad61f6e9..4cff77a25 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -334,7 +334,7 @@ val Cx16VirtualRegisters = arrayOf( RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 ) -val CpuRegisters = setOf( +val CpuRegisters = arrayOf( RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y, RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY ) diff --git a/codeCore/src/prog8/code/core/MemoryRegions.kt b/codeCore/src/prog8/code/core/MemoryRegions.kt index 9698cf496..ed418a7a9 100644 --- a/codeCore/src/prog8/code/core/MemoryRegions.kt +++ b/codeCore/src/prog8/code/core/MemoryRegions.kt @@ -38,7 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { for (reserved in options.zpReserved) reserve(reserved) - free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u)) + free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u)) } } diff --git a/codeCore/src/prog8/code/core/Operators.kt b/codeCore/src/prog8/code/core/Operators.kt index 693df556c..8bb56957a 100644 --- a/codeCore/src/prog8/code/core/Operators.kt +++ b/codeCore/src/prog8/code/core/Operators.kt @@ -1,10 +1,10 @@ package prog8.code.core -val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation -val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") -val LogicalOperators = setOf("and", "or", "xor", "not", "in") -val BitwiseOperators = setOf("&", "|", "^", "~") -val PrefixOperators = setOf("+", "-", "~", "not") +val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation +val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=") +val LogicalOperators = arrayOf("and", "or", "xor", "not", "in") +val BitwiseOperators = arrayOf("&", "|", "^", "~") +val PrefixOperators = arrayOf("+", "-", "~", "not") fun invertedComparisonOperator(operator: String) = when (operator) { diff --git a/codeCore/src/prog8/code/target/atari/AtariZeropage.kt b/codeCore/src/prog8/code/target/atari/AtariZeropage.kt index be2a8aa4c..d7e54508e 100644 --- a/codeCore/src/prog8/code/target/atari/AtariZeropage.kt +++ b/codeCore/src/prog8/code/target/atari/AtariZeropage.kt @@ -28,7 +28,7 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) { ZeropageType.FULL -> { // TODO all atari usable zero page locations, except the ones used by the system's IRQ routine free.addAll(0x00u..0xffu) - // TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ + // TODO atari free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ } ZeropageType.KERNALSAFE -> { free.addAll(0x80u..0xffu) // TODO diff --git a/codeCore/src/prog8/code/target/c128/C128Zeropage.kt b/codeCore/src/prog8/code/target/c128/C128Zeropage.kt index aaa8f3ab5..3928eb30d 100644 --- a/codeCore/src/prog8/code/target/c128/C128Zeropage.kt +++ b/codeCore/src/prog8/code/target/c128/C128Zeropage.kt @@ -33,18 +33,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { ZeropageType.FULL -> { // $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such free.addAll(0x0au..0xffu) - free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ + free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ } ZeropageType.KERNALSAFE -> { free.addAll(0x0au..0x8fu) // BASIC variables - free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, + free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, 0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u)) } ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE -> { - free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) + free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) free.addAll(0x1bu..0x23u) - free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, + free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, 0x55u, 0x56u, 0x57u, 0x58u, 0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, @@ -53,7 +53,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { // if(options.zeropage==ZeropageType.BASICSAFE) { // can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0 - free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu, + free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu, 0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u, 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u)) diff --git a/codeCore/src/prog8/code/target/c64/C64Zeropage.kt b/codeCore/src/prog8/code/target/c64/C64Zeropage.kt index 18a31eed2..cbbeb094f 100644 --- a/codeCore/src/prog8/code/target/c64/C64Zeropage.kt +++ b/codeCore/src/prog8/code/target/c64/C64Zeropage.kt @@ -21,11 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { if (options.zeropage == ZeropageType.FULL) { free.addAll(0x02u..0xffu) - free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u)) - free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ + free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u)) + free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ } else { if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) { - free.addAll(listOf( + free.addAll(arrayOf( 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, @@ -43,7 +43,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { if (options.zeropage == ZeropageType.FLOATSAFE) { // remove the zeropage locations used for floating point operations from the free list - free.removeAll(listOf( + free.removeAll(arrayOf( 0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, @@ -56,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { if(options.zeropage != ZeropageType.DONTUSE) { // add the free Zp addresses // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* - free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e, + free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e, 0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6, 0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()}) } else { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index d8f4744df..dc4cb71ee 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -228,8 +228,8 @@ class AsmGen6502Internal ( private var generatedLabelSequenceNumber: Int ) { - internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100) - internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640) + internal val optimizedByteMultiplications = arrayOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100) + internal val optimizedWordMultiplications = arrayOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640) internal val loopEndLabels = ArrayDeque() private val zeropage = options.compTarget.machine.zeropage private val allocator = VariableAllocator(symbolTable, options, errors) @@ -241,7 +241,8 @@ class AsmGen6502Internal ( private val anyExprGen = AnyExprAsmGen(this) private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen) - private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, allocator, assignmentAsmGen, errors) + private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors) + private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors) fun compileToAssembly(): IAssemblyProgram? { @@ -1370,7 +1371,7 @@ $repeatLabel""") return "${PtLabel.GeneratedLabelPrefix}${generatedLabelSequenceNumber}_$postfix" } - fun assignConstFloatToPointerAY(number: PtNumber) { + internal fun assignConstFloatToPointerAY(number: PtNumber) { val floatConst = allocator.getFloatAsmConst(number.number) out(""" pha @@ -1383,7 +1384,158 @@ $repeatLabel""") } internal fun assignIfExpression(target: AsmAssignTarget, value: PtIfExpression) { - ifElseAsmgen.assignIfExpression(target, value) + ifExpressionAsmgen.assignIfExpression(target, value) + } + + internal fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) { + val compare = if(useSbc) "sec | sbc" else "cmp" + fun cmpViaScratch() { + if(assignmentAsmGen.directIntoY(value)) { + assignExpressionToRegister(value, RegisterOrPair.Y, false) + out(" sty P8ZP_SCRATCH_REG") + } else { + out(" pha") + assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type) + out(" pla") + } + out(" $compare P8ZP_SCRATCH_REG") + } + + when(value) { + is PtArrayIndexer -> { + val constIndex = value.index.asConstInteger() + if(constIndex!=null) { + val offset = program.memsizer.memorySize(value.type, constIndex) + if(offset<256) { + return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y") + } + } + cmpViaScratch() + } + is PtMemoryByte -> { + val constAddr = value.address.asConstInteger() + if(constAddr!=null) { + out(" $compare ${constAddr.toHex()}") + } else { + cmpViaScratch() + } + } + is PtIdentifier -> { + out(" $compare ${asmVariableName(value)}") + } + is PtNumber -> { + if(value.number!=0.0) + out(" $compare #${value.number.toInt()}") + } + else -> { + cmpViaScratch() + } + } + } + + internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) { + assignExpressionToRegister(condition, RegisterOrPair.A, false) + when(condition) { + is PtNumber, + is PtBool, + is PtIdentifier, + is PtIrRegister, + is PtArrayIndexer, + is PtPrefix, + is PtIfExpression, + is PtBinaryExpression -> { /* no cmp necessary the lda has been done just prior */ } + is PtTypeCast -> { + if(!condition.value.type.isByte && !condition.value.type.isWord) + out(" cmp #0") + } + else -> out(" cmp #0") + } + } + + internal fun translateFloatsEqualsConditionIntoA(left: PtExpression, right: PtExpression) { + fun equalf(leftName: String, rightName: String) { + out(""" + lda #<$leftName + ldy #>$leftName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #<$rightName + ldy #>$rightName + jsr floats.vars_equal_f""") + } + fun equalf(expr: PtExpression, rightName: String) { + assignExpressionToRegister(expr, RegisterOrPair.FAC1, true) + out(""" + lda #<$rightName + ldy #>$rightName + jsr floats.var_fac1_equal_f""") + } + if(left is PtIdentifier) { + when (right) { + is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right)) + is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number)) + else -> { + assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) + equalf(asmVariableName(left), subroutineFloatEvalResultVar1) + subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true + } + } + } else { + when (right) { + is PtIdentifier -> equalf(left, asmVariableName(right)) + is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number)) + else -> { + assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) + equalf(left, subroutineFloatEvalResultVar1) + subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true + } + } + } + } + + internal fun translateFloatsLessConditionIntoA(left: PtExpression, right: PtExpression, lessOrEquals: Boolean) { + fun lessf(leftName: String, rightName: String) { + out(""" + lda #<$rightName + ldy #>$rightName + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + lda #<$leftName + ldy #>$leftName""") + if(lessOrEquals) + out("jsr floats.vars_lesseq_f") + else + out("jsr floats.vars_less_f") + } + fun lessf(expr: PtExpression, rightName: String) { + assignExpressionToRegister(expr, RegisterOrPair.FAC1, true) + out(" lda #<$rightName | ldy #>$rightName") + if(lessOrEquals) + out(" jsr floats.var_fac1_lesseq_f") + else + out(" jsr floats.var_fac1_less_f") + } + if(left is PtIdentifier) { + when (right) { + is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right)) + is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number)) + else -> { + assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) + lessf(asmVariableName(left), subroutineFloatEvalResultVar1) + subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true + } + } + } else { + when (right) { + is PtIdentifier -> lessf(left, asmVariableName(right)) + is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number)) + else -> { + assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) + lessf(left, subroutineFloatEvalResultVar1) + subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true + } + } + } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt index d341babe6..acc2346b8 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt @@ -19,14 +19,14 @@ internal class AssemblyProgram( private val binFile = outputDir.resolve("$name.bin") private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name)) private val listFile = outputDir.resolve("$name.list") - private val targetWithoutBreakpointsForEmulator = setOf(AtariTarget.NAME, Neo6502Target.NAME) + private val targetWithoutBreakpointsForEmulator = arrayOf(AtariTarget.NAME, Neo6502Target.NAME) override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean { val assemblerCommand: List when (compTarget.name) { - in setOf("c64", "c128", "cx16", "pet32") -> { + in arrayOf("c64", "c128", "cx16", "pet32") -> { // CBM machines .prg generation. // add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 89e8b307f..bd2237f6c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -1125,7 +1125,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, else -> throw AssemblyError("invalid reg") } } else { - if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) { + if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) { // just read the msb byte out of the word array if(arg.splitWords) { val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb" @@ -1225,7 +1225,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, else -> throw AssemblyError("invalid reg") } } else { - if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) { + if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) { // just read the lsb byte out of the word array val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable) when(resultRegister) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt index 562ba1ae9..c270bbf59 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt @@ -11,7 +11,6 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind internal class IfElseAsmGen(private val program: PtProgram, private val st: SymbolTable, private val asmgen: AsmGen6502Internal, - private val allocator: VariableAllocator, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) { @@ -48,7 +47,7 @@ internal class IfElseAsmGen(private val program: PtProgram, throw AssemblyError("not prefix in ifelse should have been replaced by swapped if-else blocks") else { checkNotExtsubReturnsStatusReg(prefixCond.value) - assignConditionValueToRegisterAndTest(prefixCond.value) + asmgen.assignConditionValueToRegisterAndTest(prefixCond.value) return if (jumpAfterIf != null) translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope) else @@ -59,84 +58,6 @@ internal class IfElseAsmGen(private val program: PtProgram, throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}") } - internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) { - // this is NOT for the if-else STATEMENT, but this is code for the IF-EXPRESSION. - require(target.datatype==expr.type) - val falseLabel = asmgen.makeLabel("ifexpr_false") - val endLabel = asmgen.makeLabel("ifexpr_end") - evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel) - when { - expr.type.isByteOrBool -> { - asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false) - asmgen.jmp(endLabel) - asmgen.out(falseLabel) - asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false) - asmgen.out(endLabel) - assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false) - } - expr.type.isWord -> { - asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false) - asmgen.jmp(endLabel) - asmgen.out(falseLabel) - asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false) - asmgen.out(endLabel) - assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) - } - expr.type.isFloat -> { - asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true) - asmgen.jmp(endLabel) - asmgen.out(falseLabel) - asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true) - asmgen.out(endLabel) - asmgen.assignRegister(RegisterOrPair.FAC1, target) - } - else -> throw AssemblyError("weird dt") - } - } - - private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) { - if (condition is PtBinaryExpression) { - val rightDt = condition.right.type - return when { - rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel) - rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel) - rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel) - else -> throw AssemblyError("weird dt") - } - } - else if(condition is PtPrefix && condition.operator=="not") { - throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values") - } else { - // 'simple' condition, check if it is a byte bittest - val bittest = condition as? PtBuiltinFunctionCall - if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) { - val variable = bittest.args[0] as PtIdentifier - val bitnumber = (bittest.args[1] as PtNumber).number.toInt() - val testForBitSet = bittest.name.endsWith("_set") - when (bitnumber) { - 7 -> { - // test via bit + N flag - asmgen.out(" bit ${variable.name}") - if(testForBitSet) asmgen.out(" bpl $falseLabel") - else asmgen.out(" bmi $falseLabel") - return - } - 6 -> { - // test via bit + V flag - asmgen.out(" bit ${variable.name}") - if(testForBitSet) asmgen.out(" bvc $falseLabel") - else asmgen.out(" bvs $falseLabel") - return - } - else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6") - } - } - - // the condition is "simple" enough to just assign its 0/1 value to a register and branch on that - assignConditionValueToRegisterAndTest(condition) - asmgen.out(" beq $falseLabel") - } - } private fun checkNotExtsubReturnsStatusReg(condition: PtExpression) { val fcall = condition as? PtFunctionCall @@ -148,25 +69,6 @@ internal class IfElseAsmGen(private val program: PtProgram, } } - private fun assignConditionValueToRegisterAndTest(condition: PtExpression) { - asmgen.assignExpressionToRegister(condition, RegisterOrPair.A, false) - when(condition) { - is PtNumber, - is PtBool, - is PtIdentifier, - is PtIrRegister, - is PtArrayIndexer, - is PtPrefix, - is PtIfExpression, - is PtBinaryExpression -> { /* no cmp necessary the lda has been done just prior */ } - is PtTypeCast -> { - if(!condition.value.type.isByte && !condition.value.type.isWord) - asmgen.out(" cmp #0") - } - else -> asmgen.out(" cmp #0") - } - } - private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) { val bittest = ifElse.condition as? PtBuiltinFunctionCall val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump @@ -241,7 +143,7 @@ internal class IfElseAsmGen(private val program: PtProgram, } // the condition is "simple" enough to just assign its 0/1 value to a register and branch on that - assignConditionValueToRegisterAndTest(ifElse.condition) + asmgen.assignConditionValueToRegisterAndTest(ifElse.condition) if(jumpAfterIf!=null) translateJumpElseBodies("bne", "beq", jumpAfterIf, ifElse.elseScope) else @@ -293,7 +195,7 @@ internal class IfElseAsmGen(private val program: PtProgram, "==" -> { // if X==value asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.right, false) + asmgen.cmpAwithByteValue(condition.right, false) return if(jumpAfterIf!=null) translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope) else @@ -302,7 +204,7 @@ internal class IfElseAsmGen(private val program: PtProgram, "!=" -> { // if X!=value asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.right, false) + asmgen.cmpAwithByteValue(condition.right, false) return if(jumpAfterIf!=null) translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope) else @@ -312,7 +214,7 @@ internal class IfElseAsmGen(private val program: PtProgram, "<=" -> { // X<=Y -> Y>=X (reverse of >=) asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.left, false) + asmgen.cmpAwithByteValue(condition.left, false) return if(signed) { if(jumpAfterIf!=null) translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope) @@ -328,7 +230,7 @@ internal class IfElseAsmGen(private val program: PtProgram, ">" -> translateByteGreater(stmt, signed, jumpAfterIf) ">=" -> { asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.right, false) + asmgen.cmpAwithByteValue(condition.right, false) return if(signed) { if(jumpAfterIf!=null) translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope) @@ -350,7 +252,7 @@ internal class IfElseAsmGen(private val program: PtProgram, translateIfElseBodies("beq", stmt) } else { errors.info("SLOW FALLBACK FOR 'IF' CODEGEN - ask for support", stmt.position) // should not occur ;-) - assignConditionValueToRegisterAndTest(stmt.condition) + asmgen.assignConditionValueToRegisterAndTest(stmt.condition) if(jumpAfterIf!=null) translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope) else @@ -361,234 +263,10 @@ internal class IfElseAsmGen(private val program: PtProgram, } } - private fun translateIfExpressionByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) { - val signed = condition.left.type.isSigned - val constValue = condition.right.asConstInteger() - if(constValue==0) { - return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel) - } - - when(condition.operator) { - "==" -> { - // if X==value - asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.right, false) - asmgen.out(" bne $falseLabel") - } - "!=" -> { - // if X!=value - asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.right, false) - asmgen.out(" beq $falseLabel") - } - in LogicalOperators -> { - val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A) - if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) { - asmgen.out(" beq $falseLabel") - } else { - errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-) - assignConditionValueToRegisterAndTest(condition) - asmgen.out(" beq $falseLabel") - } - } - else -> { - // TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does - // TODO: special cases for <, <=, >, >= above. - assignConditionValueToRegisterAndTest(condition) - asmgen.out(" beq $falseLabel") - } - } - } - - private fun translateIfExpressionWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) { - // TODO can we reuse this whole thing from IfElse ? - val constValue = condition.right.asConstInteger() - if(constValue!=null) { - if (constValue == 0) { - when (condition.operator) { - "==" -> return translateWordExprIsZero(condition.left, falseLabel) - "!=" -> return translateWordExprIsNotZero(condition.left, falseLabel) - } - } - if (constValue != 0) { - when (condition.operator) { - "==" -> return translateWordExprEqualsNumber(condition.left, constValue, falseLabel) - "!=" -> return translateWordExprNotEqualsNumber(condition.left, constValue, falseLabel) - } - } - } - val variable = condition.right as? PtIdentifier - if(variable!=null) { - when (condition.operator) { - "==" -> return translateWordExprEqualsVariable(condition.left, variable, falseLabel) - "!=" -> return translateWordExprNotEqualsVariable(condition.left, variable, falseLabel) - } - } - - // TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does - assignConditionValueToRegisterAndTest(condition) - asmgen.out(" beq $falseLabel") - } - - private fun translateWordExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) { - // if w!=variable - // TODO reuse code from ifElse? - val varRight = asmgen.asmVariableName(variable) - if(expr is PtIdentifier) { - val varLeft = asmgen.asmVariableName(expr) - asmgen.out(""" - lda $varLeft - cmp $varRight - bne + - lda $varLeft+1 - cmp $varRight+1 - beq $falseLabel -+""") - } else { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) - asmgen.out(""" - cmp $varRight - bne + - cpy $varRight+1 - beq $falseLabel -+""") - } - } - - private fun translateWordExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) { - // if w==variable - // TODO reuse code from ifElse? - val varRight = asmgen.asmVariableName(variable) - if(expr is PtIdentifier) { - val varLeft = asmgen.asmVariableName(expr) - asmgen.out(""" - lda $varLeft - cmp $varRight - bne $falseLabel - lda $varLeft+1 - cmp $varRight+1 - bne $falseLabel""") - } else { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) - asmgen.out(""" - cmp $varRight - bne $falseLabel - cpy $varRight+1 - bne $falseLabel""") - } - } - - private fun translateWordExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) { - // if w!=number - // TODO reuse code from ifElse? - if(expr is PtIdentifier) { - val varname = asmgen.asmVariableName(expr) - asmgen.out(""" - lda $varname - cmp #<$number - bne + - lda $varname+1 - cmp #>$number - beq $falseLabel -+""") - } else { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) - asmgen.out(""" - cmp #<$number - bne + - cpy #>$number - beq $falseLabel -+""") - } - } - - private fun translateWordExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) { - // if w==number - // TODO reuse code from ifElse? - if(expr is PtIdentifier) { - val varname = asmgen.asmVariableName(expr) - asmgen.out(""" - lda $varname - cmp #<$number - bne $falseLabel - lda $varname+1 - cmp #>$number - bne $falseLabel""") - } else { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) - asmgen.out( """ - cmp #<$number - bne $falseLabel - cpy #>$number - bne $falseLabel""") - } - } - - private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) { - // if w!=0 - // TODO reuse code from ifElse? - if(expr is PtIdentifier) { - val varname = asmgen.asmVariableName(expr) - asmgen.out(""" - lda $varname - ora $varname+1 - beq $falseLabel""") - } else { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) - asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel") - } - } - - private fun translateWordExprIsZero(expr: PtExpression, falseLabel: String) { - // if w==0 - // TODO reuse code from ifElse? - if(expr is PtIdentifier) { - val varname = asmgen.asmVariableName(expr) - asmgen.out(""" - lda $varname - ora $varname+1 - bne $falseLabel""") - } else { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) - asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel") - } - } - - private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) { - // optimized code for byte comparisons with 0 - assignConditionValueToRegisterAndTest(condition.left) - when (condition.operator) { - "==" -> asmgen.out(" bne $falseLabel") - "!=" -> asmgen.out(" beq $falseLabel") - ">" -> { - if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel") - else asmgen.out(" beq $falseLabel") - } - ">=" -> { - if(signed) asmgen.out(" bmi $falseLabel") - else { /* always true for unsigned */ } - } - "<" -> { - if(signed) asmgen.out(" bpl $falseLabel") - else asmgen.jmp(falseLabel) - } - "<=" -> { - if(signed) { - // inverted '>' - asmgen.out(""" - beq + - bpl $falseLabel -+""") - } else asmgen.out(" bne $falseLabel") - } - else -> throw AssemblyError("expected comparison operator") - } - } - private fun translateIfCompareWithZeroByte(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) { // optimized code for byte comparisons with 0 val condition = stmt.condition as PtBinaryExpression - assignConditionValueToRegisterAndTest(condition.left) + asmgen.assignConditionValueToRegisterAndTest(condition.left) when (condition.operator) { "==" -> { return if(jumpAfterIf!=null) @@ -718,56 +396,10 @@ internal class IfElseAsmGen(private val program: PtProgram, } } - private fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) { - val compare = if(useSbc) "sec | sbc" else "cmp" - fun cmpViaScratch() { - if(assignmentAsmGen.directIntoY(value)) { - asmgen.assignExpressionToRegister(value, RegisterOrPair.Y, false) - asmgen.out(" sty P8ZP_SCRATCH_REG") - } else { - asmgen.out(" pha") - asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type) - asmgen.out(" pla") - } - asmgen.out(" $compare P8ZP_SCRATCH_REG") - } - - when(value) { - is PtArrayIndexer -> { - val constIndex = value.index.asConstInteger() - if(constIndex!=null) { - val offset = program.memsizer.memorySize(value.type, constIndex) - if(offset<256) { - return asmgen.out(" ldy #$offset | $compare ${asmgen.asmVariableName(value.variable)},y") - } - } - cmpViaScratch() - } - is PtMemoryByte -> { - val constAddr = value.address.asConstInteger() - if(constAddr!=null) { - asmgen.out(" $compare ${constAddr.toHex()}") - } else { - cmpViaScratch() - } - } - is PtIdentifier -> { - asmgen.out(" $compare ${asmgen.asmVariableName(value)}") - } - is PtNumber -> { - if(value.number!=0.0) - asmgen.out(" $compare #${value.number.toInt()}") - } - else -> { - cmpViaScratch() - } - } - } - private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) { val condition = stmt.condition as PtBinaryExpression asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) - cmpAwithByteValue(condition.right, false) + asmgen.cmpAwithByteValue(condition.right, false) if(signed) { if(jumpAfterIf!=null) translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope) @@ -786,14 +418,14 @@ internal class IfElseAsmGen(private val program: PtProgram, if(signed) { // X>Y --> Y { - translateFloatsEqualsConditionIntoA(condition.left, condition.right) + asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right) return if (jumpAfterIf != null) translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope) else translateIfElseBodies("beq", stmt) } "!=" -> { - translateFloatsEqualsConditionIntoA(condition.left, condition.right) + asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right) return if (jumpAfterIf != null) translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope) else translateIfElseBodies("bne", stmt) } "<" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, false) + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false) return if (jumpAfterIf != null) translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope) else translateIfElseBodies("beq", stmt) } "<=" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, true) + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true) return if (jumpAfterIf != null) translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope) else translateIfElseBodies("beq", stmt) } ">" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, true) + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true) return if (jumpAfterIf != null) translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope) else translateIfElseBodies("bne", stmt) } ">=" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, false) + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false) return if (jumpAfterIf != null) translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope) else @@ -2049,136 +1681,4 @@ _jump jmp ($asmLabel) else -> throw AssemblyError("expected comparison operator") } } - - private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) { - val constValue = (condition.right as? PtNumber)?.number - if(constValue==0.0) { - if (condition.operator == "==") { - // if FL==0.0 - asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true) - asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel") - return - } else if(condition.operator=="!=") { - // if FL!=0.0 - asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true) - asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel") - return - } - } - - when(condition.operator) { - "==" -> { - translateFloatsEqualsConditionIntoA(condition.left, condition.right) - asmgen.out(" beq $elseLabel") - } - "!=" -> { - translateFloatsEqualsConditionIntoA(condition.left, condition.right) - asmgen.out(" bne $elseLabel") - } - "<" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, false) - asmgen.out(" beq $elseLabel") - } - "<=" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, true) - asmgen.out(" beq $elseLabel") - } - ">" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, true) - asmgen.out(" bne $elseLabel") - } - ">=" -> { - translateFloatsLessConditionIntoA(condition.left, condition.right, false) - asmgen.out(" bne $elseLabel") - } - else -> throw AssemblyError("expected comparison operator") - } - } - - private fun translateFloatsEqualsConditionIntoA(left: PtExpression, right: PtExpression) { - fun equalf(leftName: String, rightName: String) { - asmgen.out(""" - lda #<$leftName - ldy #>$leftName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #<$rightName - ldy #>$rightName - jsr floats.vars_equal_f""") - } - fun equalf(expr: PtExpression, rightName: String) { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true) - asmgen.out(""" - lda #<$rightName - ldy #>$rightName - jsr floats.var_fac1_equal_f""") - } - if(left is PtIdentifier) { - when (right) { - is PtIdentifier -> equalf(asmgen.asmVariableName(left), asmgen.asmVariableName(right)) - is PtNumber -> equalf(asmgen.asmVariableName(left), allocator.getFloatAsmConst(right.number)) - else -> { - asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) - equalf(asmgen.asmVariableName(left), subroutineFloatEvalResultVar1) - asmgen.subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true - } - } - } else { - when (right) { - is PtIdentifier -> equalf(left, asmgen.asmVariableName(right)) - is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number)) - else -> { - asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) - equalf(left, subroutineFloatEvalResultVar1) - asmgen.subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true - } - } - } - } - - private fun translateFloatsLessConditionIntoA(left: PtExpression, right: PtExpression, lessOrEquals: Boolean) { - fun lessf(leftName: String, rightName: String) { - asmgen.out(""" - lda #<$rightName - ldy #>$rightName - sta P8ZP_SCRATCH_W2 - sty P8ZP_SCRATCH_W2+1 - lda #<$leftName - ldy #>$leftName""") - if(lessOrEquals) - asmgen.out("jsr floats.vars_lesseq_f") - else - asmgen.out("jsr floats.vars_less_f") - } - fun lessf(expr: PtExpression, rightName: String) { - asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true) - asmgen.out(" lda #<$rightName | ldy #>$rightName") - if(lessOrEquals) - asmgen.out(" jsr floats.var_fac1_lesseq_f") - else - asmgen.out(" jsr floats.var_fac1_less_f") - } - if(left is PtIdentifier) { - when (right) { - is PtIdentifier -> lessf(asmgen.asmVariableName(left), asmgen.asmVariableName(right)) - is PtNumber -> lessf(asmgen.asmVariableName(left), allocator.getFloatAsmConst(right.number)) - else -> { - asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) - lessf(asmgen.asmVariableName(left), subroutineFloatEvalResultVar1) - asmgen.subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true - } - } - } else { - when (right) { - is PtIdentifier -> lessf(left, asmgen.asmVariableName(right)) - is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number)) - else -> { - asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) - lessf(left, subroutineFloatEvalResultVar1) - asmgen.subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true - } - } - } - } - } \ No newline at end of file diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt new file mode 100644 index 000000000..e7e3f886a --- /dev/null +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfExpressionAsmGen.kt @@ -0,0 +1,370 @@ +package prog8.codegen.cpu6502 + +import prog8.code.ast.PtBinaryExpression +import prog8.code.ast.PtBuiltinFunctionCall +import prog8.code.ast.PtExpression +import prog8.code.ast.PtIdentifier +import prog8.code.ast.PtIfExpression +import prog8.code.ast.PtNumber +import prog8.code.ast.PtPrefix +import prog8.code.core.AssemblyError +import prog8.code.core.BaseDataType +import prog8.code.core.CpuRegister +import prog8.code.core.DataType +import prog8.code.core.IErrorReporter +import prog8.code.core.LogicalOperators +import prog8.code.core.RegisterOrPair +import prog8.codegen.cpu6502.assignment.AsmAssignTarget +import prog8.codegen.cpu6502.assignment.AssignmentAsmGen +import prog8.codegen.cpu6502.assignment.TargetStorageKind + +internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) { + + internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) { + require(target.datatype==expr.type) + val falseLabel = asmgen.makeLabel("ifexpr_false") + val endLabel = asmgen.makeLabel("ifexpr_end") + evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel) + when { + expr.type.isByteOrBool -> { + asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false) + asmgen.jmp(endLabel) + asmgen.out(falseLabel) + asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false) + asmgen.out(endLabel) + assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false) + } + expr.type.isWord -> { + asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false) + asmgen.jmp(endLabel) + asmgen.out(falseLabel) + asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false) + asmgen.out(endLabel) + assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) + } + expr.type.isFloat -> { + asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true) + asmgen.jmp(endLabel) + asmgen.out(falseLabel) + asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true) + asmgen.out(endLabel) + asmgen.assignRegister(RegisterOrPair.FAC1, target) + } + else -> throw AssemblyError("weird dt") + } + } + + private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) { + if (condition is PtBinaryExpression) { + val rightDt = condition.right.type + return when { + rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel) + rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel) + rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel) + else -> throw AssemblyError("weird dt") + } + } + else if(condition is PtPrefix && condition.operator=="not") { + throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values") + } else { + // 'simple' condition, check if it is a byte bittest + val bittest = condition as? PtBuiltinFunctionCall + if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) { + val variable = bittest.args[0] as PtIdentifier + val bitnumber = (bittest.args[1] as PtNumber).number.toInt() + val testForBitSet = bittest.name.endsWith("_set") + when (bitnumber) { + 7 -> { + // test via bit + N flag + asmgen.out(" bit ${variable.name}") + if(testForBitSet) asmgen.out(" bpl $falseLabel") + else asmgen.out(" bmi $falseLabel") + return + } + 6 -> { + // test via bit + V flag + asmgen.out(" bit ${variable.name}") + if(testForBitSet) asmgen.out(" bvc $falseLabel") + else asmgen.out(" bvs $falseLabel") + return + } + else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6") + } + } + + // the condition is "simple" enough to just assign its 0/1 value to a register and branch on that + asmgen.assignConditionValueToRegisterAndTest(condition) + asmgen.out(" beq $falseLabel") + } + } + + private fun translateIfExpressionByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) { + val signed = condition.left.type.isSigned + val constValue = condition.right.asConstInteger() + if(constValue==0) { + return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel) + } + + when(condition.operator) { + "==" -> { + // if X==value + asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) + asmgen.cmpAwithByteValue(condition.right, false) + asmgen.out(" bne $falseLabel") + } + "!=" -> { + // if X!=value + asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) + asmgen.cmpAwithByteValue(condition.right, false) + asmgen.out(" beq $falseLabel") + } + in LogicalOperators -> { + val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A) + if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) { + asmgen.out(" beq $falseLabel") + } else { + errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-) + asmgen.assignConditionValueToRegisterAndTest(condition) + asmgen.out(" beq $falseLabel") + } + } + else -> { + // TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does + // TODO: special cases for <, <=, >, >= above. + asmgen.assignConditionValueToRegisterAndTest(condition) + asmgen.out(" beq $falseLabel") + } + } + } + + private fun translateIfExpressionWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) { + // TODO can we reuse this whole thing from IfElse ? + val constValue = condition.right.asConstInteger() + if(constValue!=null) { + if (constValue == 0) { + when (condition.operator) { + "==" -> return translateWordExprIsZero(condition.left, falseLabel) + "!=" -> return translateWordExprIsNotZero(condition.left, falseLabel) + } + } + if (constValue != 0) { + when (condition.operator) { + "==" -> return translateWordExprEqualsNumber(condition.left, constValue, falseLabel) + "!=" -> return translateWordExprNotEqualsNumber(condition.left, constValue, falseLabel) + } + } + } + val variable = condition.right as? PtIdentifier + if(variable!=null) { + when (condition.operator) { + "==" -> return translateWordExprEqualsVariable(condition.left, variable, falseLabel) + "!=" -> return translateWordExprNotEqualsVariable(condition.left, variable, falseLabel) + } + } + + // TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does + asmgen.assignConditionValueToRegisterAndTest(condition) + asmgen.out(" beq $falseLabel") + } + + private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) { + val constValue = (condition.right as? PtNumber)?.number + if(constValue==0.0) { + if (condition.operator == "==") { + // if FL==0.0 + asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true) + asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel") + return + } else if(condition.operator=="!=") { + // if FL!=0.0 + asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true) + asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel") + return + } + } + + when(condition.operator) { + "==" -> { + asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right) + asmgen.out(" beq $elseLabel") + } + "!=" -> { + asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right) + asmgen.out(" bne $elseLabel") + } + "<" -> { + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false) + asmgen.out(" beq $elseLabel") + } + "<=" -> { + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true) + asmgen.out(" beq $elseLabel") + } + ">" -> { + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true) + asmgen.out(" bne $elseLabel") + } + ">=" -> { + asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false) + asmgen.out(" bne $elseLabel") + } + else -> throw AssemblyError("expected comparison operator") + } + } + + private fun translateWordExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) { + // if w!=variable + // TODO reuse code from ifElse? + val varRight = asmgen.asmVariableName(variable) + if(expr is PtIdentifier) { + val varLeft = asmgen.asmVariableName(expr) + asmgen.out(""" + lda $varLeft + cmp $varRight + bne + + lda $varLeft+1 + cmp $varRight+1 + beq $falseLabel ++""") + } else { + asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) + asmgen.out(""" + cmp $varRight + bne + + cpy $varRight+1 + beq $falseLabel ++""") + } + } + + private fun translateWordExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) { + // if w==variable + // TODO reuse code from ifElse? + val varRight = asmgen.asmVariableName(variable) + if(expr is PtIdentifier) { + val varLeft = asmgen.asmVariableName(expr) + asmgen.out(""" + lda $varLeft + cmp $varRight + bne $falseLabel + lda $varLeft+1 + cmp $varRight+1 + bne $falseLabel""") + } else { + asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) + asmgen.out(""" + cmp $varRight + bne $falseLabel + cpy $varRight+1 + bne $falseLabel""") + } + } + + private fun translateWordExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) { + // if w!=number + // TODO reuse code from ifElse? + if(expr is PtIdentifier) { + val varname = asmgen.asmVariableName(expr) + asmgen.out(""" + lda $varname + cmp #<$number + bne + + lda $varname+1 + cmp #>$number + beq $falseLabel ++""") + } else { + asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) + asmgen.out(""" + cmp #<$number + bne + + cpy #>$number + beq $falseLabel ++""") + } + } + + private fun translateWordExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) { + // if w==number + // TODO reuse code from ifElse? + if(expr is PtIdentifier) { + val varname = asmgen.asmVariableName(expr) + asmgen.out(""" + lda $varname + cmp #<$number + bne $falseLabel + lda $varname+1 + cmp #>$number + bne $falseLabel""") + } else { + asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) + asmgen.out( """ + cmp #<$number + bne $falseLabel + cpy #>$number + bne $falseLabel""") + } + } + + private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) { + // if w!=0 + // TODO reuse code from ifElse? + if(expr is PtIdentifier) { + val varname = asmgen.asmVariableName(expr) + asmgen.out(""" + lda $varname + ora $varname+1 + beq $falseLabel""") + } else { + asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) + asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel") + } + } + + private fun translateWordExprIsZero(expr: PtExpression, falseLabel: String) { + // if w==0 + // TODO reuse code from ifElse? + if(expr is PtIdentifier) { + val varname = asmgen.asmVariableName(expr) + asmgen.out(""" + lda $varname + ora $varname+1 + bne $falseLabel""") + } else { + asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) + asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel") + } + } + + private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) { + // optimized code for byte comparisons with 0 + asmgen.assignConditionValueToRegisterAndTest(condition.left) + when (condition.operator) { + "==" -> asmgen.out(" bne $falseLabel") + "!=" -> asmgen.out(" beq $falseLabel") + ">" -> { + if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel") + else asmgen.out(" beq $falseLabel") + } + ">=" -> { + if(signed) asmgen.out(" bmi $falseLabel") + else { /* always true for unsigned */ } + } + "<" -> { + if(signed) asmgen.out(" bpl $falseLabel") + else asmgen.jmp(falseLabel) + } + "<=" -> { + if(signed) { + // inverted '>' + asmgen.out(""" + beq + + bpl $falseLabel ++""") + } else asmgen.out(" bne $falseLabel") + } + else -> throw AssemblyError("expected comparison operator") + } + } + +} \ No newline at end of file diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 38ef197e9..4f1bd7b8b 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -442,7 +442,7 @@ internal class AstChecker(private val program: Program, if (!pair.first.isByteOrBool) err("return type #${index + 1} should be (u)byte") } - else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { + else if(pair.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { if (!pair.first.isWord && !pair.first.isString && !pair.first.isArray) err("return type #${index + 1} should be (u)word/address") } @@ -1289,7 +1289,7 @@ internal class AstChecker(private val program: Program, if(leftDt.isBool || rightDt.isBool || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.isBool==true || (expr.right as? TypecastExpression)?.expression?.inferType(program)?.isBool==true) { - if(expr.operator in setOf("<", "<=", ">", ">=")) { + if(expr.operator in arrayOf("<", "<=", ">", ">=")) { errors.err("can't use boolean operand with this comparison operator", expr.position) } // for now, don't enforce bool type with only logical operators... @@ -1528,10 +1528,10 @@ internal class AstChecker(private val program: Program, if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) { var regname = ident.nameInSource[1].uppercase() val lastLetter = regname.last().lowercaseChar() - if(lastLetter in setOf('l', 'h', 's')) { + if(lastLetter in arrayOf('l', 'h', 's')) { regname = regname.substring(0, regname.length - 1) val lastLetter2 = regname.last().lowercaseChar() - if(lastLetter2 in setOf('l', 'h', 's')) { + if(lastLetter2 in arrayOf('l', 'h', 's')) { regname = regname.substring(0, regname.length - 1) } } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 6ee07536f..3547912c2 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -1,7 +1,6 @@ package prog8.vm import prog8.code.Either -import prog8.code.core.AssemblyError import prog8.code.core.DataType import prog8.code.left import prog8.code.right