From 7626c9fff7d208c1dee1e34b16b5d597ff81da00 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 17 Oct 2020 16:00:49 +0200 Subject: [PATCH] only allow array indexing via a number, or a variable (eliminate complex expression calcs for array indexing, force explicit use of an index variable) --- compiler/res/prog8lib/cx16/textio.p8 | 9 +- compiler/res/prog8lib/diskio.p8 | 3 +- compiler/src/prog8/ast/AstToSourceCode.kt | 8 +- .../prog8/ast/expressions/AstExpressions.kt | 21 +- .../src/prog8/ast/processing/AstChecker.kt | 47 +-- .../src/prog8/ast/processing/AstWalker.kt | 4 +- .../src/prog8/ast/processing/IAstVisitor.kt | 4 +- .../ast/processing/StatementReorderer.kt | 12 + .../prog8/ast/processing/TypecastsAdder.kt | 5 + .../src/prog8/ast/statements/AstStatements.kt | 79 ++-- .../compiler/target/c64/codegen/AsmGen.kt | 168 +++----- .../c64/codegen/BuiltinFunctionsAsmGen.kt | 57 +-- .../target/c64/codegen/ExpressionsAsmGen.kt | 16 +- .../target/c64/codegen/PostIncrDecrAsmGen.kt | 103 +++-- .../c64/codegen/assignment/AsmAssignment.kt | 9 +- .../codegen/assignment/AssignmentAsmGen.kt | 381 +++++++----------- .../src/prog8/optimizer/BinExprSplitter.kt | 19 +- .../optimizer/ConstantIdentifierReplacer.kt | 22 +- docs/source/todo.rst | 1 - 19 files changed, 440 insertions(+), 528 deletions(-) diff --git a/compiler/res/prog8lib/cx16/textio.p8 b/compiler/res/prog8lib/cx16/textio.p8 index 2f96beb97..b0e74fa92 100644 --- a/compiler/res/prog8lib/cx16/textio.p8 +++ b/compiler/res/prog8lib/cx16/textio.p8 @@ -134,13 +134,16 @@ _la lda #0 ; modified ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b] sub color (ubyte txtcol) { - c64.CHROUT(color_to_charcode[txtcol & 15]) + txtcol &= 15 + c64.CHROUT(color_to_charcode[txtcol]) } sub color2 (ubyte txtcol, ubyte bgcol) { - c64.CHROUT(color_to_charcode[bgcol & 15]) + txtcol &= 15 + bgcol &= 15 + c64.CHROUT(color_to_charcode[bgcol]) c64.CHROUT(1) ; switch fg and bg colors - c64.CHROUT(color_to_charcode[txtcol & 15]) + c64.CHROUT(color_to_charcode[txtcol]) } sub lowercase() { diff --git a/compiler/res/prog8lib/diskio.p8 b/compiler/res/prog8lib/diskio.p8 index 67f133728..b9466c16a 100644 --- a/compiler/res/prog8lib/diskio.p8 +++ b/compiler/res/prog8lib/diskio.p8 @@ -151,7 +151,8 @@ io_error: filename[0] = 'r' filename[1] = ':' memcopy(newfileptr, &filename+2, flen_new) - filename[flen_new+2] = '=' + ubyte fis_ix = flen_new+2 ; TODO is temp var for array indexing + filename[fis_ix] = '=' memcopy(oldfileptr, &filename+3+flen_new, flen_old+1) c64.SETNAM(3+flen_new+flen_old, filename) c64.SETLFS(1, drivenumber, 15) diff --git a/compiler/src/prog8/ast/AstToSourceCode.kt b/compiler/src/prog8/ast/AstToSourceCode.kt index 34c64dd24..7d6b01306 100644 --- a/compiler/src/prog8/ast/AstToSourceCode.kt +++ b/compiler/src/prog8/ast/AstToSourceCode.kt @@ -121,7 +121,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): output(datatypeString(decl.datatype)) if(decl.arraysize!=null) { - decl.arraysize!!.index.accept(this) + decl.arraysize!!.indexNum?.accept(this) + decl.arraysize!!.indexVar?.accept(this) } if(decl.isArray) output("]") @@ -352,9 +353,10 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): } override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { - arrayIndexedExpression.identifier.accept(this) + arrayIndexedExpression.arrayvar.accept(this) output("[") - arrayIndexedExpression.arrayspec.index.accept(this) + arrayIndexedExpression.indexer.indexNum?.accept(this) + arrayIndexedExpression.indexer.indexVar?.accept(this) output("]") } diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 9a5ab1bbd..5411594d4 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -41,8 +41,8 @@ sealed class Expression: Node { && other.left isSameAs left && other.right isSameAs right) is ArrayIndexedExpression -> { - (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource - && other.arrayspec.index isSameAs arrayspec.index) + (other is ArrayIndexedExpression && other.arrayvar.nameInSource == arrayvar.nameInSource + && other.indexer isSameAs indexer) } is DirectMemoryRead -> { (other is DirectMemoryRead && other.addressExpression isSameAs addressExpression) @@ -232,20 +232,19 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex } } -class ArrayIndexedExpression(var identifier: IdentifierReference, - val arrayspec: ArrayIndex, +class ArrayIndexedExpression(var arrayvar: IdentifierReference, + val indexer: ArrayIndex, override val position: Position) : Expression(), IAssignable { override lateinit var parent: Node override fun linkParents(parent: Node) { this.parent = parent - identifier.linkParents(this) - arrayspec.linkParents(this) + arrayvar.linkParents(this) + indexer.linkParents(this) } override fun replaceChildNode(node: Node, replacement: Node) { when { - node===identifier -> identifier = replacement as IdentifierReference - node===arrayspec.index -> arrayspec.index = replacement as Expression + node===arrayvar -> arrayvar = replacement as IdentifierReference else -> throw FatalAstException("invalid replace") } replacement.parent = this @@ -255,10 +254,10 @@ class ArrayIndexedExpression(var identifier: IdentifierReference, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) - override fun referencesIdentifier(vararg scopedName: String) = identifier.referencesIdentifier(*scopedName) + override fun referencesIdentifier(vararg scopedName: String) = arrayvar.referencesIdentifier(*scopedName) override fun inferType(program: Program): InferredTypes.InferredType { - val target = identifier.targetStatement(program.namespace) + val target = arrayvar.targetStatement(program.namespace) if (target is VarDecl) { return when (target.datatype) { DataType.STR -> InferredTypes.knownFor(DataType.UBYTE) @@ -270,7 +269,7 @@ class ArrayIndexedExpression(var identifier: IdentifierReference, } override fun toString(): String { - return "ArrayIndexed(ident=$identifier, arraysize=$arrayspec; pos=$position)" + return "ArrayIndexed(ident=$arrayvar, arraysize=$indexer; pos=$position)" } } diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 58ccbdc18..7f3a3de99 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -120,11 +120,6 @@ internal class AstChecker(private val program: Program, if(loopvar==null || loopvar.type== VarDeclType.CONST) { errors.err("for loop requires a variable to loop with", forLoop.position) } else { - - fun checkLoopRangeValues() { - - } - when (loopvar.datatype) { DataType.UBYTE -> { if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR) @@ -475,7 +470,7 @@ internal class AstChecker(private val program: Program, fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position) // the initializer value can't refer to the variable itself (recursive definition) - if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) + if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true) err("recursive var declaration") // CONST can only occur on simple types (byte, word, float) @@ -1013,7 +1008,7 @@ internal class AstChecker(private val program: Program, } } } else if(postIncrDecr.target.arrayindexed != null) { - val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(program.namespace) + val target = postIncrDecr.target.arrayindexed?.arrayvar?.targetStatement(program.namespace) if(target==null) { errors.err("undefined symbol", postIncrDecr.position) } @@ -1028,32 +1023,41 @@ internal class AstChecker(private val program: Program, } override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { - val target = arrayIndexedExpression.identifier.targetStatement(program.namespace) + val target = arrayIndexedExpression.arrayvar.targetStatement(program.namespace) if(target is VarDecl) { if(target.datatype !in IterableDatatypes) errors.err("indexing requires an iterable variable", arrayIndexedExpression.position) val arraysize = target.arraysize?.constIndex() if(arraysize!=null) { // check out of bounds - val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt() + val index = arrayIndexedExpression.indexer.constIndex() if(index!=null && (index<0 || index>=arraysize)) - errors.err("array index out of bounds", arrayIndexedExpression.arrayspec.position) + errors.err("array index out of bounds", arrayIndexedExpression.indexer.position) } else if(target.datatype == DataType.STR) { if(target.value is StringLiteralValue) { // check string lengths for non-memory mapped strings val stringLen = (target.value as StringLiteralValue).value.length - val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt() + val index = arrayIndexedExpression.indexer.constIndex() if (index != null && (index < 0 || index >= stringLen)) - errors.err("index out of bounds", arrayIndexedExpression.arrayspec.position) + errors.err("index out of bounds", arrayIndexedExpression.indexer.position) } } } else errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position) // check index value 0..255 - val dtx = arrayIndexedExpression.arrayspec.index.inferType(program).typeOrElse(DataType.STRUCT) - if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE) + val dtxNum = arrayIndexedExpression.indexer.indexNum?.inferType(program)?.typeOrElse(DataType.STRUCT) + if(dtxNum!=null && dtxNum != DataType.UBYTE && dtxNum != DataType.BYTE) errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position) + val dtxVar = arrayIndexedExpression.indexer.indexVar?.inferType(program)?.typeOrElse(DataType.STRUCT) + if(dtxVar!=null && dtxVar != DataType.UBYTE && dtxVar != DataType.BYTE) + errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position) + + // check type of array indexer + if(arrayIndexedExpression.indexer.indexVar==null && arrayIndexedExpression.indexer.indexNum==null) { + errors.err("array indexing can only be done with a number or a variable, not an arbitrary expression. Use a temp var?", arrayIndexedExpression.indexer.position) + // TODO we can probably deal with simple binary expressions such as array[i+1] or lsb(w)/msb(w) or array[i*2] to do this automatically... + } super.visit(arrayIndexedExpression) } @@ -1158,10 +1162,7 @@ internal class AstChecker(private val program: Program, if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize<1 || arraySpecSize>256) return err("byte array length must be 1-256") - val constX = arrayspec.index.constValue(program) - if(constX?.type !in IntegerDatatypes) - return err("array size specifier must be constant integer value") - val expectedSize = constX!!.number.toInt() + val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value") if (arraySize != expectedSize) return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return true @@ -1180,10 +1181,7 @@ internal class AstChecker(private val program: Program, if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize<1 || arraySpecSize>128) return err("word array length must be 1-128") - val constX = arrayspec.index.constValue(program) - if(constX?.type !in IntegerDatatypes) - return err("array size specifier must be constant integer value") - val expectedSize = constX!!.number.toInt() + val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value") if (arraySize != expectedSize) return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return true @@ -1202,10 +1200,7 @@ internal class AstChecker(private val program: Program, if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize < 1 || arraySpecSize>51) return err("float array length must be 1-51") - val constX = arrayspec.index.constValue(program) - if(constX?.type !in IntegerDatatypes) - return err("array size specifier must be constant integer value") - val expectedSize = constX!!.number.toInt() + val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value") if (arraySize != expectedSize) return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") } else diff --git a/compiler/src/prog8/ast/processing/AstWalker.kt b/compiler/src/prog8/ast/processing/AstWalker.kt index 3cdf97eee..24fdccb78 100644 --- a/compiler/src/prog8/ast/processing/AstWalker.kt +++ b/compiler/src/prog8/ast/processing/AstWalker.kt @@ -363,8 +363,8 @@ abstract class AstWalker { fun visit(arrayIndexedExpression: ArrayIndexedExpression, parent: Node) { track(before(arrayIndexedExpression, parent), arrayIndexedExpression, parent) - arrayIndexedExpression.identifier.accept(this, arrayIndexedExpression) - arrayIndexedExpression.arrayspec.accept(this, arrayIndexedExpression) + arrayIndexedExpression.arrayvar.accept(this, arrayIndexedExpression) + arrayIndexedExpression.indexer.accept(this, arrayIndexedExpression) track(after(arrayIndexedExpression, parent), arrayIndexedExpression, parent) } diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index 9f91db7c4..f0924bc8c 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -125,8 +125,8 @@ interface IAstVisitor { } fun visit(arrayIndexedExpression: ArrayIndexedExpression) { - arrayIndexedExpression.identifier.accept(this) - arrayIndexedExpression.arrayspec.accept(this) + arrayIndexedExpression.arrayvar.accept(this) + arrayIndexedExpression.indexer.accept(this) } fun visit(assignTarget: AssignTarget) { diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index d8b9ecdf2..41a9a9467 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -71,6 +71,18 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte return noModifications } + override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable { + val expr = arrayIndexedExpression.indexer.origExpression + if (expr is NumericLiteralValue) { + arrayIndexedExpression.indexer.indexNum = expr + arrayIndexedExpression.indexer.origExpression = null + } + else if (expr is IdentifierReference) { + arrayIndexedExpression.indexer.indexVar = expr + arrayIndexedExpression.indexer.origExpression = null + } + return noModifications + } override fun after(whenStatement: WhenStatement, parent: Node): Iterable { val choices = whenStatement.choiceValues(program).sortedBy { diff --git a/compiler/src/prog8/ast/processing/TypecastsAdder.kt b/compiler/src/prog8/ast/processing/TypecastsAdder.kt index 996bb9b57..eb638efbf 100644 --- a/compiler/src/prog8/ast/processing/TypecastsAdder.kt +++ b/compiler/src/prog8/ast/processing/TypecastsAdder.kt @@ -23,6 +23,11 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) { val valueDt = declValue.inferType(program) if(!valueDt.istype(decl.datatype)) { + + // don't add a typecast on an array initializer value + if(valueDt.typeOrElse(DataType.STRUCT) in IntegerDatatypes && decl.datatype in ArrayDatatypes) + return noModifications + return listOf(IAstModification.ReplaceNode( declValue, TypecastExpression(declValue, decl.datatype, true, declValue.position), diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index df2beea11..710990fd0 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -277,36 +277,72 @@ class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Posit : VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position) -class ArrayIndex(var index: Expression, override val position: Position) : Node { +class ArrayIndex(var origExpression: Expression?, // will be replaced later by either the number or the identifier + override val position: Position) : Node { + // for code simplicity, either indexed via a constant number or via a variable (no arbitrary expressions) override lateinit var parent: Node + var indexNum: NumericLiteralValue? = origExpression as? NumericLiteralValue + var indexVar: IdentifierReference? = origExpression as? IdentifierReference + + init { + if(indexNum!=null || indexVar!=null) + origExpression = null + } override fun linkParents(parent: Node) { this.parent = parent - index.linkParents(this) + origExpression?.linkParents(this) + indexNum?.linkParents(this) + indexVar?.linkParents(this) } override fun replaceChildNode(node: Node, replacement: Node) { - require(replacement is Expression && node===index) - index = replacement - replacement.parent = this + require(replacement is Expression) + when { + node===origExpression -> origExpression = replacement + node===indexVar -> { + when (replacement) { + is NumericLiteralValue -> { + indexVar = null + indexNum = replacement + } + is IdentifierReference -> { + indexVar = replacement + } + else -> { + throw FatalAstException("invalid replace") + } + } + } + else -> throw FatalAstException("invalid replace") + } } companion object { fun forArray(v: ArrayLiteralValue): ArrayIndex { - return ArrayIndex(NumericLiteralValue.optimalNumeric(v.value.size, v.position), v.position) + val indexnum = NumericLiteralValue.optimalNumeric(v.value.size, v.position) + return ArrayIndex(indexnum, v.position) } } - fun accept(visitor: IAstVisitor) = index.accept(visitor) - fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, this) - - override fun toString(): String { - return("ArrayIndex($index, pos=$position)") + fun accept(visitor: IAstVisitor) { + origExpression?.accept(visitor) + indexNum?.accept(visitor) + indexVar?.accept(visitor) + } + fun accept(visitor: AstWalker, parent: Node) { + origExpression?.accept(visitor, this) + indexNum?.accept(visitor, this) + indexVar?.accept(visitor, this) } - fun constIndex() = (index as? NumericLiteralValue)?.number?.toInt() + override fun toString(): String { + return("ArrayIndex($indexNum, $indexVar, pos=$position)") + } - infix fun isSameAs(other: ArrayIndex) = index.isSameAs(other.index) + fun constIndex() = indexNum?.number?.toInt() + + infix fun isSameAs(other: ArrayIndex) = indexNum==other.indexNum && indexVar == other.indexVar } open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() { @@ -454,8 +490,8 @@ data class AssignTarget(var identifier: IdentifierReference?, } identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource arrayindexed != null -> { - if(value is ArrayIndexedExpression && value.identifier.nameInSource == arrayindexed!!.identifier.nameInSource) - arrayindexed!!.arrayspec isSameAs value.arrayspec + if(value is ArrayIndexedExpression && value.arrayvar.nameInSource == arrayindexed!!.arrayvar.nameInSource) + arrayindexed!!.indexer isSameAs value.indexer else false } @@ -474,9 +510,9 @@ data class AssignTarget(var identifier: IdentifierReference?, return addr1 != null && addr2 != null && addr1 == addr2 } if (this.arrayindexed != null && other.arrayindexed != null) { - if (this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) { - val x1 = this.arrayindexed!!.arrayspec.index.constValue(program) - val x2 = other.arrayindexed!!.arrayspec.index.constValue(program) + if (this.arrayindexed!!.arrayvar.nameInSource == other.arrayindexed!!.arrayvar.nameInSource) { + val x1 = this.arrayindexed!!.indexer.constIndex() + val x2 = other.arrayindexed!!.indexer.constIndex() return x1 != null && x2 != null && x1 == x2 } } @@ -501,7 +537,7 @@ data class AssignTarget(var identifier: IdentifierReference?, } } this.arrayindexed != null -> { - val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace) + val targetStmt = this.arrayindexed!!.arrayvar.targetVarDecl(namespace) return if (targetStmt?.type == VarDeclType.MEMORY) { val addr = targetStmt.value as? NumericLiteralValue if (addr != null) @@ -902,10 +938,7 @@ class WhenStatement(var condition: Expression, val cv = it.constValue(program) cv?.number?.toInt() ?: it.hashCode() // the hashcode is a nonsensical number but it avoids weird AST validation errors later } - if(values.contains(null)) - result.add(null to choice) - else - result.add(values.filterNotNull() to choice) + result.add(values to choice) } } return result diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 5348aa93d..46c99ffa0 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -678,132 +678,75 @@ internal class AsmGen(private val program: Program, register: CpuRegister, addOneExtra: Boolean=false) { val reg = register.toString().toLowerCase() - val index = expr.arrayspec.index - if(index is NumericLiteralValue) { - val indexValue = index.number.toInt() * elementDt.memorySize() + if(addOneExtra) 1 else 0 + val indexnum = expr.indexer.constIndex() + if(indexnum!=null) { + val indexValue = indexnum * elementDt.memorySize() + if(addOneExtra) 1 else 0 out(" ld$reg #$indexValue") return } + val indexName = asmVariableName(expr.indexer.indexVar!!) if(addOneExtra) { // add 1 to the result - if (index is IdentifierReference) { - val indexName = asmVariableName(index) - when(elementDt) { - in ByteDatatypes -> { - out(" ldy $indexName | iny") - when(register) { - CpuRegister.A -> out(" tya") - CpuRegister.X -> out(" tyx") - CpuRegister.Y -> {} - } + when(elementDt) { + in ByteDatatypes -> { + out(" ldy $indexName | iny") + when(register) { + CpuRegister.A -> out(" tya") + CpuRegister.X -> out(" tyx") + CpuRegister.Y -> {} } - in WordDatatypes -> { - out(" lda $indexName | sec | rol a") - when(register) { - CpuRegister.A -> {} - CpuRegister.X -> out(" tax") - CpuRegister.Y -> out(" tay") - } - } - DataType.FLOAT -> { - require(DataType.FLOAT.memorySize()==5) - out(""" - lda $indexName - asl a - asl a - sec - adc $indexName""") - when(register) { - CpuRegister.A -> {} - CpuRegister.X -> out(" tax") - CpuRegister.Y -> out(" tay") - } - } - else -> throw AssemblyError("weird dt") } - } - else { - expressionsAsmGen.translateExpression(index) - out(""" - inc P8ESTACK_LO,x - bne + - inc P8ESTACK_HI,x -+""") - when(register) { - CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x") - CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax") - CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x") + in WordDatatypes -> { + out(" lda $indexName | sec | rol a") + when(register) { + CpuRegister.A -> {} + CpuRegister.X -> out(" tax") + CpuRegister.Y -> out(" tay") + } } + DataType.FLOAT -> { + require(DataType.FLOAT.memorySize()==5) + out(""" + lda $indexName + asl a + asl a + sec + adc $indexName""") + when(register) { + CpuRegister.A -> {} + CpuRegister.X -> out(" tax") + CpuRegister.Y -> out(" tay") + } + } + else -> throw AssemblyError("weird dt") } } else { - if (index is IdentifierReference) { - val indexName = asmVariableName(index) - when(elementDt) { - in ByteDatatypes -> out(" ld$reg $indexName") - in WordDatatypes -> { - out(" lda $indexName | asl a") - when(register) { - CpuRegister.A -> {} - CpuRegister.X -> out(" tax") - CpuRegister.Y -> out(" tay") - } + when(elementDt) { + in ByteDatatypes -> out(" ld$reg $indexName") + in WordDatatypes -> { + out(" lda $indexName | asl a") + when(register) { + CpuRegister.A -> {} + CpuRegister.X -> out(" tax") + CpuRegister.Y -> out(" tay") } - DataType.FLOAT -> { - require(DataType.FLOAT.memorySize()==5) - out(""" - lda $indexName - asl a - asl a - clc - adc $indexName""") - when(register) { - CpuRegister.A -> {} - CpuRegister.X -> out(" tax") - CpuRegister.Y -> out(" tay") - } - } - else -> throw AssemblyError("weird dt") } - } - else { - expressionsAsmGen.translateExpression(index) - when(elementDt) { - in ByteDatatypes -> { - when (register) { - CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x") - CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax") - CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x") - } + DataType.FLOAT -> { + require(DataType.FLOAT.memorySize()==5) + out(""" + lda $indexName + asl a + asl a + clc + adc $indexName""") + when(register) { + CpuRegister.A -> {} + CpuRegister.X -> out(" tax") + CpuRegister.Y -> out(" tay") } - in WordDatatypes -> { - out(""" - inx - lda P8ESTACK_LO,x - asl a""") - when (register) { - CpuRegister.A -> {} - CpuRegister.X -> out(" tax") - CpuRegister.Y -> out(" tay") - } - } - DataType.FLOAT -> { - require(DataType.FLOAT.memorySize()==5) - out(""" - inx - lda P8ESTACK_LO,x - asl a - asl a - clc - adc P8ESTACK_LO,x""") - when (register) { - CpuRegister.A -> {} - CpuRegister.X -> out(" tax") - CpuRegister.Y -> out(" tay") - } - } - else -> throw AssemblyError("weird dt") } + else -> throw AssemblyError("weird dt") } } } @@ -811,6 +754,9 @@ internal class AsmGen(private val program: Program, internal fun translateExpression(expression: Expression) = expressionsAsmGen.translateExpression(expression) + internal fun translateExpression(indexer: ArrayIndex) = + expressionsAsmGen.translateExpression(indexer) + internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) = builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index 534f0f0cc..cae8bebd5 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -159,8 +159,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UBYTE -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.ror2_array_ub") } is DirectMemoryRead -> { @@ -182,8 +182,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UWORD -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.ror2_array_uw") } is IdentifierReference -> { @@ -204,8 +204,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UBYTE -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.ror_array_ub") } is DirectMemoryRead -> { @@ -234,8 +234,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UWORD -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.ror_array_uw") } is IdentifierReference -> { @@ -256,8 +256,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UBYTE -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.rol2_array_ub") } is DirectMemoryRead -> { @@ -279,8 +279,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UWORD -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.rol2_array_uw") } is IdentifierReference -> { @@ -301,8 +301,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UBYTE -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.rol_array_ub") } is DirectMemoryRead -> { @@ -331,8 +331,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val DataType.UWORD -> { when (what) { is ArrayIndexedExpression -> { - asmgen.translateExpression(what.identifier) - asmgen.translateExpression(what.arrayspec.index) + asmgen.translateExpression(what.arrayvar) + asmgen.translateExpression(what.indexer) asmgen.out(" jsr prog8_lib.rol_array_uw") } is IdentifierReference -> { @@ -478,20 +478,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) { - val indexValue1 = first.arrayspec.index as? NumericLiteralValue - val indexName1 = first.arrayspec.index as? IdentifierReference - val indexValue2 = second.arrayspec.index as? NumericLiteralValue - val indexName2 = second.arrayspec.index as? IdentifierReference - val arrayVarName1 = asmgen.asmVariableName(first.identifier) - val arrayVarName2 = asmgen.asmVariableName(second.identifier) + val arrayVarName1 = asmgen.asmVariableName(first.arrayvar) + val arrayVarName2 = asmgen.asmVariableName(second.arrayvar) val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT) - if(indexValue1!=null && indexValue2!=null) { - swapArrayValues(elementDt, arrayVarName1, indexValue1, arrayVarName2, indexValue2) + val firstNum = first.indexer.indexNum + val firstVar = first.indexer.indexVar + val secondNum = second.indexer.indexNum + val secondVar = second.indexer.indexVar + + if(firstNum!=null && secondNum!=null) { + swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum) return - } else if(indexName1!=null && indexName2!=null) { - swapArrayValues(elementDt, arrayVarName1, indexName1, arrayVarName2, indexName2) + } else if(firstVar!=null && secondVar!=null) { + swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondVar) return + } else if(firstNum!=null && secondVar!=null) { + TODO("swap array num/var") + } else if(firstVar!=null && secondNum!=null) { + TODO("swap array var/num") } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index e19092493..4843633e4 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -3,6 +3,7 @@ package prog8.compiler.target.c64.codegen import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* +import prog8.ast.statements.ArrayIndex import prog8.compiler.AssemblyError import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CpuType @@ -1415,11 +1416,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } private fun translateExpression(arrayExpr: ArrayIndexedExpression) { - val index = arrayExpr.arrayspec.index val elementDt = arrayExpr.inferType(program).typeOrElse(DataType.STRUCT) - val arrayVarName = asmgen.asmVariableName(arrayExpr.identifier) - if(index is NumericLiteralValue) { - val indexValue = index.number.toInt() * elementDt.memorySize() + val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar) + if(arrayExpr.indexer.indexNum!=null) { + val indexValue = arrayExpr.indexer.constIndex()!! * elementDt.memorySize() when(elementDt) { in ByteDatatypes -> { asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex") @@ -1458,6 +1458,14 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } } + fun translateExpression(indexer: ArrayIndex) { + // it is either a number, or a variable + val indexNum = indexer.indexNum + val indexVar = indexer.indexVar + indexNum?.let { asmgen.translateExpression(indexNum) } + indexVar?.let { asmgen.translateExpression(indexVar) } + } + private fun translateBinaryOperatorBytes(operator: String, types: DataType) { when(operator) { "**" -> throw AssemblyError("** operator requires floats") diff --git a/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt index 1d78b0dfc..b3142c227 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt @@ -70,67 +70,64 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg } } targetArrayIdx!=null -> { - val index = targetArrayIdx.arrayspec.index - val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.identifier) + val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar) val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT) - when(index) { - is NumericLiteralValue -> { - val indexValue = index.number.toInt() * elementDt.memorySize() - when(elementDt) { - in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue") - in WordDatatypes -> { - if(incr) - asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+") - else - asmgen.out(""" - lda $asmArrayvarname+$indexValue - bne + - dec $asmArrayvarname+$indexValue+1 + if(targetArrayIdx.indexer.indexNum!=null) { + val indexValue = targetArrayIdx.indexer.constIndex()!! * elementDt.memorySize() + when(elementDt) { + in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue") + in WordDatatypes -> { + if(incr) + asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+") + else + asmgen.out(""" + lda $asmArrayvarname+$indexValue + bne + + dec $asmArrayvarname+$indexValue+1 + dec $asmArrayvarname+$indexValue """) - } - DataType.FLOAT -> { - asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue") - asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f") - } - else -> throw AssemblyError("need numeric type") } - } - else -> { - asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A) - asmgen.saveRegister(CpuRegister.X, false, scope) - asmgen.out(" tax") - when(elementDt) { - in ByteDatatypes -> { - asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x") - } - in WordDatatypes -> { - if(incr) - asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+") - else - asmgen.out(""" - lda $asmArrayvarname,x - bne + - dec $asmArrayvarname+1,x -+ dec $asmArrayvarname -""") - } - DataType.FLOAT -> { - asmgen.out(""" - ldy #>$asmArrayvarname - clc - adc #<$asmArrayvarname - bcc + - iny -+ jsr floats.inc_var_f""") - } - else -> throw AssemblyError("weird array elt dt") + DataType.FLOAT -> { + asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue") + asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f") } - asmgen.restoreRegister(CpuRegister.X, false) + else -> throw AssemblyError("need numeric type") } } + else + { + asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A) + asmgen.saveRegister(CpuRegister.X, false, scope) + asmgen.out(" tax") + when(elementDt) { + in ByteDatatypes -> { + asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x") + } + in WordDatatypes -> { + if(incr) + asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+") + else + asmgen.out(""" + lda $asmArrayvarname,x + bne + + dec $asmArrayvarname+1,x ++ dec $asmArrayvarname +""") + } + DataType.FLOAT -> { + asmgen.out(""" + ldy #>$asmArrayvarname + clc + adc #<$asmArrayvarname + bcc + + iny ++ jsr floats.inc_var_f""") + } + else -> throw AssemblyError("weird array elt dt") + } + asmgen.restoreRegister(CpuRegister.X, false) + } } - else -> throw AssemblyError("weird target type ${stmt.target}") } } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt index dcdd5c7a0..37d437067 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AsmAssignment.kt @@ -1,6 +1,5 @@ package prog8.compiler.target.c64.codegen.assignment -import prog8.ast.INameScope import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* @@ -43,12 +42,12 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, ) { val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0} - val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() } + val constArrayIndexValue by lazy { array?.indexer?.constIndex() } val asmVarname: String get() = if(array==null) variableAsmName!! else - asmgen.asmVariableName(array.identifier) + asmgen.asmVariableName(array.arrayvar) lateinit var origAssign: AsmAssignment @@ -93,13 +92,13 @@ internal class AsmAssignSource(val kind: SourceStorageKind, ) { val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0} - val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() } + val constArrayIndexValue by lazy { array?.indexer?.constIndex() } val asmVarname: String get() = if(array==null) variableAsmName!! else - asmgen.asmVariableName(array.identifier) + asmgen.asmVariableName(array.arrayvar) companion object { fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt index c764c55b3..34ed054c3 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -59,11 +59,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen SourceStorageKind.ARRAY -> { val value = assign.source.array!! val elementDt = assign.source.datatype - val index = value.arrayspec.index - val arrayVarName = asmgen.asmVariableName(value.identifier) - if (index is NumericLiteralValue) { + val arrayVarName = asmgen.asmVariableName(value.arrayvar) + if (value.indexer.indexNum!=null) { // constant array index value - val indexValue = index.number.toInt() * elementDt.memorySize() + val indexValue = value.indexer.constIndex()!! * elementDt.memorySize() when (elementDt) { in ByteDatatypes -> asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex") @@ -265,66 +264,60 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!) } TargetStorageKind.ARRAY -> { - val index = target.array!!.arrayspec.index - when { - target.constArrayIndexValue!=null -> { - val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize() - when(target.datatype) { - in ByteDatatypes -> { - asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx") - } - in WordDatatypes -> { - asmgen.out(""" - inx - lda P8ESTACK_LO,x - sta ${target.asmVarname}+$scaledIdx - lda P8ESTACK_HI,x - sta ${target.asmVarname}+$scaledIdx+1 - """) - } - DataType.FLOAT -> { - asmgen.out(""" - lda #<${target.asmVarname}+$scaledIdx - ldy #>${target.asmVarname}+$scaledIdx - jsr floats.pop_float - """) - } - else -> throw AssemblyError("weird target variable type ${target.datatype}") + if(target.constArrayIndexValue!=null) { + val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize() + when(target.datatype) { + in ByteDatatypes -> { + asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx") } + in WordDatatypes -> { + asmgen.out(""" + inx + lda P8ESTACK_LO,x + sta ${target.asmVarname}+$scaledIdx + lda P8ESTACK_HI,x + sta ${target.asmVarname}+$scaledIdx+1 + """) + } + DataType.FLOAT -> { + asmgen.out(""" + lda #<${target.asmVarname}+$scaledIdx + ldy #>${target.asmVarname}+$scaledIdx + jsr floats.pop_float + """) + } + else -> throw AssemblyError("weird target variable type ${target.datatype}") } - index is IdentifierReference -> { - when(target.datatype) { - DataType.UBYTE, DataType.BYTE -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) - asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y") - } - DataType.UWORD, DataType.WORD -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) - asmgen.out(""" - inx - lda P8ESTACK_LO,x - sta ${target.asmVarname},y - lda P8ESTACK_HI,x - sta ${target.asmVarname}+1,y - """) - } - DataType.FLOAT -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A) - asmgen.out(""" - ldy #>${target.asmVarname} - clc - adc #<${target.asmVarname} - bcc + - iny + } + else + { + target.array!! + when(target.datatype) { + DataType.UBYTE, DataType.BYTE -> { + asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) + asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y") + } + DataType.UWORD, DataType.WORD -> { + asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) + asmgen.out(""" + inx + lda P8ESTACK_LO,x + sta ${target.asmVarname},y + lda P8ESTACK_HI,x + sta ${target.asmVarname}+1,y + """) + } + DataType.FLOAT -> { + asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A) + asmgen.out(""" + ldy #>${target.asmVarname} + clc + adc #<${target.asmVarname} + bcc + + iny + jsr floats.pop_float""") - } - else -> throw AssemblyError("weird dt") } - } - else -> { - asmgen.translateExpression(index) - asmgen.out(" inx | lda P8ESTACK_LO,x") - popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname) + else -> throw AssemblyError("weird dt") } } } @@ -441,73 +434,66 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memory}") } TargetStorageKind.ARRAY -> { - val index = target.array!!.arrayspec.index - when { - target.constArrayIndexValue!=null -> { - val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize() - when(target.datatype) { - in ByteDatatypes -> { - asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") - } - in WordDatatypes -> { - asmgen.out(""" - lda $sourceName - sta ${target.asmVarname}+$scaledIdx - lda $sourceName+1 - sta ${target.asmVarname}+$scaledIdx+1 - """) - } - DataType.FLOAT -> { - asmgen.out(""" - lda #<$sourceName - ldy #>$sourceName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #<${target.asmVarname}+$scaledIdx - ldy #>${target.asmVarname}+$scaledIdx - jsr floats.copy_float - """) - } - else -> throw AssemblyError("weird target variable type ${target.datatype}") + target.array!! + if(target.constArrayIndexValue!=null) { + val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize() + when(target.datatype) { + in ByteDatatypes -> { + asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") } + in WordDatatypes -> { + asmgen.out(""" + lda $sourceName + sta ${target.asmVarname}+$scaledIdx + lda $sourceName+1 + sta ${target.asmVarname}+$scaledIdx+1 + """) + } + DataType.FLOAT -> { + asmgen.out(""" + lda #<$sourceName + ldy #>$sourceName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #<${target.asmVarname}+$scaledIdx + ldy #>${target.asmVarname}+$scaledIdx + jsr floats.copy_float + """) + } + else -> throw AssemblyError("weird target variable type ${target.datatype}") } - index is IdentifierReference -> { - when(target.datatype) { - DataType.UBYTE, DataType.BYTE -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) - asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") - } - DataType.UWORD, DataType.WORD -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) - asmgen.out(""" - lda $sourceName - sta ${target.asmVarname},y - lda $sourceName+1 - sta ${target.asmVarname}+1,y - """) - } - DataType.FLOAT -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A) - asmgen.out(""" - ldy #<$sourceName - sty P8ZP_SCRATCH_W1 - ldy #>$sourceName - sty P8ZP_SCRATCH_W1+1 - ldy #>${target.asmVarname} - clc - adc #<${target.asmVarname} - bcc + - iny + } + else + { + when(target.datatype) { + DataType.UBYTE, DataType.BYTE -> { + asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) + asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") + } + DataType.UWORD, DataType.WORD -> { + asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) + asmgen.out(""" + lda $sourceName + sta ${target.asmVarname},y + lda $sourceName+1 + sta ${target.asmVarname}+1,y + """) + } + DataType.FLOAT -> { + asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A) + asmgen.out(""" + ldy #<$sourceName + sty P8ZP_SCRATCH_W1 + ldy #>$sourceName + sty P8ZP_SCRATCH_W1+1 + ldy #>${target.asmVarname} + clc + adc #<${target.asmVarname} + bcc + + iny + jsr floats.copy_float""") - } - else -> throw AssemblyError("weird dt") } - } - else -> { - asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda $sourceName+1 | sta P8ESTACK_HI,x | dex") - asmgen.translateExpression(index) - asmgen.out(" inx | lda P8ESTACK_LO,x") - popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname) + else -> throw AssemblyError("weird dt") } } } @@ -553,9 +539,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen // } else if(target.array!!.arrayspec.index is IdentifierReference) { // TODO("array[var] ${target.constArrayIndexValue}") // } - val index = target.array!!.arrayspec.index asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.push_float") - asmgen.translateExpression(index) + target.array!! + target.array.indexer.indexNum?.let { asmgen.translateExpression(it) } + target.array.indexer.indexVar?.let { asmgen.translateExpression(it) } asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname} | jsr floats.pop_float_to_indexed_var") } TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte") @@ -576,22 +563,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!) } TargetStorageKind.ARRAY -> { - val index = target.array!!.arrayspec.index - when { - target.constArrayIndexValue!=null -> { - val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize() - asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") - } - index is IdentifierReference -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) - asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") - } - else -> { - asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex") - asmgen.translateExpression(index) - asmgen.out(" inx | lda P8ESTACK_LO,x") - popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname) - } + if (target.constArrayIndexValue!=null) { + val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize() + asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") + } + else { + asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y) + asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") } } TargetStorageKind.REGISTER -> { @@ -686,22 +664,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen """) } TargetStorageKind.ARRAY -> { - val index = wordtarget.array!!.arrayspec.index - when { - wordtarget.constArrayIndexValue!=null -> { - val scaledIdx = wordtarget.constArrayIndexValue!! * 2 - asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1") - } - index is IdentifierReference -> { - asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y) - asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y") - } - else -> { - asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex") - asmgen.translateExpression(index) - asmgen.out(" inx | lda P8ESTACK_LO,x") - popAndWriteArrayvalueWithUnscaledIndexA(wordtarget.datatype, wordtarget.asmVarname) - } + if (wordtarget.constArrayIndexValue!=null) { + val scaledIdx = wordtarget.constArrayIndexValue!! * 2 + asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1") + } + else { + asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y) + asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y") } } TargetStorageKind.REGISTER -> { @@ -734,40 +703,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen storeRegisterInMemoryAddress(register, target.memory!!) } TargetStorageKind.ARRAY -> { - val index = target.array!!.arrayspec.index - when (index) { - is NumericLiteralValue -> { - val memindex = index.number.toInt() + when { + target.constArrayIndexValue!=null -> { when (register) { - CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+$memindex") - CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+$memindex") - CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+$memindex") + CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}") + CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}") + CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}") } } - is IdentifierReference -> { + else -> { when (register) { CpuRegister.A -> {} CpuRegister.X -> asmgen.out(" txa") CpuRegister.Y -> asmgen.out(" tya") } - asmgen.out(" ldy ${asmgen.asmVariableName(index)} | sta ${target.asmVarname},y") - } - else -> { - asmgen.saveRegister(register, false, target.scope) - asmgen.translateExpression(index) - asmgen.restoreRegister(register, false) - when (register) { - CpuRegister.A -> asmgen.out(" sta P8ZP_SCRATCH_B1") - CpuRegister.X -> asmgen.out(" stx P8ZP_SCRATCH_B1") - CpuRegister.Y -> asmgen.out(" sty P8ZP_SCRATCH_B1") - } - asmgen.out(""" - inx - lda P8ESTACK_LO,x - tay - lda P8ZP_SCRATCH_B1 - sta ${target.asmVarname},y - """) + asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y") } } } @@ -886,8 +836,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen // } else if(target.array!!.arrayspec.index is IdentifierReference) { // TODO("array[var] ${target.constArrayIndexValue}") // } - val index = target.array!!.arrayspec.index - asmgen.translateExpression(index) + target.array!! + target.array.indexer.indexNum?.let { asmgen.translateExpression(it) } + target.array.indexer.indexVar?.let { asmgen.translateExpression(it) } asmgen.out(""" inx lda P8ESTACK_LO,x @@ -927,25 +878,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!) } TargetStorageKind.ARRAY -> { - val index = target.array!!.arrayspec.index - when { - target.constArrayIndexValue!=null -> { - val indexValue = target.constArrayIndexValue!! - asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue") - } - index is IdentifierReference -> { - asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y) - asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y") - } - else -> { - asmgen.translateExpression(index) - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - lda #${byte.toHex()} - sta ${target.asmVarname},y - """) - } + if (target.constArrayIndexValue!=null) { + val indexValue = target.constArrayIndexValue!! + asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue") + } + else { + asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) + asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y") } } TargetStorageKind.REGISTER -> when(target.register!!) { @@ -995,9 +934,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen // } else if(target.array!!.arrayspec.index is IdentifierReference) { // TODO("array[var] ${target.constArrayIndexValue}") // } - val index = target.array!!.arrayspec.index - if (index is NumericLiteralValue) { - val indexValue = index.number.toInt() * DataType.FLOAT.memorySize() + if (target.array!!.indexer.indexNum!=null) { + val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize() asmgen.out(""" lda #0 sta ${target.asmVarname}+$indexValue @@ -1007,7 +945,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen sta ${target.asmVarname}+$indexValue+4 """) } else { - asmgen.translateExpression(index) + asmgen.translateExpression(target.array.indexer.indexVar!!) asmgen.out(""" lda #<${target.asmVarname} sta P8ZP_SCRATCH_W1 @@ -1049,10 +987,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen // } else if(target.array!!.arrayspec.index is IdentifierReference) { // TODO("array[var] ${target.constArrayIndexValue}") // } - val index = target.array!!.arrayspec.index val arrayVarName = target.asmVarname - if (index is NumericLiteralValue) { - val indexValue = index.number.toInt() * DataType.FLOAT.memorySize() + if (target.array!!.indexer.indexNum!=null) { + val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize() asmgen.out(""" lda $constFloat sta $arrayVarName+$indexValue @@ -1066,7 +1003,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen sta $arrayVarName+$indexValue+4 """) } else { - asmgen.translateExpression(index) + asmgen.translateExpression(target.array.indexer.indexVar!!) asmgen.out(""" lda #<${constFloat} sta P8ZP_SCRATCH_W1 @@ -1267,24 +1204,4 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } } - - private fun popAndWriteArrayvalueWithUnscaledIndexA(elementDt: DataType, asmArrayvarname: String) { - when (elementDt) { - in ByteDatatypes -> - asmgen.out(" tay | inx | lda P8ESTACK_LO,x | sta $asmArrayvarname,y") - in WordDatatypes -> - asmgen.out(" asl a | tay | inx | lda P8ESTACK_LO,x | sta $asmArrayvarname,y | lda P8ESTACK_HI,x | sta $asmArrayvarname+1,y") - DataType.FLOAT -> - // scaling * 5 is done in the subroutine that's called - asmgen.out(""" - sta P8ESTACK_LO,x - dex - lda #<$asmArrayvarname - ldy #>$asmArrayvarname - jsr floats.pop_float_to_indexed_var - """) - else -> - throw AssemblyError("weird array type") - } - } } diff --git a/compiler/src/prog8/optimizer/BinExprSplitter.kt b/compiler/src/prog8/optimizer/BinExprSplitter.kt index 7bede0e79..ca4135672 100644 --- a/compiler/src/prog8/optimizer/BinExprSplitter.kt +++ b/compiler/src/prog8/optimizer/BinExprSplitter.kt @@ -71,19 +71,10 @@ X = BinExpr X = LeftExpr return noModifications } - private fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean { - return when { - target.identifier!=null -> target.isInRegularRAM(namespace) - target.memoryAddress!=null -> target.isInRegularRAM(namespace) - target.arrayindexed!=null -> { - val index = target.arrayindexed!!.arrayspec.index - if(index is NumericLiteralValue) - target.isInRegularRAM(namespace) - else - false - } - else -> false - } - } + private fun isSimpleTarget(target: AssignTarget, namespace: INameScope) = + if (target.identifier!=null || target.memoryAddress!=null || target.arrayindexed!=null) + target.isInRegularRAM(namespace) + else + false } diff --git a/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt index 4b5e89b3b..ecbf3c984 100644 --- a/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -58,15 +58,16 @@ internal class ConstantIdentifierReplacer(private val program: Program, private override fun before(decl: VarDecl, parent: Node): Iterable { // the initializer value can't refer to the variable itself (recursive definition) // TODO: use call graph for this? - if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) { + if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true) { errors.err("recursive var declaration", decl.position) return noModifications } if(decl.type== VarDeclType.CONST || decl.type== VarDeclType.VAR) { if(decl.isArray){ - if(decl.arraysize==null) { - // for arrays that have no size specifier (or a non-constant one) attempt to deduce the size + val arraysize = decl.arraysize + if(arraysize==null) { + // for arrays that have no size specifier attempt to deduce the size val arrayval = decl.value as? ArrayLiteralValue if(arrayval!=null) { return listOf(IAstModification.SetExpression( @@ -75,14 +76,13 @@ internal class ConstantIdentifierReplacer(private val program: Program, private decl )) } - } - else if(decl.arraysize?.constIndex()==null) { - val size = decl.arraysize!!.index.constValue(program) - if(size!=null) { - return listOf(IAstModification.SetExpression( - { decl.arraysize = ArrayIndex(it, decl.position) }, - size, decl - )) + } else if(arraysize.constIndex()==null) { + // see if we can calculate the size from other fields + val cval = arraysize.indexVar?.constValue(program) ?: arraysize.origExpression?.constValue(program) + if(cval!=null) { + arraysize.indexVar = null + arraysize.origExpression = null + arraysize.indexNum = cval } } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 55d3e31be..ec1fd333b 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO ==== - get rid of all other TODO's in the code ;-) -- only allow array indexing via a number, a variable, or a typecast of one of those (eliminate complex expression calcs for array indexing, force explicit use of an index variable) - implement @stack for asmsub parameters - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_' - option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)