From 0c461ffe2e5578ba4a85f33d1e33ee321ad2cdab Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 24 Jul 2020 22:57:19 +0200 Subject: [PATCH] removed Register expression (directly accessing cpu register) --- README.md | 2 +- compiler/res/version.txt | 2 +- compiler/src/prog8/ast/AstToSourceCode.kt | 17 +- compiler/src/prog8/ast/antlr/Antr2Kotlin.kt | 64 +- compiler/src/prog8/ast/base/Base.kt | 15 +- .../prog8/ast/expressions/AstExpressions.kt | 38 +- .../src/prog8/ast/processing/AstChecker.kt | 88 ++- .../ast/processing/AstIdentifiersChecker.kt | 15 - .../ast/processing/AstVariousTransforms.kt | 2 +- .../src/prog8/ast/processing/AstWalker.kt | 9 +- .../src/prog8/ast/processing/IAstVisitor.kt | 5 +- .../ast/processing/StatementReorderer.kt | 6 +- .../src/prog8/ast/statements/AstStatements.kt | 68 +- .../compiler/BeforeAsmGenerationAstChanger.kt | 2 +- .../target/c64/C64MachineDefinition.kt | 89 --- .../compiler/target/c64/codegen/AsmGen.kt | 39 +- .../target/c64/codegen/AssignmentAsmGen.kt | 605 +----------------- .../c64/codegen/BuiltinFunctionsAsmGen.kt | 42 -- .../target/c64/codegen/ExpressionsAsmGen.kt | 9 - .../target/c64/codegen/ForLoopsAsmGen.kt | 250 ++------ .../target/c64/codegen/FunctionCallAsmGen.kt | 39 +- .../target/c64/codegen/PostIncrDecrAsmGen.kt | 25 +- .../optimizer/ConstantFoldingOptimizer.kt | 62 +- .../prog8/optimizer/ExpressionSimplifier.kt | 3 +- .../src/prog8/optimizer/StatementOptimizer.kt | 14 +- docs/source/index.rst | 4 +- docs/source/programming.rst | 35 +- docs/source/syntaxreference.rst | 19 +- docs/source/targetsystem.rst | 23 +- examples/arithmetic/aggregates.p8 | 11 - examples/arithmetic/bitshift.p8 | 2 + examples/arithmetic/div.p8 | 10 - examples/arithmetic/minus.p8 | 11 - examples/arithmetic/mult.p8 | 10 - examples/arithmetic/plus.p8 | 11 - examples/arithmetic/postincrdecr.p8 | 12 +- examples/arithmetic/remainder.p8 | 10 - examples/comparison_ifs_byte.p8 | 11 - examples/comparison_ifs_float.p8 | 11 - examples/comparison_ifs_ubyte.p8 | 11 - examples/comparison_ifs_uword.p8 | 11 - examples/comparison_ifs_word.p8 | 11 - examples/comparisons_byte.p8 | 10 - examples/comparisons_float.p8 | 8 - examples/comparisons_ubyte.p8 | 9 - examples/comparisons_uword.p8 | 10 - examples/comparisons_word.p8 | 10 - examples/fibonacci.p8 | 6 +- examples/hello.p8 | 12 - examples/mandelbrot.p8 | 10 - examples/numbergame.p8 | 12 - examples/primes.p8 | 12 - examples/romfloats.p8 | 12 - examples/screencodes.p8 | 12 - examples/sorting.p8 | 11 - examples/structs.p8 | 14 +- examples/tehtriz.p8 | 12 - examples/test.p8 | 1 + examples/testarrays.p8 | 1 + examples/testforloops.p8 | 12 +- parser/antlr/prog8.g4 | 18 +- 61 files changed, 311 insertions(+), 1604 deletions(-) diff --git a/README.md b/README.md index ee054d3a0..6c5a04180 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ which aims to provide many conveniences over raw assembly code (even when using - modularity, symbol scoping, subroutines - various data types other than just bytes (16-bit words, floats, strings) - automatic variable allocations, automatic string and array variables and string sharing -- subroutines with a input- and output parameter signature +- subroutines with an input- and output parameter signature - constant folding in expressions - conditional branches - 'when' statement to provide a concise jump table alternative to if/elseif chains diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 6b4950e3d..9f55b2ccb 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -2.4 +3.0 diff --git a/compiler/src/prog8/ast/AstToSourceCode.kt b/compiler/src/prog8/ast/AstToSourceCode.kt index 2ecfb8150..4c2c97908 100644 --- a/compiler/src/prog8/ast/AstToSourceCode.kt +++ b/compiler/src/prog8/ast/AstToSourceCode.kt @@ -310,10 +310,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): override fun visit(forLoop: ForLoop) { output("for ") - if(forLoop.loopRegister!=null) - output(forLoop.loopRegister.toString()) - else - forLoop.loopVar!!.accept(this) + forLoop.loopVar.accept(this) output(" in ") forLoop.iterable.accept(this) output(" ") @@ -352,12 +349,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): } override fun visit(assignTarget: AssignTarget) { - if(assignTarget.register!=null) - output(assignTarget.register.toString()) - else { - assignTarget.memoryAddress?.accept(this) - assignTarget.identifier?.accept(this) - } + assignTarget.memoryAddress?.accept(this) + assignTarget.identifier?.accept(this) assignTarget.arrayindexed?.accept(this) } @@ -398,10 +391,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): outputlni("}}") } - override fun visit(registerExpr: RegisterExpr) { - output(registerExpr.register.toString()) - } - override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) { output(builtinFunctionStatementPlaceholder.name) } diff --git a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt index 3643f3349..9ec6ed4b6 100644 --- a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt +++ b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt @@ -247,7 +247,7 @@ private class AsmsubDecl(val name: String, val returntypes: List, val asmParameterRegisters: List, val asmReturnvaluesRegisters: List, - val asmClobbers: Set) + val asmClobbers: Set) private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl { val name = identifier().text @@ -274,24 +274,43 @@ private class AsmSubroutineReturn(val type: DataType, val stack: Boolean, val position: Position) -private fun prog8Parser.ClobberContext.toAst(): Set - = this.register().asSequence().map { it.toAst() }.toSet() - private fun prog8Parser.Asmsub_returnsContext.toAst(): List - = asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) } + = asmsub_return().map { + val register = it.identifier()?.toAst() + var registerorpair: RegisterOrPair? = null + var statusregister: Statusflag? = null + if(register!=null) { + when (val name = register.nameInSource.single()) { + in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name) + in Statusflag.names -> statusregister = Statusflag.valueOf(name) + else -> throw FatalAstException("invalid register or status flag in $it") + } + } + AsmSubroutineReturn( + it.datatype().toAst(), + registerorpair, + statusregister, + !it.stack?.text.isNullOrEmpty(), toPosition()) + } private fun prog8Parser.Asmsub_paramsContext.toAst(): List = asmsub_param().map { val vardecl = it.vardecl() val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT - AsmSubroutineParameter(vardecl.varname.text, datatype, - it.registerorpair()?.toAst(), - it.statusregister()?.toAst(), + val register = it.identifier()?.toAst() + var registerorpair: RegisterOrPair? = null + var statusregister: Statusflag? = null + if(register!=null) { + when (val name = register.nameInSource.single()) { + in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name) + in Statusflag.names -> statusregister = Statusflag.valueOf(name) + else -> throw FatalAstException("invalid register or status flag in $it") + } + } + AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister, !it.stack?.text.isNullOrEmpty(), toPosition()) } -private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text) - private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement { val void = this.VOID() != null val location = scoped_identifier().toAst() @@ -350,23 +369,22 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List = } private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget { - val register = register()?.toAst() val identifier = scoped_identifier() return when { - register!=null -> AssignTarget(register, null, null, null, toPosition()) - identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition()) - arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition()) - directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition()) - else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition()) + identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition()) + arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition()) + directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition()) + else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition()) } } -private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase()) +private fun prog8Parser.ClobberContext.toAst() : Set { + val names = this.identifier().map { it.toAst().nameInSource.single() } + return names.map { CpuRegister.valueOf(it) }.toSet() +} private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase()) -private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase()) - private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex = ArrayIndex(expression().toAst(), toPosition()) @@ -478,9 +496,6 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression { } } - if(register()!=null) - return RegisterExpr(register().toAst(), register().toPosition()) - if(scoped_identifier()!=null) return scoped_identifier().toAst() @@ -572,15 +587,14 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement { private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase()) private fun prog8Parser.ForloopContext.toAst(): ForLoop { - val loopregister = register()?.toAst() - val loopvar = identifier()?.toAst() + val loopvar = identifier().toAst() val iterable = expression()!!.toAst() val scope = if(statement()!=null) AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition()) else AnonymousScope(statement_block().toAst(), statement_block().toPosition()) - return ForLoop(loopregister, loopvar, iterable, scope, toPosition()) + return ForLoop(loopvar, iterable, scope, toPosition()) } private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition()) diff --git a/compiler/src/prog8/ast/base/Base.kt b/compiler/src/prog8/ast/base/Base.kt index 71a5a9027..3f018818c 100644 --- a/compiler/src/prog8/ast/base/Base.kt +++ b/compiler/src/prog8/ast/base/Base.kt @@ -64,7 +64,7 @@ enum class DataType { } } -enum class Register { +enum class CpuRegister { A, X, Y @@ -76,14 +76,23 @@ enum class RegisterOrPair { Y, AX, AY, - XY + XY; + + companion object { + val names by lazy { values().map { it.toString()} } + } + } // only used in parameter and return value specs in asm subroutines enum class Statusflag { Pc, Pz, Pv, - Pn + Pn; + + companion object { + val names by lazy { values().map { it.toString()} } + } } enum class BranchCondition { diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index a1c49e67d..dc698f48b 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -28,22 +28,20 @@ sealed class Expression: Node { infix fun isSameAs(other: Expression): Boolean { if(this===other) return true - when(this) { - is RegisterExpr -> - return (other is RegisterExpr && other.register==register) + return when(this) { is IdentifierReference -> - return (other is IdentifierReference && other.nameInSource==nameInSource) + (other is IdentifierReference && other.nameInSource==nameInSource) is PrefixExpression -> - return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression) + (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression) is BinaryExpression -> - return (other is BinaryExpression && other.operator==operator + (other is BinaryExpression && other.operator==operator && other.left isSameAs left && other.right isSameAs right) is ArrayIndexedExpression -> { - return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource + (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource && other.arrayspec.index isSameAs arrayspec.index) } - else -> return other==this + else -> other==this } } } @@ -696,29 +694,6 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression { } } -class RegisterExpr(val register: Register, override val position: Position) : Expression(), IAssignable { - override lateinit var parent: Node - - override fun linkParents(parent: Node) { - this.parent = parent - } - - override fun replaceChildNode(node: Node, replacement: Node) { - throw FatalAstException("can't replace here") - } - - override fun constValue(program: Program): NumericLiteralValue? = null - override fun accept(visitor: IAstVisitor) = visitor.visit(this) - override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) - - override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name - override fun toString(): String { - return "RegisterExpr(register=$register, pos=$position)" - } - - override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE) -} - data class IdentifierReference(val nameInSource: List, override val position: Position) : Expression(), IAssignable { override lateinit var parent: Node @@ -732,6 +707,7 @@ data class IdentifierReference(val nameInSource: List, override val posi fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource + override fun hashCode() = nameInSource.hashCode() override fun linkParents(parent: Node) { this.parent = parent diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 8f1e9a008..0c4756e01 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -117,42 +117,33 @@ internal class AstChecker(private val program: Program, if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) { errors.err("can only loop over an iterable type", forLoop.position) } else { - if (forLoop.loopRegister != null) { - // loop register - if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR) - errors.err("register can only loop over bytes", forLoop.position) - if(forLoop.loopRegister!=Register.A) - errors.err("it's only possible to use A as a loop register", forLoop.position) + val loopvar = forLoop.loopVar.targetVarDecl(program.namespace) + if(loopvar==null || loopvar.type== VarDeclType.CONST) { + errors.err("for loop requires a variable to loop with", forLoop.position) } else { - // loop variable - val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace) - if(loopvar==null || loopvar.type== VarDeclType.CONST) { - errors.err("for loop requires a variable to loop with", forLoop.position) - } else { - when (loopvar.datatype) { - DataType.UBYTE -> { - if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR) - errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position) - } - DataType.UWORD -> { - if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR && - iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW) - errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position) - } - DataType.BYTE -> { - if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B) - errors.err("byte loop variable can only loop over bytes", forLoop.position) - } - DataType.WORD -> { - if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD && - iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W) - errors.err("word loop variable can only loop over bytes or words", forLoop.position) - } - DataType.FLOAT -> { - errors.err("for loop only supports integers", forLoop.position) - } - else -> errors.err("loop variable must be numeric type", forLoop.position) + when (loopvar.datatype) { + DataType.UBYTE -> { + if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR) + errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position) } + DataType.UWORD -> { + if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR && + iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW) + errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position) + } + DataType.BYTE -> { + if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B) + errors.err("byte loop variable can only loop over bytes", forLoop.position) + } + DataType.WORD -> { + if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD && + iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W) + errors.err("word loop variable can only loop over bytes or words", forLoop.position) + } + DataType.FLOAT -> { + errors.err("for loop only supports integers", forLoop.position) + } + else -> errors.err("loop variable must be numeric type", forLoop.position) } } } @@ -260,27 +251,27 @@ internal class AstChecker(private val program: Program, } } - val regCounts = mutableMapOf().withDefault { 0 } + val regCounts = mutableMapOf().withDefault { 0 } val statusflagCounts = mutableMapOf().withDefault { 0 } fun countRegisters(from: Iterable) { regCounts.clear() statusflagCounts.clear() for(p in from) { when(p.registerOrPair) { - RegisterOrPair.A -> regCounts[Register.A]=regCounts.getValue(Register.A)+1 - RegisterOrPair.X -> regCounts[Register.X]=regCounts.getValue(Register.X)+1 - RegisterOrPair.Y -> regCounts[Register.Y]=regCounts.getValue(Register.Y)+1 + RegisterOrPair.A -> regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1 + RegisterOrPair.X -> regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1 + RegisterOrPair.Y -> regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1 RegisterOrPair.AX -> { - regCounts[Register.A]=regCounts.getValue(Register.A)+1 - regCounts[Register.X]=regCounts.getValue(Register.X)+1 + regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1 + regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1 } RegisterOrPair.AY -> { - regCounts[Register.A]=regCounts.getValue(Register.A)+1 - regCounts[Register.Y]=regCounts.getValue(Register.Y)+1 + regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1 + regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1 } RegisterOrPair.XY -> { - regCounts[Register.X]=regCounts.getValue(Register.X)+1 - regCounts[Register.Y]=regCounts.getValue(Register.Y)+1 + regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1 + regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1 } null -> if(p.statusflag!=null) @@ -326,16 +317,12 @@ internal class AstChecker(private val program: Program, } override fun visit(repeatLoop: RepeatLoop) { - if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y")) - errors.warn("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position) if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes) errors.err("condition value should be an integer type", repeatLoop.untilCondition.position) super.visit(repeatLoop) } override fun visit(whileLoop: WhileLoop) { - if(whileLoop.condition.referencesIdentifiers("A", "X", "Y")) - errors.warn("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position) if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes) errors.err("condition value should be an integer type", whileLoop.condition.position) super.visit(whileLoop) @@ -397,8 +384,7 @@ internal class AstChecker(private val program: Program, val targetIdentifier = assignTarget.identifier if (targetIdentifier != null) { val targetName = targetIdentifier.nameInSource - val targetSymbol = program.namespace.lookup(targetName, assignment) - when (targetSymbol) { + when (val targetSymbol = program.namespace.lookup(targetName, assignment)) { null -> { errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position) return @@ -844,7 +830,7 @@ internal class AstChecker(private val program: Program, if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) { // in-place modification, can't be done on literals - if(functionCallStatement.args.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) { + if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) { errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position) } } diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 04acd275f..f3a66c9bc 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -137,21 +137,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e super.visit(label) } - override fun visit(forLoop: ForLoop) { - if (forLoop.loopRegister != null) { - if (forLoop.loopRegister == Register.X) - errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position) - } - - super.visit(forLoop) - } - - override fun visit(assignTarget: AssignTarget) { - if(assignTarget.register== Register.X) - errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position) - super.visit(assignTarget) - } - override fun visit(string: StringLiteralValue) { if (string.value.length !in 1..255) errors.err("string literal length must be between 1 and 255", string.position) diff --git a/compiler/src/prog8/ast/processing/AstVariousTransforms.kt b/compiler/src/prog8/ast/processing/AstVariousTransforms.kt index b5760aac8..b5ae7ecb8 100644 --- a/compiler/src/prog8/ast/processing/AstVariousTransforms.kt +++ b/compiler/src/prog8/ast/processing/AstVariousTransforms.kt @@ -25,7 +25,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker() val tempvardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, tempname, null, null, isArray = false, autogeneratedDontRemove = true, position = first.position) val tempvar = IdentifierReference(listOf(tempname), first.position) val assignTemp = Assignment( - AssignTarget(null, tempvar, null, null, first.position), + AssignTarget(tempvar, null, null, first.position), null, first, first.position diff --git a/compiler/src/prog8/ast/processing/AstWalker.kt b/compiler/src/prog8/ast/processing/AstWalker.kt index cbc8feba5..c233f4861 100644 --- a/compiler/src/prog8/ast/processing/AstWalker.kt +++ b/compiler/src/prog8/ast/processing/AstWalker.kt @@ -110,7 +110,6 @@ abstract class AstWalker { open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable = emptyList() open fun before(program: Program, parent: Node): Iterable = emptyList() open fun before(range: RangeExpr, parent: Node): Iterable = emptyList() - open fun before(registerExpr: RegisterExpr, parent: Node): Iterable = emptyList() open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable = emptyList() open fun before(returnStmt: Return, parent: Node): Iterable = emptyList() open fun before(scope: AnonymousScope, parent: Node): Iterable = emptyList() @@ -154,7 +153,6 @@ abstract class AstWalker { open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable = emptyList() open fun after(program: Program, parent: Node): Iterable = emptyList() open fun after(range: RangeExpr, parent: Node): Iterable = emptyList() - open fun after(registerExpr: RegisterExpr, parent: Node): Iterable = emptyList() open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable = emptyList() open fun after(returnStmt: Return, parent: Node): Iterable = emptyList() open fun after(scope: AnonymousScope, parent: Node): Iterable = emptyList() @@ -325,7 +323,7 @@ abstract class AstWalker { fun visit(forLoop: ForLoop, parent: Node) { track(before(forLoop, parent), forLoop, parent) - forLoop.loopVar?.accept(this, forLoop) + forLoop.loopVar.accept(this, forLoop) forLoop.iterable.accept(this, forLoop) forLoop.body.accept(this, forLoop) track(after(forLoop, parent), forLoop, parent) @@ -407,11 +405,6 @@ abstract class AstWalker { track(after(inlineAssembly, parent), inlineAssembly, parent) } - fun visit(registerExpr: RegisterExpr, parent: Node) { - track(before(registerExpr, parent), registerExpr, parent) - track(after(registerExpr, parent), registerExpr, parent) - } - fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) { track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent) track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent) diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index 7d7f4e400..21e035f64 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -102,7 +102,7 @@ interface IAstVisitor { } fun visit(forLoop: ForLoop) { - forLoop.loopVar?.accept(this) + forLoop.loopVar.accept(this) forLoop.iterable.accept(this) forLoop.body.accept(this) } @@ -159,9 +159,6 @@ interface IAstVisitor { fun visit(inlineAssembly: InlineAssembly) { } - fun visit(registerExpr: RegisterExpr) { - } - fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) { } diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index fbeef2b0e..ed7192fda 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -78,7 +78,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() { if(declConstValue==null) { // move the vardecl (without value) to the scope and replace this with a regular assignment decl.value = null - val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) + val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) val assign = Assignment(target, null, declValue, decl.position) return listOf( IAstModification.ReplaceNode(decl, assign, parent), @@ -136,7 +136,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() { targetDecl as VarDecl val mangled = mangledStructMemberName(identifierName, targetDecl.name) val idref = IdentifierReference(listOf(mangled), structAssignment.position) - val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), + val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), null, sourceValue, sourceValue.position) assign.linkParents(structAssignment) assign @@ -168,7 +168,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() { val idref = IdentifierReference(listOf(mangled), structAssignment.position) val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name) val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position) - val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), + val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), null, sourceIdref, member.second.position) assign.linkParents(structAssignment) assign diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 92a78f30c..372ecb0de 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -354,7 +354,6 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value: val leftOperand: Expression = when { - target.register != null -> RegisterExpr(target.register!!, target.position) target.identifier != null -> target.identifier!! target.arrayindexed != null -> target.arrayindexed!! target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position) @@ -374,8 +373,7 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value: } } -data class AssignTarget(val register: Register?, - var identifier: IdentifierReference?, +data class AssignTarget(var identifier: IdentifierReference?, var arrayindexed: ArrayIndexedExpression?, val memoryAddress: DirectMemoryWrite?, override val position: Position) : Node { @@ -403,19 +401,15 @@ data class AssignTarget(val register: Register?, companion object { fun fromExpr(expr: Expression): AssignTarget { return when (expr) { - is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position) - is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position) - is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position) - is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position) + is IdentifierReference -> AssignTarget(expr, null, null, expr.position) + is ArrayIndexedExpression -> AssignTarget(null, expr, null, expr.position) + is DirectMemoryRead -> AssignTarget(null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position) else -> throw FatalAstException("invalid expression object $expr") } } } fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { - if(register!=null) - return InferredTypes.knownFor(DataType.UBYTE) - if(identifier!=null) { val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown() if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype) @@ -440,7 +434,6 @@ data class AssignTarget(val register: Register?, else false } - this.register!=null -> value is RegisterExpr && value.register==register this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource this.arrayindexed!=null -> value is ArrayIndexedExpression && value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource && @@ -454,8 +447,6 @@ data class AssignTarget(val register: Register?, fun isSameAs(other: AssignTarget, program: Program): Boolean { if(this===other) return true - if(this.register!=null && other.register!=null) - return this.register==other.register if(this.identifier!=null && other.identifier!=null) return this.identifier!!.nameInSource==other.identifier!!.nameInSource if(this.memoryAddress!=null && other.memoryAddress!=null) { @@ -474,8 +465,6 @@ data class AssignTarget(val register: Register?, } fun isNotMemory(namespace: INameScope): Boolean { - if(this.register!=null) - return true if(this.memoryAddress!=null) return false if(this.arrayindexed!=null) { @@ -616,14 +605,6 @@ class NopStatement(override val position: Position): Statement() { override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - companion object { - fun insteadOf(stmt: Statement): NopStatement { - val nop = NopStatement(stmt.position) - nop.parent = stmt.parent - return nop - } - } } // the subroutine class covers both the normal user-defined subroutines, @@ -634,7 +615,7 @@ class Subroutine(override val name: String, val returntypes: List, val asmParameterRegisters: List, val asmReturnvaluesRegisters: List, - val asmClobbers: Set, + val asmClobbers: Set, val asmAddress: Int?, val isAsmSubroutine: Boolean, override var statements: MutableList, @@ -671,32 +652,6 @@ class Subroutine(override val name: String, .filter { it is InlineAssembly } .map { (it as InlineAssembly).assembly } .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it } - - fun countStatements(): Int { - class StatementCounter: IAstVisitor { - var count = 0 - - override fun visit(block: Block) { - count += block.statements.size - super.visit(block) - } - - override fun visit(subroutine: Subroutine) { - count += subroutine.statements.size - super.visit(subroutine) - } - - override fun visit(scope: AnonymousScope) { - count += scope.statements.size - super.visit(scope) - } - } - - // the (recursive) number of statements - val counter = StatementCounter() - counter.visit(this) - return counter.count - } } @@ -768,8 +723,7 @@ class BranchStatement(var condition: BranchCondition, } -class ForLoop(val loopRegister: Register?, - var loopVar: IdentifierReference?, +class ForLoop(var loopVar: IdentifierReference, var iterable: Expression, var body: AnonymousScope, override val position: Position) : Statement() { @@ -777,7 +731,7 @@ class ForLoop(val loopRegister: Register?, override fun linkParents(parent: Node) { this.parent=parent - loopVar?.linkParents(this) + loopVar.linkParents(this) iterable.linkParents(this) body.linkParents(this) } @@ -796,14 +750,10 @@ class ForLoop(val loopRegister: Register?, override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun toString(): String { - return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)" + return "ForLoop(loopVar: $loopVar, iterable: $iterable, pos=$position)" } - fun loopVarDt(program: Program): InferredTypes.InferredType { - val lv = loopVar - return if(loopRegister!=null) InferredTypes.InferredType.known(DataType.UBYTE) - else lv?.inferType(program) ?: InferredTypes.InferredType.unknown() - } + fun loopVarDt(program: Program) = loopVar.inferType(program) } class WhileLoop(var condition: Expression, diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index cbe803c8d..46b7a6d1e 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -39,7 +39,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E return numericVarsWithValue.map { val initValue = it.value!! // assume here that value has always been set by now it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous) - val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position) + val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position) val assign = Assignment(target, null, initValue, it.position) initValue.parent = assign IAstModification.InsertFirst(assign, scope) diff --git a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt index cfcc53fae..7b4eb20aa 100644 --- a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt @@ -5,9 +5,6 @@ import prog8.compiler.CompilerException import prog8.compiler.Zeropage import prog8.compiler.ZeropageType import prog8.compiler.target.IMachineDefinition -import java.awt.Color -import java.awt.image.BufferedImage -import javax.imageio.ImageIO import kotlin.math.absoluteValue import kotlin.math.pow @@ -177,90 +174,4 @@ object C64MachineDefinition: IMachineDefinition { return if (sign) -result else result } } - - object Charset { - private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png")) - private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png")) - - private fun scanChars(img: BufferedImage): Array { - - val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB) - transparent.createGraphics().drawImage(img, 0, 0, null) - - val black = Color(0, 0, 0).rgb - val nopixel = Color(0, 0, 0, 0).rgb - for (y in 0 until transparent.height) { - for (x in 0 until transparent.width) { - val col = transparent.getRGB(x, y) - if (col == black) - transparent.setRGB(x, y, nopixel) - } - } - - val numColumns = transparent.width / 8 - val charImages = (0..255).map { - val charX = it % numColumns - val charY = it / numColumns - transparent.getSubimage(charX * 8, charY * 8, 8, 8) - } - return charImages.toTypedArray() - } - - val normalChars = scanChars(normalImg) - val shiftedChars = scanChars(shiftedImg) - - private val coloredNormalChars = mutableMapOf>() - - fun getColoredChar(screenCode: Short, color: Short): BufferedImage { - val colorIdx = (color % colorPalette.size).toShort() - val chars = coloredNormalChars[colorIdx] - if (chars != null) - return chars[screenCode.toInt()] - - val coloredChars = mutableListOf() - val transparent = Color(0, 0, 0, 0).rgb - val rgb = colorPalette[colorIdx.toInt()].rgb - for (c in normalChars) { - val colored = c.copy() - for (y in 0 until colored.height) - for (x in 0 until colored.width) { - if (colored.getRGB(x, y) != transparent) { - colored.setRGB(x, y, rgb) - } - } - coloredChars.add(colored) - } - coloredNormalChars[colorIdx] = coloredChars.toTypedArray() - return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()] - } - - } - - private fun BufferedImage.copy(): BufferedImage { - val bcopy = BufferedImage(this.width, this.height, this.type) - val g = bcopy.graphics - g.drawImage(this, 0, 0, null) - g.dispose() - return bcopy - } - - val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/ - Color(0x000000), // 0 = black - Color(0xFFFFFF), // 1 = white - Color(0x813338), // 2 = red - Color(0x75cec8), // 3 = cyan - Color(0x8e3c97), // 4 = purple - Color(0x56ac4d), // 5 = green - Color(0x2e2c9b), // 6 = blue - Color(0xedf171), // 7 = yellow - Color(0x8e5029), // 8 = orange - Color(0x553800), // 9 = brown - Color(0xc46c71), // 10 = light red - Color(0x4a4a4a), // 11 = dark grey - Color(0x7b7b7b), // 12 = medium grey - Color(0xa9ff9f), // 13 = light green - Color(0x706deb), // 14 = light blue - Color(0xb2b2b2) // 15 = light grey - ) - } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 48049050f..8d1d6e481 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -183,7 +183,7 @@ internal class AsmGen(private val program: Program, blockLevelVarInits.getValue(block).forEach { decl -> val scopedFullName = decl.makeScopedName(decl.name).split('.') require(scopedFullName.first()==block.name) - val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position) + val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position) val assign = Assignment(target, null, decl.value!!, decl.position) assign.linkParents(decl.parent) assignmentAsmGen.translate(assign) @@ -561,19 +561,19 @@ internal class AsmGen(private val program: Program, } } - internal fun saveRegister(register: Register) { + internal fun saveRegister(register: CpuRegister) { when(register) { - Register.A -> out(" pha") - Register.X -> out(" txa | pha") - Register.Y -> out(" tya | pha") + CpuRegister.A -> out(" pha") + CpuRegister.X -> out(" txa | pha") + CpuRegister.Y -> out(" tya | pha") } } - internal fun restoreRegister(register: Register) { + internal fun restoreRegister(register: CpuRegister) { when(register) { - Register.A -> out(" pla") - Register.X -> out(" pla | tax") - Register.Y -> out(" pla | tay") + CpuRegister.A -> out(" pla") + CpuRegister.X -> out(" pla | tax") + CpuRegister.Y -> out(" pla | tay") } } @@ -836,7 +836,7 @@ internal class AsmGen(private val program: Program, } inits.add(stmt) } else { - val target = AssignTarget(null, IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position) + val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position) val assign = Assignment(target, null, stmt.value!!, stmt.position) assign.linkParents(stmt.parent) translate(assign) @@ -901,13 +901,6 @@ internal class AsmGen(private val program: Program, internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) { when (val index = expr.arrayspec.index) { is NumericLiteralValue -> throw AssemblyError("this should be optimized directly") - is RegisterExpr -> { - when (index.register) { - Register.A -> {} - Register.X -> out(" txa") - Register.Y -> out(" tya") - } - } is IdentifierReference -> { val indexName = asmIdentifierName(index) out(" lda $indexName") @@ -923,13 +916,6 @@ internal class AsmGen(private val program: Program, internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) { when (val index = expr.arrayspec.index) { is NumericLiteralValue -> throw AssemblyError("this should be optimized directly") - is RegisterExpr -> { - when (index.register) { - Register.A -> out(" tay") - Register.X -> out(" txa | tay") - Register.Y -> {} - } - } is IdentifierReference -> { val indexName = asmIdentifierName(index) out(" ldy $indexName") @@ -972,9 +958,12 @@ internal class AsmGen(private val program: Program, fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) = assignmentAsmGen.assignFromFloatVariable(target, variable) - fun assignFromRegister(target: AssignTarget, register: Register) = + fun assignFromRegister(target: AssignTarget, register: CpuRegister) = assignmentAsmGen.assignFromRegister(target, register) fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) = assignmentAsmGen.assignFromMemoryByte(target, address, identifier) + + fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) = + assignmentAsmGen.assignToRegister(reg, value, identifier) } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 0e4e6636f..12af35bb9 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -28,10 +28,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors require(assign.aug_op != null) when { - assign.target.register != null -> { - if (inplaceAssignToRegister(assign)) - return - } assign.target.identifier != null -> { if (inplaceAssignToIdentifier(assign)) return @@ -99,37 +95,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors // non-const value. // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! when (assign.value) { - is RegisterExpr -> { - when(assign.aug_op) { - "setvalue" -> { - val reg = (assign.value as RegisterExpr).register - if(reg!=Register.Y) { - if (arrayIndex is NumericLiteralValue) - asmgen.out(" ldy #${arrayIndex.number.toHex()}") - else - asmgen.translateArrayIndexIntoY(targetArray) - } - when (reg) { - Register.A -> asmgen.out(" sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" stx (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> { - if (arrayIndex is NumericLiteralValue) - asmgen.out(" lda #${arrayIndex.number.toHex()}") - else - asmgen.translateArrayIndexIntoA(targetArray) - asmgen.out(""" - sta ${C64Zeropage.SCRATCH_REG} - tya - ldy ${C64Zeropage.SCRATCH_REG} - sta (${C64Zeropage.SCRATCH_W1}),y - """) - } - } - } - else -> TODO("$assign") - } - return true - } is IdentifierReference -> { val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) when(arrayDt) { @@ -171,11 +136,8 @@ internal class AssignmentAsmGen(private val program: Program, private val errors return false // we don't put effort into optimizing anything beside simple assignment val valueArrayExpr = assign.value as ArrayIndexedExpression val valueArrayIndex = valueArrayExpr.arrayspec.index - if(valueArrayIndex is RegisterExpr || arrayIndex is RegisterExpr) { - throw AssemblyError("cannot generate code for array operations with registers as index") - } val valueVariablename = asmgen.asmIdentifierName(valueArrayExpr.identifier) - val valueDt = valueArrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT) + // val valueDt = valueArrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT) when(arrayDt) { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> { if (valueArrayIndex is NumericLiteralValue) @@ -309,59 +271,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors // non-const value. when (assign.value) { - is RegisterExpr -> { - when (assign.aug_op) { - "setvalue" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" sta $hexAddr") - Register.X -> asmgen.out(" stx $hexAddr") - Register.Y -> asmgen.out(" sty $hexAddr") - } - } - "+=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" clc | adc $hexAddr | sta $hexAddr") - Register.X -> asmgen.out(" txa | clc | adc $hexAddr | sta $hexAddr") - Register.Y -> asmgen.out(" tya | clc | adc $hexAddr | sta $hexAddr") - } - } - "-=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1} | lda $hexAddr | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta $hexAddr") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | lda $hexAddr | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta $hexAddr") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | lda $hexAddr | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta $hexAddr") - } - } - "/=" -> TODO("membyte /= register") - "*=" -> TODO("membyte *= register") - "&=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" and $hexAddr | sta $hexAddr") - Register.X -> asmgen.out(" txa | and $hexAddr | sta $hexAddr") - Register.Y -> asmgen.out(" tya | and $hexAddr | sta $hexAddr") - } - } - "|=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ora $hexAddr | sta $hexAddr") - Register.X -> asmgen.out(" txa | ora $hexAddr | sta $hexAddr") - Register.Y -> asmgen.out(" tya | ora $hexAddr | sta $hexAddr") - } - } - "^=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" eor $hexAddr | sta $hexAddr") - Register.X -> asmgen.out(" txa | eor $hexAddr | sta $hexAddr") - Register.Y -> asmgen.out(" tya | eor $hexAddr | sta $hexAddr") - } - } - "%=" -> TODO("membyte %= register") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - return true - } is IdentifierReference -> { val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) when(assign.aug_op) { @@ -426,59 +335,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors // non-const value. // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! when (assign.value) { - is RegisterExpr -> { - when (assign.aug_op) { - "setvalue" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ldy #0 | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" ldy #0 | stx (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> asmgen.out(" tya | ldy #0 | sta (${C64Zeropage.SCRATCH_W1}),y") - } - } - "+=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ldy #0 | clc | adc (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" ldy #0 | txa | clc | adc (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> asmgen.out(" tya | ldy #0 | clc | adc (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - } - } - "-=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ldy #0 | sta ${C64Zeropage.SCRATCH_B1} | lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" ldy #0 | stx ${C64Zeropage.SCRATCH_B1} | lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> asmgen.out(" tya | ldy #0 | sta ${C64Zeropage.SCRATCH_B1} | lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc ${C64Zeropage.SCRATCH_B1} | sta (${C64Zeropage.SCRATCH_W1}),y") - } - } - "/=" -> TODO("membyte /= register") - "*=" -> TODO("membyte *= register") - "&=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ldy #0 | and (${C64Zeropage.SCRATCH_W1}),y| sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" ldy #0 | txa | and (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> asmgen.out(" tya | ldy #0 | and (${C64Zeropage.SCRATCH_W1}),y| sta (${C64Zeropage.SCRATCH_W1}),y") - } - } - "|=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ldy #0 | ora (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" ldy #0 | txa | ora (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> asmgen.out(" tya | ldy #0 | ora (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - } - } - "^=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" ldy #0 | eor (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.X -> asmgen.out(" ldy #0 | txa | eor (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - Register.Y -> asmgen.out(" tya | ldy #0 | eor (${C64Zeropage.SCRATCH_W1}),y | sta (${C64Zeropage.SCRATCH_W1}),y") - } - } - "%=" -> TODO("membyte %= register") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - return true - } is IdentifierReference -> { val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) TODO("membyte = variable $assign") @@ -551,19 +407,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors // non-const (u)byte value // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! when (assign.value) { - is RegisterExpr -> { - when(assign.aug_op) { - "setvalue" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> asmgen.out(" sta $targetName") - Register.X -> asmgen.out(" stx $targetName") - Register.Y -> asmgen.out(" sty $targetName") - } - } - else -> TODO("aug.assign variable = register $assign") - } - return true - } is IdentifierReference -> { val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) when (assign.aug_op) { @@ -654,7 +497,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors // non-const value // !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!! when (assign.value) { - is RegisterExpr -> throw AssemblyError("expected a typecast for assigning register to word") is IdentifierReference -> { val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) when (assign.aug_op) { @@ -867,324 +709,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors return false } - private fun inplaceAssignToRegister(assign: Assignment): Boolean { - val constValue = assign.value.constValue(program) - if (constValue != null) { - val hexValue = constValue.number.toHex() - when (assign.target.register) { - Register.A -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" lda #$hexValue") - "+=" -> asmgen.out(" clc | adc #$hexValue") - "-=" -> asmgen.out(" sec | sbc #$hexValue") - "/=" -> TODO("A /= const $hexValue") - "*=" -> TODO("A *= const $hexValue") - "&=" -> asmgen.out(" and #$hexValue") - "|=" -> asmgen.out(" ora #$hexValue") - "^=" -> asmgen.out(" eor #$hexValue") - "%=" -> TODO("A %= const $hexValue") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - Register.X -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" ldx #$hexValue") - "+=" -> asmgen.out(" txa | clc | adc #$hexValue | tax") - "-=" -> asmgen.out(" txa | sec | sbc #$hexValue | tax") - "/=" -> TODO("X /= const $hexValue") - "*=" -> TODO("X *= const $hexValue") - "&=" -> asmgen.out(" txa | and #$hexValue | tax") - "|=" -> asmgen.out(" txa | ora #$hexValue | tax") - "^=" -> asmgen.out(" txa | eor #$hexValue | tax") - "%=" -> TODO("X %= const $hexValue") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - Register.Y -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" ldy #$hexValue") - "+=" -> asmgen.out(" tya | clc | adc #$hexValue | tay") - "-=" -> asmgen.out(" tya | sec | sbc #$hexValue | tay") - "/=" -> TODO("Y /= const $hexValue") - "*=" -> TODO("Y *= const $hexValue") - "&=" -> asmgen.out(" tya | and #$hexValue | tay") - "|=" -> asmgen.out(" tya | ora #$hexValue | tay") - "^=" -> asmgen.out(" tya | eor #$hexValue | tay") - "%=" -> TODO("Y %= const $hexValue") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - } - return true - } - - // non-const value. - when (assign.value) { - is IdentifierReference -> { - val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) - when (assign.target.register) { - Register.A -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" lda $sourceName") - "+=" -> asmgen.out(" clc | adc $sourceName") - "-=" -> asmgen.out(" sec | sbc $sourceName") - "/=" -> TODO("A /= variable") - "*=" -> TODO("A *= variable") - "&=" -> asmgen.out(" and $sourceName") - "|=" -> asmgen.out(" ora $sourceName") - "^=" -> asmgen.out(" eor $sourceName") - "%=" -> TODO("A %= variable") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - Register.X -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" ldx $sourceName") - "+=" -> asmgen.out(" txa | clc | adc $sourceName | tax") - "-=" -> asmgen.out(" txa | sec | sbc $sourceName | tax") - "/=" -> TODO("X /= variable") - "*=" -> TODO("X *= variable") - "&=" -> asmgen.out(" txa | and $sourceName | tax") - "|=" -> asmgen.out(" txa | ora $sourceName | tax") - "^=" -> asmgen.out(" txa | eor $sourceName | tax") - "%=" -> TODO("X %= variable") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - Register.Y -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" ldy $sourceName") - "+=" -> asmgen.out(" tya | clc | adc $sourceName | tay") - "-=" -> asmgen.out(" tya | sec | sbc $sourceName | tay") - "/=" -> TODO("Y /= variable") - "*=" -> TODO("Y *= variable") - "&=" -> asmgen.out(" tya | and $sourceName | tay") - "|=" -> asmgen.out(" tya | ora $sourceName | tay") - "^=" -> asmgen.out(" tya | eor $sourceName | tay") - "%=" -> TODO("Y %= variable") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - } - return true - } - is RegisterExpr -> { - when (assign.aug_op) { - "setvalue" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> { - when (assign.target.register!!) { - Register.A -> {} - Register.X -> asmgen.out(" tax") - Register.Y -> asmgen.out(" tay") - } - } - Register.X -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" txa") - Register.X -> {} - Register.Y -> asmgen.out(" txa | tay") - } - } - Register.Y -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" tya") - Register.X -> asmgen.out(" tya | tax") - Register.Y -> {} - } - } - } - } - "+=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> { - when (assign.target.register!!) { - Register.A -> throw AssemblyError("should have been optimized away") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1} | tax") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1} | tay") - } - } - Register.X -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1}") - Register.X -> throw AssemblyError("should have been optimized away") - Register.Y -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | clc | adc ${C64Zeropage.SCRATCH_B1} | tay") - } - } - Register.Y -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | clc | adc ${C64Zeropage.SCRATCH_B1}") - Register.X -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | txa | clc | adc ${C64Zeropage.SCRATCH_B1} | tax") - Register.Y -> throw AssemblyError("should have been optimized away") - } - } - } - } - "-=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> { - when (assign.target.register!!) { - Register.A -> throw AssemblyError("should have been optimized away") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1} | tax") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1} | tay") - } - } - Register.X -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1}") - Register.X -> throw AssemblyError("should have been optimized away") - Register.Y -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | sec | sbc ${C64Zeropage.SCRATCH_B1} | tay") - } - } - Register.Y -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | sec | sbc ${C64Zeropage.SCRATCH_B1}") - Register.X -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | txa | sec | sbc ${C64Zeropage.SCRATCH_B1} | tax") - Register.Y -> throw AssemblyError("should have been optimized away") - } - } - } - } - "/=" -> TODO("register /= register") - "*=" -> TODO("register *= register") - "&=" -> { - when ((assign.value as RegisterExpr).register) { - Register.A -> { - when (assign.target.register!!) { - Register.A -> throw AssemblyError("should have been optimized away") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1} | tax") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1} | tay") - } - } - Register.X -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1}") - Register.X -> throw AssemblyError("should have been optimized away") - Register.Y -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | and ${C64Zeropage.SCRATCH_B1} | tay") - } - } - Register.Y -> { - when (assign.target.register!!) { - Register.A -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1} | and ${C64Zeropage.SCRATCH_B1}") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1} | tya | and ${C64Zeropage.SCRATCH_B1} | tax") - Register.Y -> throw AssemblyError("should have been optimized away") - } - } - } - } - "|=" -> TODO("register |= register") - "^=" -> TODO("register ^= register") - "%=" -> TODO("register %= register") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - return true - } - is DirectMemoryRead -> { - val address = (assign.value as DirectMemoryRead).addressExpression.constValue(program)?.number - if (address != null) { - val hexAddr = address.toHex() - when (assign.target.register) { - Register.A -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" lda $hexAddr") - "+=" -> asmgen.out(" clc | adc $hexAddr") - "-=" -> asmgen.out(" sec | sbc $hexAddr") - "/=" -> TODO("A /= memory $hexAddr") - "*=" -> TODO("A *= memory $hexAddr") - "&=" -> asmgen.out(" and $hexAddr") - "|=" -> asmgen.out(" ora $hexAddr") - "^=" -> asmgen.out(" eor $hexAddr") - "%=" -> TODO("A %= memory $hexAddr") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - Register.X -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" ldx $hexAddr") - "+=" -> asmgen.out(" txa | clc | adc $hexAddr | tax") - "-=" -> asmgen.out(" txa | sec | sbc $hexAddr | tax") - "/=" -> TODO("X /= memory $hexAddr") - "*=" -> TODO("X *= memory $hexAddr") - "&=" -> asmgen.out(" txa | and $hexAddr | tax") - "|=" -> asmgen.out(" txa | ora $hexAddr | tax") - "^=" -> asmgen.out(" txa | eor $hexAddr | tax") - "%=" -> TODO("X %= memory $hexAddr") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - Register.Y -> { - when (assign.aug_op) { - "setvalue" -> asmgen.out(" ldy $hexAddr") - "+=" -> asmgen.out(" tya | clc | adc $hexAddr | tay") - "-=" -> asmgen.out(" tya | sec | sbc $hexAddr | tay") - "/=" -> TODO("Y /= memory $hexAddr") - "*=" -> TODO("Y *= memory $hexAddr") - "&=" -> asmgen.out(" tya | and $hexAddr | tay") - "|=" -> asmgen.out(" tya | ora $hexAddr | tay") - "^=" -> asmgen.out(" tya | eor $hexAddr | tay") - "%=" -> TODO("Y %= memory $hexAddr") - "<<=" -> throw AssemblyError("<<= should have been replaced by lsl()") - ">>=" -> throw AssemblyError("<<= should have been replaced by lsr()") - else -> throw AssemblyError("invalid aug_op ${assign.aug_op}") - } - } - } - return true - } - } - is ArrayIndexedExpression -> { - if (assign.aug_op == "setvalue") { - val arrayExpr = assign.value as ArrayIndexedExpression - val arrayIndex = arrayExpr.arrayspec.index - val variablename = asmgen.asmIdentifierName(arrayExpr.identifier) - val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT) - if (arrayDt != DataType.ARRAY_B && arrayDt != DataType.ARRAY_UB && arrayDt != DataType.STR) - throw AssemblyError("assign to register: expected byte array or string source") - if (arrayIndex is NumericLiteralValue) - asmgen.out(" ldy #${arrayIndex.number.toHex()}") - else - asmgen.translateArrayIndexIntoY(arrayExpr) - when(assign.target.register!!) { - Register.A -> asmgen.out(" lda $variablename,y") - Register.X -> asmgen.out(" ldx $variablename,y") - Register.Y -> asmgen.out(" lda $variablename,y | tay") - } - } else { - // TODO optimize more augmented assignment cases - val normalAssign = assign.asDesugaredNonaugmented() - asmgen.translateExpression(normalAssign.value) - assignFromEvalResult(normalAssign.target) - } - return true - } - is AddressOf -> throw AssemblyError("can't load a memory address into a register") - else -> { - fallbackAssignment(assign) - return true - } - } - - return false - } - private fun fallbackAssignment(assign: Assignment) { if (assign.aug_op != "setvalue") { /* stack-based evaluation of the expression is required */ @@ -1226,12 +750,8 @@ internal class AssignmentAsmGen(private val program: Program, private val errors else -> throw AssemblyError("weird numval type") } } - is RegisterExpr -> { - assignFromRegister(assign.target, (assign.value as RegisterExpr).register) - } is IdentifierReference -> { - val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT) - when (type) { + when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) { DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference) DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference) DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference) @@ -1320,15 +840,9 @@ internal class AssignmentAsmGen(private val program: Program, private val errors internal fun assignFromEvalResult(target: AssignTarget) { val targetIdent = target.identifier when { - target.register != null -> { - if (target.register == Register.X) - throw AssemblyError("can't pop into X register - use variable instead") - asmgen.out(" inx | ld${target.register.name.toLowerCase()} $ESTACK_LO_HEX,x ") - } targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) - val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT) - when (targetDt) { + when (val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)) { DataType.UBYTE, DataType.BYTE -> { asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $targetName") } @@ -1353,7 +867,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } target.memoryAddress != null -> { asmgen.out(" inx | ldy $ESTACK_LO_HEX,x") - storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) + storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress) } target.arrayindexed != null -> { val arrayDt = target.arrayindexed!!.identifier.inferType(program).typeOrElse(DataType.STRUCT) @@ -1470,9 +984,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors val targetIdent = target.identifier val targetArrayIdx = target.arrayindexed when { - target.register != null -> { - asmgen.out(" ld${target.register.name.toLowerCase()} $sourceName") - } targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) asmgen.out(""" @@ -1516,7 +1027,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } } - internal fun assignFromRegister(target: AssignTarget, register: Register) { + internal fun assignFromRegister(target: AssignTarget, register: CpuRegister) { val targetIdent = target.identifier val targetArrayIdx = target.arrayindexed when { @@ -1524,28 +1035,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors val targetName = asmgen.asmIdentifierName(targetIdent) asmgen.out(" st${register.name.toLowerCase()} $targetName") } - target.register != null -> { - when (register) { - Register.A -> when (target.register) { - Register.A -> { - } - Register.X -> asmgen.out(" tax") - Register.Y -> asmgen.out(" tay") - } - Register.X -> when (target.register) { - Register.A -> asmgen.out(" txa") - Register.X -> { - } - Register.Y -> asmgen.out(" txy") - } - Register.Y -> when (target.register) { - Register.A -> asmgen.out(" tya") - Register.X -> asmgen.out(" tyx") - Register.Y -> { - } - } - } - } target.memoryAddress != null -> { storeRegisterInMemoryAddress(register, target.memoryAddress) } @@ -1556,34 +1045,16 @@ internal class AssignmentAsmGen(private val program: Program, private val errors is NumericLiteralValue -> { val memindex = index.number.toInt() when (register) { - Register.A -> asmgen.out(" sta $targetName+$memindex") - Register.X -> asmgen.out(" stx $targetName+$memindex") - Register.Y -> asmgen.out(" sty $targetName+$memindex") + CpuRegister.A -> asmgen.out(" sta $targetName+$memindex") + CpuRegister.X -> asmgen.out(" stx $targetName+$memindex") + CpuRegister.Y -> asmgen.out(" sty $targetName+$memindex") } } - is RegisterExpr -> { - when (register) { - Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") - } - when (index.register) { - Register.A -> { - } - Register.X -> asmgen.out(" txa") - Register.Y -> asmgen.out(" tya") - } - asmgen.out(""" - tay - lda ${C64Zeropage.SCRATCH_B1} - sta $targetName,y - """) - } is IdentifierReference -> { when (register) { - Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") + CpuRegister.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") + CpuRegister.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") + CpuRegister.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") } asmgen.out(""" lda ${asmgen.asmIdentifierName(index)} @@ -1597,9 +1068,9 @@ internal class AssignmentAsmGen(private val program: Program, private val errors asmgen.translateExpression(index) asmgen.restoreRegister(register) when (register) { - Register.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") - Register.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") - Register.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") + CpuRegister.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}") + CpuRegister.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}") + CpuRegister.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}") } asmgen.out(""" inx @@ -1615,7 +1086,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } } - private fun storeRegisterInMemoryAddress(register: Register, memoryAddress: DirectMemoryWrite) { + private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) { val addressExpr = memoryAddress.addressExpression val addressLv = addressExpr as? NumericLiteralValue val registerName = register.name.toLowerCase() @@ -1624,19 +1095,19 @@ internal class AssignmentAsmGen(private val program: Program, private val errors addressExpr is IdentifierReference -> { val targetName = asmgen.asmIdentifierName(addressExpr) when (register) { - Register.A -> asmgen.out(""" + CpuRegister.A -> asmgen.out(""" ldy $targetName sty (+) +1 ldy $targetName+1 sty (+) +2 + sta ${'$'}ffff ; modified""") - Register.X -> asmgen.out(""" + CpuRegister.X -> asmgen.out(""" ldy $targetName sty (+) +1 ldy $targetName+1 sty (+) +2 + stx ${'$'}ffff ; modified""") - Register.Y -> asmgen.out(""" + CpuRegister.Y -> asmgen.out(""" lda $targetName sta (+) +1 lda $targetName+1 @@ -1649,10 +1120,9 @@ internal class AssignmentAsmGen(private val program: Program, private val errors asmgen.translateExpression(addressExpr) asmgen.restoreRegister(register) when (register) { - Register.A -> asmgen.out(" tay") - Register.X -> throw AssemblyError("can't use X register here") - Register.Y -> { - } + CpuRegister.A -> asmgen.out(" tay") + CpuRegister.X -> throw AssemblyError("can't use X register here") + CpuRegister.Y -> {} } asmgen.out(""" inx @@ -1715,16 +1185,13 @@ internal class AssignmentAsmGen(private val program: Program, private val errors val targetIdent = target.identifier val targetArrayIdx = target.arrayindexed when { - target.register != null -> { - asmgen.out(" ld${target.register.name.toLowerCase()} #${byte.toHex()}") - } targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) asmgen.out(" lda #${byte.toHex()} | sta $targetName ") } target.memoryAddress != null -> { asmgen.out(" ldy #${byte.toHex()}") - storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) + storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress) } targetArrayIdx != null -> { val index = targetArrayIdx.arrayspec.index @@ -1847,9 +1314,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors val targetArrayIdx = target.arrayindexed if (address != null) { when { - target.register != null -> { - asmgen.out(" ld${target.register.name.toLowerCase()} ${address.toHex()}") - } targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) asmgen.out(""" @@ -1859,7 +1323,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } target.memoryAddress != null -> { asmgen.out(" ldy ${address.toHex()}") - storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) + storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress) } targetArrayIdx != null -> { val index = targetArrayIdx.arrayspec.index @@ -1871,18 +1335,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } else if (identifier != null) { val sourceName = asmgen.asmIdentifierName(identifier) when { - target.register != null -> { - asmgen.out(""" - lda $sourceName - sta (+) + 1 - lda $sourceName+1 - sta (+) + 2""") - when (target.register) { - Register.A -> asmgen.out("+ lda ${'$'}ffff\t; modified") - Register.X -> asmgen.out("+ ldx ${'$'}ffff\t; modified") - Register.Y -> asmgen.out("+ ldy ${'$'}ffff\t; modified") - } - } targetIdent != null -> { val targetName = asmgen.asmIdentifierName(targetIdent) asmgen.out(""" @@ -1895,7 +1347,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } target.memoryAddress != null -> { asmgen.out(" ldy $sourceName") - storeRegisterInMemoryAddress(Register.Y, target.memoryAddress) + storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress) } targetArrayIdx != null -> { val index = targetArrayIdx.arrayspec.index @@ -1926,4 +1378,13 @@ internal class AssignmentAsmGen(private val program: Program, private val errors throw AssemblyError("weird array type") } } + + fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) { + if(value!=null) { + asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}") + } else if(identifier!=null) { + val name = asmgen.asmIdentifierName(identifier) + asmgen.out(" ld${reg.toString().toLowerCase()} $name") + } + } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 866f41b30..3734d8cf3 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -172,13 +172,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.out(" jsr prog8_lib.ror2_mem_ub") } } - is RegisterExpr -> { - when (what.register) { - Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ") - Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ") - Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ") - } - } is IdentifierReference -> { val variable = asmgen.asmIdentifierName(what) asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable") @@ -231,13 +224,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val """) } } - is RegisterExpr -> { - when (what.register) { - Register.A -> asmgen.out(" ror a") - Register.X -> asmgen.out(" txa | ror a | tax") - Register.Y -> asmgen.out(" tya | ror a | tay") - } - } is IdentifierReference -> { val variable = asmgen.asmIdentifierName(what) asmgen.out(" ror $variable") @@ -283,13 +269,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.out(" jsr prog8_lib.rol2_mem_ub") } } - is RegisterExpr -> { - when (what.register) { - Register.A -> asmgen.out(" cmp #\$80 | rol a ") - Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax") - Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay") - } - } is IdentifierReference -> { val variable = asmgen.asmIdentifierName(what) asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable") @@ -342,13 +321,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val """) } } - is RegisterExpr -> { - when (what.register) { - Register.A -> asmgen.out(" rol a") - Register.X -> asmgen.out(" txa | rol a | tax") - Register.Y -> asmgen.out(" tya | rol a | tay") - } - } is IdentifierReference -> { val variable = asmgen.asmIdentifierName(what) asmgen.out(" rol $variable") @@ -380,13 +352,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val when (dt.typeOrElse(DataType.STRUCT)) { DataType.UBYTE -> { when (what) { - is RegisterExpr -> { - when (what.register) { - Register.A -> asmgen.out(" lsr a") - Register.X -> asmgen.out(" txa | lsr a | tax") - Register.Y -> asmgen.out(" tya | lsr a | tay") - } - } is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}") is DirectMemoryRead -> { if (what.addressExpression is NumericLiteralValue) { @@ -464,13 +429,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val when (dt.typeOrElse(DataType.STRUCT)) { in ByteDatatypes -> { when (what) { - is RegisterExpr -> { - when (what.register) { - Register.A -> asmgen.out(" asl a") - Register.X -> asmgen.out(" txa | asl a | tax") - Register.Y -> asmgen.out(" tya | asl a | tay") - } - } is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}") is DirectMemoryRead -> { if (what.addressExpression is NumericLiteralValue) { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index 8c099d50e..840fd284c 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -25,7 +25,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge is AddressOf -> translateExpression(expression) is DirectMemoryRead -> translateExpression(expression) is NumericLiteralValue -> translateExpression(expression) - is RegisterExpr -> translateExpression(expression) is IdentifierReference -> translateExpression(expression) is FunctionCall -> translateExpression(expression) is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable") @@ -175,14 +174,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } } - private fun translateExpression(expr: RegisterExpr) { - when(expr.register) { - Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex") - Register.X -> asmgen.out(" pha | txa | sta $ESTACK_LO_HEX,x | dex | pla") - Register.Y -> asmgen.out(" pha | tya | sta $ESTACK_LO_HEX,x | dex | pla") - } - } - private fun translateExpression(expr: IdentifierReference) { val varname = asmgen.asmIdentifierName(expr) when(expr.inferType(program).typeOrElse(DataType.STRUCT)) { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt index d796aa1b1..c2c180fcc 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt @@ -2,7 +2,6 @@ package prog8.compiler.target.c64.codegen import prog8.ast.Program import prog8.ast.base.DataType -import prog8.ast.base.Register import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.RangeExpr import prog8.ast.statements.AssignTarget @@ -55,31 +54,11 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen: // bytes, step 1 or -1 val incdec = if(stepsize==1) "inc" else "dec" - if (stmt.loopRegister != null) { - // loop register over range - if(stmt.loopRegister!= Register.A) - throw AssemblyError("can only use A") - asmgen.translateExpression(range.to) - asmgen.translateExpression(range.from) - asmgen.out(""" - inx - lda ${ESTACK_LO_HEX},x - sta $loopLabel+1 -$loopLabel lda #0 ; modified""") - asmgen.translate(stmt.body) - asmgen.out(""" -$continueLabel lda $loopLabel+1 - cmp $ESTACK_LO_PLUS1_HEX,x - beq $endLabel - $incdec $loopLabel+1 - jmp $loopLabel -$endLabel inx""") - } else { - // loop over byte range via loopvar - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) - asmgen.translateExpression(range.to) - asmgen.translateExpression(range.from) - asmgen.out(""" + // loop over byte range via loopvar + val varname = asmgen.asmIdentifierName(stmt.loopVar) + asmgen.translateExpression(range.to) + asmgen.translateExpression(range.from) + asmgen.out(""" inx lda ${ESTACK_LO_HEX},x sta $varname @@ -92,76 +71,41 @@ $continueLabel lda $varname $incdec $varname jmp $loopLabel $endLabel inx""") - } } else { // bytes, step >= 2 or <= -2 - if (stmt.loopRegister != null) { - // loop register over range - if(stmt.loopRegister!= Register.A) - throw AssemblyError("can only use A") - asmgen.translateExpression(range.to) - asmgen.translateExpression(range.from) - asmgen.out(""" - inx - lda ${ESTACK_LO_HEX},x - sta $loopLabel+1 -$loopLabel lda #0 ; modified""") - asmgen.translate(stmt.body) - asmgen.out(""" -$continueLabel lda $loopLabel+1""") - if(stepsize>0) { - asmgen.out(""" - clc - adc #$stepsize - sta $loopLabel+1 - cmp $ESTACK_LO_PLUS1_HEX,x - bcc $loopLabel - beq $loopLabel""") - } else { - asmgen.out(""" - sec - sbc #${stepsize.absoluteValue} - sta $loopLabel+1 - cmp $ESTACK_LO_PLUS1_HEX,x - bcs $loopLabel""") - } - asmgen.out(""" -$endLabel inx""") - } else { - // loop over byte range via loopvar - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) - asmgen.translateExpression(range.to) - asmgen.translateExpression(range.from) - asmgen.out(""" + // loop over byte range via loopvar + val varname = asmgen.asmIdentifierName(stmt.loopVar) + asmgen.translateExpression(range.to) + asmgen.translateExpression(range.from) + asmgen.out(""" inx lda ${ESTACK_LO_HEX},x sta $varname $loopLabel""") - asmgen.translate(stmt.body) - asmgen.out(""" + asmgen.translate(stmt.body) + asmgen.out(""" $continueLabel lda $varname""") - if(stepsize>0) { - asmgen.out(""" + if(stepsize>0) { + asmgen.out(""" clc adc #$stepsize sta $varname cmp $ESTACK_LO_PLUS1_HEX,x bcc $loopLabel beq $loopLabel""") - } else { - asmgen.out(""" + } else { + asmgen.out(""" sec sbc #${stepsize.absoluteValue} sta $varname cmp $ESTACK_LO_PLUS1_HEX,x bcs $loopLabel""") - } - asmgen.out(""" -$endLabel inx""") } + asmgen.out(""" +$endLabel inx""") } } DataType.ARRAY_W, DataType.ARRAY_UW -> { @@ -171,8 +115,8 @@ $endLabel inx""") stepsize == 1 || stepsize == -1 -> { asmgen.translateExpression(range.to) - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) - val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position), + val varname = asmgen.asmIdentifierName(stmt.loopVar) + val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), null, range.from, range.position) assignLoopvar.linkParents(stmt) asmgen.translate(assignLoopvar) @@ -207,8 +151,8 @@ $endLabel inx""") // (u)words, step >= 2 asmgen.translateExpression(range.to) - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) - val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position), + val varname = asmgen.asmIdentifierName(stmt.loopVar) + val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), null, range.from, range.position) assignLoopvar.linkParents(stmt) asmgen.translate(assignLoopvar) @@ -256,8 +200,8 @@ $endLabel inx""") // (u)words, step <= -2 asmgen.translateExpression(range.to) - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) - val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position), + val varname = asmgen.asmIdentifierName(stmt.loopVar) + val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), null, range.from, range.position) assignLoopvar.linkParents(stmt) asmgen.translate(assignLoopvar) @@ -319,8 +263,6 @@ $endLabel inx""") val decl = ident.targetVarDecl(program.namespace)!! when(iterableDt) { DataType.STR -> { - if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A) - throw AssemblyError("can only use A") asmgen.out(""" lda #<$iterableName ldy #>$iterableName @@ -328,8 +270,7 @@ $endLabel inx""") sty $loopLabel+2 $loopLabel lda ${65535.toHex()} ; modified beq $endLabel""") - if(stmt.loopVar!=null) - asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}") + asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}") asmgen.translate(stmt.body) asmgen.out(""" $continueLabel inc $loopLabel+1 @@ -341,8 +282,6 @@ $endLabel""") DataType.ARRAY_UB, DataType.ARRAY_B -> { // TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases val length = decl.arraysize!!.size()!! - if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A) - throw AssemblyError("can only use A") val counterLabel = asmgen.makeLabel("for_counter") val modifiedLabel = asmgen.makeLabel("for_modified") asmgen.out(""" @@ -353,8 +292,7 @@ $endLabel""") ldy #0 $loopLabel sty $counterLabel $modifiedLabel lda ${65535.toHex()},y ; modified""") - if(stmt.loopVar!=null) - asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}") + asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}") asmgen.translate(stmt.body) asmgen.out(""" $continueLabel ldy $counterLabel @@ -368,12 +306,10 @@ $endLabel""") DataType.ARRAY_W, DataType.ARRAY_UW -> { // TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases val length = decl.arraysize!!.size()!! * 2 - if(stmt.loopRegister!=null) - throw AssemblyError("can't use register to loop over words") val counterLabel = asmgen.makeLabel("for_counter") val modifiedLabel = asmgen.makeLabel("for_modified") val modifiedLabel2 = asmgen.makeLabel("for_modified2") - val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!) + val loopvarName = asmgen.asmIdentifierName(stmt.loopVar) asmgen.out(""" lda #<$iterableName ldy #>$iterableName @@ -421,93 +357,12 @@ $endLabel""") when(iterableDt) { DataType.ARRAY_B, DataType.ARRAY_UB -> { val counterLabel = asmgen.makeLabel("for_counter") - if(stmt.loopRegister!=null) { - - // loop register over range - - if(stmt.loopRegister!= Register.A) - throw AssemblyError("can only use A") - when { - range.step==1 -> { - // step = 1 - asmgen.out(""" - lda #${range.first} - sta $loopLabel+1 - lda #${range.last-range.first+1 and 255} - sta $counterLabel -$loopLabel lda #0 ; modified""") - asmgen.translate(stmt.body) - asmgen.out(""" -$continueLabel dec $counterLabel - beq $endLabel - inc $loopLabel+1 - jmp $loopLabel -$counterLabel .byte 0 -$endLabel""") - } - range.step==-1 -> { - // step = -1 - asmgen.out(""" - lda #${range.first} - sta $loopLabel+1 - lda #${range.first-range.last+1 and 255} - sta $counterLabel -$loopLabel lda #0 ; modified """) - asmgen.translate(stmt.body) - asmgen.out(""" -$continueLabel dec $counterLabel - beq $endLabel - dec $loopLabel+1 - jmp $loopLabel -$counterLabel .byte 0 -$endLabel""") - } - range.step >= 2 -> { - // step >= 2 - asmgen.out(""" - lda #${(range.last-range.first) / range.step + 1} - sta $counterLabel - lda #${range.first} -$loopLabel pha""") - asmgen.translate(stmt.body) - asmgen.out(""" -$continueLabel pla - dec $counterLabel - beq $endLabel - clc - adc #${range.step} - jmp $loopLabel -$counterLabel .byte 0 -$endLabel""") - } - else -> { - // step <= -2 - asmgen.out(""" - lda #${(range.first-range.last) / range.step.absoluteValue + 1} - sta $counterLabel - lda #${range.first} -$loopLabel pha""") - asmgen.translate(stmt.body) - asmgen.out(""" -$continueLabel pla - dec $counterLabel - beq $endLabel - sec - sbc #${range.step.absoluteValue} - jmp $loopLabel -$counterLabel .byte 0 -$endLabel""") - } - } - - } else { - - // loop over byte range via loopvar - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) - when { - range.step==1 -> { - // step = 1 - asmgen.out(""" + // loop over byte range via loopvar + val varname = asmgen.asmIdentifierName(stmt.loopVar) + when { + range.step==1 -> { + // step = 1 + asmgen.out(""" lda #${range.first} sta $varname lda #${range.last-range.first+1 and 255} @@ -521,34 +376,34 @@ $continueLabel dec $counterLabel jmp $loopLabel $counterLabel .byte 0 $endLabel""") - } - range.step==-1 -> { - // step = -1 - asmgen.out(""" + } + range.step==-1 -> { + // step = -1 + asmgen.out(""" lda #${range.first} sta $varname lda #${range.first-range.last+1 and 255} sta $counterLabel $loopLabel""") - asmgen.translate(stmt.body) - asmgen.out(""" + asmgen.translate(stmt.body) + asmgen.out(""" $continueLabel dec $counterLabel beq $endLabel dec $varname jmp $loopLabel $counterLabel .byte 0 $endLabel""") - } - range.step >= 2 -> { - // step >= 2 - asmgen.out(""" + } + range.step >= 2 -> { + // step >= 2 + asmgen.out(""" lda #${(range.last-range.first) / range.step + 1} sta $counterLabel lda #${range.first} sta $varname $loopLabel""") - asmgen.translate(stmt.body) - asmgen.out(""" + asmgen.translate(stmt.body) + asmgen.out(""" $continueLabel dec $counterLabel beq $endLabel lda $varname @@ -558,17 +413,17 @@ $continueLabel dec $counterLabel jmp $loopLabel $counterLabel .byte 0 $endLabel""") - } - else -> { - // step <= -2 - asmgen.out(""" + } + else -> { + // step <= -2 + asmgen.out(""" lda #${(range.first-range.last) / range.step.absoluteValue + 1} sta $counterLabel lda #${range.first} sta $varname $loopLabel""") - asmgen.translate(stmt.body) - asmgen.out(""" + asmgen.translate(stmt.body) + asmgen.out(""" $continueLabel dec $counterLabel beq $endLabel lda $varname @@ -578,13 +433,12 @@ $continueLabel dec $counterLabel jmp $loopLabel $counterLabel .byte 0 $endLabel""") - } } } } DataType.ARRAY_W, DataType.ARRAY_UW -> { // loop over word range via loopvar - val varname = asmgen.asmIdentifierName(stmt.loopVar!!) + val varname = asmgen.asmIdentifierName(stmt.loopVar) when { range.step == 1 -> { // word, step = 1 diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index fa42b5bf6..1d6138914 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -19,7 +19,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg // output the code to setup the parameters and perform the actual call // does NOT output the code to deal with the result values! val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") - val saveX = Register.X in sub.asmClobbers || sub.regXasResult() + val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() if(saveX) asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y... @@ -50,14 +50,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg argumentViaRegister(sub, arg.first, arg.second) } } - stmt.args.all {it is RegisterExpr} -> { - val argRegisters = stmt.args.map {(it as RegisterExpr).register.toString()} - val paramRegisters = sub.asmParameterRegisters.map { it.registerOrPair?.toString() } - if(argRegisters != paramRegisters) { - // all args are registers but differ from the function params. Can't pass directly, work via stack. - argsViaStackEvaluation(stmt, sub) - } - } else -> { // Risk of clobbering due to complex expression args. Work via the stack. argsViaStackEvaluation(stmt, sub) @@ -115,7 +107,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg val paramVar = parameter.value val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".") - val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) + val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) target.linkParents(value.parent) when (value) { is NumericLiteralValue -> { @@ -138,9 +130,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg else -> throw AssemblyError("weird parameter datatype") } } - is RegisterExpr -> { - asmgen.assignFromRegister(target, value.register) - } is DirectMemoryRead -> { when(value.addressExpression) { is NumericLiteralValue -> { @@ -153,7 +142,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg else -> { asmgen.translateExpression(value.addressExpression) asmgen.out(" jsr prog8_lib.read_byte_from_address | inx") - asmgen.assignFromRegister(target, Register.A) + asmgen.assignFromRegister(target, CpuRegister.A) } } } @@ -201,20 +190,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg bcs ++ + clc + -""") - } - is RegisterExpr -> { - when(value.register) { - Register.A -> asmgen.out(" cmp #0") - Register.X -> asmgen.out(" txa") - Register.Y -> asmgen.out(" tya") - } - asmgen.out(""" - beq + - sec - bcs ++ -+ clc -+ """) } else -> { @@ -237,14 +212,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg register!=null && register.name.length==1 -> { when (value) { is NumericLiteralValue -> { - val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position) - target.linkParents(value.parent) - asmgen.assignFromByteConstant(target, value.number.toShort()) + asmgen.assignToRegister(CpuRegister.valueOf(register.name), value.number.toShort(), null) } is IdentifierReference -> { - val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position) - target.linkParents(value.parent) - asmgen.assignFromByteVariable(target, value) + asmgen.assignToRegister(CpuRegister.valueOf(register.name), null, value) } else -> { asmgen.translateExpression(value) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt index d4273bfd8..86db3994b 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt @@ -4,7 +4,6 @@ import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.expressions.RegisterExpr import prog8.ast.statements.PostIncrDecr import prog8.compiler.AssemblyError import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage @@ -17,28 +16,10 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg val targetIdent = stmt.target.identifier val targetMemory = stmt.target.memoryAddress val targetArrayIdx = stmt.target.arrayindexed - val targetRegister = stmt.target.register when { - targetRegister!=null -> { - when(targetRegister) { - Register.A -> { - if(incr) - asmgen.out(" clc | adc #1 ") - else - asmgen.out(" sec | sbc #1 ") - } - Register.X -> { - if(incr) asmgen.out(" inx") else asmgen.out(" dex") - } - Register.Y -> { - if(incr) asmgen.out(" iny") else asmgen.out(" dey") - } - } - } targetIdent!=null -> { val what = asmgen.asmIdentifierName(targetIdent) - val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT) - when (dt) { + when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) { in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what") in WordDatatypes -> { if(incr) @@ -103,10 +84,6 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg else -> throw AssemblyError("need numeric type") } } - is RegisterExpr -> { - asmgen.translateArrayIndexIntoA(targetArrayIdx) - incrDecrArrayvalueWithIndexA(incr, arrayDt, what) - } is IdentifierReference -> { asmgen.translateArrayIndexIntoA(targetArrayIdx) incrDecrArrayvalueWithIndexA(incr, arrayDt, what) diff --git a/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt index 71e7c6f3b..702b03837 100644 --- a/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -342,40 +342,38 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va val rangeTo = iterableRange.to as? NumericLiteralValue if(rangeFrom==null || rangeTo==null) return noModifications - val loopvar = forLoop.loopVar?.targetVarDecl(program.namespace) - if(loopvar!=null) { - val stepLiteral = iterableRange.step as? NumericLiteralValue - when(loopvar.datatype) { - DataType.UBYTE -> { - if(rangeFrom.type!= DataType.UBYTE) { - // attempt to translate the iterable into ubyte values - val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) - return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) - } + val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)!! + val stepLiteral = iterableRange.step as? NumericLiteralValue + when(loopvar.datatype) { + DataType.UBYTE -> { + if(rangeFrom.type!= DataType.UBYTE) { + // attempt to translate the iterable into ubyte values + val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) + return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) } - DataType.BYTE -> { - if(rangeFrom.type!= DataType.BYTE) { - // attempt to translate the iterable into byte values - val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) - return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) - } - } - DataType.UWORD -> { - if(rangeFrom.type!= DataType.UWORD) { - // attempt to translate the iterable into uword values - val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) - return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) - } - } - DataType.WORD -> { - if(rangeFrom.type!= DataType.WORD) { - // attempt to translate the iterable into word values - val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) - return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) - } - } - else -> throw FatalAstException("invalid loopvar datatype $loopvar") } + DataType.BYTE -> { + if(rangeFrom.type!= DataType.BYTE) { + // attempt to translate the iterable into byte values + val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) + return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) + } + } + DataType.UWORD -> { + if(rangeFrom.type!= DataType.UWORD) { + // attempt to translate the iterable into uword values + val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) + return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) + } + } + DataType.WORD -> { + if(rangeFrom.type!= DataType.WORD) { + // attempt to translate the iterable into word values + val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange) + return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop)) + } + } + else -> throw FatalAstException("invalid loopvar datatype $loopvar") } return noModifications diff --git a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt index b51674066..4c901fb10 100644 --- a/compiler/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/compiler/src/prog8/optimizer/ExpressionSimplifier.kt @@ -610,8 +610,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker() if (amount == 0) { return expr.left } - val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT) - when (targetDt) { + when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) { DataType.UBYTE -> { if (amount >= 8) { return NumericLiteralValue.optimalInteger(0, expr.position) diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 6b2840af8..a4f2d2f53 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -46,7 +46,11 @@ internal class StatementOptimizer(private val program: Program, if(subroutine.asmAddress==null && !forceOutput) { if(subroutine.containsNoCodeNorVars()) { errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position) - return listOf(IAstModification.Remove(subroutine, parent)) + val removals = callgraph.calledBy.getValue(subroutine).map { + IAstModification.Remove(it, it.parent) + }.toMutableList() + removals += IAstModification.Remove(subroutine, parent) + return removals } } @@ -195,7 +199,7 @@ internal class StatementOptimizer(private val program: Program, return listOf(IAstModification.Remove(forLoop, parent)) } else if(forLoop.body.statements.size==1) { val loopvar = forLoop.body.statements[0] as? VarDecl - if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) { + if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) { // remove empty for loop (only loopvar decl in it) return listOf(IAstModification.Remove(forLoop, parent)) } @@ -207,7 +211,7 @@ internal class StatementOptimizer(private val program: Program, // for loop over a (constant) range of just a single value-- optimize the loop away // loopvar/reg = range value , follow by block val scope = AnonymousScope(mutableListOf(), forLoop.position) - scope.statements.add(Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position)) + scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position)) scope.statements.addAll(forLoop.body.statements) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) } @@ -222,7 +226,7 @@ internal class StatementOptimizer(private val program: Program, val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0] val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position) val scope = AnonymousScope(mutableListOf(), forLoop.position) - scope.statements.add(Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position)) + scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position)) scope.statements.addAll(forLoop.body.statements) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) } @@ -235,7 +239,7 @@ internal class StatementOptimizer(private val program: Program, if(av!=null) { val scope = AnonymousScope(mutableListOf(), forLoop.position) scope.statements.add(Assignment( - AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position), + AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position), forLoop.position)) scope.statements.addAll(forLoop.body.statements) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) diff --git a/docs/source/index.rst b/docs/source/index.rst index 8d5c221be..39d588dc7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -139,8 +139,8 @@ Design principles and features - 'One statement per line' code, resulting in clear readable programs. - Modular programming and scoping via modules, code blocks, and subroutines. - Provide high level programming constructs but at the same time stay close to the metal; - still able to directly use memory addresses, CPU registers and ROM subroutines, - and inline assembly to have full control when every cycle or byte matters + still able to directly use memory addresses and ROM subroutines, + and inline assembly to have full control when every register, cycle or byte matters - Arbitrary number of subroutine parameters - Complex nested expressions are possible - Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters diff --git a/docs/source/programming.rst b/docs/source/programming.rst index a8fbd3c47..3d0ae6321 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -204,13 +204,6 @@ Example:: byte @zp zeropageCounter = 42 -Variables that represent CPU hardware registers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following variables are reserved -and map directly (read/write) to a CPU hardware register: ``A``, ``X``, ``Y``. - - Integers ^^^^^^^^ @@ -393,7 +386,7 @@ expected when the program is restarted. Loops ----- -The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this. +The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this. The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions. Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead. @@ -407,7 +400,7 @@ a forever-loop. You can also create loops by using the ``goto`` statement, but this should usually be avoided. .. attention:: - The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately + The value of the loop variable after executing the loop *is undefined*. Don't use it immediately after the loop without first assigning a new value to it! (this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value) @@ -421,15 +414,15 @@ if statements Conditional execution means that the flow of execution changes based on certiain conditions, rather than having fixed gotos or subroutine calls:: - if A>4 goto overflow + if aa>4 goto overflow - if X==3 Y = 4 - if X==3 Y = 4 else A = 2 + if xx==3 yy = 4 + if xx==3 yy = 4 else aa = 2 - if X==5 { - Y = 99 + if xx==5 { + yy = 99 } else { - A = 3 + aa = 3 } @@ -493,16 +486,16 @@ Assignments ----------- Assignment statements assign a single value to a target variable or memory location. -Augmented assignments (such as ``A += X``) are also available, but these are just shorthands -for normal assignments (``A = A + X``). +Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands +for normal assignments (``aa = aa + xx``). -Only register variables and variables of type byte, word and float can be assigned a new value. +Only variables of type byte, word and float can be assigned a new value. It's not possible to set a new value to string or array variables etc, because they get allocated -a fixed amount of memory which will not change. +a fixed amount of memory which will not change. (You *can* change the value of elements in a string or array though). .. attention:: **Data type conversion (in assignments):** - When assigning a value with a 'smaller' datatype to a register or variable with a 'larger' datatype, + When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype, the value will be automatically converted to the target datatype: byte --> word --> float. So assigning a byte to a word variable, or a word to a floating point variable, is fine. The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to @@ -518,7 +511,7 @@ as the memory mapped address $d021. If you want to access a memory location directly (by using the address itself), without defining a memory mapped location, you can do so by enclosing the address in ``@(...)``:: - A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)") + color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)") @($d020) = 0 ; set the c64 screen border to black ("poke 53280,0") @(vic+$20) = 6 ; you can also use expressions to 'calculate' the address diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 9f36ff03b..0b138d377 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -24,7 +24,7 @@ Everything after a semicolon ``;`` is a comment and is ignored. If the whole line is just a comment, it will be copied into the resulting assembly source code. This makes it easier to understand and relate the generated code. Examples:: - A = 42 ; set the initial value to 42 + counter = 42 ; set the initial value to 42 ; next is the code that... @@ -313,7 +313,7 @@ Direct access to memory locations Instead of defining a memory mapped name for a specific memory location, you can also directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that:: - A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)") + color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)") @($d020) = 0 ; set the c64 screen border to black ("poke 53280,0") @(vic+$20) = 6 ; a dynamic expression to 'calculate' the address @@ -333,8 +333,6 @@ Reserved names The following names are reserved, they have a special meaning:: - A X Y ; 6502 hardware registers - Pc Pz Pn Pv ; 6502 status register flags true false ; boolean values 1 and 0 @@ -406,10 +404,10 @@ assignment: ``=`` Note that an assignment sometimes is not possible or supported. augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=`` - Syntactic sugar; ``A += X`` is equivalent to ``A = A + X`` + Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx`` postfix increment and decrement: ``++`` ``--`` - Syntactic sugar; ``A++`` is equivalent to ``A = A + 1``, and ``A--`` is equivalent to ``A = A - 1``. + Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``. Because these operations are so common, we have these short forms. comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=`` @@ -427,9 +425,9 @@ range creation: ``to`` 0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant) - A = 5 - X = 10 - A to X ; range of 5, 6, 7, 8, 9, 10 + aa = 5 + aa = 10 + aa to xx ; range of 5, 6, 7, 8, 9, 10 byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4] @@ -551,7 +549,7 @@ Loops for loop ^^^^^^^^ -The loop variable must be a register or a byte/word variable, +The loop variable must be a byte or word variable, and must be defined first in the local scope of the for loop. The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``, array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported). @@ -702,3 +700,4 @@ case you have to use { } to enclose them:: } else -> c64scr.print("don't know") } + diff --git a/docs/source/targetsystem.rst b/docs/source/targetsystem.rst index 5033c7967..aeaeb3b5d 100644 --- a/docs/source/targetsystem.rst +++ b/docs/source/targetsystem.rst @@ -113,22 +113,14 @@ CPU Directly Usable Registers ------------------------- -The following 6502 CPU hardware registers are directly usable in program code (and are reserved symbols): +The hardware CPU registers are not directly accessible from regular Prog8 code. +If you need to mess with them, you'll have to use inline assembly. +Be extra wary of the ``X`` register because it is used as an evaluation stack pointer and +changing its value you will destroy the evaluation stack and likely crash the program. -- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits) -- the status register (P) carry flag and interrupt disable flag can be written via a couple of special - builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``), - and read via the ``read_flags()`` function. - -However, you must assume that the 3 hardware registers ``A``, ``X`` and ``Y`` -are volatile. Their values cannot be depended upon, the compiler will use them as required. -Even simple assignments may require modification of one or more of the registers (for instance, when using arrays). - -Even more important, the ``X`` register is used as an evaluation stack pointer. -If you mess with it, you will destroy the evaluation stack and likely crash your program. -In some cases the compiler will warn you about this, but you should really avoid to use -this register. It's possible to store/restore the register's value (using special built in functions) -for the cases you really really need to use it directly. +The status register (P) carry flag and interrupt disable flag can be written via a couple of special +builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``), +and read via the ``read_flags()`` function. Subroutine Calling Conventions @@ -173,3 +165,4 @@ as a subroutine ``irq`` in the module ``irq`` so like this:: ; ... irq handling here ... } } + diff --git a/examples/arithmetic/aggregates.p8 b/examples/arithmetic/aggregates.p8 index 00d2f70ad..727139755 100644 --- a/examples/arithmetic/aggregates.p8 +++ b/examples/arithmetic/aggregates.p8 @@ -104,17 +104,6 @@ main { ub = all(farr) if ub==0 c64scr.print("error all10\n") - check_eval_stack() - c64scr.print("\nyou should see no errors printed above (only at first run).") } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/arithmetic/bitshift.p8 b/examples/arithmetic/bitshift.p8 index 37bed8e24..581e5173d 100644 --- a/examples/arithmetic/bitshift.p8 +++ b/examples/arithmetic/bitshift.p8 @@ -5,6 +5,8 @@ main { sub start() { + ubyte A + c64scr.print("ubyte shift left\n") A = shiftlb0() c64scr.print_ubbin(A, true) diff --git a/examples/arithmetic/div.p8 b/examples/arithmetic/div.p8 index db55aa5c5..fc363e29e 100644 --- a/examples/arithmetic/div.p8 +++ b/examples/arithmetic/div.p8 @@ -24,8 +24,6 @@ main { div_float(0,1,0) div_float(999.9,111.0,9.008108108108107) - - check_eval_stack() } sub div_ubyte(ubyte a1, ubyte a2, ubyte c) { @@ -103,12 +101,4 @@ main { c64flt.print_f(r) c64.CHROUT('\n') } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/examples/arithmetic/minus.p8 b/examples/arithmetic/minus.p8 index 21ac4727a..64134842d 100644 --- a/examples/arithmetic/minus.p8 +++ b/examples/arithmetic/minus.p8 @@ -32,8 +32,6 @@ main { minus_float(0,0,0) minus_float(2.5,1.5,1.0) minus_float(-1.5,3.5,-5.0) - - check_eval_stack() } sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) { @@ -111,13 +109,4 @@ main { c64flt.print_f(r) c64.CHROUT('\n') } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/arithmetic/mult.p8 b/examples/arithmetic/mult.p8 index 641756c59..c95566312 100644 --- a/examples/arithmetic/mult.p8 +++ b/examples/arithmetic/mult.p8 @@ -26,8 +26,6 @@ main { mul_float(0,0,0) mul_float(2.5,10,25) mul_float(-1.5,10,-15) - - check_eval_stack() } sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) { @@ -105,12 +103,4 @@ main { c64flt.print_f(r) c64.CHROUT('\n') } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/examples/arithmetic/plus.p8 b/examples/arithmetic/plus.p8 index ff6e5d99e..4133543c5 100644 --- a/examples/arithmetic/plus.p8 +++ b/examples/arithmetic/plus.p8 @@ -30,8 +30,6 @@ main { plus_float(1.5,2.5,4.0) plus_float(-1.5,3.5,2.0) plus_float(-1.1,3.3,2.2) - - check_eval_stack() } sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) { @@ -109,13 +107,4 @@ main { c64flt.print_f(r) c64.CHROUT('\n') } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/arithmetic/postincrdecr.p8 b/examples/arithmetic/postincrdecr.p8 index 9aca48a05..bf77825e4 100644 --- a/examples/arithmetic/postincrdecr.p8 +++ b/examples/arithmetic/postincrdecr.p8 @@ -9,6 +9,7 @@ main { c64scr.plot(0,24) + ubyte Y ubyte ub=200 byte bb=-100 uword uw = 2000 @@ -76,8 +77,6 @@ main { check_b(barr[1], -100) check_uw(uwarr[1], 2000) check_w(warr[1], -1000) - - check_eval_stack() } sub check_ub(ubyte value, ubyte expected) { @@ -139,13 +138,4 @@ main { c64flt.print_f(expected) c64.CHROUT('\n') } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/examples/arithmetic/remainder.p8 b/examples/arithmetic/remainder.p8 index fd499a15f..77a6f4dda 100644 --- a/examples/arithmetic/remainder.p8 +++ b/examples/arithmetic/remainder.p8 @@ -15,8 +15,6 @@ main { remainder_uword(40000,511,142) remainder_uword(40000,500,0) remainder_uword(43211,12,11) - - check_eval_stack() } sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) { @@ -48,12 +46,4 @@ main { c64scr.print_uw(r) c64.CHROUT('\n') } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/examples/comparison_ifs_byte.p8 b/examples/comparison_ifs_byte.p8 index 84cdb6c0a..8f8ce65fa 100644 --- a/examples/comparison_ifs_byte.p8 +++ b/examples/comparison_ifs_byte.p8 @@ -105,16 +105,5 @@ main { c64scr.print("ok: 22 >= 22\n") else c64scr.print("error in 22>=22!\n") - - check_eval_stack() - } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } } } diff --git a/examples/comparison_ifs_float.p8 b/examples/comparison_ifs_float.p8 index 381f6cb9d..d37e2297c 100644 --- a/examples/comparison_ifs_float.p8 +++ b/examples/comparison_ifs_float.p8 @@ -105,16 +105,5 @@ main { c64scr.print("ok: -22.2 >= -22.2\n") else c64scr.print("error in -22.2>=-22.2!\n") - - check_eval_stack() } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparison_ifs_ubyte.p8 b/examples/comparison_ifs_ubyte.p8 index 44f430b92..d91a1ba0e 100644 --- a/examples/comparison_ifs_ubyte.p8 +++ b/examples/comparison_ifs_ubyte.p8 @@ -105,16 +105,5 @@ main { c64scr.print("ok: 22 >= 22\n") else c64scr.print("error in 22>=22!\n") - - check_eval_stack() } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparison_ifs_uword.p8 b/examples/comparison_ifs_uword.p8 index c337da068..08845b740 100644 --- a/examples/comparison_ifs_uword.p8 +++ b/examples/comparison_ifs_uword.p8 @@ -105,16 +105,5 @@ main { c64scr.print("ok: 322 >= 322\n") else c64scr.print("error in 322>=322!\n") - - check_eval_stack() } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparison_ifs_word.p8 b/examples/comparison_ifs_word.p8 index b782c9345..aed726b17 100644 --- a/examples/comparison_ifs_word.p8 +++ b/examples/comparison_ifs_word.p8 @@ -137,16 +137,5 @@ main { c64scr.print("ok: 1000 >= 1000\n") else c64scr.print("error in 1000>=1000!\n") - - check_eval_stack() } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparisons_byte.p8 b/examples/comparisons_byte.p8 index b5458ef0a..eace5606d 100644 --- a/examples/comparisons_byte.p8 +++ b/examples/comparisons_byte.p8 @@ -52,7 +52,6 @@ main { c64scr.print("v1=20, v2=-111\n") compare() - check_eval_stack() return sub compare() { @@ -91,13 +90,4 @@ main { } } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparisons_float.p8 b/examples/comparisons_float.p8 index b7bac68ef..7390e4ddc 100644 --- a/examples/comparisons_float.p8 +++ b/examples/comparisons_float.p8 @@ -68,7 +68,6 @@ main { c64scr.print("v1 = v2 = 0\n") compare() - check_eval_stack() return sub compare() { @@ -108,11 +107,4 @@ main { } - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/examples/comparisons_ubyte.p8 b/examples/comparisons_ubyte.p8 index 336f4140a..9c5fdd47b 100644 --- a/examples/comparisons_ubyte.p8 +++ b/examples/comparisons_ubyte.p8 @@ -52,7 +52,6 @@ main { c64scr.print("v1=220, v2=10\n") compare() - check_eval_stack() return sub compare() { @@ -92,12 +91,4 @@ main { } - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparisons_uword.p8 b/examples/comparisons_uword.p8 index e597b7dd0..badd51ea8 100644 --- a/examples/comparisons_uword.p8 +++ b/examples/comparisons_uword.p8 @@ -82,7 +82,6 @@ main { c64scr.print("v1 = v2 = aa\n") compare() - check_eval_stack() return sub compare() { @@ -121,13 +120,4 @@ main { } } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/comparisons_word.p8 b/examples/comparisons_word.p8 index 459eb3d44..b934b72ce 100644 --- a/examples/comparisons_word.p8 +++ b/examples/comparisons_word.p8 @@ -118,7 +118,6 @@ main { c64scr.print("v1 = v2 = aa\n") compare() - check_eval_stack() return sub compare() { @@ -157,13 +156,4 @@ main { } } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/fibonacci.p8 b/examples/fibonacci.p8 index 1cfd5a72a..b552c5aa9 100644 --- a/examples/fibonacci.p8 +++ b/examples/fibonacci.p8 @@ -6,7 +6,11 @@ main { sub start() { c64scr.print("fibonacci sequence\n") - for A in 0 to 20 { + + ; TODO fix the double i=0 assignment generation in the asm code: + + ubyte i + for i in 0 to 20 { c64scr.print_uw(fib_next()) c64.CHROUT('\n') } diff --git a/examples/hello.p8 b/examples/hello.p8 index 1ea77d046..01b7e5061 100644 --- a/examples/hello.p8 +++ b/examples/hello.p8 @@ -42,17 +42,5 @@ main { c64.CHROUT('\n') c64scr.print("bye!\n") - - check_eval_stack() } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/mandelbrot.p8 b/examples/mandelbrot.p8 index 9b859db37..d6a2d283a 100644 --- a/examples/mandelbrot.p8 +++ b/examples/mandelbrot.p8 @@ -48,15 +48,5 @@ main { c64scr.print("finished in ") c64flt.print_f(duration) c64scr.print(" seconds!\n") - check_eval_stack() } - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/numbergame.p8 b/examples/numbergame.p8 index b529eb3ce..ea99693ed 100644 --- a/examples/numbergame.p8 +++ b/examples/numbergame.p8 @@ -57,18 +57,6 @@ main { c64scr.print("Thanks for playing, ") c64scr.print(name) c64scr.print(".\n") - - check_eval_stack() } } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/primes.p8 b/examples/primes.p8 index 45058c20c..e4bbe90e2 100644 --- a/examples/primes.p8 +++ b/examples/primes.p8 @@ -24,8 +24,6 @@ main { c64scr.print("number of primes (expected 54): ") c64scr.print_ub(amount) c64.CHROUT('\n') - - check_eval_stack() } @@ -48,14 +46,4 @@ main { } return candidate_prime } - - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/examples/romfloats.p8 b/examples/romfloats.p8 index 407069412..f4c979a63 100644 --- a/examples/romfloats.p8 +++ b/examples/romfloats.p8 @@ -52,17 +52,5 @@ main { c64flt.print_f(0.0) c64.CHROUT('\n') - - check_eval_stack() } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/screencodes.p8 b/examples/screencodes.p8 index a54c66d9d..4bdff0a5c 100644 --- a/examples/screencodes.p8 +++ b/examples/screencodes.p8 @@ -34,17 +34,5 @@ main { c64scr.print("\nscreencode z=") c64scr.print_ub(c2) c64scr.print("\n") - - check_eval_stack() - } - - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } } } diff --git a/examples/sorting.p8 b/examples/sorting.p8 index 62706bb9a..7528522a0 100644 --- a/examples/sorting.p8 +++ b/examples/sorting.p8 @@ -30,7 +30,6 @@ main { c64scr.print("reversed\n") print_arrays() - check_eval_stack() return @@ -65,14 +64,4 @@ main { c64.CHROUT('\n') } } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/structs.p8 b/examples/structs.p8 index 1c44fc4d3..09e614d13 100644 --- a/examples/structs.p8 +++ b/examples/structs.p8 @@ -1,7 +1,7 @@ %import c64utils %zeropage basicsafe -; TODO fix compiler errors when compiling ( /= ) +; TODO fix compiler errors when compiling without optimization ( /= ) main { @@ -30,17 +30,5 @@ main { c64.CHROUT(',') c64scr.print_ub(other.blue) c64.CHROUT('\n') - - check_eval_stack() } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/tehtriz.p8 b/examples/tehtriz.p8 index 53596ba90..7921eeb15 100644 --- a/examples/tehtriz.p8 +++ b/examples/tehtriz.p8 @@ -39,8 +39,6 @@ newgame: spawnNextBlock() waitkey: - check_eval_stack() - if c64.TIME_LO>=(60-4*speedlevel) { c64.TIME_LO = 0 @@ -391,16 +389,6 @@ waitkey: c64scr.setcc((i&3)+x, (i/4)+y, character, c) } } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } - } diff --git a/examples/test.p8 b/examples/test.p8 index 561358f78..c63cbdc06 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -8,5 +8,6 @@ main { sub start() { + @($d020)=0 } } diff --git a/examples/testarrays.p8 b/examples/testarrays.p8 index 73b0939a3..3cbd8565e 100644 --- a/examples/testarrays.p8 +++ b/examples/testarrays.p8 @@ -29,6 +29,7 @@ main { &uword[4] muwarray = $c000 &float[4] mflarray = $c000 + ubyte A byte bb ubyte ub word ww diff --git a/examples/testforloops.p8 b/examples/testforloops.p8 index 2c0050141..604cd6f6b 100644 --- a/examples/testforloops.p8 +++ b/examples/testforloops.p8 @@ -12,6 +12,7 @@ main { ubyte ub byte bb word total + ubyte A c64scr.plot(0,24) @@ -959,8 +960,6 @@ main { c64scr.print("ok\n") else c64scr.print("fail!!!\n") - - check_eval_stack() } sub wait_input() { @@ -969,13 +968,4 @@ main { void c64scr.input_chars(input) c64scr.print("\n\n") } - - - sub check_eval_stack() { - if X!=255 { - c64scr.print("stack x=") - c64scr.print_ub(X) - c64scr.print(" error!\n") - } - } } diff --git a/parser/antlr/prog8.g4 b/parser/antlr/prog8.g4 index 99213829b..5dd06c50c 100644 --- a/parser/antlr/prog8.g4 +++ b/parser/antlr/prog8.g4 @@ -158,8 +158,7 @@ augassignment : ; assign_target: - register - | scoped_identifier + scoped_identifier | arrayindexed | directmemory ; @@ -184,7 +183,6 @@ expression : | left = expression EOL? bop = 'xor' EOL? right = expression | prefix = 'not' expression | literalvalue - | register | scoped_identifier | arrayindexed | directmemory @@ -222,12 +220,6 @@ identifier : NAME ; scoped_identifier : NAME ('.' NAME)* ; -register : 'A' | 'X' | 'Y' ; - -registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // pairs can only be used in subroutine params and returnvalues - -statusregister : 'Pc' | 'Pz' | 'Pn' | 'Pv' ; - integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) wordsuffix? ; wordsuffix : '.w' ; @@ -287,15 +279,15 @@ asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns? asmsub_params : asmsub_param (',' EOL? asmsub_param)* ; -asmsub_param : vardecl '@' (registerorpair | statusregister | stack='stack') ; +asmsub_param : vardecl '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed asmsub_clobbers : 'clobbers' '(' clobber? ')' ; -clobber : register (',' register)* ; +clobber : identifier (',' identifier)* ; // A,X,Y allowed asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ; -asmsub_return : datatype '@' (registerorpair | statusregister | stack='stack') ; +asmsub_return : datatype '@' (identifier | stack='stack') ; // A,X,Y,AX,AY,XY,Pc,Pz,Pn,Pv allowed if_stmt : 'if' expression EOL? (statement | statement_block) EOL? else_part? ; // statement is constrained later @@ -308,7 +300,7 @@ branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part? branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ; -forloop : 'for' (register | identifier) 'in' expression EOL? (statement | statement_block) ; +forloop : 'for' identifier 'in' expression EOL? (statement | statement_block) ; whileloop: 'while' expression EOL? (statement | statement_block) ;