From 299ea72d701a9543bd0855717d356baa94955d8d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 31 Jul 2019 21:31:44 +0200 Subject: [PATCH] various for loops --- .../src/prog8/ast/processing/AstChecker.kt | 3 +- .../compiler/target/c64/codegen2/AsmGen2.kt | 169 ++++++++++++++++-- examples/test.p8 | 30 +++- 3 files changed, 182 insertions(+), 20 deletions(-) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index f55ec048b..a9a9f7001 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -119,10 +119,11 @@ internal class AstChecker(private val program: Program, checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position)) } else { if (forLoop.loopRegister != null) { - printWarning("using a register as loop variable is risky (it could get clobbered)", forLoop.position) // loop register if (iterableDt != DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes) checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position)) + if(forLoop.loopRegister!=Register.A) + checkResult.add(ExpressionError("it's only possible to use A as a loop register", forLoop.position)) } else { // loop variable val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace) diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt index 975029507..a84ab921f 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt @@ -339,9 +339,9 @@ internal class AsmGen2(val program: Program, .filter {it.datatype in StringDatatypes } .map { it to encodeStr((it.value as ReferenceLiteralValue).str!!, it.datatype) } .groupBy({it.second}, {it.first}) - for((encoded, vars) in encodedstringVars) { - vars.dropLast(1).forEach { out(it.name) } - val lastvar = vars.last() + for((encoded, variables) in encodedstringVars) { + variables.dropLast(1).forEach { out(it.name) } + val lastvar = variables.last() outputStringvar(lastvar, encoded) } @@ -953,35 +953,178 @@ internal class AsmGen2(val program: Program, private fun translate(stmt: ForLoop) { val iterableDt = stmt.iterable.inferType(program) + val loopLabel = makeLabel("for_loop") + val endLabel = makeLabel("for_end") when(stmt.iterable) { is RangeExpr -> { + val range = (stmt.iterable as RangeExpr).toConstantIntegerRange() + if(range==null) + TODO("non-const range loop") + if(range.isEmpty()) + throw AssemblyError("empty range") when(iterableDt) { in ByteDatatypes -> { - println("forloop over byte range $stmt") // TODO + if(stmt.loopRegister!=null) { + // loop register + if(stmt.loopRegister!=Register.A) + throw AssemblyError("can only use A") + when { + range.step==1 -> { + val counterLabel = makeLabel("for_counter") + out(""" + lda #${range.first} + sta $loopLabel+1 + lda #${range.last-range.first+1 and 255} + sta $counterLabel +$loopLabel lda #0 ; modified""") + translate(stmt.body) + out(""" + dec $counterLabel + beq $endLabel + inc $loopLabel+1 + jmp $loopLabel +$counterLabel .byte 0 +$endLabel""") + } + range.step==-1 -> { + val counterLabel = makeLabel("for_counter") + out(""" + lda #${range.first} + sta $loopLabel+1 + lda #${range.first-range.last+1 and 255} + sta $counterLabel +$loopLabel lda #0 ; modified """) + translate(stmt.body) + out(""" + dec $counterLabel + beq $endLabel + dec $loopLabel+1 + jmp $loopLabel +$counterLabel .byte 0 +$endLabel""") + } + range.step>0 -> { + val counterLabel = makeLabel("for_counter") + out(""" + lda #${(range.last-range.first) / range.step + 1} + sta $counterLabel + lda #${range.first} +$loopLabel pha""") + translate(stmt.body) + out(""" + pla + dec $counterLabel + beq $endLabel + clc + adc #${range.step} + jmp $loopLabel +$counterLabel .byte 0 +$endLabel""") + } + else -> { + val counterLabel = makeLabel("for_counter") + out(""" + lda #${(range.first-range.last) / range.step.absoluteValue + 1} + sta $counterLabel + lda #${range.first} +$loopLabel pha""") + translate(stmt.body) + out(""" + pla + dec $counterLabel + beq $endLabel + sec + sbc #${range.step.absoluteValue} + jmp $loopLabel +$counterLabel .byte 0 +$endLabel""") + } + } + } else { + TODO("loop over byte range via loopvar $stmt") + } } in WordDatatypes -> { - println("forloop over word range $stmt") // TODO + TODO("forloop over word range $stmt") // TODO } else -> throw AssemblyError("range expression can only be byte or word") } } is IdentifierReference -> { - val decl = (stmt.iterable as IdentifierReference).targetVarDecl(program.namespace)!! + val ident = (stmt.iterable as IdentifierReference) + val iterableName = asmIdentifierName(ident) + val decl = ident.targetVarDecl(program.namespace)!! when(iterableDt) { DataType.STR, DataType.STR_S -> { - println("forloop over string $stmt") // TODO (ends at 0 byte) + if(stmt.loopRegister!=null) { + // loop register + if(stmt.loopRegister!=Register.A) + throw AssemblyError("can only use A") + out(""" + lda #<$iterableName + ldy #>$iterableName + sta $loopLabel+1 + sty $loopLabel+2 +$loopLabel lda ${65535.toHex()} ; modified + beq $endLabel""") + translate(stmt.body) + out(""" + inc $loopLabel+1 + bne $loopLabel + inc $loopLabel+2 + bne $loopLabel +$endLabel""") + } else { + TODO("loop over string via loopvar $stmt") + } } DataType.ARRAY_UB, DataType.ARRAY_B -> { - val size = decl.arraysize!!.size() - println("forloop over byte array of len $size $stmt") // TODO (ends at length of array) + val length = decl.arraysize!!.size() + if(stmt.loopRegister!=null) { + // loop register + if(stmt.loopRegister!=Register.A) + throw AssemblyError("can only use A") + val counterLabel = makeLabel("for_counter") + val modifiedLabel = makeLabel("for_modified") + out(""" + lda #<$iterableName + ldy #>$iterableName + sta $modifiedLabel+1 + sty $modifiedLabel+2 + ldy #0 +$loopLabel sty $counterLabel + cpy #$length + beq $endLabel +$modifiedLabel lda ${65535.toHex()},y ; modified + """) + translate(stmt.body) + out(""" + ldy $counterLabel + iny + jmp $loopLabel +$counterLabel .byte 0 +$endLabel""") + } else { + TODO("loop variable over bytearray of len $length") + } } DataType.ARRAY_W, DataType.ARRAY_UW -> { - val size = decl.arraysize!!.size() - println("forloop over word array len $size $stmt") // TODO + val length = decl.arraysize!!.size() + println("forloop over word array len $length $stmt") // TODO + if(stmt.loopRegister!=null) { + TODO("loop register over wordarray of len $length") + } else { + TODO("loop variable over wordarray of len $length") + } } DataType.ARRAY_F -> { - val size = decl.arraysize!!.size() - println("forloop over float array len $size $stmt") // TODO + val length = decl.arraysize!!.size() + println("forloop over float array len $length $stmt") // TODO + if(stmt.loopRegister!=null) { + throw AssemblyError("can't use register to loop over floats") + } else { + TODO("loop variable over floatarray of len $length") + } } else -> throw AssemblyError("can't iterate over $iterableDt") } diff --git a/examples/test.p8 b/examples/test.p8 index 4ddcf1e88..ff30f3ab3 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -19,12 +19,30 @@ main { c64.CHROUT(',') } c64.CHROUT('\n') -; -; for A in 10 to 20 { -; c64scr.print_ub(A) -; c64.CHROUT(',') -; } -; c64.CHROUT('\n') + + for A in 10 to 20 { + c64scr.print_ub(A) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for A in 20 to 10 step -1 { + c64scr.print_ub(A) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for A in 10 to 21 step 3 { + c64scr.print_ub(A) + c64.CHROUT(',') + } + c64.CHROUT('\n') + + for A in 24 to 10 step -3 { + c64scr.print_ub(A) + c64.CHROUT(',') + } + c64.CHROUT('\n') ; for var2 in 10 to 20 { ; c64scr.print_ub(var2)